Commit 7f754cf4 authored by Linus Torvalds's avatar Linus Torvalds

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.
parent 58908bd4
...@@ -6,7 +6,8 @@ EXTRA_TARGETS := head.o init_task.o ...@@ -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 \ 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 \ 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 += cpu/
obj-y += timers/ obj-y += timers/
......
...@@ -490,6 +490,10 @@ void __init cpu_init (void) ...@@ -490,6 +490,10 @@ void __init cpu_init (void)
load_TR_desc(); load_TR_desc();
load_LDT(&init_mm.context); 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. */ /* Clear %fs and %gs. */
asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs"); asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs");
......
#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)
};
...@@ -476,6 +476,13 @@ ENTRY(cpu_gdt_table) ...@@ -476,6 +476,13 @@ ENTRY(cpu_gdt_table)
.quad 0x00009a0000000000 /* 0xc0 APM CS 16 code (16 bit) */ .quad 0x00009a0000000000 /* 0xc0 APM CS 16 code (16 bit) */
.quad 0x0040920000000000 /* 0xc8 APM DS data */ .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 #if CONFIG_SMP
.fill (NR_CPUS-1)*GDT_ENTRIES,8,0 /* other CPU's GDT */ .fill (NR_CPUS-1)*GDT_ENTRIES,8,0 /* other CPU's GDT */
#endif #endif
......
...@@ -775,7 +775,7 @@ void __init trap_init_f00f_bug(void) ...@@ -775,7 +775,7 @@ void __init trap_init_f00f_bug(void)
} }
#endif #endif
#define _set_gate(gate_addr,type,dpl,addr) \ #define _set_gate(gate_addr,type,dpl,addr,seg) \
do { \ do { \
int __d0, __d1; \ int __d0, __d1; \
__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
...@@ -785,7 +785,7 @@ do { \ ...@@ -785,7 +785,7 @@ do { \
:"=m" (*((long *) (gate_addr))), \ :"=m" (*((long *) (gate_addr))), \
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \ "=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1) \
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
"3" ((char *) (addr)),"2" (__KERNEL_CS << 16)); \ "3" ((char *) (addr)),"2" ((seg) << 16)); \
} while (0) } while (0)
...@@ -797,22 +797,27 @@ do { \ ...@@ -797,22 +797,27 @@ do { \
*/ */
void set_intr_gate(unsigned int n, void *addr) 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) 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) 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) 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) ...@@ -843,7 +848,7 @@ void __init trap_init(void)
set_system_gate(5,&bounds); set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op); set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available); 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(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS); set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present); set_trap_gate(11,&segment_not_present);
......
...@@ -42,11 +42,13 @@ __asm__ __volatile__ ("movw %w3,0(%2)\n\t" \ ...@@ -42,11 +42,13 @@ __asm__ __volatile__ ("movw %w3,0(%2)\n\t" \
"rorl $16,%%eax" \ "rorl $16,%%eax" \
: "=m"(*(n)) : "a" (addr), "r"(n), "ir"(limit), "i"(type)) : "=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) 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); _set_tssldt_desc(&cpu_gdt_table[cpu][GDT_ENTRY_LDT], (int)addr, ((size << 3)-1), 0x82);
......
...@@ -83,6 +83,7 @@ struct cpuinfo_x86 { ...@@ -83,6 +83,7 @@ struct cpuinfo_x86 {
extern struct cpuinfo_x86 boot_cpu_data; extern struct cpuinfo_x86 boot_cpu_data;
extern struct cpuinfo_x86 new_cpu_data; extern struct cpuinfo_x86 new_cpu_data;
extern struct tss_struct init_tss[NR_CPUS]; extern struct tss_struct init_tss[NR_CPUS];
extern struct tss_struct doublefault_tss;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
extern struct cpuinfo_x86 cpu_data[]; extern struct cpuinfo_x86 cpu_data[];
......
...@@ -37,6 +37,13 @@ ...@@ -37,6 +37,13 @@
* 23 - APM BIOS support * 23 - APM BIOS support
* 24 - APM BIOS support * 24 - APM BIOS support
* 25 - 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_ENTRIES 3
#define GDT_ENTRY_TLS_MIN 6 #define GDT_ENTRY_TLS_MIN 6
...@@ -64,10 +71,12 @@ ...@@ -64,10 +71,12 @@
#define GDT_ENTRY_PNPBIOS_BASE (GDT_ENTRY_KERNEL_BASE + 6) #define GDT_ENTRY_PNPBIOS_BASE (GDT_ENTRY_KERNEL_BASE + 6)
#define GDT_ENTRY_APMBIOS_BASE (GDT_ENTRY_KERNEL_BASE + 11) #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) #define GDT_SIZE (GDT_ENTRIES * 8)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment