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