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
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/
......
......@@ -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");
......
#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)
.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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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[];
......
......@@ -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)
......
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