x86: allow IDT vectors to be task gates

This has one use-case: configuring the double-fault #DF
exception handler to do an IA task switch to a special
IA task with a known good stack, such that we can dump
diagnostic information and then panic.

Will be used for stack overflow detection in kernel mode,
as otherwise the CPU will triple-fault and reset.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2017-07-14 13:29:19 -07:00 committed by Anas Nashif
commit 8a102e44ed
3 changed files with 71 additions and 11 deletions

View file

@ -18,6 +18,7 @@
".long -1\n\t" /* ISR_LIST.priority */ \
".long " STRINGIFY(vector) "\n\t" /* ISR_LIST.vec */ \
".long 0\n\t" /* ISR_LIST.dpl */ \
".long 0\n\t" /* ISR_LIST.tss */ \
".popsection\n\t" \
/* Extra preprocessor indirection to ensure arguments get expanded before

View file

@ -76,6 +76,12 @@ typedef struct s_isrList {
unsigned int vec;
/** Privilege level associated with ISR/stub */
unsigned int dpl;
/** If nonzero, specifies a TSS segment selector. Will configure
* a task gate instead of an interrupt gate. fnc parameter will be
* ignored
*/
unsigned int tss;
} ISR_LIST;
@ -106,8 +112,39 @@ typedef struct s_isrList {
#define NANO_CPU_INT_REGISTER(r, n, p, v, d) \
static ISR_LIST __attribute__((section(".intList"))) \
__attribute__((used)) MK_ISR_NAME(r) = \
{&r, n, p, v, d}
{ \
.fnc = &(r), \
.irq = (n), \
.priority = (p), \
.vec = (v), \
.dpl = (d), \
.tss = 0 \
}
/**
* @brief Connect an IA hardware task to an interrupt vector
*
* This is very similar to NANO_CPU_INT_REGISTER but instead of connecting
* a handler function, the interrupt will induce an IA hardware task
* switch to another hardware task instead.
*
* @param tss_p GDT/LDT segment selector for the TSS representing the task
* @param irq_p IRQ number
* @param priority_p IRQ priority
* @param vec_p Interrupt vector
* @param dpl_p Descriptor privilege level
*/
#define _X86_IDT_TSS_REGISTER(tss_p, irq_p, priority_p, vec_p, dpl_p) \
static ISR_LIST __attribute__((section(".intList"))) \
__attribute__((used)) MK_ISR_NAME(r) = \
{ \
.fnc = NULL, \
.irq = (irq_p), \
.priority = (priority_p), \
.vec = (vec_p), \
.dpl = (dpl_p), \
.tss = (tss_p) \
}
/**
* Code snippets for populating the vector ID and priority into the intList
@ -172,6 +209,7 @@ typedef struct s_isrList {
".long %c[priority]\n\t" /* ISR_LIST.priority */ \
".long %c[vector]\n\t" /* ISR_LIST.vec */ \
".long 0\n\t" /* ISR_LIST.dpl */ \
".long 0\n\t" /* ISR_LIST.tss */ \
".popsection\n\t" \
".pushsection .text.irqstubs\n\t" \
".global %c[isr]_irq%c[irq]_stub\n\t" \

View file

@ -27,7 +27,7 @@ def error(text):
sys.exit(1)
# See Section 6.11 of the Intel Architecture Software Developer's Manual
irq_gate_desc_format = "<HHBBH"
gate_desc_format = "<HHBBH"
def create_irq_gate(handler, dpl):
present = 1
@ -37,14 +37,34 @@ def create_irq_gate(handler, dpl):
offset_hi = handler >> 16
offset_lo = handler & 0xFFFF
data = struct.pack(irq_gate_desc_format, offset_lo, KERNEL_CODE_SEG, 0,
data = struct.pack(gate_desc_format, offset_lo, KERNEL_CODE_SEG, 0,
type_attr, offset_hi)
return data
def create_task_gate(tss, dpl):
present = 1
gate_type = 0x5 # 32-bit task gate
type_attr = gate_type | (dpl << 5) | (present << 7)
data = struct.pack(gate_desc_format, 0, tss, 0, type_attr, 0)
return data
def create_idt_binary(idt_config, filename):
with open(filename, "wb") as fp:
for handler, dpl in idt_config:
data = create_irq_gate(handler, dpl)
for handler, tss, dpl in idt_config:
if handler and tss:
error("entry specifies both handler function and tss")
if not handler and not tss:
error("entry does not specify either handler or tss")
if handler:
data = create_irq_gate(handler, dpl)
else:
data = create_task_gate(tss, dpl)
fp.write(data)
map_fmt = "<B"
@ -83,7 +103,7 @@ def setup_idt(spur_code, spur_nocode, intlist, max_vec, max_irq):
vectors = [None for i in range(max_vec)]
# Pass 1: sanity check and set up hard-coded interrupt vectors
for handler, irq, prio, vec, dpl in intlist:
for handler, irq, prio, vec, dpl, tss in intlist:
if vec == -1:
if prio == -1:
error("entry does not specify vector or priority level")
@ -96,11 +116,11 @@ def setup_idt(spur_code, spur_nocode, intlist, max_vec, max_irq):
if vectors[vec] != None:
error("Multiple assignments for vector %d" % vec)
vectors[vec] = (handler, dpl)
vectors[vec] = (handler, tss, dpl)
update_irq_vec_map(irq_vec_map, irq, vec, max_irq)
# Pass 2: set up priority-based interrupt vectors
for handler, irq, prio, vec, dpl in intlist:
for handler, irq, prio, vec, dpl, tss in intlist:
if vec != -1:
continue
@ -114,7 +134,7 @@ def setup_idt(spur_code, spur_nocode, intlist, max_vec, max_irq):
if vec == -1:
error("can't find a free vector in priority level %d" % prio)
vectors[vec] = (handler, dpl)
vectors[vec] = (handler, tss, dpl)
update_irq_vec_map(irq_vec_map, irq, vec, max_irq)
# Pass 3: fill in unused vectors with spurious handler at dpl=0
@ -127,7 +147,7 @@ def setup_idt(spur_code, spur_nocode, intlist, max_vec, max_irq):
else:
handler = spur_nocode
vectors[i] = (handler, 0)
vectors[i] = (handler, 0, 0)
return vectors, irq_vec_map
@ -154,9 +174,10 @@ intlist_header_fmt = "<IIi"
# int32_t priority;
# int32_t vector_id;
# int32_t dpl;
# int32_t tss;
# };
intlist_entry_fmt = "<Iiiii"
intlist_entry_fmt = "<Iiiiii"
def get_intlist(elf):
intdata = elf.get_section_by_name("intList").data()