diff options
author | jhb <jhb@FreeBSD.org> | 2007-04-24 21:17:45 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2007-04-24 21:17:45 +0000 |
commit | f98690eabe7bc9093b1dea1146b7389b03ead106 (patch) | |
tree | 610a4c73d0d92b669772e965766a2e350f38bff5 | |
parent | cafaaf6a395869780ffefbd420ab2cc885b4852f (diff) | |
download | FreeBSD-src-f98690eabe7bc9093b1dea1146b7389b03ead106.zip FreeBSD-src-f98690eabe7bc9093b1dea1146b7389b03ead106.tar.gz |
Fix the triple fault used as a last resort during a reboot to actually
fault. The previous method zero'd out the page tables, invalidated the
TLB, and then entered a spin loop. The idea was that the instruction after
the TLB invalidate would result in a page fault and the page fault and
subsequent double fault wouldn't be able to determine the physical page
for their fault handlers' first instruction. This stopped working when
PGE (PG_G PTE/PDE bit) support was added as a TLB invalidate via %cr3
reload doesn't clear TLB entries with PG_G set. Thus, the CPU was still
able to map the virtual address for the spin loop and happily performed
its infinite loop.
The triple fault now uses a much more deterministic sledge-hammer approach
to generate a triple fault. First, the IDT descriptor is set to point to
an empty IDT, so any interrupts (including a double fault) will instantly
fault. Second, we trigger a int 3 breakpoint to force an interrupt and
kick off a triple fault.
MFC after: 3 days
-rw-r--r-- | sys/amd64/amd64/vm_machdep.c | 11 | ||||
-rw-r--r-- | sys/i386/i386/vm_machdep.c | 10 |
2 files changed, 15 insertions, 6 deletions
diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c index 0bc3604..ad256b4 100644 --- a/sys/amd64/amd64/vm_machdep.c +++ b/sys/amd64/amd64/vm_machdep.c @@ -457,6 +457,7 @@ cpu_reset() static void cpu_reset_real() { + struct region_descriptor null_idt; int b; disable_intr(); @@ -494,14 +495,18 @@ cpu_reset_real() outb(0x92, b | 0x1); DELAY(500000); /* wait 0.5 sec to see if that did it */ } + printf("No known reset method worked, attempting CPU shutdown\n"); DELAY(1000000); /* wait 1 sec for printf to complete */ - /* Force a shutdown by unmapping entire address space. */ - bzero((caddr_t)PML4map, PAGE_SIZE); + /* Wipe the IDT. */ + null_idt.rd_limit = 0; + null_idt.rd_base = 0; + lidt(&null_idt); /* "good night, sweet prince .... <THUNK!>" */ - invltlb(); + breakpoint(); + /* NOTREACHED */ while(1); } diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c index 25e5a96..b0e4d21 100644 --- a/sys/i386/i386/vm_machdep.c +++ b/sys/i386/i386/vm_machdep.c @@ -590,6 +590,7 @@ cpu_reset() static void cpu_reset_real() { + struct region_descriptor null_idt; #ifndef PC98 int b; #endif @@ -656,11 +657,14 @@ cpu_reset_real() printf("No known reset method worked, attempting CPU shutdown\n"); DELAY(1000000); /* wait 1 sec for printf to complete */ - /* Force a shutdown by unmapping entire address space. */ - bzero((caddr_t)PTD, NBPTD); + /* Wipe the IDT. */ + null_idt.rd_limit = 0; + null_idt.rd_base = 0; + lidt(&null_idt); /* "good night, sweet prince .... <THUNK!>" */ - invltlb(); + breakpoint(); + /* NOTREACHED */ while(1); } |