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)
This commit is contained in:
Stefan Reinauer 2012-04-02 13:24:04 -07:00 committed by Peter Stuge
parent 6efbebdb58
commit 3aa067f595
8 changed files with 440 additions and 3 deletions

View File

@ -62,6 +62,11 @@ endif
smm-c-ccopts:=-D__SMM__ smm-c-ccopts:=-D__SMM__
smm-S-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) ramstage-c-deps:=$$(OPTION_TABLE_H)
romstage-c-deps:=$$(OPTION_TABLE_H) romstage-c-deps:=$$(OPTION_TABLE_H)

View File

@ -6,6 +6,7 @@
#include <string.h> #include <string.h>
#include <div64.h> #include <div64.h>
#include <console/console.h>
#include <console/vtxprintf.h> #include <console/vtxprintf.h>
/* haha, don't need ctype.c */ /* 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; int count;
#if defined(__SMM__) && CONFIG_SMM_TSEG
/* Fix pointer in TSEG */
tx_byte = console_tx_byte;
#endif
for (count=0; *fmt ; ++fmt) { for (count=0; *fmt ; ++fmt) {
if (*fmt != '%') { if (*fmt != '%') {
tx_byte(*fmt), count++; tx_byte(*fmt), count++;

View File

@ -22,15 +22,25 @@ ifeq ($(CONFIG_HAVE_SMI_HANDLER),y)
ramstage-srcs += $(obj)/cpu/x86/smm/smm_wrap ramstage-srcs += $(obj)/cpu/x86/smm/smm_wrap
endif 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-y += smmhandler.S
SMM_LDFLAGS := $(LDFLAGFS)
SMM_LDSCRIPT := smm.ld
endif
smm-y += smihandler.c smm-y += smihandler.c
smm-y += smiutil.c smm-y += smiutil.c
$(obj)/cpu/x86/smm/smm.o: $$(smm-objs) $(obj)/cpu/x86/smm/smm.o: $$(smm-objs)
$(CC) $(LDFLAGS) -nostdlib -r -o $@ $^ $(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 $(obj)/cpu/x86/smm/smm_wrap: $(obj)/cpu/x86/smm/smm.o $(src)/cpu/x86/smm/$(SMM_LDSCRIPT) $(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 $(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 $(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 $(OBJCOPY) -O binary $(obj)/cpu/x86/smm/smm.elf $(obj)/cpu/x86/smm/smm

View File

@ -25,10 +25,11 @@
#include <cpu/x86/cache.h> #include <cpu/x86/cache.h>
#include <cpu/x86/smm.h> #include <cpu/x86/smm.h>
#if !CONFIG_SMM_TSEG /* TSEG handler locks in assembly */
typedef enum { SMI_LOCKED, SMI_UNLOCKED } smi_semaphore; typedef enum { SMI_LOCKED, SMI_UNLOCKED } smi_semaphore;
/* SMI multiprocessing 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) static int smi_obtain_lock(void)
{ {
@ -56,6 +57,7 @@ void smi_release_lock(void)
: "eax" : "eax"
); );
} }
#endif
#define LAPIC_ID 0xfee00020 #define LAPIC_ID 0xfee00020
static inline __attribute__((always_inline)) unsigned long nodeid(void) static inline __attribute__((always_inline)) unsigned long nodeid(void)
@ -116,6 +118,7 @@ void smi_handler(u32 smm_revision)
unsigned int node; unsigned int node;
smm_state_save_area_t state_save; smm_state_save_area_t state_save;
#if !CONFIG_SMM_TSEG
/* Are we ok to execute the handler? */ /* Are we ok to execute the handler? */
if (!smi_obtain_lock()) { if (!smi_obtain_lock()) {
/* For security reasons we don't release the other CPUs /* For security reasons we don't release the other CPUs
@ -128,6 +131,7 @@ void smi_handler(u32 smm_revision)
} }
return; return;
} }
#endif
smi_backup_pci_address(); smi_backup_pci_address();
@ -145,6 +149,7 @@ void smi_handler(u32 smm_revision)
(0xa8000 + 0x7e00 - (node * 0x400)); (0xa8000 + 0x7e00 - (node * 0x400));
break; break;
case 0x00030100: case 0x00030100:
case 0x00030101: /* SandyBridge */
state_save.type = EM64T; state_save.type = EM64T;
state_save.em64t_state_save = (em64t_smm_state_save_area_t *) state_save.em64t_state_save = (em64t_smm_state_save_area_t *)
(0xa8000 + 0x7d00 - (node * 0x400)); (0xa8000 + 0x7d00 - (node * 0x400));
@ -173,7 +178,9 @@ void smi_handler(u32 smm_revision)
smi_restore_pci_address(); smi_restore_pci_address();
#if !CONFIG_SMM_TSEG
smi_release_lock(); smi_release_lock();
#endif
/* De-assert SMI# signal to allow another SMI */ /* De-assert SMI# signal to allow another SMI */
smi_set_eos(); smi_set_eos();

View File

@ -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.*)
}
}

View File

@ -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

View File

@ -39,6 +39,12 @@
#error "Southbridge needs SMM handler support." #error "Southbridge needs SMM handler support."
#endif #endif
#if CONFIG_SMM_TSEG
#include <cpu/x86/mtrr.h>
#endif /* CONFIG_SMM_TSEG */
#define LAPIC_ID 0xfee00020 #define LAPIC_ID 0xfee00020
.global smm_relocation_start .global smm_relocation_start
@ -100,6 +106,7 @@ smm_relocation_start:
/* Check revision to see if AMD64 style SMM_BASE /* Check revision to see if AMD64 style SMM_BASE
* Intel Core Solo/Duo: 0x30007 * Intel Core Solo/Duo: 0x30007
* Intel Core2 Solo/Duo: 0x30100 * Intel Core2 Solo/Duo: 0x30100
* Intel SandyBridge: 0x30101
* AMD64: 0x3XX64 * AMD64: 0x3XX64
* This check does not make much sense, unless someone ports * This check does not make much sense, unless someone ports
* SMI handling to AMD64 CPUs. * SMI handling to AMD64 CPUs.
@ -127,11 +134,53 @@ smm_relocate:
movl %ecx, %edx movl %ecx, %edx
shl $10, %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 movl $0xa0000, %eax
#endif
subl %edx, %eax /* subtract offset, see above */ subl %edx, %eax /* subtract offset, see above */
addr32 movl %eax, (%ebx) 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 */ /* The next section of code is potentially southbridge specific */

View 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); void __attribute__((weak)) mainboard_smi_gpi(u16 gpi_sts);
int __attribute__((weak)) mainboard_apm_cnt(u8 data); int __attribute__((weak)) mainboard_apm_cnt(u8 data);
#if !CONFIG_SMM_TSEG
void smi_release_lock(void); void smi_release_lock(void);
#endif
#endif #endif