summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86/machine/cpu.c64
-rw-r--r--arch/x86/machine/cpu.h26
-rw-r--r--arch/x86/machine/trap.c51
-rw-r--r--arch/x86/machine/trap.h8
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.
*/