patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / x86_64 / kernel / reboot.c
1 /* Various gunk just to reboot the machine. */ 
2 #include <linux/module.h>
3 #include <linux/reboot.h>
4 #include <linux/init.h>
5 #include <linux/smp.h>
6 #include <linux/kernel.h>
7 #include <linux/ctype.h>
8 #include <linux/string.h>
9 #include <asm/io.h>
10 #include <asm/kdebug.h>
11 #include <asm/delay.h>
12 #include <asm/hw_irq.h>
13 #include <asm/system.h>
14 #include <asm/pgtable.h>
15 #include <asm/tlbflush.h>
16 #include <asm/apic.h>
17
18 /*
19  * Power off function, if any
20  */
21 void (*pm_power_off)(void);
22
23 static long no_idt[3];
24 static enum { 
25         BOOT_BIOS = 'b',
26         BOOT_TRIPLE = 't',
27         BOOT_KBD = 'k'
28 } reboot_type = BOOT_KBD;
29 static int reboot_mode = 0;
30
31 /* reboot=b[ios] | t[riple] | k[bd] [, [w]arm | [c]old]
32    bios   Use the CPU reboot vector for warm reset
33    warm   Don't set the cold reboot flag
34    cold   Set the cold reboot flag
35    triple Force a triple fault (init)
36    kbd    Use the keyboard controller. cold reset (default)
37  */ 
38 static int __init reboot_setup(char *str)
39 {
40         for (;;) {
41                 switch (*str) {
42                 case 'w': 
43                         reboot_mode = 0x1234;
44                         break;
45
46                 case 'c':
47                         reboot_mode = 0;
48                         break;
49
50                 case 't':
51                 case 'b':
52                 case 'k':
53                         reboot_type = *str;
54                         break;
55                 }
56                 if((str = strchr(str,',')) != NULL)
57                         str++;
58                 else
59                         break;
60         }
61         return 1;
62 }
63
64 __setup("reboot=", reboot_setup);
65
66 /* overwrites random kernel memory. Should not be kernel .text */
67 #define WARMBOOT_TRAMP 0x1000UL
68
69 static void reboot_warm(void)
70 {
71         extern unsigned char warm_reboot[], warm_reboot_end[];
72         printk("warm reboot\n");
73
74         local_irq_disable(); 
75                 
76         /* restore identity mapping */
77         init_level4_pgt[0] = __pml4(__pa(level3_ident_pgt) | 7); 
78         __flush_tlb_all(); 
79
80         /* Move the trampoline to low memory */
81         memcpy(__va(WARMBOOT_TRAMP), warm_reboot, warm_reboot_end - warm_reboot); 
82
83         /* Start it in compatibility mode. */
84         asm volatile( "   pushq $0\n"           /* ss */
85                      "   pushq $0x2000\n"       /* rsp */
86                      "   pushfq\n"              /* eflags */
87                      "   pushq %[cs]\n"
88                      "   pushq %[target]\n"
89                      "   iretq" :: 
90                       [cs] "i" (__KERNEL_COMPAT32_CS), 
91                       [target] "b" (WARMBOOT_TRAMP));
92 }
93
94 #ifdef CONFIG_SMP
95 static void smp_halt(void)
96 {
97         int cpuid = safe_smp_processor_id(); 
98                 static int first_entry = 1;
99
100                 if (first_entry) { 
101                         first_entry = 0;
102                         smp_call_function((void *)machine_restart, NULL, 1, 0);
103                 } 
104                         
105         smp_stop_cpu(); 
106
107         /* AP calling this. Just halt */
108         if (cpuid != boot_cpu_id) { 
109                 for (;;) 
110                         asm("hlt");
111         }
112
113         /* Wait for all other CPUs to have run smp_stop_cpu */
114         while (!cpus_empty(cpu_online_map))
115                 rep_nop(); 
116 }
117 #endif
118
119 static inline void kb_wait(void)
120 {
121         int i;
122
123         for (i=0; i<0x10000; i++)
124                 if ((inb_p(0x64) & 0x02) == 0)
125                         break;
126 }
127
128 void machine_restart(char * __unused)
129 {
130         int i;
131
132 #ifdef CONFIG_SMP
133         smp_halt(); 
134 #endif
135
136         local_irq_disable();
137        
138 #ifndef CONFIG_SMP
139         disable_local_APIC();
140 #endif
141
142         disable_IO_APIC();
143         
144         local_irq_enable();
145         
146         /* Tell the BIOS if we want cold or warm reboot */
147         *((unsigned short *)__va(0x472)) = reboot_mode;
148        
149         for (;;) {
150                 /* Could also try the reset bit in the Hammer NB */
151                 switch (reboot_type) { 
152                 case BOOT_BIOS:
153                         reboot_warm();
154
155                 case BOOT_KBD:
156                 for (i=0; i<100; i++) {
157                         kb_wait();
158                         udelay(50);
159                         outb(0xfe,0x64);         /* pulse reset low */
160                         udelay(50);
161                 }
162
163                 case BOOT_TRIPLE: 
164                 __asm__ __volatile__("lidt (%0)": :"r" (&no_idt));
165                 __asm__ __volatile__("int3");
166
167                         reboot_type = BOOT_KBD;
168                         break;
169                 }      
170         }      
171 }
172
173 EXPORT_SYMBOL(machine_restart);
174
175 void machine_halt(void)
176 {
177 }
178
179 EXPORT_SYMBOL(machine_halt);
180
181 void machine_power_off(void)
182 {
183         if (pm_power_off)
184                 pm_power_off();
185 }
186
187 EXPORT_SYMBOL(machine_power_off);