From 4b032e457f1377fc06d12214b0450eae48653565 Mon Sep 17 00:00:00 2001 From: Aaron Durbin Date: Fri, 20 Apr 2018 01:39:30 -0600 Subject: arch/x86: allow idt to be available to link in all stages Add Kconfig IDT_IN_EVERY_STAGE to optionally specify having the interrupt handling code available to all stages. In order to do this the idt setup is moved to a C module. The vecX entries are made global so that a table of references to all the interrupt vector entry points can be used to dynamically initialize the idt. The ramification for ramstage is that exceptions are initialized later (lib/hardwaremain.c). Not all stages initialize exceptions when this Kconfig variable is selected, but bootblock for the C, stages using assembly_entry.S, and of course ramstage do. Anything left out just needs a call to exception_init() at the right location. BUG=b:72728953 Change-Id: I4146a040e5e43bed7ccc6cb0a7dc2271f1e7b7fa Signed-off-by: Aaron Durbin Reviewed-on: https://review.coreboot.org/25761 Tested-by: build bot (Jenkins) Reviewed-by: Furquan Shaikh --- src/arch/x86/Kconfig | 7 +++ src/arch/x86/Makefile.inc | 10 +++ src/arch/x86/assembly_entry.S | 3 + src/arch/x86/c_start.S | 32 +--------- src/arch/x86/exception.c | 112 ++++++++++++++++++++++++++++++++++ src/arch/x86/idt.S | 3 +- src/arch/x86/include/arch/exception.h | 7 +++ 7 files changed, 142 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/arch/x86/Kconfig b/src/arch/x86/Kconfig index d7f144e179..1facfb9420 100644 --- a/src/arch/x86/Kconfig +++ b/src/arch/x86/Kconfig @@ -302,3 +302,10 @@ config NUM_CAR_PAGE_TABLE_PAGES depends on PAGING_IN_CACHE_AS_RAM help The number of 4KiB pages that should be pre-allocated for page tables. + +# Provide the interrupt handlers to every stage. Not all +# stages may take advantage. +config IDT_IN_EVERY_STAGE + bool + default n + depends on ARCH_X86 diff --git a/src/arch/x86/Makefile.inc b/src/arch/x86/Makefile.inc index b8cf3a6493..07bef93453 100644 --- a/src/arch/x86/Makefile.inc +++ b/src/arch/x86/Makefile.inc @@ -92,6 +92,8 @@ ifeq ($(CONFIG_ARCH_BOOTBLOCK_X86_32)$(CONFIG_ARCH_BOOTBLOCK_X86_64),y) bootblock-y += boot.c bootblock-y += cpu_common.c +bootblock-$(CONFIG_IDT_IN_EVERY_STAGE) += exception.c +bootblock-$(CONFIG_IDT_IN_EVERY_STAGE) += idt.S bootblock-y += memcpy.c bootblock-y += memset.c bootblock-$(CONFIG_COLLECT_TIMESTAMPS_TSC) += timestamp.c @@ -169,6 +171,8 @@ endif # CONFIG_ARCH_BOOTBLOCK_X86_32 / CONFIG_ARCH_BOOTBLOCK_X86_64 ifeq ($(CONFIG_ARCH_VERSTAGE_X86_32)$(CONFIG_ARCH_VERSTAGE_X86_64),y) verstage-y += boot.c +verstage-$(CONFIG_IDT_IN_EVERY_STAGE) += exception.c +verstage-$(CONFIG_IDT_IN_EVERY_STAGE) += idt.S verstage-$(CONFIG_ARCH_RAMSTAGE_X86_32) += cpu_common.c verstage-y += memset.c @@ -206,6 +210,8 @@ romstage-y += cbmem.c romstage-y += cbfs_and_run.c romstage-$(CONFIG_ARCH_RAMSTAGE_X86_32) += cpu_common.c romstage-$(CONFIG_EARLY_EBDA_INIT) += ebda.c +romstage-$(CONFIG_IDT_IN_EVERY_STAGE) += exception.c +romstage-$(CONFIG_IDT_IN_EVERY_STAGE) += idt.S romstage-y += memcpy.c romstage-y += memmove.c romstage-y += memset.c @@ -281,6 +287,8 @@ postcar-y += cbfs_and_run.c postcar-y += cbmem.c postcar-y += cpu_common.c postcar-$(CONFIG_EARLY_EBDA_INIT) += ebda.c +postcar-$(CONFIG_IDT_IN_EVERY_STAGE) += exception.c +postcar-$(CONFIG_IDT_IN_EVERY_STAGE) += idt.S postcar-y += exit_car.S postcar-y += memcpy.c postcar-y += memmove.c @@ -343,6 +351,8 @@ ramstage-$(CONFIG_COOP_MULTITASKING) += thread_switch.S ramstage-$(CONFIG_COLLECT_TIMESTAMPS_TSC) += timestamp.c ramstage-$(CONFIG_HAVE_ACPI_RESUME) += wakeup.S +smm-$(CONFIG_IDT_IN_EVERY_STAGE) += exception.c +smm-$(CONFIG_IDT_IN_EVERY_STAGE) += idt.S smm-y += memcpy.c smm-y += memmove.c smm-y += memset.c diff --git a/src/arch/x86/assembly_entry.S b/src/arch/x86/assembly_entry.S index 220cc6e400..e5e7d4835b 100644 --- a/src/arch/x86/assembly_entry.S +++ b/src/arch/x86/assembly_entry.S @@ -64,6 +64,9 @@ debug_spinloop: #endif andl $0xfffffff0, %esp +#if IS_ENABLED(CONFIG_IDT_IN_EVERY_STAGE) + call exception_init +#endif call car_stage_entry /* This is here for linking purposes. */ diff --git a/src/arch/x86/c_start.S b/src/arch/x86/c_start.S index 6cea5c3167..6426ef3693 100644 --- a/src/arch/x86/c_start.S +++ b/src/arch/x86/c_start.S @@ -81,28 +81,6 @@ _start: push $0 push $0 - /* Initialize the Interrupt Descriptor table */ - leal _idt, %edi - leal vec0, %ebx - movl $(0x10 << 16), %eax /* cs selector */ - -1: movw %bx, %ax - movl %ebx, %edx - movw $0x8E00, %dx /* Interrupt gate - dpl=0, present */ - movl %eax, 0(%edi) - movl %edx, 4(%edi) - addl $6, %ebx - addl $8, %edi - cmpl $_idt_end, %edi - jne 1b - - /* Load the Interrupt descriptor table */ -#ifndef __x86_64__ - lidt idtarg -#else - // FIXME port table to x64 - lidt idtarg -#endif - /* * Now we are finished. Memory is up, data is copied and * bss is cleared. Now we call the main routine and @@ -145,7 +123,7 @@ gdb_stub_breakpoint: jmp int_hand #endif - .globl gdt, gdt_end, idtarg + .globl gdt, gdt_end gdtaddr: .word gdt_end - gdt - 1 @@ -227,14 +205,6 @@ gdt: #endif gdt_end: -idtarg: - .word _idt_end - _idt - 1 /* limit */ - .long _idt - .word 0 -_idt: - .fill 20, 8, 0 # idt is uninitialized -_idt_end: - .section ".text._start", "ax", @progbits #ifdef __x86_64__ SetCodeSelector: diff --git a/src/arch/x86/exception.c b/src/arch/x86/exception.c index 36697c6b91..5a5dfd2831 100644 --- a/src/arch/x86/exception.c +++ b/src/arch/x86/exception.c @@ -11,8 +11,14 @@ * GNU General Public License for more details. */ +#include +#include +#include +#include #include #include +#include +#include #include #if IS_ENABLED(CONFIG_GDB_STUB) @@ -518,3 +524,109 @@ void x86_exception(struct eregs *info) die(""); #endif } + +#define GATE_P (1 << 15) +#define GATE_DPL(x) (((x) & 0x3) << 13) +#define GATE_SIZE_16 (0 << 11) +#define GATE_SIZE_32 (1 << 11) + +#define IGATE_FLAGS (GATE_P | GATE_DPL(0) | GATE_SIZE_32 | (0x6 << 8)) + +struct intr_gate { + uint16_t offset_0; + uint16_t segsel; + uint16_t flags; + uint16_t offset_1; +#if ENV_X86_64 + uint32_t offset_2; + uint32_t reserved; +#endif +} __packed; + +/* Even though the vecX symbols are interrupt entry points just treat them + like data to more easily get the pointer values in C. Because IDT entries + format splits the offset field up one can't use the linker to resolve + parts of a relecation on x86 ABI an array of pointers is used to gather + the symbols. The IDT is initialized at runtime when exception_init() is + called. */ +extern u8 vec0[], vec1[], vec2[], vec3[], vec4[], vec5[], vec6[], vec7[]; +extern u8 vec8[], vec9[], vec10[], vec11[], vec12[], vec13[], vec14[], vec15[]; +extern u8 vec16[], vec17[], vec18[], vec19[]; + +static const uintptr_t intr_entries[] = { + (uintptr_t)vec0, (uintptr_t)vec1, (uintptr_t)vec2, (uintptr_t)vec3, + (uintptr_t)vec4, (uintptr_t)vec5, (uintptr_t)vec6, (uintptr_t)vec7, + (uintptr_t)vec8, (uintptr_t)vec9, (uintptr_t)vec10, (uintptr_t)vec11, + (uintptr_t)vec12, (uintptr_t)vec13, (uintptr_t)vec14, (uintptr_t)vec15, + (uintptr_t)vec16, (uintptr_t)vec17, (uintptr_t)vec18, (uintptr_t)vec19, +}; + +static struct intr_gate idt[ARRAY_SIZE(intr_entries)] __aligned(8) CAR_GLOBAL; + +static inline uint16_t get_cs(void) +{ + uint16_t segment; + + asm volatile ( + "mov %%cs, %0\n" + : "=r" (segment) + : + : "memory" + ); + + return segment; +} + +struct lidtarg { + uint16_t limit; +#if ENV_X86_32 + uint32_t base; +#else + uint64_t base; +#endif +} __packed; + +/* This global is for src/cpu/x86/lapic/secondary.S usage which is only + used during ramstage. */ +struct lidtarg idtarg; + +static void load_idt(void *table, size_t sz) +{ + struct lidtarg lidtarg = { + .limit = sz - 1, + .base = (uintptr_t)table, + }; + + asm volatile ( + "lidt %0" + : + : "m" (lidtarg) + : "memory" + ); + + if (ENV_RAMSTAGE) + memcpy(&idtarg, &lidtarg, sizeof(idtarg)); +} + +asmlinkage void exception_init(void) +{ + int i; + uint16_t segment; + struct intr_gate *gates = car_get_var_ptr(idt); + + segment = get_cs(); + gates = car_get_var_ptr(idt); + + /* Initialize IDT. */ + for (i = 0; i < ARRAY_SIZE(idt); i++) { + gates[i].offset_0 = intr_entries[i]; + gates[i].segsel = segment; + gates[i].flags = IGATE_FLAGS; + gates[i].offset_1 = intr_entries[i] >> 16; +#if ENV_X86_64 + gates[i].offset_2 = intr_entries[i] >> 32; +#endif + } + + load_idt(gates, sizeof(idt)); +} diff --git a/src/arch/x86/idt.S b/src/arch/x86/idt.S index 17ddb57fb7..e119c32b93 100644 --- a/src/arch/x86/idt.S +++ b/src/arch/x86/idt.S @@ -17,7 +17,8 @@ #else .code32 #endif -.global vec0 +.global vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7, vec8, vec9 +.global vec10, vec11, vec12, vec13, vec14, vec15, vec16, vec17, vec18, vec19 vec0: push $0 /* error code */ push $0 /* vector */ diff --git a/src/arch/x86/include/arch/exception.h b/src/arch/x86/include/arch/exception.h index d4e9658f75..8f7213d27e 100644 --- a/src/arch/x86/include/arch/exception.h +++ b/src/arch/x86/include/arch/exception.h @@ -30,6 +30,13 @@ #ifndef _ARCH_EXCEPTION_H #define _ARCH_EXCEPTION_H +#include +#include + +#if IS_ENABLED(CONFIG_IDT_IN_EVERY_STAGE) || ENV_RAMSTAGE +asmlinkage void exception_init(void); +#else static inline void exception_init(void) { /* not implemented */ } +#endif #endif -- cgit v1.2.3