summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cpu/x86/smm/smihandler.c2
-rw-r--r--src/include/cpu/x86/smm.h1
-rw-r--r--src/southbridge/intel/i82801gx/smi.c28
-rw-r--r--src/southbridge/intel/i82801gx/smihandler.c7
4 files changed, 31 insertions, 7 deletions
diff --git a/src/cpu/x86/smm/smihandler.c b/src/cpu/x86/smm/smihandler.c
index bdaedf8dbc..d44a3eae1b 100644
--- a/src/cpu/x86/smm/smihandler.c
+++ b/src/cpu/x86/smm/smihandler.c
@@ -46,7 +46,7 @@ static int smi_obtain_lock(void)
return (ret == SMI_UNLOCKED);
}
-static void smi_release_lock(void)
+void smi_release_lock(void)
{
asm volatile (
"movb %1, %%al\n"
diff --git a/src/include/cpu/x86/smm.h b/src/include/cpu/x86/smm.h
index 5605453eab..07a9cae30e 100644
--- a/src/include/cpu/x86/smm.h
+++ b/src/include/cpu/x86/smm.h
@@ -272,3 +272,4 @@ void __attribute__((weak)) southbridge_smi_handler(unsigned int node, smm_state_
void __attribute__((weak)) mainboard_smi_gpi(u16 gpi_sts);
int __attribute__((weak)) mainboard_apm_cnt(u8 data);
+void smi_release_lock(void);
diff --git a/src/southbridge/intel/i82801gx/smi.c b/src/southbridge/intel/i82801gx/smi.c
index 95ec1129a3..5be24049be 100644
--- a/src/southbridge/intel/i82801gx/smi.c
+++ b/src/southbridge/intel/i82801gx/smi.c
@@ -320,6 +320,16 @@ static void smm_relocate(void)
static int smm_handler_copied = 0;
+static int is_wakeup(void)
+{
+ device_t dev0 = dev_find_slot(0, PCI_DEVFN(0,0));
+
+ if (!dev0)
+ return 0;
+
+ return pci_read_config32(dev0, 0xdc) == SKPAD_ACPI_S3_MAGIC;
+}
+
static void smm_install(void)
{
/* The first CPU running this gets to copy the SMM handler. But not all
@@ -329,13 +339,19 @@ static void smm_install(void)
return;
smm_handler_copied = 1;
- /* enable the SMM memory window */
- pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
- D_OPEN | G_SMRAME | C_BASE_SEG);
- /* copy the real SMM handler */
- memcpy((void *)0xa0000, &_binary_smm_start, (size_t)&_binary_smm_size);
- wbinvd();
+ /* if we're resuming from S3, the SMM code is already in place,
+ * so don't copy it again to keep the current SMM state */
+
+ if (!is_wakeup()) {
+ /* enable the SMM memory window */
+ pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
+ D_OPEN | G_SMRAME | C_BASE_SEG);
+
+ /* copy the real SMM handler */
+ memcpy((void *)0xa0000, &_binary_smm_start, (size_t)&_binary_smm_size);
+ wbinvd();
+ }
/* close the SMM memory window and enable normal SMM */
pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
diff --git a/src/southbridge/intel/i82801gx/smihandler.c b/src/southbridge/intel/i82801gx/smihandler.c
index 9befbf9ac1..434bfebc18 100644
--- a/src/southbridge/intel/i82801gx/smihandler.c
+++ b/src/southbridge/intel/i82801gx/smihandler.c
@@ -328,6 +328,13 @@ static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *stat
default: printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n"); break;
}
+ /* Unlock the SMI semaphore. We're currently in SMM, and the semaphore
+ * will never be unlocked because the next outl will switch off the CPU.
+ * This might open a small race between the smi_release_lock() and the outl()
+ * for other SMI handlers. Not sure if this could cause trouble. */
+ if (slp_typ == 5)
+ smi_release_lock();
+
/* Write back to the SLP register to cause the originally intended
* event again. We need to set BIT13 (SLP_EN) though to make the
* sleep happen.