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