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:
Aaron Durbin 2018-04-20 01:39:30 -06:00
parent e74ba1984d
commit 4b032e457f
7 changed files with 142 additions and 32 deletions

View File

@ -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

View File

@ -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

View File

@ -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. */

View File

@ -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:

View File

@ -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));
}

View File

@ -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 */

View File

@ -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