Stefan Reinauer (stefan.reinauer@coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/842
-gerrit
commit 5ba6dbcb1df71478d561a3e12f29a840ae53cbd1 Author: Stefan Reinauer reinauer@chromium.org Date: Mon Apr 2 13:24:04 2012 -0700
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 --- Makefile.inc | 5 + src/console/vtxprintf.c | 6 + src/cpu/x86/smm/Makefile.inc | 14 ++- src/cpu/x86/smm/smihandler.c | 17 ++- src/cpu/x86/smm/smm_tseg.ld | 58 +++++++ src/cpu/x86/smm/smmhandler.S | 3 - src/cpu/x86/smm/smmhandler_tseg.S | 300 +++++++++++++++++++++++++++++++++++++ src/cpu/x86/smm/smmrelocate.S | 57 +++++++ src/include/cpu/x86/smm.h | 1 - 9 files changed, 448 insertions(+), 13 deletions(-)
diff --git a/Makefile.inc b/Makefile.inc index 857ad16..92d0749 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -62,6 +62,11 @@ endif smm-c-ccopts:=-D__SMM__ smm-S-ccopts:=-D__SMM__
+# SMM TSEG base is dynamic +ifeq ($(CONFIG_SMM_TSEG),y) +smm-c-ccopts += -fpic +endif + ramstage-c-deps:=$$(OPTION_TABLE_H) romstage-c-deps:=$$(OPTION_TABLE_H)
diff --git a/src/console/vtxprintf.c b/src/console/vtxprintf.c index 4053023..a370e5f 100644 --- a/src/console/vtxprintf.c +++ b/src/console/vtxprintf.c @@ -6,6 +6,7 @@
#include <string.h> #include <div64.h> +#include <console/console.h> #include <console/vtxprintf.h>
/* haha, don't need ctype.c */ @@ -115,6 +116,11 @@ int vtxprintf(void (*tx_byte)(unsigned char byte), const char *fmt, va_list args
int count;
+#if defined(__SMM__) && CONFIG_SMM_TSEG + /* Fix pointer in TSEG */ + tx_byte = console_tx_byte; +#endif + for (count=0; *fmt ; ++fmt) { if (*fmt != '%') { tx_byte(*fmt), count++; diff --git a/src/cpu/x86/smm/Makefile.inc b/src/cpu/x86/smm/Makefile.inc index 85bb454..108f8f9 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 a6ab87f..a971d81 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 = SMI_UNLOCKED;
static int smi_obtain_lock(void) { @@ -46,7 +47,7 @@ static int smi_obtain_lock(void) return (ret == SMI_UNLOCKED); }
-void smi_release_lock(void) +static void smi_release_lock(void) { asm volatile ( "movb %1, %%al\n" @@ -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,18 +118,16 @@ 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 * until the CPU with the lock is actually done */ - while (smi_handler_status == SMI_LOCKED) { - asm volatile ( - ".byte 0xf3, 0x90\n" /* hint a CPU we are in spinlock (PAUSE instruction, REP NOP) */ - ); - } + while (smi_handler_status == SMI_LOCKED) /* wait */ ; return; } +#endif
smi_backup_pci_address();
@@ -145,6 +145,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 +174,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 0000000..016b5a0 --- /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.S b/src/cpu/x86/smm/smmhandler.S index 774088e..3dd0b14 100644 --- a/src/cpu/x86/smm/smmhandler.S +++ b/src/cpu/x86/smm/smmhandler.S @@ -89,9 +89,6 @@ smm_handler_start:
.code32 1: - /* flush the cache after disabling it */ - wbinvd - /* Use flat data segment */ movw $0x10, %ax movw %ax, %ds diff --git a/src/cpu/x86/smm/smmhandler_tseg.S b/src/cpu/x86/smm/smmhandler_tseg.S new file mode 100644 index 0000000..d3e4d21 --- /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 4 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 7b38348..93786a0 100644 --- a/src/cpu/x86/smm/smmrelocate.S +++ b/src/cpu/x86/smm/smmrelocate.S @@ -35,10 +35,24 @@ #include "../../../southbridge/intel/i82801dx/i82801dx.h" #elif CONFIG_SOUTHBRIDGE_INTEL_SCH #include "../../../southbridge/intel/sch/sch.h" +#elif CONFIG_SOUTHBRIDGE_INTEL_BD82X6X || CONFIG_SOUTHBRIDGE_INTEL_C216 +#include "../../../southbridge/intel/bd82x6x/pch.h" #else #error "Southbridge needs SMM handler support." #endif
+#if CONFIG_SMM_TSEG + +#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 +#include <cpu/x86/mtrr.h> + +#endif /* CONFIG_SMM_TSEG */ + #define LAPIC_ID 0xfee00020
.global smm_relocation_start @@ -100,6 +114,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 +142,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 */
diff --git a/src/include/cpu/x86/smm.h b/src/include/cpu/x86/smm.h index c314c39..dee9a70 100644 --- a/src/include/cpu/x86/smm.h +++ b/src/include/cpu/x86/smm.h @@ -280,6 +280,5 @@ 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);
#endif