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/exception.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) (limited to 'src/arch/x86/exception.c') 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)); +} -- cgit v1.2.3