- Updated to 2.6.22-rc2-git7:
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / core / machine_reboot.c
1 #include <linux/version.h>
2 #include <linux/kernel.h>
3 #include <linux/mm.h>
4 #include <linux/unistd.h>
5 #include <linux/module.h>
6 #include <linux/reboot.h>
7 #include <linux/sysrq.h>
8 #include <linux/stringify.h>
9 #include <linux/stop_machine.h>
10 #include <asm/irq.h>
11 #include <asm/mmu_context.h>
12 #include <xen/evtchn.h>
13 #include <asm/hypervisor.h>
14 #include <xen/xenbus.h>
15 #include <linux/cpu.h>
16 #include <linux/kthread.h>
17 #include <xen/gnttab.h>
18 #include <xen/xencons.h>
19 #include <xen/cpu_hotplug.h>
20 #include <xen/interface/vcpu.h>
21
22 #if defined(__i386__) || defined(__x86_64__)
23
24 /*
25  * Power off function, if any
26  */
27 void (*pm_power_off)(void);
28 EXPORT_SYMBOL(pm_power_off);
29
30 void machine_emergency_restart(void)
31 {
32         /* We really want to get pending console data out before we die. */
33         xencons_force_flush();
34         HYPERVISOR_shutdown(SHUTDOWN_reboot);
35 }
36
37 void machine_restart(char * __unused)
38 {
39         machine_emergency_restart();
40 }
41
42 void machine_halt(void)
43 {
44         machine_power_off();
45 }
46
47 void machine_power_off(void)
48 {
49         /* We really want to get pending console data out before we die. */
50         xencons_force_flush();
51         if (pm_power_off)
52                 pm_power_off();
53         HYPERVISOR_shutdown(SHUTDOWN_poweroff);
54 }
55
56 int reboot_thru_bios = 0;       /* for dmi_scan.c */
57 EXPORT_SYMBOL(machine_restart);
58 EXPORT_SYMBOL(machine_halt);
59 EXPORT_SYMBOL(machine_power_off);
60
61 static void pre_suspend(void)
62 {
63         HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page;
64         HYPERVISOR_update_va_mapping(fix_to_virt(FIX_SHARED_INFO),
65                                      __pte_ma(0), 0);
66
67         xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn);
68         xen_start_info->console.domU.mfn =
69                 mfn_to_pfn(xen_start_info->console.domU.mfn);
70 }
71
72 static void post_suspend(int suspend_cancelled)
73 {
74         int i, j, k, fpp;
75         unsigned long shinfo_mfn;
76         extern unsigned long max_pfn;
77         extern unsigned long *pfn_to_mfn_frame_list_list;
78         extern unsigned long *pfn_to_mfn_frame_list[];
79
80         if (suspend_cancelled) {
81                 xen_start_info->store_mfn =
82                         pfn_to_mfn(xen_start_info->store_mfn);
83                 xen_start_info->console.domU.mfn =
84                         pfn_to_mfn(xen_start_info->console.domU.mfn);
85         } else {
86 #ifdef CONFIG_SMP
87                 cpu_initialized_map = cpu_online_map;
88 #endif
89         }
90
91         shinfo_mfn = xen_start_info->shared_info >> PAGE_SHIFT;
92         HYPERVISOR_update_va_mapping(fix_to_virt(FIX_SHARED_INFO),
93                                      pfn_pte_ma(shinfo_mfn, PAGE_KERNEL), 0);
94         HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO);
95
96         memset(empty_zero_page, 0, PAGE_SIZE);
97
98         fpp = PAGE_SIZE/sizeof(unsigned long);
99         for (i = 0, j = 0, k = -1; i < max_pfn; i += fpp, j++) {
100                 if ((j % fpp) == 0) {
101                         k++;
102                         pfn_to_mfn_frame_list_list[k] =
103                                 virt_to_mfn(pfn_to_mfn_frame_list[k]);
104                         j = 0;
105                 }
106                 pfn_to_mfn_frame_list[k][j] =
107                         virt_to_mfn(&phys_to_machine_mapping[i]);
108         }
109         HYPERVISOR_shared_info->arch.max_pfn = max_pfn;
110         HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
111                 virt_to_mfn(pfn_to_mfn_frame_list_list);
112 }
113
114 #else /* !(defined(__i386__) || defined(__x86_64__)) */
115
116 #define switch_idle_mm()        ((void)0)
117 #define mm_pin_all()            ((void)0)
118 #define pre_suspend()           ((void)0)
119 #define post_suspend(x)         ((void)0)
120
121 #endif
122
123 static int take_machine_down(void *p_fast_suspend)
124 {
125         int fast_suspend = *(int *)p_fast_suspend;
126         int suspend_cancelled, err;
127         extern void time_resume(void);
128
129         if (fast_suspend) {
130                 BUG_ON(!irqs_disabled());
131         } else {
132                 BUG_ON(irqs_disabled());
133
134                 for (;;) {
135                         err = smp_suspend();
136                         if (err)
137                                 return err;
138
139                         xenbus_suspend();
140                         preempt_disable();
141
142                         if (num_online_cpus() == 1)
143                                 break;
144
145                         preempt_enable();
146                         xenbus_suspend_cancel();
147                 }
148
149                 local_irq_disable();
150         }
151
152         mm_pin_all();
153         gnttab_suspend();
154         pre_suspend();
155
156         /*
157          * This hypercall returns 1 if suspend was cancelled or the domain was
158          * merely checkpointed, and 0 if it is resuming in a new domain.
159          */
160         suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
161
162         post_suspend(suspend_cancelled);
163         gnttab_resume();
164         if (!suspend_cancelled) {
165                 irq_resume();
166 #ifdef __x86_64__
167                 /*
168                  * Older versions of Xen do not save/restore the user %cr3.
169                  * We do it here just in case, but there's no need if we are
170                  * in fast-suspend mode as that implies a new enough Xen.
171                  */
172                 if (!fast_suspend) {
173                         struct mmuext_op op;
174                         op.cmd = MMUEXT_NEW_USER_BASEPTR;
175                         op.arg1.mfn = pfn_to_mfn(__pa(__user_pgd(
176                                 current->active_mm->pgd)) >> PAGE_SHIFT);
177                         if (HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF))
178                                 BUG();
179                 }
180 #endif
181         }
182         time_resume();
183
184         if (!fast_suspend)
185                 local_irq_enable();
186
187         return suspend_cancelled;
188 }
189
190 int __xen_suspend(int fast_suspend)
191 {
192         int err, suspend_cancelled;
193
194         BUG_ON(smp_processor_id() != 0);
195         BUG_ON(in_interrupt());
196
197 #if defined(__i386__) || defined(__x86_64__)
198         if (xen_feature(XENFEAT_auto_translated_physmap)) {
199                 printk(KERN_WARNING "Cannot suspend in "
200                        "auto_translated_physmap mode.\n");
201                 return -EOPNOTSUPP;
202         }
203 #endif
204
205         /* If we are definitely UP then 'slow mode' is actually faster. */
206         if (num_possible_cpus() == 1)
207                 fast_suspend = 0;
208
209         if (fast_suspend) {
210                 xenbus_suspend();
211                 err = stop_machine_run(take_machine_down, &fast_suspend, 0);
212                 if (err < 0)
213                         xenbus_suspend_cancel();
214         } else {
215                 err = take_machine_down(&fast_suspend);
216         }
217
218         if (err < 0)
219                 return err;
220
221         suspend_cancelled = err;
222         if (!suspend_cancelled) {
223                 xencons_resume();
224                 xenbus_resume();
225         } else {
226                 xenbus_suspend_cancel();
227         }
228
229         if (!fast_suspend)
230                 smp_resume();
231
232         return 0;
233 }