diff options
-rw-r--r-- | src/cpu/x86/smm/smihandler.c | 2 | ||||
-rw-r--r-- | src/include/cpu/x86/smm.h | 1 | ||||
-rw-r--r-- | src/southbridge/intel/i82801gx/smi.c | 28 | ||||
-rw-r--r-- | src/southbridge/intel/i82801gx/smihandler.c | 7 |
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. |