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 <adurbin@chromium.org> Reviewed-on: https://review.coreboot.org/25761 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Furquan Shaikh <furquan@google.com>
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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. */ | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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)); | ||||
| } | ||||
|   | ||||
| @@ -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 */ | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user