summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.transmeta.com>2003-02-19 01:48:59 -0800
committerLinus Torvalds <torvalds@home.transmeta.com>2003-02-19 01:48:59 -0800
commit7f754cf427eb4724f6d9066e2cc96a31dfb4ce71 (patch)
tree74aa28655bcbffedd6554ac5b802d6e3e5207403
parent58908bd450e44c44bba6809a441402847f8fcaab (diff)
Add doublefault handling with a task gate.
This potentially helps debugging, since otherwise a double fault would generate a triple fault and then reboot the machine. Now instead it can print out a note about where the problem happened, unless all the kernel data structures are truly buggered.
-rw-r--r--arch/i386/kernel/Makefile3
-rw-r--r--arch/i386/kernel/cpu/common.c4
-rw-r--r--arch/i386/kernel/doublefault.c65
-rw-r--r--arch/i386/kernel/head.S7
-rw-r--r--arch/i386/kernel/traps.c19
-rw-r--r--include/asm-i386/desc.h6
-rw-r--r--include/asm-i386/processor.h1
-rw-r--r--include/asm-i386/segment.h13
8 files changed, 106 insertions, 12 deletions
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index 02e8222ddf37..400829ad9e89 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -6,7 +6,8 @@ EXTRA_TARGETS := head.o init_task.o
obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \
- pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o
+ pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \
+ doublefault.o
obj-y += cpu/
obj-y += timers/
diff --git a/arch/i386/kernel/cpu/common.c b/arch/i386/kernel/cpu/common.c
index e5ae21380f4b..a7845172e7de 100644
--- a/arch/i386/kernel/cpu/common.c
+++ b/arch/i386/kernel/cpu/common.c
@@ -490,6 +490,10 @@ void __init cpu_init (void)
load_TR_desc();
load_LDT(&init_mm.context);
+ /* Set up doublefault TSS pointer in the GDT */
+ __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss);
+ cpu_gdt_table[cpu][GDT_ENTRY_DOUBLEFAULT_TSS].b &= 0xfffffdff;
+
/* Clear %fs and %gs. */
asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs");
diff --git a/arch/i386/kernel/doublefault.c b/arch/i386/kernel/doublefault.c
new file mode 100644
index 000000000000..c8eb444d2eea
--- /dev/null
+++ b/arch/i386/kernel/doublefault.c
@@ -0,0 +1,65 @@
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/init_task.h>
+#include <linux/fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/desc.h>
+
+#define DOUBLEFAULT_STACKSIZE (1024)
+static unsigned long doublefault_stack[DOUBLEFAULT_STACKSIZE];
+#define STACK_START (unsigned long)(doublefault_stack+DOUBLEFAULT_STACKSIZE)
+
+#define ptr_ok(x) ((x) > 0xc0000000 && (x) < 0xc1000000)
+
+static void doublefault_fn(void)
+{
+ struct Xgt_desc_struct gdt_desc = {0, 0};
+ unsigned long gdt, tss;
+
+ __asm__ __volatile__("sgdt %0": "=m" (gdt_desc): :"memory");
+ gdt = gdt_desc.address;
+
+ printk("double fault, gdt at %08lx [%d bytes]\n", gdt, gdt_desc.size);
+
+ if (ptr_ok(gdt)) {
+ gdt += GDT_ENTRY_TSS << 3;
+ tss = *(u16 *)(gdt+2);
+ tss += *(u8 *)(gdt+4) << 16;
+ tss += *(u8 *)(gdt+7) << 24;
+ printk("double fault, tss at %08lx\n", tss);
+
+ if (ptr_ok(tss)) {
+ struct tss_struct *t = (struct tss_struct *)tss;
+
+ printk("eip = %08lx, esp = %08lx\n", t->eip, t->esp);
+
+ printk("eax = %08lx, ebx = %08lx, ecx = %08lx, edx = %08lx\n",
+ t->eax, t->ebx, t->ecx, t->edx);
+ printk("esi = %08lx, edi = %08lx\n",
+ t->esi, t->edi);
+ }
+ }
+
+ for (;;) /* nothing */;
+}
+
+struct tss_struct doublefault_tss __cacheline_aligned = {
+ .esp0 = STACK_START,
+ .ss0 = __KERNEL_DS,
+ .ldt = 0,
+ .bitmap = INVALID_IO_BITMAP_OFFSET,
+ .io_bitmap = { [0 ... IO_BITMAP_SIZE ] = ~0 },
+
+ .eip = (unsigned long) doublefault_fn,
+ .eflags = 0x00000082,
+ .esp = STACK_START,
+ .es = __USER_DS,
+ .cs = __KERNEL_CS,
+ .ss = __KERNEL_DS,
+ .ds = __USER_DS,
+
+ .__cr3 = __pa(swapper_pg_dir)
+};
diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S
index 59c5b1d02a13..c03351ac5a39 100644
--- a/arch/i386/kernel/head.S
+++ b/arch/i386/kernel/head.S
@@ -476,6 +476,13 @@ ENTRY(cpu_gdt_table)
.quad 0x00009a0000000000 /* 0xc0 APM CS 16 code (16 bit) */
.quad 0x0040920000000000 /* 0xc8 APM DS data */
+ .quad 0x0000000000000000 /* 0xd0 - unused */
+ .quad 0x0000000000000000 /* 0xd8 - unused */
+ .quad 0x0000000000000000 /* 0xe0 - unused */
+ .quad 0x0000000000000000 /* 0xe8 - unused */
+ .quad 0x0000000000000000 /* 0xf0 - unused */
+ .quad 0x0000000000000000 /* 0xf8 - GDT entry 31: double-fault TSS */
+
#if CONFIG_SMP
.fill (NR_CPUS-1)*GDT_ENTRIES,8,0 /* other CPU's GDT */
#endif
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index bca393dbee66..210d9069e14d 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -775,7 +775,7 @@ void __init trap_init_f00f_bug(void)
}
#endif
-#define _set_gate(gate_addr,type,dpl,addr) \
+#define _set_gate(gate_addr,type,dpl,addr,seg) \
do { \
int __d0, __d1; \
__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
@@ -785,7 +785,7 @@ do { \
:"=m" (*((long *) (gate_addr))), \
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
- "3" ((char *) (addr)),"2" (__KERNEL_CS << 16)); \
+ "3" ((char *) (addr)),"2" ((seg) << 16)); \
} while (0)
@@ -797,22 +797,27 @@ do { \
*/
void set_intr_gate(unsigned int n, void *addr)
{
- _set_gate(idt_table+n,14,0,addr);
+ _set_gate(idt_table+n,14,0,addr,__KERNEL_CS);
}
static void __init set_trap_gate(unsigned int n, void *addr)
{
- _set_gate(idt_table+n,15,0,addr);
+ _set_gate(idt_table+n,15,0,addr,__KERNEL_CS);
}
static void __init set_system_gate(unsigned int n, void *addr)
{
- _set_gate(idt_table+n,15,3,addr);
+ _set_gate(idt_table+n,15,3,addr,__KERNEL_CS);
}
static void __init set_call_gate(void *a, void *addr)
{
- _set_gate(a,12,3,addr);
+ _set_gate(a,12,3,addr,__KERNEL_CS);
+}
+
+static void __init set_task_gate(unsigned int n, unsigned int gdt_entry)
+{
+ _set_gate(idt_table+n,5,0,0,(gdt_entry<<3));
}
@@ -843,7 +848,7 @@ void __init trap_init(void)
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
- set_trap_gate(8,&double_fault);
+ set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
diff --git a/include/asm-i386/desc.h b/include/asm-i386/desc.h
index d42e28c1e3fa..9361aff2e25c 100644
--- a/include/asm-i386/desc.h
+++ b/include/asm-i386/desc.h
@@ -42,11 +42,13 @@ __asm__ __volatile__ ("movw %w3,0(%2)\n\t" \
"rorl $16,%%eax" \
: "=m"(*(n)) : "a" (addr), "r"(n), "ir"(limit), "i"(type))
-static inline void set_tss_desc(unsigned int cpu, void *addr)
+static inline void __set_tss_desc(unsigned int cpu, unsigned int entry, void *addr)
{
- _set_tssldt_desc(&cpu_gdt_table[cpu][GDT_ENTRY_TSS], (int)addr, 235, 0x89);
+ _set_tssldt_desc(&cpu_gdt_table[cpu][entry], (int)addr, 235, 0x89);
}
+#define set_tss_desc(cpu,addr) __set_tss_desc(cpu, GDT_ENTRY_TSS, addr)
+
static inline void set_ldt_desc(unsigned int cpu, void *addr, unsigned int size)
{
_set_tssldt_desc(&cpu_gdt_table[cpu][GDT_ENTRY_LDT], (int)addr, ((size << 3)-1), 0x82);
diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h
index 83fc8ffb9cab..5ed85117e7ef 100644
--- a/include/asm-i386/processor.h
+++ b/include/asm-i386/processor.h
@@ -83,6 +83,7 @@ struct cpuinfo_x86 {
extern struct cpuinfo_x86 boot_cpu_data;
extern struct cpuinfo_x86 new_cpu_data;
extern struct tss_struct init_tss[NR_CPUS];
+extern struct tss_struct doublefault_tss;
#ifdef CONFIG_SMP
extern struct cpuinfo_x86 cpu_data[];
diff --git a/include/asm-i386/segment.h b/include/asm-i386/segment.h
index 3f272860a6b3..abe3440a93ce 100644
--- a/include/asm-i386/segment.h
+++ b/include/asm-i386/segment.h
@@ -37,6 +37,13 @@
* 23 - APM BIOS support
* 24 - APM BIOS support
* 25 - APM BIOS support
+ *
+ * 26 - unused
+ * 27 - unused
+ * 28 - unused
+ * 29 - unused
+ * 30 - unused
+ * 31 - TSS for double fault handler
*/
#define GDT_ENTRY_TLS_ENTRIES 3
#define GDT_ENTRY_TLS_MIN 6
@@ -64,10 +71,12 @@
#define GDT_ENTRY_PNPBIOS_BASE (GDT_ENTRY_KERNEL_BASE + 6)
#define GDT_ENTRY_APMBIOS_BASE (GDT_ENTRY_KERNEL_BASE + 11)
+#define GDT_ENTRY_DOUBLEFAULT_TSS 31
+
/*
- * The GDT has 25 entries but we pad it to cacheline boundary:
+ * The GDT has 32 entries
*/
-#define GDT_ENTRIES 28
+#define GDT_ENTRIES 32
#define GDT_SIZE (GDT_ENTRIES * 8)