diff options
Diffstat (limited to 'src/arch/x86/exception.c')
-rw-r--r-- | src/arch/x86/exception.c | 112 |
1 files changed, 112 insertions, 0 deletions
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 <arch/early_variables.h> +#include <arch/exception.h> +#include <commonlib/helpers.h> +#include <compiler.h> #include <console/console.h> #include <console/streams.h> +#include <rules.h> +#include <stdint.h> #include <string.h> #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)); +} |