* i386/i386/idt.c: add selector for the interrupt-specific stack * i386/i386/ktss.c: configure ist1 * i386/i386/trap.c: add double fault handler, which just prints the state and panics. There is not much else to do in this case but it's useful for troubleshooting * x86_64/idt_inittab.S: allow to specify an interrupt stack for custom handlers * x86_64/locore.S: add double fault handler --- i386/i386/idt.c | 12 +++++++++++- i386/i386/ktss.c | 1 + i386/i386/trap.c | 6 ++++++ x86_64/idt_inittab.S | 25 +++++++++++++------------ x86_64/locore.S | 15 +++++++++++++++ 5 files changed, 46 insertions(+), 13 deletions(-)
diff --git a/i386/i386/idt.c b/i386/i386/idt.c index cdfb9a88..caa44d71 100644 --- a/i386/i386/idt.c +++ b/i386/i386/idt.c @@ -34,6 +34,10 @@ struct idt_init_entry unsigned long entrypoint; unsigned short vector; unsigned short type; +#ifdef __x86_64__ + unsigned short ist; + unsigned short pad_0; +#endif }; extern struct idt_init_entry idt_inittab[]; @@ -49,7 +53,13 @@ idt_fill(struct real_gate *myidt) /* Initialize the exception vectors from the idt_inittab. */ while (iie->entrypoint) { - fill_idt_gate(myidt, iie->vector, iie->entrypoint, KERNEL_CS, iie->type, 0); + fill_idt_gate(myidt, iie->vector, iie->entrypoint, KERNEL_CS, iie->type, +#ifdef __x86_64__ + iie->ist +#else + 0 +#endif + ); iie++; } diff --git a/i386/i386/ktss.c b/i386/i386/ktss.c index 1d880167..52f3722c 100644 --- a/i386/i386/ktss.c +++ b/i386/i386/ktss.c @@ -61,6 +61,7 @@ ktss_fill(struct task_tss *myktss, struct real_descriptor *mygdt) /* Initialize the master TSS. */ #ifdef __x86_64__ myktss->tss.rsp0 = (unsigned long)(exception_stack+1024); + myktss->tss.ist1 = (unsigned long)(exception_stack+1024); #else /* ! __x86_64__ */ myktss->tss.ss0 = KERNEL_DS; myktss->tss.esp0 = (unsigned long)(exception_stack+1024); diff --git a/i386/i386/trap.c b/i386/i386/trap.c index f7bd8e38..b3689c9a 100644 --- a/i386/i386/trap.c +++ b/i386/i386/trap.c @@ -666,3 +666,9 @@ db_debug_all_traps (boolean_t enable) } #endif /* MACH_KDB */ + +void handle_double_fault(struct i386_saved_state *regs) +{ + dump_ss(regs); + panic("DOUBLE FAULT! This is critical\n"); +} diff --git a/x86_64/idt_inittab.S b/x86_64/idt_inittab.S index f021b56d..fc1df0c7 100644 --- a/x86_64/idt_inittab.S +++ b/x86_64/idt_inittab.S @@ -50,12 +50,13 @@ ENTRY(idt_inittab) .quad entry ;\ .text #else /* MACH_PV_DESCRIPTORS */ -#define IDT_ENTRY(n,entry,type) \ +#define IDT_ENTRY(n,entry,type,ist) \ .data 2 ;\ .quad entry ;\ .word n ;\ .word type ;\ - .long 0 /*pad*/ ;\ + .word ist ;\ + .word 0 /*pad*/ ;\ .text #endif /* MACH_PV_DESCRIPTORS */ @@ -63,7 +64,7 @@ ENTRY(idt_inittab) * No error code. Clear error code and push trap number. */ #define EXCEPTION(n,name) \ - IDT_ENTRY(n,EXT(name),ACC_PL_K|ACC_TRAP_GATE);\ + IDT_ENTRY(n,EXT(name),ACC_PL_K|ACC_TRAP_GATE, 0);\ ENTRY(name) ;\ INT_FIX ;\ pushq $(0) ;\ @@ -74,7 +75,7 @@ ENTRY(name) ;\ * User-accessible exception. Otherwise, same as above. */ #define EXCEP_USR(n,name) \ - IDT_ENTRY(n,EXT(name),ACC_PL_U|ACC_TRAP_GATE);\ + IDT_ENTRY(n,EXT(name),ACC_PL_U|ACC_TRAP_GATE, 0);\ ENTRY(name) ;\ INT_FIX ;\ pushq $(0) ;\ @@ -85,7 +86,7 @@ ENTRY(name) ;\ * Error code has been pushed. Just push trap number. */ #define EXCEP_ERR(n,name) \ - IDT_ENTRY(n,EXT(name),ACC_PL_K|ACC_INTR_GATE);\ + IDT_ENTRY(n,EXT(name),ACC_PL_K|ACC_INTR_GATE, 0);\ ENTRY(name) ;\ INT_FIX ;\ pushq $(n) ;\ @@ -95,25 +96,25 @@ ENTRY(name) ;\ * Special interrupt code: dispatches to a unique entrypoint, * not defined automatically here. */ -#define EXCEP_SPC(n,name) \ - IDT_ENTRY(n,EXT(name),ACC_PL_K|ACC_TRAP_GATE) +#define EXCEP_SPC(n,name, ist) \ + IDT_ENTRY(n,EXT(name),ACC_PL_K|ACC_TRAP_GATE, ist) EXCEPTION(0x00,t_zero_div) -EXCEP_SPC(0x01,t_debug) +EXCEP_SPC(0x01,t_debug, 0) /* skip NMI interrupt - let more specific code figure that out. */ EXCEP_USR(0x03,t_int3) EXCEP_USR(0x04,t_into) EXCEP_USR(0x05,t_bounds) EXCEPTION(0x06,t_invop) EXCEPTION(0x07,t_nofpu) -EXCEPTION(0x08,a_dbl_fault) +EXCEP_SPC(0x08,t_dbl_fault, 1) EXCEPTION(0x09,a_fpu_over) EXCEPTION(0x0a,a_inv_tss) -EXCEP_SPC(0x0b,t_segnp) +EXCEP_SPC(0x0b,t_segnp, 0) EXCEP_ERR(0x0c,t_stack_fault) -EXCEP_SPC(0x0d,t_gen_prot) -EXCEP_SPC(0x0e,t_page_fault) +EXCEP_SPC(0x0d,t_gen_prot, 0) +EXCEP_SPC(0x0e,t_page_fault, 0) #ifdef MACH_PV_DESCRIPTORS EXCEP_ERR(0x0f,t_trap_0f) #else diff --git a/x86_64/locore.S b/x86_64/locore.S index 4d61d618..a6697fb9 100644 --- a/x86_64/locore.S +++ b/x86_64/locore.S @@ -345,6 +345,21 @@ ENTRY(start_timer) * */ +/* Try to save/show some information when a double fault happens + * We can't recover to a working state, so if we have a debugger wait for it, + * otherwise reset */ +ENTRY(t_dbl_fault) + INT_FIX + cli /* disable interrupts that might corrupt the state*/ + pusha + movq %cr2,%rax + movq %rax,R_CR2-R_R15(%rsp) /* CR2 might contain the faulting address */ + subq $48,%rsp // FIXME remove when segments are cleaned up + movq %rsp,%rdi /* pass the saved state */ + call handle_double_fault + jmp cpu_shutdown /* reset */ +END(t_dbl_fault) + /* * General protection or segment-not-present fault. * Check for a GP/NP fault in the kernel_return -- 2.39.2