diff options
-rw-r--r-- | arch/x86/machine/cpu.c | 64 | ||||
-rw-r--r-- | arch/x86/machine/cpu.h | 26 | ||||
-rw-r--r-- | arch/x86/machine/trap.c | 51 | ||||
-rw-r--r-- | arch/x86/machine/trap.h | 8 |
4 files changed, 141 insertions, 8 deletions
diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 62656c33..307911df 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -83,6 +83,12 @@ unsigned int cpu_array_size; */ static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8); +/* + * Double fault handler, and stack for the main processor. + */ +static unsigned long cpu_double_fault_handler; +static char cpu_double_fault_stack[STACK_SIZE] __aligned(DATA_ALIGN); + static void cpu_seg_set_null(char *table, unsigned int selector) { @@ -170,6 +176,7 @@ cpu_init_gdt(struct cpu *cpu) cpu_seg_set_tss(cpu->gdt, CPU_GDT_SEL_TSS, &cpu->tss); #ifndef __LP64__ + cpu_seg_set_tss(cpu->gdt, CPU_GDT_SEL_DF_TSS, &cpu->double_fault_tss); cpu_seg_set_data(cpu->gdt, CPU_GDT_SEL_CPU, (unsigned long)cpu); #endif /* __LP64__ */ @@ -191,9 +198,39 @@ cpu_init_tss(struct cpu *cpu) tss = &cpu->tss; memset(tss, 0, sizeof(*tss)); + +#ifdef __LP64__ + assert(cpu->double_fault_stack != 0); + tss->ist[CPU_TSS_IST_DF] = cpu->double_fault_stack; +#endif /* __LP64__ */ + asm volatile("ltr %w0" : : "q" (CPU_GDT_SEL_TSS)); } +#ifndef __LP64__ +static void __init +cpu_init_double_fault_tss(struct cpu *cpu) +{ + struct cpu_tss *tss; + + assert(cpu_double_fault_handler != 0); + assert(cpu->double_fault_stack != 0); + + tss = &cpu->double_fault_tss; + memset(tss, 0, sizeof(*tss)); + tss->cr3 = cpu_get_cr3(); + tss->eip = cpu_double_fault_handler; + tss->eflags = CPU_EFL_ONE; + tss->ebp = cpu->double_fault_stack + STACK_SIZE; + tss->esp = tss->ebp; + tss->es = CPU_GDT_SEL_DATA; + tss->cs = CPU_GDT_SEL_CODE; + tss->ss = CPU_GDT_SEL_DATA; + tss->ds = CPU_GDT_SEL_DATA; + tss->fs = CPU_GDT_SEL_CPU; +} +#endif /* __LP64__ */ + void cpu_idt_set_gate(unsigned int vector, void (*isr)(void)) { @@ -215,6 +252,24 @@ cpu_idt_set_gate(unsigned int vector, void (*isr)(void)) | ((unsigned long)isr & CPU_DESC_GATE_OFFSET_LOW_MASK); } +void +cpu_idt_set_double_fault(void (*isr)(void)) +{ + struct cpu_gate_desc *desc; + + cpu_double_fault_handler = (unsigned long)isr; + +#ifdef __LP64__ + cpu_idt_set_gate(TRAP_DF, isr); + desc = &cpu_idt[TRAP_DF]; + desc->word2 |= CPU_TSS_IST_DF & CPU_DESC_SEG_IST_MASK; +#else /* __LP64__ */ + desc = &cpu_idt[TRAP_DF]; + desc->word2 = CPU_DESC_PRESENT | CPU_DESC_TYPE_GATE_TASK; + desc->word1 = CPU_GDT_SEL_DF_TSS << 16; +#endif /* __LP64__ */ +} + static void cpu_load_idt(void) { @@ -253,6 +308,9 @@ cpu_init(struct cpu *cpu) cpu_init_gdt(cpu); cpu_init_ldt(); cpu_init_tss(cpu); +#ifndef __LP64__ + cpu_init_double_fault_tss(cpu); +#endif /* __LP64__ */ cpu_load_idt(); eax = 0; @@ -347,6 +405,7 @@ cpu_setup(void) cpu_boot_array_size = 1; cpu_array_size = 1; + cpu_array[0].double_fault_stack = (unsigned long)cpu_double_fault_stack; cpu_init(&cpu_array[0]); } @@ -450,6 +509,11 @@ cpu_mp_start_aps(void) if (cpu->boot_stack == 0) panic("cpu: unable to allocate boot stack for cpu%u", i); + + cpu->double_fault_stack = vm_kmem_alloc(STACK_SIZE); + + if (cpu->double_fault_stack == 0) + panic("cpu: unable to allocate double fault stack for cpu%u", i); } /* Perform the "Universal Start-up Algorithm" */ diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index 1a48b135..14ed73cb 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -29,8 +29,9 @@ #ifdef __LP64__ #define CPU_GDT_SIZE 40 #else /* __LP64__ */ -#define CPU_GDT_SEL_CPU 32 -#define CPU_GDT_SIZE 40 +#define CPU_GDT_SEL_DF_TSS 32 +#define CPU_GDT_SEL_CPU 40 +#define CPU_GDT_SIZE 48 #endif /* __LP64__ */ #define CPU_IDT_SIZE 256 @@ -106,9 +107,10 @@ struct cpu_pseudo_desc { * Gate/segment descriptor bits and masks. */ #define CPU_DESC_TYPE_DATA 0x00000200 -#define CPU_DESC_TYPE_TSS 0x00000900 #define CPU_DESC_TYPE_CODE 0x00000a00 +#define CPU_DESC_TYPE_TSS 0x00000900 #define CPU_DESC_TYPE_GATE_INTR 0x00000e00 +#define CPU_DESC_TYPE_GATE_TASK 0x00000500 #define CPU_DESC_S 0x00001000 #define CPU_DESC_PRESENT 0x00008000 #define CPU_DESC_LONG 0x00200000 @@ -117,6 +119,7 @@ struct cpu_pseudo_desc { #define CPU_DESC_GATE_OFFSET_LOW_MASK 0x0000ffff #define CPU_DESC_GATE_OFFSET_HIGH_MASK 0xffff0000 +#define CPU_DESC_SEG_IST_MASK 0x00000007 #define CPU_DESC_SEG_BASE_LOW_MASK 0x0000ffff #define CPU_DESC_SEG_BASE_MID_MASK 0x00ff0000 #define CPU_DESC_SEG_BASE_HIGH_MASK 0xff000000 @@ -155,16 +158,20 @@ struct cpu_sysseg_desc { #endif /* __LP64__ */ } __packed; +/* + * IST indexes (0 is reserved). + */ +#define CPU_TSS_IST_DF 1 + struct cpu_tss { #ifdef __LP64__ uint32_t reserved0; uint64_t rsp0; uint64_t rsp1; uint64_t rsp2; + uint64_t ist[8]; uint64_t reserved1; - uint64_t ist[7]; - uint64_t reserved2; - uint16_t reserved3; + uint16_t reserved2; #else /* __LP64__ */ uint32_t link; uint32_t esp0; @@ -226,8 +233,14 @@ struct cpu { unsigned int features4; char gdt[CPU_GDT_SIZE] __aligned(8); struct cpu_tss tss; +#ifndef __LP64__ + struct cpu_tss double_fault_tss; +#endif /* __LP64__ */ volatile int state; + + /* The following members have special initialization paths */ unsigned long boot_stack; + unsigned long double_fault_stack; } __aligned(CPU_ALIGN); extern struct cpu cpu_array[MAX_CPUS]; @@ -469,6 +482,7 @@ void cpu_load_gdt(struct cpu *cpu, struct cpu_pseudo_desc *gdtr); * Install an interrupt handler in the IDT. */ void cpu_idt_set_gate(unsigned int vector, void (*isr)(void)); +void cpu_idt_set_double_fault(void (*isr)(void)); /* * Set up the cpu module. diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index 26656204..60139000 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -92,6 +92,52 @@ trap_install(unsigned int vector, trap_isr_fn_t isr, trap_handler_fn_t fn) } static void +trap_double_fault(struct trap_frame *frame) +{ +#ifndef __LP64__ + struct trap_frame frame_store; + struct cpu *cpu; + + /* + * Double faults are catched through a task gate, which makes the given + * frame useless. The interrupted state is automatically saved in the + * main TSS by the processor. Build a proper trap frame from there. + */ + frame = &frame_store; + cpu = cpu_current(); + frame->eax = cpu->tss.eax; + frame->ebx = cpu->tss.ebx; + frame->ecx = cpu->tss.ecx; + frame->edx = cpu->tss.edx; + frame->ebp = cpu->tss.ebp; + frame->esi = cpu->tss.esi; + frame->edi = cpu->tss.edi; + frame->ds = cpu->tss.ds; + frame->es = cpu->tss.es; + frame->fs = cpu->tss.fs; + frame->gs = cpu->tss.gs; + frame->vector = TRAP_DF; + frame->error = 0; + frame->eip = cpu->tss.eip; + frame->cs = cpu->tss.cs; + frame->eflags = cpu->tss.eflags; + frame->esp = cpu->tss.esp; + frame->ss = cpu->tss.ss; +#endif /* __LP64__ */ + + printk("trap: double fault:\n"); + trap_frame_show(frame); + cpu_halt(); +} + +static void __init +trap_install_double_fault(void) +{ + trap_handler_init(&trap_handlers[TRAP_DF], trap_double_fault); + cpu_idt_set_double_fault(trap_isr_double_fault); +} + +static void trap_default(struct trap_frame *frame) { printk("trap: unhandled interrupt or exception:\n"); @@ -120,7 +166,7 @@ trap_setup(void) trap_install(TRAP_BR, trap_isr_bound_range, trap_default); trap_install(TRAP_UD, trap_isr_invalid_opcode, trap_default); trap_install(TRAP_NM, trap_isr_device_not_available, trap_default); - trap_install(TRAP_DF, trap_isr_double_fault, trap_default); + trap_install_double_fault(); trap_install(TRAP_TS, trap_isr_invalid_tss, trap_default); trap_install(TRAP_NP, trap_isr_segment_not_present, trap_default); trap_install(TRAP_SS, trap_isr_stack_segment_fault, trap_default); @@ -204,7 +250,8 @@ trap_frame_show(struct trap_frame *frame) printk("trap: cs: %#010lx\n", frame->cs); printk("trap: eflags: %#010lx\n", frame->eflags); - if (frame->cs & CPU_PL_USER) { + if ((frame->cs & CPU_PL_USER) + || (frame->vector == TRAP_DF)) { printk("trap: esp: %#010lx\n", frame->esp); printk("trap: ss: %#010lx\n", frame->ss); } diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index 778769c1..96ba61f4 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -66,6 +66,7 @@ #ifndef __ASSEMBLER__ #include <kern/macros.h> +#include <kern/printk.h> #ifdef __LP64__ @@ -119,6 +120,13 @@ struct trap_frame { #endif /* __LP64__ */ +static inline void +trap_test_double_fault(void) +{ + printk("trap: double fault test\n"); + asm volatile("movl $0x1234, %esp; push $0"); +} + /* * Set up the trap module. */ |