xref: /minix3/minix/kernel/arch/i386/arch_reset.c (revision 9624407e7addfd8b88486acfe3a0e056e2b92ee3)
1*433d6423SLionel Sambuc 
2*433d6423SLionel Sambuc #include "kernel/kernel.h"
3*433d6423SLionel Sambuc 
4*433d6423SLionel Sambuc #include <ctype.h>
5*433d6423SLionel Sambuc #include <string.h>
6*433d6423SLionel Sambuc #include <machine/cmos.h>
7*433d6423SLionel Sambuc #include <machine/bios.h>
8*433d6423SLionel Sambuc #include <machine/cpu.h>
9*433d6423SLionel Sambuc #include <minix/cpufeature.h>
10*433d6423SLionel Sambuc #include <sys/reboot.h>
11*433d6423SLionel Sambuc #include <assert.h>
12*433d6423SLionel Sambuc #include <signal.h>
13*433d6423SLionel Sambuc 
14*433d6423SLionel Sambuc #include <minix/u64.h>
15*433d6423SLionel Sambuc 
16*433d6423SLionel Sambuc #include "arch_proto.h"
17*433d6423SLionel Sambuc #include "oxpcie.h"
18*433d6423SLionel Sambuc #include "direct_utils.h"
19*433d6423SLionel Sambuc 
20*433d6423SLionel Sambuc #ifdef USE_ACPI
21*433d6423SLionel Sambuc #include "acpi.h"
22*433d6423SLionel Sambuc #endif
23*433d6423SLionel Sambuc 
24*433d6423SLionel Sambuc #define     KBCMDP          4       /* kbd controller port (O) */
25*433d6423SLionel Sambuc #define      KBC_PULSE0     0xfe    /* pulse output bit 0 */
26*433d6423SLionel Sambuc #define      IO_KBD          0x060           /* 8042 Keyboard */
27*433d6423SLionel Sambuc 
28*433d6423SLionel Sambuc int cpu_has_tsc;
29*433d6423SLionel Sambuc 
30*433d6423SLionel Sambuc void
reset(void)31*433d6423SLionel Sambuc reset(void)
32*433d6423SLionel Sambuc {
33*433d6423SLionel Sambuc         uint8_t b;
34*433d6423SLionel Sambuc         /*
35*433d6423SLionel Sambuc          * The keyboard controller has 4 random output pins, one of which is
36*433d6423SLionel Sambuc          * connected to the RESET pin on the CPU in many PCs.  We tell the
37*433d6423SLionel Sambuc          * keyboard controller to pulse this line a couple of times.
38*433d6423SLionel Sambuc          */
39*433d6423SLionel Sambuc         outb(IO_KBD + KBCMDP, KBC_PULSE0);
40*433d6423SLionel Sambuc         busy_delay_ms(100);
41*433d6423SLionel Sambuc         outb(IO_KBD + KBCMDP, KBC_PULSE0);
42*433d6423SLionel Sambuc         busy_delay_ms(100);
43*433d6423SLionel Sambuc 
44*433d6423SLionel Sambuc         /*
45*433d6423SLionel Sambuc          * Attempt to force a reset via the Reset Control register at
46*433d6423SLionel Sambuc          * I/O port 0xcf9.  Bit 2 forces a system reset when it
47*433d6423SLionel Sambuc          * transitions from 0 to 1.  Bit 1 selects the type of reset
48*433d6423SLionel Sambuc          * to attempt: 0 selects a "soft" reset, and 1 selects a
49*433d6423SLionel Sambuc          * "hard" reset.  We try a "hard" reset.  The first write sets
50*433d6423SLionel Sambuc          * bit 1 to select a "hard" reset and clears bit 2.  The
51*433d6423SLionel Sambuc          * second write forces a 0 -> 1 transition in bit 2 to trigger
52*433d6423SLionel Sambuc          * a reset.
53*433d6423SLionel Sambuc          */
54*433d6423SLionel Sambuc         outb(0xcf9, 0x2);
55*433d6423SLionel Sambuc         outb(0xcf9, 0x6);
56*433d6423SLionel Sambuc         busy_delay_ms(500);  /* wait 0.5 sec to see if that did it */
57*433d6423SLionel Sambuc 
58*433d6423SLionel Sambuc         /*
59*433d6423SLionel Sambuc          * Attempt to force a reset via the Fast A20 and Init register
60*433d6423SLionel Sambuc          * at I/O port 0x92.  Bit 1 serves as an alternate A20 gate.
61*433d6423SLionel Sambuc          * Bit 0 asserts INIT# when set to 1.  We are careful to only
62*433d6423SLionel Sambuc          * preserve bit 1 while setting bit 0.  We also must clear bit
63*433d6423SLionel Sambuc          * 0 before setting it if it isn't already clear.
64*433d6423SLionel Sambuc          */
65*433d6423SLionel Sambuc         b = inb(0x92);
66*433d6423SLionel Sambuc         if (b != 0xff) {
67*433d6423SLionel Sambuc                 if ((b & 0x1) != 0)
68*433d6423SLionel Sambuc                         outb(0x92, b & 0xfe);
69*433d6423SLionel Sambuc                 outb(0x92, b | 0x1);
70*433d6423SLionel Sambuc                 busy_delay_ms(500);  /* wait 0.5 sec to see if that did it */
71*433d6423SLionel Sambuc         }
72*433d6423SLionel Sambuc 
73*433d6423SLionel Sambuc 	/* Triple fault */
74*433d6423SLionel Sambuc 	x86_triplefault();
75*433d6423SLionel Sambuc 
76*433d6423SLionel Sambuc 	/* Give up on resetting */
77*433d6423SLionel Sambuc 	while(1) {
78*433d6423SLionel Sambuc 		;
79*433d6423SLionel Sambuc 	}
80*433d6423SLionel Sambuc }
81*433d6423SLionel Sambuc 
82*433d6423SLionel Sambuc static __dead void
halt(void)83*433d6423SLionel Sambuc halt(void)
84*433d6423SLionel Sambuc {
85*433d6423SLionel Sambuc 	for ( ; ; )
86*433d6423SLionel Sambuc 		halt_cpu();
87*433d6423SLionel Sambuc }
88*433d6423SLionel Sambuc 
89*433d6423SLionel Sambuc static __dead void
poweroff(void)90*433d6423SLionel Sambuc poweroff(void)
91*433d6423SLionel Sambuc {
92*433d6423SLionel Sambuc 	const char *shutdown_str;
93*433d6423SLionel Sambuc 
94*433d6423SLionel Sambuc #ifdef USE_ACPI
95*433d6423SLionel Sambuc 	acpi_poweroff();
96*433d6423SLionel Sambuc #endif
97*433d6423SLionel Sambuc 	/* Bochs/QEMU poweroff */
98*433d6423SLionel Sambuc 	shutdown_str = "Shutdown";
99*433d6423SLionel Sambuc         while (*shutdown_str) outb(0x8900, *(shutdown_str++));
100*433d6423SLionel Sambuc 
101*433d6423SLionel Sambuc 	/* VMware magic power off; likely to halt CPU */
102*433d6423SLionel Sambuc 	poweroff_vmware_clihlt();
103*433d6423SLionel Sambuc 
104*433d6423SLionel Sambuc 	/* fallback option: hang */
105*433d6423SLionel Sambuc 	halt();
106*433d6423SLionel Sambuc }
107*433d6423SLionel Sambuc 
arch_shutdown(int how)108*433d6423SLionel Sambuc __dead void arch_shutdown(int how)
109*433d6423SLionel Sambuc {
110*433d6423SLionel Sambuc 	unsigned char unused_ch;
111*433d6423SLionel Sambuc 	/* Mask all interrupts, including the clock. */
112*433d6423SLionel Sambuc 	outb( INT_CTLMASK, ~0);
113*433d6423SLionel Sambuc 
114*433d6423SLionel Sambuc 	/* Empty buffer */
115*433d6423SLionel Sambuc 	while(direct_read_char(&unused_ch))
116*433d6423SLionel Sambuc 		;
117*433d6423SLionel Sambuc 
118*433d6423SLionel Sambuc 	if(kinfo.minix_panicing) {
119*433d6423SLionel Sambuc 		/* Printing is done synchronously over serial. */
120*433d6423SLionel Sambuc 		if (kinfo.do_serial_debug)
121*433d6423SLionel Sambuc 			reset();
122*433d6423SLionel Sambuc 
123*433d6423SLionel Sambuc 		/* Print accumulated diagnostics buffer and reset. */
124*433d6423SLionel Sambuc 		direct_cls();
125*433d6423SLionel Sambuc 		direct_print("Minix panic. System diagnostics buffer:\n\n");
126*433d6423SLionel Sambuc 		direct_print(kmess.kmess_buf);
127*433d6423SLionel Sambuc 		direct_print("\nSystem has panicked, press any key to reboot");
128*433d6423SLionel Sambuc 		while (!direct_read_char(&unused_ch))
129*433d6423SLionel Sambuc 			;
130*433d6423SLionel Sambuc 		reset();
131*433d6423SLionel Sambuc 	}
132*433d6423SLionel Sambuc 
133*433d6423SLionel Sambuc 	if((how & RB_POWERDOWN) == RB_POWERDOWN) {
134*433d6423SLionel Sambuc 		/* Power off if possible, hang otherwise */
135*433d6423SLionel Sambuc 		poweroff();
136*433d6423SLionel Sambuc 		NOT_REACHABLE;
137*433d6423SLionel Sambuc 	}
138*433d6423SLionel Sambuc 
139*433d6423SLionel Sambuc 	if(how & RB_HALT) {
140*433d6423SLionel Sambuc 		/* Hang */
141*433d6423SLionel Sambuc 		for (; ; ) halt_cpu();
142*433d6423SLionel Sambuc 		NOT_REACHABLE;
143*433d6423SLionel Sambuc 	}
144*433d6423SLionel Sambuc 
145*433d6423SLionel Sambuc 	/* Reset the system by forcing a processor shutdown.
146*433d6423SLionel Sambuc 	 * First stop the BIOS memory test by setting a soft
147*433d6423SLionel Sambuc 	 * reset flag.
148*433d6423SLionel Sambuc 	 */
149*433d6423SLionel Sambuc 	reset();
150*433d6423SLionel Sambuc 	NOT_REACHABLE;
151*433d6423SLionel Sambuc }
152*433d6423SLionel Sambuc 
153*433d6423SLionel Sambuc #ifdef DEBUG_SERIAL
ser_putc(char c)154*433d6423SLionel Sambuc void ser_putc(char c)
155*433d6423SLionel Sambuc {
156*433d6423SLionel Sambuc         int i;
157*433d6423SLionel Sambuc         int lsr, thr;
158*433d6423SLionel Sambuc 
159*433d6423SLionel Sambuc #if CONFIG_OXPCIE
160*433d6423SLionel Sambuc         oxpcie_putc(c);
161*433d6423SLionel Sambuc #else
162*433d6423SLionel Sambuc         lsr= COM1_LSR;
163*433d6423SLionel Sambuc         thr= COM1_THR;
164*433d6423SLionel Sambuc         for (i= 0; i<100000; i++)
165*433d6423SLionel Sambuc         {
166*433d6423SLionel Sambuc                 if (inb( lsr) & LSR_THRE)
167*433d6423SLionel Sambuc                         break;
168*433d6423SLionel Sambuc         }
169*433d6423SLionel Sambuc         outb( thr, c);
170*433d6423SLionel Sambuc #endif
171*433d6423SLionel Sambuc }
172*433d6423SLionel Sambuc 
173*433d6423SLionel Sambuc #endif
174