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)
downloadcoreboot-3aa067f595115a62afdfc9acc33f08e9c96da850.tar.xz
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 */