This series converts the SMM handler to C. It is based on top of Paolo's SMM patch series, which I've included here as reference.
-Kevin
Kevin O'Connor (2): smm: Replace SMI assembler code with C code. smm: Use a C struct to define the layout of the SMM area.
Paolo Bonzini (3): piix: add and use dev-piix.h smm: complete SMM setup smm: unify SMM handlers
src/config.h | 6 +-- src/fw/acpi.c | 18 +++---- src/fw/dev-piix.h | 29 +++++++++++ src/fw/dev-q35.h | 3 ++ src/fw/pciinit.c | 13 ++--- src/fw/shadow.c | 3 +- src/fw/smm.c | 141 +++++++++++++++++++++++++++++++++--------------------- src/romlayout.S | 22 +++++++-- 8 files changed, 154 insertions(+), 81 deletions(-) create mode 100644 src/fw/dev-piix.h
Move all definitions for PIIX registers to a single header file, like there is one already for Q35, and make the naming more consistent.
Signed-off-by: Paolo Bonzini pbonzini@redhat.com --- src/fw/acpi.c | 18 ++++++------------ src/fw/dev-piix.h | 27 +++++++++++++++++++++++++++ src/fw/pciinit.c | 13 +++++++------ src/fw/shadow.c | 3 +-- src/fw/smm.c | 9 +++------ 5 files changed, 44 insertions(+), 26 deletions(-) create mode 100644 src/fw/dev-piix.h
diff --git a/src/fw/acpi.c b/src/fw/acpi.c index 733ca4d..47e4c07 100644 --- a/src/fw/acpi.c +++ b/src/fw/acpi.c @@ -9,6 +9,7 @@ #include "byteorder.h" // cpu_to_le16 #include "config.h" // CONFIG_* #include "dev-q35.h" +#include "dev-piix.h" #include "hw/pci.h" // pci_find_init_device #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL #include "hw/pci_regs.h" // PCI_INTERRUPT_LINE @@ -38,31 +39,24 @@ build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev) h->checksum -= checksum(h, len); }
-#define PIIX4_ACPI_ENABLE 0xf1 -#define PIIX4_ACPI_DISABLE 0xf0 -#define PIIX4_GPE0_BLK 0xafe0 -#define PIIX4_GPE0_BLK_LEN 4 - -#define PIIX4_PM_INTRRUPT 9 // irq 9 - static void piix4_fadt_setup(struct pci_device *pci, void *arg) { struct fadt_descriptor_rev1 *fadt = arg;
fadt->model = 1; fadt->reserved1 = 0; - fadt->sci_int = cpu_to_le16(PIIX4_PM_INTRRUPT); + fadt->sci_int = cpu_to_le16(PIIX_PM_INTRRUPT); fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); - fadt->acpi_enable = PIIX4_ACPI_ENABLE; - fadt->acpi_disable = PIIX4_ACPI_DISABLE; + fadt->acpi_enable = PIIX_ACPI_ENABLE; + fadt->acpi_disable = PIIX_ACPI_DISABLE; fadt->pm1a_evt_blk = cpu_to_le32(acpi_pm_base); fadt->pm1a_cnt_blk = cpu_to_le32(acpi_pm_base + 0x04); fadt->pm_tmr_blk = cpu_to_le32(acpi_pm_base + 0x08); - fadt->gpe0_blk = cpu_to_le32(PIIX4_GPE0_BLK); + fadt->gpe0_blk = cpu_to_le32(PIIX_GPE0_BLK); fadt->pm1_evt_len = 4; fadt->pm1_cnt_len = 2; fadt->pm_tmr_len = 4; - fadt->gpe0_blk_len = PIIX4_GPE0_BLK_LEN; + fadt->gpe0_blk_len = PIIX_GPE0_BLK_LEN; fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported /* WBINVD + PROC_C1 + SLP_BUTTON + RTC_S4 + USE_PLATFORM_CLOCK */ diff --git a/src/fw/dev-piix.h b/src/fw/dev-piix.h new file mode 100644 index 0000000..c6dce03 --- /dev/null +++ b/src/fw/dev-piix.h @@ -0,0 +1,27 @@ +#ifndef __DEV_PIIX_H +#define __DEV_PIIX_H + +#define I440FX_PAM0 0x59 +#define I440FX_SMRAM 0x72 + +#define PIIX_PMBASE 0x40 +#define PIIX_PMREGMISC 0x80 +#define PIIX_SMBHSTBASE 0x90 +#define PIIX_SMBHSTCFG 0xd2 +#define PIIX_DEVACTB 0x58 +#define PIIX_DEVACTB_APMC_EN (1 << 25) + +#define PIIX_PORT_ELCR1 0x4d0 +#define PIIX_PORT_ELCR2 0x4d1 + +/* ICH9 PM I/O registers */ +#define PIIX_GPE0_BLK 0xafe0 +#define PIIX_GPE0_BLK_LEN 4 + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define PIIX_ACPI_ENABLE 0xf1 +#define PIIX_ACPI_DISABLE 0xf0 + +#define PIIX_PM_INTRRUPT 9 // irq 9 + +#endif // dev-piix.h diff --git a/src/fw/pciinit.c b/src/fw/pciinit.c index 2e6382f..0ad548f 100644 --- a/src/fw/pciinit.c +++ b/src/fw/pciinit.c @@ -8,6 +8,7 @@ #include "byteorder.h" // le64_to_cpu #include "config.h" // CONFIG_* #include "dev-q35.h" // Q35_HOST_BRIDGE_PCIEXBAR_ADDR +#include "dev-piix.h" // PIIX_* #include "hw/ata.h" // PORT_ATA1_CMD_BASE #include "hw/pci.h" // pci_config_readl #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL @@ -152,8 +153,8 @@ static void piix_isa_bridge_setup(struct pci_device *pci, void *arg) /* activate irq remapping in PIIX */ pci_config_writeb(pci->bdf, 0x60 + i, irq); } - outb(elcr[0], 0x4d0); - outb(elcr[1], 0x4d1); + outb(elcr[0], PIIX_PORT_ELCR1); + outb(elcr[1], PIIX_PORT_ELCR2); dprintf(1, "PIIX3/PIIX4 init: elcr=%02x %02x\n", elcr[0], elcr[1]); }
@@ -229,10 +230,10 @@ static void piix4_pm_config_setup(u16 bdf) // acpi sci is hardwired to 9 pci_config_writeb(bdf, PCI_INTERRUPT_LINE, 9);
- pci_config_writel(bdf, 0x40, acpi_pm_base | 1); - pci_config_writeb(bdf, 0x80, 0x01); /* enable PM io space */ - pci_config_writel(bdf, 0x90, (acpi_pm_base + 0x100) | 1); - pci_config_writeb(bdf, 0xd2, 0x09); /* enable SMBus io space */ + pci_config_writel(bdf, PIIX_PMBASE, acpi_pm_base | 1); + pci_config_writeb(bdf, PIIX_PMREGMISC, 0x01); /* enable PM io space */ + pci_config_writel(bdf, PIIX_SMBHSTBASE, (acpi_pm_base + 0x100) | 1); + pci_config_writeb(bdf, PIIX_SMBHSTCFG, 0x09); /* enable SMBus io space */ }
static int PiixPmBDF = -1; diff --git a/src/fw/shadow.c b/src/fw/shadow.c index 82d6753..4f00006 100644 --- a/src/fw/shadow.c +++ b/src/fw/shadow.c @@ -7,6 +7,7 @@
#include "config.h" // CONFIG_* #include "dev-q35.h" // PCI_VENDOR_ID_INTEL +#include "dev-piix.h" // I440FX_PAM0 #include "hw/pci.h" // pci_config_writeb #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL #include "hw/pci_regs.h" // PCI_VENDOR_ID @@ -20,8 +21,6 @@ // On the emulators, the bios at 0xf0000 is also at 0xffff0000 #define BIOS_SRC_OFFSET 0xfff00000
-#define I440FX_PAM0 0x59 - // Enable shadowing and copy bios. static void __make_bios_writable_intel(u16 bdf, u32 pam0) diff --git a/src/fw/smm.c b/src/fw/smm.c index 0f59f20..20bf631 100644 --- a/src/fw/smm.c +++ b/src/fw/smm.c @@ -7,6 +7,7 @@
#include "config.h" // CONFIG_* #include "dev-q35.h" +#include "dev-piix.h" #include "hw/pci.h" // pci_config_writel #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL #include "hw/pci_regs.h" // PCI_DEVICE_ID @@ -86,16 +87,12 @@ smm_relocate_and_restore(void) wbinvd(); }
-#define I440FX_SMRAM 0x72 -#define PIIX_DEVACTB 0x58 -#define PIIX_APMC_EN (1 << 25) - // This code is hardcoded for PIIX4 Power Management device. static void piix4_apmc_smm_setup(int isabdf, int i440_bdf) { /* check if SMM init is already done */ u32 value = pci_config_readl(isabdf, PIIX_DEVACTB); - if (value & PIIX_APMC_EN) + if (value & PIIX_DEVACTB_APMC_EN) return;
/* enable the SMM memory window */ @@ -104,7 +101,7 @@ static void piix4_apmc_smm_setup(int isabdf, int i440_bdf) smm_save_and_copy();
/* enable SMI generation when writing to the APMC register */ - pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_APMC_EN); + pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_DEVACTB_APMC_EN);
smm_relocate_and_restore();
SMI generation requires two bits to be set in PIIX4, one for APMC interrupts specifically and a general one.
For Q35 it is the same, plus it is a good thing to lock SMIs after enabling them.
Signed-off-by: Paolo Bonzini pbonzini@redhat.com --- src/fw/dev-piix.h | 2 ++ src/fw/dev-q35.h | 3 +++ src/fw/smm.c | 12 +++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/fw/dev-piix.h b/src/fw/dev-piix.h index c6dce03..c389f17 100644 --- a/src/fw/dev-piix.h +++ b/src/fw/dev-piix.h @@ -17,6 +17,8 @@ /* ICH9 PM I/O registers */ #define PIIX_GPE0_BLK 0xafe0 #define PIIX_GPE0_BLK_LEN 4 +#define PIIX_PMIO_GLBCTL 0x28 +#define PIIX_PMIO_GLBCTL_SMI_EN 1
/* FADT ACPI_ENABLE/ACPI_DISABLE */ #define PIIX_ACPI_ENABLE 0xf1 diff --git a/src/fw/dev-q35.h b/src/fw/dev-q35.h index 6ae039f..c6f8bd9 100644 --- a/src/fw/dev-q35.h +++ b/src/fw/dev-q35.h @@ -23,6 +23,8 @@ #define ICH9_LPC_PIRQA_ROUT 0x60 #define ICH9_LPC_PIRQE_ROUT 0x68 #define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 +#define ICH9_LPC_GEN_PMCON_1 0xa0 +#define ICH9_LPC_GEN_PMCON_1_SMI_LOCK (1 << 4) #define ICH9_LPC_PORT_ELCR1 0x4d0 #define ICH9_LPC_PORT_ELCR2 0x4d1 #define PCI_DEVICE_ID_INTEL_ICH9_SMBUS 0x2930 @@ -38,6 +40,7 @@ #define ICH9_PMIO_GPE0_BLK_LEN 0x10 #define ICH9_PMIO_SMI_EN 0x30 #define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) +#define ICH9_PMIO_SMI_EN_GLB_SMI_EN (1 << 0)
/* FADT ACPI_ENABLE/ACPI_DISABLE */ #define ICH9_APM_ACPI_ENABLE 0x2 diff --git a/src/fw/smm.c b/src/fw/smm.c index 20bf631..eccef88 100644 --- a/src/fw/smm.c +++ b/src/fw/smm.c @@ -103,6 +103,11 @@ static void piix4_apmc_smm_setup(int isabdf, int i440_bdf) /* enable SMI generation when writing to the APMC register */ pci_config_writel(isabdf, PIIX_DEVACTB, value | PIIX_DEVACTB_APMC_EN);
+ /* enable SMI generation */ + value = inl(acpi_pm_base + PIIX_PMIO_GLBCTL); + outl(acpi_pm_base + PIIX_PMIO_GLBCTL, + value | PIIX_PMIO_GLBCTL_SMI_EN); + smm_relocate_and_restore();
/* close the SMM memory window and enable normal SMM */ @@ -123,9 +128,14 @@ void ich9_lpc_apmc_smm_setup(int isabdf, int mch_bdf) smm_save_and_copy();
/* enable SMI generation when writing to the APMC register */ - outl(value | ICH9_PMIO_SMI_EN_APMC_EN, + outl(value | ICH9_PMIO_SMI_EN_APMC_EN | ICH9_PMIO_SMI_EN_GLB_SMI_EN, acpi_pm_base + ICH9_PMIO_SMI_EN);
+ /* lock SMI generation */ + value = pci_config_readw(isabdf, ICH9_LPC_GEN_PMCON_1); + pci_config_writel(isabdf, ICH9_LPC_GEN_PMCON_1, + value | ICH9_LPC_GEN_PMCON_1_SMI_LOCK); + smm_relocate_and_restore();
/* close the SMM memory window and enable normal SMM */
The next patch will add shared code between the initial handler for SMBASE relocation and the actual SMI handler. Combine the code of the two handlers for simplicity.
Signed-off-by: Paolo Bonzini pbonzini@redhat.com --- src/fw/smm.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-)
diff --git a/src/fw/smm.c b/src/fw/smm.c index eccef88..f486c78 100644 --- a/src/fw/smm.c +++ b/src/fw/smm.c @@ -17,13 +17,17 @@ #include "util.h" // smm_setup #include "x86.h" // wbinvd
-extern u8 smm_relocation_start, smm_relocation_end; +extern u8 smm_code_start, smm_code_end; + ASM32FLAT( - ".global smm_relocation_start, smm_relocation_end\n" + ".global smm_code_start, smm_code_end\n" " .code16gcc\n" + "smm_code_start:\n" + " mov %cs, %ax\n" + " cmp $0xa000, %ax\n" + " je smm_exit\n"
/* code to relocate SMBASE to 0xa0000 */ - "smm_relocation_start:\n" " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7efc, %ebx\n" " addr32 movb (%ebx), %al\n" /* revision ID to see if x86_64 or x86 */ " cmpb $0x64, %al\n" @@ -39,16 +43,7 @@ ASM32FLAT( " movb $0x00, %al\n" " movw $" __stringify(PORT_SMI_STATUS) ", %dx\n" " outb %al, %dx\n" - " rsm\n" - "smm_relocation_end:\n" - " .code32\n" - ); - -extern u8 smm_code_start, smm_code_end; -ASM32FLAT( - ".global smm_code_start, smm_code_end\n" - " .code16gcc\n" - "smm_code_start:\n" + "smm_exit:\n" " rsm\n" "smm_code_end:\n" " .code32\n" @@ -60,9 +55,9 @@ smm_save_and_copy(void) /* save original memory content */ memcpy((void *)BUILD_SMM_ADDR, (void *)BUILD_SMM_INIT_ADDR, BUILD_SMM_SIZE);
- /* copy the SMM relocation code */ - memcpy((void *)BUILD_SMM_INIT_ADDR, &smm_relocation_start, - &smm_relocation_end - &smm_relocation_start); + /* copy the SMM code, which will relocate itself on the first execution */ + memcpy((void *)BUILD_SMM_INIT_ADDR, &smm_code_start, + &smm_code_end - &smm_code_start); }
static void
Convert the SMI handler from assembly to C. This makes the handler easier to understand and enhance.
The new handler will use references to the reserved memory at 0xf0000-0x100000. If the physical memory in that range is modified at runtime, then the SMI handler will cease to function properly (and may allow unintended code to run in SMM mode). However, that area is marked as reserved and is normally made read-only at runtime, so there is little risk in relying on it.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/config.h | 1 + src/fw/smm.c | 69 +++++++++++++++++++++++++-------------------------------- src/romlayout.S | 22 ++++++++++++++---- 3 files changed, 49 insertions(+), 43 deletions(-)
diff --git a/src/config.h b/src/config.h index d705615..269a022 100644 --- a/src/config.h +++ b/src/config.h @@ -95,6 +95,7 @@ #define DEBUG_ISR_76 10 #define DEBUG_ISR_hwpic1 5 #define DEBUG_ISR_hwpic2 5 +#define DEBUG_HDL_smi 9 #define DEBUG_HDL_pnp 1 #define DEBUG_HDL_pmm 1 #define DEBUG_HDL_pcibios 9 diff --git a/src/fw/smm.c b/src/fw/smm.c index f486c78..3d0caa1 100644 --- a/src/fw/smm.c +++ b/src/fw/smm.c @@ -1,6 +1,6 @@ // System Management Mode support (on emulators) // -// Copyright (C) 2008 Kevin O'Connor kevin@koconnor.net +// Copyright (C) 2008-2014 Kevin O'Connor kevin@koconnor.net // Copyright (C) 2006 Fabrice Bellard // // This file may be distributed under the terms of the GNU LGPLv3 license. @@ -17,47 +17,39 @@ #include "util.h" // smm_setup #include "x86.h" // wbinvd
-extern u8 smm_code_start, smm_code_end; - -ASM32FLAT( - ".global smm_code_start, smm_code_end\n" - " .code16gcc\n" - "smm_code_start:\n" - " mov %cs, %ax\n" - " cmp $0xa000, %ax\n" - " je smm_exit\n" - - /* code to relocate SMBASE to 0xa0000 */ - " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7efc, %ebx\n" - " addr32 movb (%ebx), %al\n" /* revision ID to see if x86_64 or x86 */ - " cmpb $0x64, %al\n" - " je 1f\n" - " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7ef8, %ebx\n" - " jmp 2f\n" - "1:\n" - " movl $" __stringify(BUILD_SMM_INIT_ADDR) " + 0x7f00, %ebx\n" - "2:\n" - " movl $" __stringify(BUILD_SMM_ADDR) " - 0x8000, %eax\n" - " addr32 movl %eax, (%ebx)\n" - /* indicate to the BIOS that the SMM code was executed */ - " movb $0x00, %al\n" - " movw $" __stringify(PORT_SMI_STATUS) ", %dx\n" - " outb %al, %dx\n" - "smm_exit:\n" - " rsm\n" - "smm_code_end:\n" - " .code32\n" - ); +void VISIBLE32FLAT +handle_smi(u16 cs) +{ + u8 cmd = inb(PORT_SMI_CMD); + dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x cs=%x\n", cmd, cs); + + void *smbase = MAKE_FLATPTR(cs, 0) + 0x8000; + if (smbase == (void*)BUILD_SMM_INIT_ADDR) { + // relocate SMBASE to 0xa0000 + u8 *smrev = smbase + 0x7efc; + u32 *newbase = smbase + 0x7ef8; + if (*smrev == 0x64) + newbase = smbase + 0x7f00; + *newbase = BUILD_SMM_ADDR - 0x8000; + // indicate to smm_relocate_and_restore() that the SMM code was executed + outb(0x00, PORT_SMI_STATUS); + return; + } +} + +extern void entry_smi(void); +// movw %cs, %ax; ljmpw $SEG_BIOS, $(entry_smi - BUILD_BIOS_ADDR) +#define SMI_INSN (0xeac88c | ((u64)SEG_BIOS<<40) \ + | ((u64)((u32)entry_smi - BUILD_BIOS_ADDR) << 24))
static void smm_save_and_copy(void) { - /* save original memory content */ + // save original memory content memcpy((void *)BUILD_SMM_ADDR, (void *)BUILD_SMM_INIT_ADDR, BUILD_SMM_SIZE);
- /* copy the SMM code, which will relocate itself on the first execution */ - memcpy((void *)BUILD_SMM_INIT_ADDR, &smm_code_start, - &smm_code_end - &smm_code_start); + // Setup code entry point. + *(u64*)BUILD_SMM_INIT_ADDR = SMI_INSN; }
static void @@ -76,9 +68,8 @@ smm_relocate_and_restore(void) /* restore original memory content */ memcpy((void *)BUILD_SMM_INIT_ADDR, (void *)BUILD_SMM_ADDR, BUILD_SMM_SIZE);
- /* copy the SMM code */ - memcpy((void *)BUILD_SMM_ADDR, &smm_code_start - , &smm_code_end - &smm_code_start); + // Setup code entry point. + *(u64*)BUILD_SMM_ADDR = SMI_INSN; wbinvd(); }
diff --git a/src/romlayout.S b/src/romlayout.S index 57e8bcc..5b48062 100644 --- a/src/romlayout.S +++ b/src/romlayout.S @@ -22,6 +22,9 @@ // Clobbers: ecx, flags, segment registers, cr0, idt/gdt DECLFUNC transition32 .code16gcc +transition32_for_smi: + movl %eax, %ecx + jmp 1f transition32: movl %eax, %ecx
@@ -40,7 +43,7 @@ transition32: outb %al, $PORT_A20
// Set segment descriptors - lidtw %cs:pmode_IDT_info +1: lidtw %cs:pmode_IDT_info lgdtw %cs:rombios32_gdt_48
// Enable protected mode @@ -49,12 +52,11 @@ transition32: movl %eax, %cr0
// start 32bit protected mode code - ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 1f) + ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 2f)
.code32 -1: // init data segments - movl $SEG32_MODE32_DS, %eax +2: movl $SEG32_MODE32_DS, %eax movw %ax, %ds movw %ax, %es movw %ax, %ss @@ -260,6 +262,18 @@ __farcall16: * Misc. entry points. ****************************************************************/
+// Entry point for QEMU smi interrupts. + DECLFUNC entry_smi +entry_smi: + // Transition to 32bit mode. + movl $1f + BUILD_BIOS_ADDR, %edx + jmp transition32_for_smi + .code32 +1: movl $BUILD_SMM_ADDR, %esp + calll _cfunc32flat_handle_smi - BUILD_BIOS_ADDR + rsm + .code16gcc + // Resume (and reboot) entry point - called from entry_post DECLFUNC entry_resume entry_resume:
Describe the memory layout using a struct instead of hard coded offsets.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/config.h | 5 ++--- src/fw/smm.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++----------- src/romlayout.S | 2 +- 3 files changed, 55 insertions(+), 16 deletions(-)
diff --git a/src/config.h b/src/config.h index 269a022..6f1a5b9 100644 --- a/src/config.h +++ b/src/config.h @@ -39,9 +39,8 @@ #define BUILD_EXTRA_STACK_SIZE 0x800 // 32KB for shadow ram copying (works around emulator deficiencies) #define BUILD_BIOS_TMP_ADDR 0x30000 -#define BUILD_SMM_INIT_ADDR 0x38000 -#define BUILD_SMM_ADDR 0xa8000 -#define BUILD_SMM_SIZE 0x8000 +#define BUILD_SMM_INIT_ADDR 0x30000 +#define BUILD_SMM_ADDR 0xa0000
#define BUILD_PCIMEM_START 0xe0000000 #define BUILD_PCIMEM_END 0xfec00000 /* IOAPIC is mapped at */ diff --git a/src/fw/smm.c b/src/fw/smm.c index 3d0caa1..c2ce5c3 100644 --- a/src/fw/smm.c +++ b/src/fw/smm.c @@ -17,20 +17,54 @@ #include "util.h" // smm_setup #include "x86.h" // wbinvd
+#define SMM_REV_I32 0x00020000 +#define SMM_REV_I64 0x00020064 + +struct smm_state { + union { + struct { + u8 pad_000[0xf8]; + u32 smm_base; + u32 smm_rev; + u8 pad_100[0xd0]; + u32 eax, ecx, edx, ebx, esp, ebp, esi, edi, eip, eflags; + u8 pad_1f8[0x08]; + } i32; + struct { + u8 pad_000[0xfc]; + u32 smm_rev; + u32 smm_base; + u8 pad_104[0x6c]; + u64 rflags, rip, r15, r14, r13, r12, r11, r10, r9, r8; + u64 rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax; + } i64; + }; +}; + +struct smm_layout { + u8 stack[0x8000]; + u64 codeentry; + u8 pad_8008[0x7df8]; + struct smm_state cpu; +}; + void VISIBLE32FLAT handle_smi(u16 cs) { u8 cmd = inb(PORT_SMI_CMD); - dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x cs=%x\n", cmd, cs); + struct smm_layout *smm = MAKE_FLATPTR(cs, 0); + dprintf(DEBUG_HDL_smi, "handle_smi cmd=%x smbase=%p\n", cmd, smm);
- void *smbase = MAKE_FLATPTR(cs, 0) + 0x8000; - if (smbase == (void*)BUILD_SMM_INIT_ADDR) { + if (smm == (void*)BUILD_SMM_INIT_ADDR) { // relocate SMBASE to 0xa0000 - u8 *smrev = smbase + 0x7efc; - u32 *newbase = smbase + 0x7ef8; - if (*smrev == 0x64) - newbase = smbase + 0x7f00; - *newbase = BUILD_SMM_ADDR - 0x8000; + if (smm->cpu.i32.smm_rev == SMM_REV_I32) { + smm->cpu.i32.smm_base = BUILD_SMM_ADDR; + } else if (smm->cpu.i64.smm_rev == SMM_REV_I64) { + smm->cpu.i64.smm_base = BUILD_SMM_ADDR; + } else { + warn_internalerror(); + return; + } // indicate to smm_relocate_and_restore() that the SMM code was executed outb(0x00, PORT_SMI_STATUS); return; @@ -46,10 +80,13 @@ static void smm_save_and_copy(void) { // save original memory content - memcpy((void *)BUILD_SMM_ADDR, (void *)BUILD_SMM_INIT_ADDR, BUILD_SMM_SIZE); + struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; + struct smm_layout *smm = (void*)BUILD_SMM_ADDR; + memcpy(&smm->cpu, &initsmm->cpu, sizeof(smm->cpu)); + memcpy(&smm->codeentry, &initsmm->codeentry, sizeof(smm->codeentry));
// Setup code entry point. - *(u64*)BUILD_SMM_INIT_ADDR = SMI_INSN; + initsmm->codeentry = SMI_INSN; }
static void @@ -66,10 +103,13 @@ smm_relocate_and_restore(void) ;
/* restore original memory content */ - memcpy((void *)BUILD_SMM_INIT_ADDR, (void *)BUILD_SMM_ADDR, BUILD_SMM_SIZE); + struct smm_layout *initsmm = (void*)BUILD_SMM_INIT_ADDR; + struct smm_layout *smm = (void*)BUILD_SMM_ADDR; + memcpy(&initsmm->cpu, &smm->cpu, sizeof(initsmm->cpu)); + memcpy(&initsmm->codeentry, &smm->codeentry, sizeof(initsmm->codeentry));
// Setup code entry point. - *(u64*)BUILD_SMM_ADDR = SMI_INSN; + smm->codeentry = SMI_INSN; wbinvd(); }
diff --git a/src/romlayout.S b/src/romlayout.S index 5b48062..0d6af39 100644 --- a/src/romlayout.S +++ b/src/romlayout.S @@ -269,7 +269,7 @@ entry_smi: movl $1f + BUILD_BIOS_ADDR, %edx jmp transition32_for_smi .code32 -1: movl $BUILD_SMM_ADDR, %esp +1: movl $BUILD_SMM_ADDR + 0x8000, %esp calll _cfunc32flat_handle_smi - BUILD_BIOS_ADDR rsm .code16gcc