Add support to run SMM handler in TSEG instead of ASEG
authorStefan Reinauer <reinauer@chromium.org>
Mon, 2 Apr 2012 20:24:04 +0000 (13:24 -0700)
committerPeter Stuge <peter@stuge.se>
Wed, 4 Apr 2012 02:49:09 +0000 (04:49 +0200)
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)
Makefile.inc
src/console/vtxprintf.c
src/cpu/x86/smm/Makefile.inc
src/cpu/x86/smm/smihandler.c
src/cpu/x86/smm/smm_tseg.ld [new file with mode: 0644]
src/cpu/x86/smm/smmhandler_tseg.S [new file with mode: 0644]
src/cpu/x86/smm/smmrelocate.S
src/include/cpu/x86/smm.h

index 81d40df84477e3b2231573ae56efcbf908cb8065..9b67496e1e3b4131b6176a87eacfd22102fe5a33 100644 (file)
@@ -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)
 
index 405302311d6d6a6c85ca6692ea3fbdbf7f61f827..a370e5f21ddd591cef4c08fbc74e7117b47675b4 100644 (file)
@@ -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++;
index 85bb45472bed329782a06154b711bf65177c7528..108f8f979b47f34b93744e5196eb7cadb368891e 100644 (file)
@@ -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
 
index a6ab87fd65ecdebfadbd0203233069add3c969ae..bbed0f195ef9bd0954d4ecf3b690e44f433097f9 100644 (file)
 #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 (file)
index 0000000..016b5a0
--- /dev/null
@@ -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 (file)
index 0000000..8fdd75f
--- /dev/null
@@ -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
index 7b383485f9bb24cfe811795038dd704bb9aaf6bf..bc5b2da41b5ffb9f029fbbd4c9d526dea7f846c2 100644 (file)
 #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 */
 
index c314c3971ac8b0f73f5e133de235de126a2f2cac..60959f52f69219885d119cc5b3ba36a6034417fb 100644 (file)
@@ -280,6 +280,8 @@ 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);
+#if !CONFIG_SMM_TSEG
 void smi_release_lock(void);
+#endif
 
 #endif