From ca47635d0ac52464047c32b3e1c5a6023b90e1b9 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Mon, 20 Feb 2012 18:58:17 +0100 Subject: [PATCH] try multiple reset methods . fixes reboot-hang under vbox . makes experience nicer under vmware . taken from netbsd reset code --- kernel/arch/i386/arch_clock.c | 10 +++++ kernel/arch/i386/arch_system.c | 56 +++++++++++++++++++++++++++ kernel/arch/i386/include/arch_proto.h | 1 + kernel/arch/i386/klib.S | 6 +-- kernel/proto.h | 1 + 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/kernel/arch/i386/arch_clock.c b/kernel/arch/i386/arch_clock.c index 4af6b35ae..7ac9ae2f7 100644 --- a/kernel/arch/i386/arch_clock.c +++ b/kernel/arch/i386/arch_clock.c @@ -364,3 +364,13 @@ PUBLIC short cpu_load(void) *last_idle = *current_idle; return load; } + +PUBLIC void busy_delay_ms(int ms) +{ + u64_t cycles = ms_2_cpu_time(ms), tsc0, tsc, tsc1; + read_tsc_64(&tsc0); + tsc1 = tsc0 + cycles; + do { read_tsc_64(&tsc); } while(tsc < tsc1); + return; +} + diff --git a/kernel/arch/i386/arch_system.c b/kernel/arch/i386/arch_system.c index 8cc0ab11a..a1ddfa4c1 100644 --- a/kernel/arch/i386/arch_system.c +++ b/kernel/arch/i386/arch_system.c @@ -63,6 +63,62 @@ PUBLIC __dead void arch_monitor(void) monitor(); } +#define KBCMDP 4 /* kbd controller port (O) */ +#define KBC_PULSE0 0xfe /* pulse output bit 0 */ +#define IO_KBD 0x060 /* 8042 Keyboard */ + +void +reset(void) +{ + uint8_t b; + /* + * The keyboard controller has 4 random output pins, one of which is + * connected to the RESET pin on the CPU in many PCs. We tell the + * keyboard controller to pulse this line a couple of times. + */ + outb(IO_KBD + KBCMDP, KBC_PULSE0); + busy_delay_ms(100); + outb(IO_KBD + KBCMDP, KBC_PULSE0); + busy_delay_ms(100); + + /* + * Attempt to force a reset via the Reset Control register at + * I/O port 0xcf9. Bit 2 forces a system reset when it + * transitions from 0 to 1. Bit 1 selects the type of reset + * to attempt: 0 selects a "soft" reset, and 1 selects a + * "hard" reset. We try a "hard" reset. The first write sets + * bit 1 to select a "hard" reset and clears bit 2. The + * second write forces a 0 -> 1 transition in bit 2 to trigger + * a reset. + */ + outb(0xcf9, 0x2); + outb(0xcf9, 0x6); + busy_delay_ms(500); /* wait 0.5 sec to see if that did it */ + + /* + * Attempt to force a reset via the Fast A20 and Init register + * at I/O port 0x92. Bit 1 serves as an alternate A20 gate. + * Bit 0 asserts INIT# when set to 1. We are careful to only + * preserve bit 1 while setting bit 0. We also must clear bit + * 0 before setting it if it isn't already clear. + */ + b = inb(0x92); + if (b != 0xff) { + if ((b & 0x1) != 0) + outb(0x92, b & 0xfe); + outb(0x92, b | 0x1); + busy_delay_ms(500); /* wait 0.5 sec to see if that did it */ + } + + /* Triple fault */ + x86_triplefault(); + + /* Give up on resetting */ + while(1) { + ; + } +} + PRIVATE __dead void arch_bios_poweroff(void) { u32_t cr0; diff --git a/kernel/arch/i386/include/arch_proto.h b/kernel/arch/i386/include/arch_proto.h index 215724e16..f97466fa3 100644 --- a/kernel/arch/i386/include/arch_proto.h +++ b/kernel/arch/i386/include/arch_proto.h @@ -75,6 +75,7 @@ _PROTOTYPE( void exception, (struct exception_frame * frame)); /* klib386.s */ _PROTOTYPE( __dead void monitor, (void) ); _PROTOTYPE( __dead void reset, (void) ); +_PROTOTYPE( __dead void x86_triplefault, (void) ); _PROTOTYPE( void int86, (void) ); _PROTOTYPE( reg_t read_cr0, (void) ); _PROTOTYPE( reg_t read_cr2, (void) ); diff --git a/kernel/arch/i386/klib.S b/kernel/arch/i386/klib.S index 5f7bcfbb3..a46f9e99d 100644 --- a/kernel/arch/i386/klib.S +++ b/kernel/arch/i386/klib.S @@ -499,13 +499,13 @@ ENTRY(mem_rdw) /*===========================================================================*/ -/* reset */ +/* x86_triplefault */ /*===========================================================================*/ /* - * PUBLIC void reset(); + * PUBLIC void x86_triplefault(); * Reset the system by loading IDT with offset 0 and interrupting. */ -ENTRY(reset) +ENTRY(x86_triplefault) lidt idt_zero int $3 /* anything goes, the 386 will not like it */ .data diff --git a/kernel/proto.h b/kernel/proto.h index b38e2c30d..abe52df3d 100644 --- a/kernel/proto.h +++ b/kernel/proto.h @@ -230,6 +230,7 @@ _PROTOTYPE(void disable_fpu_exception, (void)); _PROTOTYPE(void release_fpu, (struct proc * p)); _PROTOTYPE(void arch_pause,(void)); _PROTOTYPE(short cpu_load, (void)); +_PROTOTYPE(void busy_delay_ms, (int ms)); /* utility.c */ _PROTOTYPE( void cpu_print_freq, (unsigned cpu));