Initial revision
[linux-2.6.git] / arch / xen / kernel / reboot.c
1
2 #define __KERNEL_SYSCALLS__
3 static int errno;
4 #include <linux/errno.h>
5 #include <linux/version.h>
6 #include <linux/kernel.h>
7 #include <linux/mm.h>
8 #include <linux/unistd.h>
9 #include <linux/module.h>
10 #include <linux/reboot.h>
11 #include <linux/sysrq.h>
12 #include <asm/irq.h>
13 #include <asm/mmu_context.h>
14 #include <asm-xen/ctrl_if.h>
15 #include <asm-xen/hypervisor.h>
16 #include <asm-xen/xen-public/dom0_ops.h>
17 #include <asm-xen/linux-public/suspend.h>
18 #include <asm-xen/queues.h>
19
20 void machine_restart(char * __unused)
21 {
22         /* We really want to get pending console data out before we die. */
23         extern void xencons_force_flush(void);
24         xencons_force_flush();
25         HYPERVISOR_reboot();
26 }
27
28 void machine_halt(void)
29 {
30         machine_power_off();
31 }
32
33 void machine_power_off(void)
34 {
35         /* We really want to get pending console data out before we die. */
36         extern void xencons_force_flush(void);
37         xencons_force_flush();
38         HYPERVISOR_shutdown();
39 }
40
41 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
42 int reboot_thru_bios = 0;       /* for dmi_scan.c */
43 EXPORT_SYMBOL(machine_restart);
44 EXPORT_SYMBOL(machine_halt);
45 EXPORT_SYMBOL(machine_power_off);
46 #endif
47
48
49 /******************************************************************************
50  * Stop/pickle callback handling.
51  */
52
53 /* Ignore multiple shutdown requests. */
54 static int shutting_down = -1;
55 static int pending_sysrq = -1;
56
57 static void __do_suspend(void)
58 {
59     int i, j;
60     suspend_record_t *suspend_record;
61
62     /* Hmmm... a cleaner interface to suspend/resume blkdevs would be nice. */
63         /* XXX SMH: yes it would :-( */ 
64 #ifdef CONFIG_XEN_BLKDEV_FRONTEND
65     extern void blkdev_suspend(void);
66     extern void blkdev_resume(void);
67 #else
68 #define blkdev_suspend() do{}while(0)
69 #define blkdev_resume()  do{}while(0)
70 #endif
71
72 #ifdef CONFIG_XEN_NETDEV_FRONTEND
73     extern void netif_suspend(void);
74     extern void netif_resume(void);  
75 #else
76 #define netif_suspend() do{}while(0)
77 #define netif_resume()  do{}while(0)
78 #endif
79
80 #ifdef CONFIG_XEN_USB_FRONTEND
81     extern void usbif_resume();
82 #else
83 #define usbif_resume() do{}while(0)
84 #endif
85
86 #ifdef CONFIG_XEN_BLKDEV_GRANT
87     extern int gnttab_suspend(void);
88     extern int gnttab_resume(void);
89 #else
90 #define gnttab_suspend() do{}while(0)
91 #define gnttab_resume()  do{}while(0)
92 #endif
93
94     extern void time_suspend(void);
95     extern void time_resume(void);
96     extern unsigned long max_pfn;
97     extern unsigned int *pfn_to_mfn_frame_list;
98
99     suspend_record = (suspend_record_t *)__get_free_page(GFP_KERNEL);
100     if ( suspend_record == NULL )
101         goto out;
102
103     suspend_record->nr_pfns = max_pfn; /* final number of pfns */
104
105     __cli();
106
107     netif_suspend();
108
109     blkdev_suspend();
110
111     time_suspend();
112
113     ctrl_if_suspend();
114
115     irq_suspend();
116
117     gnttab_suspend();
118
119     HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page;
120     clear_fixmap(FIX_SHARED_INFO);
121
122     memcpy(&suspend_record->resume_info, &xen_start_info,
123            sizeof(xen_start_info));
124
125     HYPERVISOR_suspend(virt_to_machine(suspend_record) >> PAGE_SHIFT);
126
127     shutting_down = -1; 
128
129     memcpy(&xen_start_info, &suspend_record->resume_info,
130            sizeof(xen_start_info));
131
132 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
133     set_fixmap_ma(FIX_SHARED_INFO, xen_start_info.shared_info);
134 #else
135     set_fixmap(FIX_SHARED_INFO, xen_start_info.shared_info);
136 #endif
137
138     HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO);
139
140     memset(empty_zero_page, 0, PAGE_SIZE);
141
142     for ( i=0, j=0; i < max_pfn; i+=(PAGE_SIZE/sizeof(unsigned long)), j++ )
143     {
144         pfn_to_mfn_frame_list[j] = 
145             virt_to_machine(&phys_to_machine_mapping[i]) >> PAGE_SHIFT;
146     }
147     HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list =
148         virt_to_machine(pfn_to_mfn_frame_list) >> PAGE_SHIFT;
149
150     gnttab_resume();
151
152     irq_resume();
153
154     ctrl_if_resume();
155
156     time_resume();
157
158     blkdev_resume();
159
160     netif_resume();
161
162     usbif_resume();
163
164     __sti();
165
166  out:
167     if ( suspend_record != NULL )
168         free_page((unsigned long)suspend_record);
169 }
170
171 static int shutdown_process(void *__unused)
172 {
173     static char *envp[] = { "HOME=/", "TERM=linux", 
174                             "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
175     static char *restart_argv[]  = { "/sbin/shutdown", "-r", "now", NULL };
176     static char *poweroff_argv[] = { "/sbin/halt",     "-p",        NULL };
177
178     extern asmlinkage long sys_reboot(int magic1, int magic2,
179                                       unsigned int cmd, void *arg);
180
181     daemonize(
182 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
183         "shutdown"
184 #endif
185         );
186
187     switch ( shutting_down )
188     {
189     case CMSG_SHUTDOWN_POWEROFF:
190         if ( execve("/sbin/halt", poweroff_argv, envp) < 0 )
191         {
192             sys_reboot(LINUX_REBOOT_MAGIC1,
193                        LINUX_REBOOT_MAGIC2,
194                        LINUX_REBOOT_CMD_POWER_OFF,
195                        NULL);
196         }
197         break;
198
199     case CMSG_SHUTDOWN_REBOOT:
200         if ( execve("/sbin/shutdown", restart_argv, envp) < 0 )
201         {
202             sys_reboot(LINUX_REBOOT_MAGIC1,
203                        LINUX_REBOOT_MAGIC2,
204                        LINUX_REBOOT_CMD_RESTART,
205                        NULL);
206         }
207         break;
208     }
209
210     shutting_down = -1; /* could try again */
211
212     return 0;
213 }
214
215 static void __shutdown_handler(void *unused)
216 {
217     int err;
218
219     if ( shutting_down != CMSG_SHUTDOWN_SUSPEND )
220     {
221         err = kernel_thread(shutdown_process, NULL, CLONE_FS | CLONE_FILES);
222         if ( err < 0 )
223             printk(KERN_ALERT "Error creating shutdown process!\n");
224     }
225     else
226     {
227         __do_suspend();
228     }
229 }
230
231 static void __sysrq_handler(void *unused)
232 {
233 #ifdef CONFIG_MAGIC_SYSRQ
234 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
235     handle_sysrq(pending_sysrq, NULL, NULL);
236 #else
237     handle_sysrq(pending_sysrq, NULL, NULL, NULL);
238 #endif
239 #endif
240     pending_sysrq = -1;
241 }
242
243 static void shutdown_handler(ctrl_msg_t *msg, unsigned long id)
244 {
245     static DECLARE_WORK(shutdown_work, __shutdown_handler, NULL);
246     static DECLARE_WORK(sysrq_work, __sysrq_handler, NULL);
247
248     if ( (shutting_down == -1) &&
249          ((msg->subtype == CMSG_SHUTDOWN_POWEROFF) ||
250           (msg->subtype == CMSG_SHUTDOWN_REBOOT) ||
251           (msg->subtype == CMSG_SHUTDOWN_SUSPEND)) )
252     {
253         shutting_down = msg->subtype;
254         schedule_work(&shutdown_work);
255     }
256     else if ( (pending_sysrq == -1) && 
257               (msg->subtype == CMSG_SHUTDOWN_SYSRQ) )
258     {
259         pending_sysrq = ((shutdown_sysrq_t *)&msg->msg[0])->key;
260         schedule_work(&sysrq_work);
261     }
262     else
263     {
264         printk("Ignore spurious shutdown request\n");
265     }
266
267     ctrl_if_send_response(msg);
268 }
269
270 static int __init setup_shutdown_event(void)
271 {
272     ctrl_if_register_receiver(CMSG_SHUTDOWN, shutdown_handler, 0);
273     return 0;
274 }
275
276 __initcall(setup_shutdown_event);