summaryrefslogtreecommitdiff
path: root/src/cpu/x86
diff options
context:
space:
mode:
authorStefan Reinauer <reinauer@chromium.org>2012-04-02 13:24:04 -0700
committerPeter Stuge <peter@stuge.se>2012-04-04 04:49:09 +0200
commit3aa067f595115a62afdfc9acc33f08e9c96da850 (patch)
tree1dad56c263c7f84d59440ec32654de76b78d6f2b /src/cpu/x86
parent6efbebdb58357b8d1aad43f51c91defd452296f6 (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/x86')
-rw-r--r--src/cpu/x86/smm/Makefile.inc14
-rw-r--r--src/cpu/x86/smm/smihandler.c9
-rw-r--r--src/cpu/x86/smm/smm_tseg.ld58
-rw-r--r--src/cpu/x86/smm/smmhandler_tseg.S300
-rw-r--r--src/cpu/x86/smm/smmrelocate.S49
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 */