Added patch headers.
[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 <xen/clock.h>
17 #include <xen/gnttab.h>
18 #include <xen/xencons.h>
19 #include <xen/cpu_hotplug.h>
20 #include <xen/interface/vcpu.h>
21 #include "../../base/base.h"
22
23 #if defined(__i386__) || defined(__x86_64__)
24 #include <asm/pci_x86.h>
25 /* TBD: Dom0 should propagate the determined value to Xen. */
26 bool port_cf9_safe = false;
27
28 /*
29  * Power off function, if any
30  */
31 void (*pm_power_off)(void);
32 EXPORT_SYMBOL(pm_power_off);
33
34 void machine_emergency_restart(void)
35 {
36         /* We really want to get pending console data out before we die. */
37         xencons_force_flush();
38         HYPERVISOR_shutdown(SHUTDOWN_reboot);
39 }
40
41 void machine_restart(char * __unused)
42 {
43         machine_emergency_restart();
44 }
45
46 void machine_halt(void)
47 {
48         machine_power_off();
49 }
50
51 void machine_power_off(void)
52 {
53         /* We really want to get pending console data out before we die. */
54         xencons_force_flush();
55         if (pm_power_off)
56                 pm_power_off();
57         HYPERVISOR_shutdown(SHUTDOWN_poweroff);
58 }
59
60 int reboot_thru_bios = 0;       /* for dmi_scan.c */
61 EXPORT_SYMBOL(machine_restart);
62 EXPORT_SYMBOL(machine_halt);
63 EXPORT_SYMBOL(machine_power_off);
64
65 #ifdef CONFIG_PM_SLEEP
66 static void pre_suspend(void)
67 {
68         HYPERVISOR_shared_info = (shared_info_t *)empty_zero_page;
69         WARN_ON(HYPERVISOR_update_va_mapping(fix_to_virt(FIX_SHARED_INFO),
70                                              __pte_ma(0), 0));
71
72         xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn);
73         xen_start_info->console.domU.mfn =
74                 mfn_to_pfn(xen_start_info->console.domU.mfn);
75 }
76
77 static void post_suspend(int suspend_cancelled, int fast_suspend)
78 {
79         int i, j, k, fpp;
80         unsigned long shinfo_mfn;
81         extern unsigned long max_pfn;
82         extern unsigned long *pfn_to_mfn_frame_list_list;
83         extern unsigned long **pfn_to_mfn_frame_list;
84
85         if (suspend_cancelled) {
86                 xen_start_info->store_mfn =
87                         pfn_to_mfn(xen_start_info->store_mfn);
88                 xen_start_info->console.domU.mfn =
89                         pfn_to_mfn(xen_start_info->console.domU.mfn);
90         } else {
91 #ifdef CONFIG_SMP
92                 cpumask_copy(vcpu_initialized_mask, cpu_online_mask);
93 #endif
94                 for_each_possible_cpu(i) {
95                         setup_runstate_area(i);
96
97 #ifdef CONFIG_XEN_VCPU_INFO_PLACEMENT
98                         if (fast_suspend && i != smp_processor_id()
99                             && HYPERVISOR_vcpu_op(VCPUOP_down, i, NULL))
100                                 BUG();
101
102                         setup_vcpu_info(i);
103
104                         if (fast_suspend && i != smp_processor_id()
105                             && HYPERVISOR_vcpu_op(VCPUOP_up, i, NULL))
106                                 BUG();
107 #endif
108                 }
109         }
110
111         shinfo_mfn = xen_start_info->shared_info >> PAGE_SHIFT;
112         if (HYPERVISOR_update_va_mapping(fix_to_virt(FIX_SHARED_INFO),
113                                          pfn_pte_ma(shinfo_mfn, PAGE_KERNEL),
114                                          0))
115                 BUG();
116         HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO);
117
118         clear_page(empty_zero_page);
119
120         fpp = PAGE_SIZE/sizeof(unsigned long);
121         for (i = 0, j = 0, k = -1; i < max_pfn; i += fpp, j++) {
122                 if ((j % fpp) == 0) {
123                         k++;
124                         pfn_to_mfn_frame_list_list[k] =
125                                 virt_to_mfn(pfn_to_mfn_frame_list[k]);
126                         j = 0;
127                 }
128                 pfn_to_mfn_frame_list[k][j] =
129                         virt_to_mfn(&phys_to_machine_mapping[i]);
130         }
131         HYPERVISOR_shared_info->arch.max_pfn = max_pfn;
132         HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
133                 virt_to_mfn(pfn_to_mfn_frame_list_list);
134 }
135 #endif
136
137 #else /* !(defined(__i386__) || defined(__x86_64__)) */
138
139 #ifndef HAVE_XEN_PRE_SUSPEND
140 #define xen_pre_suspend()       ((void)0)
141 #endif
142
143 #ifndef HAVE_XEN_POST_SUSPEND
144 #define xen_post_suspend(x)     ((void)0)
145 #endif
146
147 #define switch_idle_mm()        ((void)0)
148 #define mm_pin_all()            ((void)0)
149 #define pre_suspend()           xen_pre_suspend()
150 #define post_suspend(x, f)      xen_post_suspend(x)
151
152 #endif
153
154 #ifdef CONFIG_PM_SLEEP
155 struct suspend {
156         int fast_suspend;
157         void (*resume_notifier)(int);
158 };
159
160 static int take_machine_down(void *_suspend)
161 {
162         struct suspend *suspend = _suspend;
163         int suspend_cancelled;
164
165         BUG_ON(!irqs_disabled());
166
167         mm_pin_all();
168         suspend_cancelled = sysdev_suspend(PMSG_SUSPEND);
169         if (!suspend_cancelled) {
170                 pre_suspend();
171
172                 /*
173                  * This hypercall returns 1 if suspend was cancelled or the domain was
174                  * merely checkpointed, and 0 if it is resuming in a new domain.
175                  */
176                 suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
177         } else
178                 BUG_ON(suspend_cancelled > 0);
179         suspend->resume_notifier(suspend_cancelled);
180         if (suspend_cancelled >= 0)
181                 post_suspend(suspend_cancelled, suspend->fast_suspend);
182         if (!suspend_cancelled)
183                 xen_clockevents_resume();
184         if (suspend_cancelled >= 0)
185                 sysdev_resume();
186         if (!suspend_cancelled) {
187 #ifdef __x86_64__
188                 /*
189                  * Older versions of Xen do not save/restore the user %cr3.
190                  * We do it here just in case, but there's no need if we are
191                  * in fast-suspend mode as that implies a new enough Xen.
192                  */
193                 if (!suspend->fast_suspend)
194                         xen_new_user_pt(current->active_mm->pgd);
195 #endif
196         }
197
198         return suspend_cancelled;
199 }
200
201 int __xen_suspend(int fast_suspend, void (*resume_notifier)(int))
202 {
203         int err, suspend_cancelled;
204         const char *what;
205         struct suspend suspend;
206
207 #define _check(fn, args...) ({ \
208         what = #fn; \
209         err = (fn)(args); \
210 })
211
212         BUG_ON(smp_processor_id() != 0);
213         BUG_ON(in_interrupt());
214
215 #if defined(__i386__) || defined(__x86_64__)
216         if (xen_feature(XENFEAT_auto_translated_physmap)) {
217                 printk(KERN_WARNING "Cannot suspend in "
218                        "auto_translated_physmap mode.\n");
219                 return -EOPNOTSUPP;
220         }
221 #endif
222
223         /* If we are definitely UP then 'slow mode' is actually faster. */
224         if (num_possible_cpus() == 1)
225                 fast_suspend = 0;
226
227         if (fast_suspend && _check(stop_machine_create)) {
228                 printk(KERN_ERR "%s() failed: %d\n", what, err);
229                 return err;
230         }
231
232         suspend.fast_suspend = fast_suspend;
233         suspend.resume_notifier = resume_notifier;
234
235         if (_check(dpm_suspend_start, PMSG_SUSPEND)) {
236                 if (fast_suspend)
237                         stop_machine_destroy();
238                 printk(KERN_ERR "%s() failed: %d\n", what, err);
239                 return err;
240         }
241
242         if (fast_suspend) {
243                 xenbus_suspend();
244
245                 if (_check(dpm_suspend_noirq, PMSG_SUSPEND)) {
246                         xenbus_suspend_cancel();
247                         dpm_resume_end(PMSG_RESUME);
248                         stop_machine_destroy();
249                         printk(KERN_ERR "%s() failed: %d\n", what, err);
250                         return err;
251                 }
252
253                 err = stop_machine(take_machine_down, &suspend,
254                                    &cpumask_of_cpu(0));
255                 if (err < 0)
256                         xenbus_suspend_cancel();
257         } else {
258                 BUG_ON(irqs_disabled());
259
260                 for (;;) {
261                         xenbus_suspend();
262
263                         if (!_check(dpm_suspend_noirq, PMSG_SUSPEND)
264                             && _check(smp_suspend))
265                                 dpm_resume_noirq(PMSG_RESUME);
266                         if (err) {
267                                 xenbus_suspend_cancel();
268                                 dpm_resume_end(PMSG_RESUME);
269                                 printk(KERN_ERR "%s() failed: %d\n",
270                                        what, err);
271                                 return err;
272                         }
273
274                         preempt_disable();
275
276                         if (num_online_cpus() == 1)
277                                 break;
278
279                         preempt_enable();
280
281                         dpm_resume_noirq(PMSG_RESUME);
282
283                         xenbus_suspend_cancel();
284                 }
285
286                 local_irq_disable();
287                 err = take_machine_down(&suspend);
288                 local_irq_enable();
289         }
290
291         dpm_resume_noirq(PMSG_RESUME);
292
293         if (err >= 0) {
294                 suspend_cancelled = err;
295                 if (!suspend_cancelled) {
296                         xencons_resume();
297                         xenbus_resume();
298                 } else {
299                         xenbus_suspend_cancel();
300                         err = 0;
301                 }
302
303                 if (!fast_suspend)
304                         smp_resume();
305         }
306
307         dpm_resume_end(PMSG_RESUME);
308
309         if (fast_suspend)
310                 stop_machine_destroy();
311
312         return err;
313 }
314 #endif