diff options
author | Stefan Reinauer <reinauer@chromium.org> | 2012-04-02 13:24:04 -0700 |
---|---|---|
committer | Peter Stuge <peter@stuge.se> | 2012-04-04 04:49:09 +0200 |
commit | 3aa067f595115a62afdfc9acc33f08e9c96da850 (patch) | |
tree | 1dad56c263c7f84d59440ec32654de76b78d6f2b /src/cpu | |
parent | 6efbebdb58357b8d1aad43f51c91defd452296f6 (diff) |
Add support to run SMM handler in TSEG instead of ASEG
Traditionally coreboot's SMM handler runs in ASEG (0xa0000),
"behind" the graphics memory. This approach has two issues:
- It limits the possible size of the SMM handler (and the
number of CPUs supported in a system)
- It's not considered a supported path anymore in newer CPUs.
Change-Id: I9f2877e46873ab2ea8f1157ead4bc644a50be19e
Signed-off-by: Duncan Laurie <dlaurie@google.com>
Acked-by: Stefan Reinauer <reinauer@google.com>
Reviewed-on: http://review.coreboot.org/842
Reviewed-by: Peter Stuge <peter@stuge.se>
Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/cpu')
-rw-r--r-- | src/cpu/x86/smm/Makefile.inc | 14 | ||||
-rw-r--r-- | src/cpu/x86/smm/smihandler.c | 9 | ||||
-rw-r--r-- | src/cpu/x86/smm/smm_tseg.ld | 58 | ||||
-rw-r--r-- | src/cpu/x86/smm/smmhandler_tseg.S | 300 | ||||
-rw-r--r-- | src/cpu/x86/smm/smmrelocate.S | 49 |
5 files changed, 427 insertions, 3 deletions
diff --git a/src/cpu/x86/smm/Makefile.inc b/src/cpu/x86/smm/Makefile.inc index 85bb45472b..108f8f979b 100644 --- a/src/cpu/x86/smm/Makefile.inc +++ b/src/cpu/x86/smm/Makefile.inc @@ -22,15 +22,25 @@ ifeq ($(CONFIG_HAVE_SMI_HANDLER),y) ramstage-srcs += $(obj)/cpu/x86/smm/smm_wrap endif +# Use TSEG specific entry point and linker script +ifeq ($(CONFIG_SMM_TSEG),y) +smm-y += smmhandler_tseg.S +SMM_LDFLAGS := $(LDFLAGS) -pie +SMM_LDSCRIPT := smm_tseg.ld +else smm-y += smmhandler.S +SMM_LDFLAGS := $(LDFLAGFS) +SMM_LDSCRIPT := smm.ld +endif + smm-y += smihandler.c smm-y += smiutil.c $(obj)/cpu/x86/smm/smm.o: $$(smm-objs) $(CC) $(LDFLAGS) -nostdlib -r -o $@ $^ -$(obj)/cpu/x86/smm/smm_wrap: $(obj)/cpu/x86/smm/smm.o $(src)/cpu/x86/smm/smm.ld $(obj)/ldoptions - $(CC) $(LDFLAGS) -nostdlib -nostartfiles -static -o $(obj)/cpu/x86/smm/smm.elf -T $(src)/cpu/x86/smm/smm.ld $(obj)/cpu/x86/smm/smm.o +$(obj)/cpu/x86/smm/smm_wrap: $(obj)/cpu/x86/smm/smm.o $(src)/cpu/x86/smm/$(SMM_LDSCRIPT) $(obj)/ldoptions + $(CC) $(SMM_LDFLAGS) -nostdlib -nostartfiles -static -o $(obj)/cpu/x86/smm/smm.elf -T $(src)/cpu/x86/smm/$(SMM_LDSCRIPT) $(obj)/cpu/x86/smm/smm.o $(NM) -n $(obj)/cpu/x86/smm/smm.elf | sort > $(obj)/cpu/x86/smm/smm.map $(OBJCOPY) -O binary $(obj)/cpu/x86/smm/smm.elf $(obj)/cpu/x86/smm/smm diff --git a/src/cpu/x86/smm/smihandler.c b/src/cpu/x86/smm/smihandler.c index a6ab87fd65..bbed0f195e 100644 --- a/src/cpu/x86/smm/smihandler.c +++ b/src/cpu/x86/smm/smihandler.c @@ -25,10 +25,11 @@ #include <cpu/x86/cache.h> #include <cpu/x86/smm.h> +#if !CONFIG_SMM_TSEG /* TSEG handler locks in assembly */ typedef enum { SMI_LOCKED, SMI_UNLOCKED } smi_semaphore; /* SMI multiprocessing semaphore */ -static volatile smi_semaphore smi_handler_status __attribute__ ((aligned (4))) = SMI_UNLOCKED; +static volatile smi_semaphore smi_handler_status __attribute__ ((aligned (4))) = SMI_UNLOCKED; static int smi_obtain_lock(void) { @@ -56,6 +57,7 @@ void smi_release_lock(void) : "eax" ); } +#endif #define LAPIC_ID 0xfee00020 static inline __attribute__((always_inline)) unsigned long nodeid(void) @@ -116,6 +118,7 @@ void smi_handler(u32 smm_revision) unsigned int node; smm_state_save_area_t state_save; +#if !CONFIG_SMM_TSEG /* Are we ok to execute the handler? */ if (!smi_obtain_lock()) { /* For security reasons we don't release the other CPUs @@ -128,6 +131,7 @@ void smi_handler(u32 smm_revision) } return; } +#endif smi_backup_pci_address(); @@ -145,6 +149,7 @@ void smi_handler(u32 smm_revision) (0xa8000 + 0x7e00 - (node * 0x400)); break; case 0x00030100: + case 0x00030101: /* SandyBridge */ state_save.type = EM64T; state_save.em64t_state_save = (em64t_smm_state_save_area_t *) (0xa8000 + 0x7d00 - (node * 0x400)); @@ -173,7 +178,9 @@ void smi_handler(u32 smm_revision) smi_restore_pci_address(); +#if !CONFIG_SMM_TSEG smi_release_lock(); +#endif /* De-assert SMI# signal to allow another SMI */ smi_set_eos(); diff --git a/src/cpu/x86/smm/smm_tseg.ld b/src/cpu/x86/smm/smm_tseg.ld new file mode 100644 index 0000000000..016b5a0fcf --- /dev/null +++ b/src/cpu/x86/smm/smm_tseg.ld @@ -0,0 +1,58 @@ +/* Maximum number of CPUs/cores */ +CPUS = 16; + +SECTIONS +{ + /* This is the actual SMM handler. + * + * We just put code, rodata, data and bss all in a row. + */ + .handler (.): { + /* Assembler stub */ + *(.handler) + + /* C code of the SMM handler */ + *(.text); + *(.text.*); + + /* C read-only data of the SMM handler */ + . = ALIGN(16); + *(.rodata) + *(.rodata.*) + *(.data.rel.ro.*) + + /* C read-write data of the SMM handler */ + . = ALIGN(4); + *(.data) + + /* C uninitialized data of the SMM handler */ + . = ALIGN(4); + *(.bss) + *(.sbss) + + /* What is this? */ + *(COMMON) + . = ALIGN(4); + } + + /* We are using the TSEG interleaved to stuff the SMM handlers + * for all CPU cores in there. The jump table redirects the execution + * to the actual SMM handler + */ + . = 0x8000 - (( CPUS - 1) * 0x400); + .jumptable : { + *(.jumptable) + } + + /* Data used in early SMM TSEG handler. */ + . = 0x8400; + .earlydata : { + *(.earlydata) + } + + /DISCARD/ : { + *(.comment) + *(.note) + *(.note.*) + } +} diff --git a/src/cpu/x86/smm/smmhandler_tseg.S b/src/cpu/x86/smm/smmhandler_tseg.S new file mode 100644 index 0000000000..8fdd75fb17 --- /dev/null +++ b/src/cpu/x86/smm/smmhandler_tseg.S @@ -0,0 +1,300 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* + * +--------------------------------+ 0xffff + * | Save State Map Node 0 | + * | Save State Map Node 1 | + * | Save State Map Node 2 | + * | Save State Map Node 3 | + * | ... | + * +--------------------------------+ 0xf000 + * | | + * | | + * | EARLY DATA (lock, vectors) | + * +--------------------------------+ 0x8400 + * | SMM Entry Node 0 (+ stack) | + * +--------------------------------+ 0x8000 + * | SMM Entry Node 1 (+ stack) | + * | SMM Entry Node 2 (+ stack) | + * | SMM Entry Node 3 (+ stack) | + * | ... | + * +--------------------------------+ 0x7400 + * | | + * | SMM Handler | + * | | + * +--------------------------------+ TSEG + * + */ + +#define LAPIC_ID 0xfee00020 +#define SMM_STACK_SIZE (0x400 - 0x10) + +/* Values for the xchg lock */ +#define SMI_LOCKED 0 +#define SMI_UNLOCKED 1 + +#define __PRE_RAM__ +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#include <northbridge/intel/sandybridge/sandybridge.h> +#define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) +#else +#error "Northbridge must define TSEG_BAR." +#endif + +/* initially SMM is some sort of real mode. Let gcc know + * how to treat the SMM handler stub + */ + +.section ".handler", "a", @progbits + +.code16 + +/** + * SMM code to enable protected mode and jump to the + * C-written function void smi_handler(u32 smm_revision) + * + * All the bad magic is not all that bad after all. + */ +smm_handler_start: + movl $(TSEG_BAR), %eax /* Get TSEG base from PCIE */ + addr32 movl (%eax), %edx /* Save TSEG_BAR in %edx */ + andl $~1, %edx /* Remove lock bit */ + + /* Obtain lock */ + movl %edx, %ebx + addl $(smm_lock), %ebx + movw $SMI_LOCKED, %ax + addr32 xchg %ax, (%ebx) + cmpw $SMI_UNLOCKED, %ax + + /* Proceed if we got the lock */ + je smm_check_prot_vector + + /* If we did not get the lock, wait for release */ +wait_for_unlock: + addr32 movw (%ebx), %ax + cmpw $SMI_LOCKED, %ax + je wait_for_unlock + rsm + +smm_check_prot_vector: + /* See if we need to adjust protected vector */ + movl %edx, %eax + addl $(smm_prot_vector), %eax + addr32 movl (%eax), %ebx + cmpl $(smm_prot_start), %ebx + jne smm_check_gdt_vector + + /* Adjust vector with TSEG offset */ + addl %edx, %ebx + addr32 movl %ebx, (%eax) + +smm_check_gdt_vector: + /* See if we need to adjust GDT vector */ + movl %edx, %eax + addl $(smm_gdt_vector + 2), %eax + addr32 movl (%eax), %ebx + cmpl $(smm_gdt - smm_handler_start), %ebx + jne smm_load_gdt + + /* Adjust vector with TSEG offset */ + addl %edx, %ebx + addr32 movl %ebx, (%eax) + +smm_load_gdt: + movl $(smm_gdt_vector), %ebx + addl %edx, %ebx /* TSEG base in %edx */ + data32 lgdt (%ebx) + + movl %cr0, %eax + andl $0x1FFAFFD1, %eax /* CD,NW,PG,AM,WP,NE,TS,EM,MP = 0 */ + orl $0x1, %eax /* PE = 1 */ + movl %eax, %cr0 + + /* Enable protected mode */ + movl $(smm_prot_vector), %eax + addl %edx, %eax + data32 ljmp *(%eax) + +.code32 +smm_prot_start: + /* Use flat data segment */ + movw $0x10, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + /* Get this CPU's LAPIC ID */ + movl $LAPIC_ID, %esi + movl (%esi), %ecx + shr $24, %ecx + + /* calculate stack offset by multiplying the APIC ID + * by 1024 (0x400), and save that offset in ebp. + */ + shl $10, %ecx + movl %ecx, %ebp + + /* We put the stack for each core right above + * its SMM entry point. Core 0 starts at SMM_BASE + 0x8000, + * we spare 0x10 bytes for the jump to be sure. + */ + movl $0x8010, %eax /* core 0 address */ + addl %edx, %eax /* addjust for TSEG */ + subl %ecx, %eax /* subtract offset, see above */ + movl %eax, %ebx /* Save bottom of stack in ebx */ + + /* clear stack */ + cld + movl %eax, %edi + movl $(SMM_STACK_SIZE >> 2), %ecx + xorl %eax, %eax + rep stosl + + /* set new stack */ + addl $SMM_STACK_SIZE, %ebx + movl %ebx, %esp + + /* Get SMM revision */ + movl $0xfefc, %ebx /* core 0 address */ + addl %edx, %ebx /* addjust for TSEG */ + subl %ebp, %ebx /* subtract core X offset */ + movl (%ebx), %eax + pushl %eax + + /* Call 32bit C handler */ + call smi_handler + + /* Release lock */ + movl $(TSEG_BAR), %eax /* Get TSEG base from PCIE */ + movl (%eax), %ebx /* Save TSEG_BAR in %ebx */ + andl $~1, %ebx /* Remove lock bit */ + addl $(smm_lock), %ebx + movw $SMI_UNLOCKED, %ax + xchg %ax, (%ebx) + + /* To return, just do rsm. It will "clean up" protected mode */ + rsm + +smm_gdt: + /* The first GDT entry can not be used. Keep it zero */ + .long 0x00000000, 0x00000000 + + /* gdt selector 0x08, flat code segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x9b, 0xcf, 0x00 /* G=1 and 0x0f, 4GB limit */ + + /* gdt selector 0x10, flat data segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x93, 0xcf, 0x00 + +smm_gdt_end: + +.section ".earlydata", "a", @progbits + +.code16 + +.align 4, 0xff + +smm_lock: + .word SMI_UNLOCKED + +.align 4, 0xff + +smm_prot_vector: + .long smm_prot_start + .short 8 + +.align 4, 0xff + +smm_gdt_vector: + .word smm_gdt_end - smm_gdt - 1 + .long smm_gdt - smm_handler_start + +.section ".jumptable", "a", @progbits + +/* This is the SMM jump table. All cores use the same SMM handler + * for simplicity. But SMM Entry needs to be different due to the + * save state area. The jump table makes sure all CPUs jump into the + * real handler on SMM entry. + */ + +/* This code currently supports up to 16 CPU cores. If more than 16 CPU cores + * shall be used, below table has to be updated, as well as smm_tseg.ld + */ + +/* When using TSEG do a relative jump and fix up the CS later since we + * do not know what our TSEG base is yet. + */ + +.code16 +jumptable: + /* core 15 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 14 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 13 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 12 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 11 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 10 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 9 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 8 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 7 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 6 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 5 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 4 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 3 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 2 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 1 */ + jmp smm_handler_start +.align 1024, 0x00 + /* core 0 */ + jmp smm_handler_start +.align 1024, 0x00 diff --git a/src/cpu/x86/smm/smmrelocate.S b/src/cpu/x86/smm/smmrelocate.S index 7b383485f9..bc5b2da41b 100644 --- a/src/cpu/x86/smm/smmrelocate.S +++ b/src/cpu/x86/smm/smmrelocate.S @@ -39,6 +39,12 @@ #error "Southbridge needs SMM handler support." #endif +#if CONFIG_SMM_TSEG + +#include <cpu/x86/mtrr.h> + +#endif /* CONFIG_SMM_TSEG */ + #define LAPIC_ID 0xfee00020 .global smm_relocation_start @@ -100,6 +106,7 @@ smm_relocation_start: /* Check revision to see if AMD64 style SMM_BASE * Intel Core Solo/Duo: 0x30007 * Intel Core2 Solo/Duo: 0x30100 + * Intel SandyBridge: 0x30101 * AMD64: 0x3XX64 * This check does not make much sense, unless someone ports * SMI handling to AMD64 CPUs. @@ -127,11 +134,53 @@ smm_relocate: movl %ecx, %edx shl $10, %edx +#if CONFIG_SMM_TSEG + movl $(TSEG_BAR), %ecx /* Get TSEG base from PCIE */ + addr32 movl (%ecx), %eax /* Save TSEG_BAR in %eax */ + andl $~1, %eax /* Remove lock bit */ +#else movl $0xa0000, %eax +#endif subl %edx, %eax /* subtract offset, see above */ addr32 movl %eax, (%ebx) +#if CONFIG_SMM_TSEG + /* Check for SMRR capability in MTRRCAP[11] */ + movl $MTRRcap_MSR, %ecx + rdmsr + bt $11, %eax + jnc skip_smrr + + /* TSEG base */ + movl $(TSEG_BAR), %ecx /* Get TSEG base from PCIE */ + addr32 movl (%ecx), %eax /* Save TSEG_BAR in %eax */ + andl $~1, %eax /* Remove lock bit */ + movl %eax, %ebx + + /* Set SMRR base address. */ + movl $SMRRphysBase_MSR, %ecx + orl $MTRR_TYPE_WRBACK, %eax + xorl %edx, %edx + wrmsr + + /* Set SMRR mask. */ + movl $SMRRphysMask_MSR, %ecx + movl $(~(CONFIG_SMM_TSEG_SIZE - 1) | MTRRphysMaskValid), %eax + xorl %edx, %edx + wrmsr + +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE + /* + * IED base is top 4M of TSEG + */ + addl $(CONFIG_SMM_TSEG_SIZE - IED_SIZE), %ebx + movl $(0x30000 + 0x8000 + 0x7eec), %eax + addr32 movl %ebx, (%eax) +#endif + +skip_smrr: +#endif /* The next section of code is potentially southbridge specific */ |