summaryrefslogtreecommitdiff
path: root/src/arch
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch')
-rw-r--r--src/arch/x86/Kconfig7
-rw-r--r--src/arch/x86/Makefile.inc10
-rw-r--r--src/arch/x86/assembly_entry.S3
-rw-r--r--src/arch/x86/c_start.S32
-rw-r--r--src/arch/x86/exception.c112
-rw-r--r--src/arch/x86/idt.S3
-rw-r--r--src/arch/x86/include/arch/exception.h7
7 files changed, 142 insertions, 32 deletions
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 <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));
+}
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 <arch/cpu.h>
+#include <rules.h>
+
+#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