diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2003-02-19 01:48:59 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-02-19 01:48:59 -0800 |
| commit | 7f754cf427eb4724f6d9066e2cc96a31dfb4ce71 (patch) | |
| tree | 74aa28655bcbffedd6554ac5b802d6e3e5207403 | |
| parent | 58908bd450e44c44bba6809a441402847f8fcaab (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/Makefile | 3 | ||||
| -rw-r--r-- | arch/i386/kernel/cpu/common.c | 4 | ||||
| -rw-r--r-- | arch/i386/kernel/doublefault.c | 65 | ||||
| -rw-r--r-- | arch/i386/kernel/head.S | 7 | ||||
| -rw-r--r-- | arch/i386/kernel/traps.c | 19 | ||||
| -rw-r--r-- | include/asm-i386/desc.h | 6 | ||||
| -rw-r--r-- | include/asm-i386/processor.h | 1 | ||||
| -rw-r--r-- | include/asm-i386/segment.h | 13 |
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) |
