blob: 8da1ed1cf3c85113f80ab6667dd47caa0093b5fb [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
2 * Handle extern requests for shutdown, reboot and sysrq
3 */
4#include <linux/kernel.h>
5#include <linux/err.h>
6#include <linux/slab.h>
7#include <linux/reboot.h>
8#include <linux/sysrq.h>
9#include <linux/stop_machine.h>
10#include <linux/freezer.h>
11#include <linux/syscore_ops.h>
12#include <linux/export.h>
13
14#include <xen/xen.h>
15#include <xen/xenbus.h>
16#include <xen/grant_table.h>
17#include <xen/events.h>
18#include <xen/hvc-console.h>
19#include <xen/xen-ops.h>
20
21#include <asm/xen/hypercall.h>
22#include <asm/xen/page.h>
23#include <asm/xen/hypervisor.h>
24
25enum shutdown_state {
26 SHUTDOWN_INVALID = -1,
27 SHUTDOWN_POWEROFF = 0,
28 SHUTDOWN_SUSPEND = 2,
29 /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
30 report a crash, not be instructed to crash!
31 HALT is the same as POWEROFF, as far as we're concerned. The tools use
32 the distinction when we return the reason code to them. */
33 SHUTDOWN_HALT = 4,
34};
35
36/* Ignore multiple shutdown requests. */
37static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
38
39struct suspend_info {
40 int cancelled;
41 unsigned long arg; /* extra hypercall argument */
42 void (*pre)(void);
43 void (*post)(int cancelled);
44};
45
46static void xen_hvm_post_suspend(int cancelled)
47{
48 xen_arch_hvm_post_suspend(cancelled);
49 gnttab_resume();
50}
51
52static void xen_pre_suspend(void)
53{
54 xen_mm_pin_all();
55 gnttab_suspend();
56 xen_arch_pre_suspend();
57}
58
59static void xen_post_suspend(int cancelled)
60{
61 xen_arch_post_suspend(cancelled);
62 gnttab_resume();
63 xen_mm_unpin_all();
64}
65
66#ifdef CONFIG_HIBERNATE_CALLBACKS
67static int xen_suspend(void *data)
68{
69 struct suspend_info *si = data;
70 int err;
71
72 BUG_ON(!irqs_disabled());
73
74 err = syscore_suspend();
75 if (err) {
76 printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n",
77 err);
78 return err;
79 }
80
81 if (si->pre)
82 si->pre();
83
84 /*
85 * This hypercall returns 1 if suspend was cancelled
86 * or the domain was merely checkpointed, and 0 if it
87 * is resuming in a new domain.
88 */
89 si->cancelled = HYPERVISOR_suspend(si->arg);
90
91 if (si->post)
92 si->post(si->cancelled);
93
94 if (!si->cancelled) {
95 xen_irq_resume();
96 xen_console_resume();
97 xen_timer_resume();
98 }
99
100 syscore_resume();
101
102 return 0;
103}
104
105static void do_suspend(void)
106{
107 int err;
108 struct suspend_info si;
109
110 shutting_down = SHUTDOWN_SUSPEND;
111
112 err = freeze_processes();
113 if (err) {
114 printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
115 goto out;
116 }
117
118 err = dpm_suspend_start(PMSG_FREEZE);
119 if (err) {
120 printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
121 goto out_thaw;
122 }
123
124 printk(KERN_DEBUG "suspending xenstore...\n");
125 xs_suspend();
126
127 err = dpm_suspend_end(PMSG_FREEZE);
128 if (err) {
129 printk(KERN_ERR "dpm_suspend_end failed: %d\n", err);
130 si.cancelled = 0;
131 goto out_resume;
132 }
133
134 si.cancelled = 1;
135
136 if (xen_hvm_domain()) {
137 si.arg = 0UL;
138 si.pre = NULL;
139 si.post = &xen_hvm_post_suspend;
140 } else {
141 si.arg = virt_to_mfn(xen_start_info);
142 si.pre = &xen_pre_suspend;
143 si.post = &xen_post_suspend;
144 }
145
146 err = stop_machine(xen_suspend, &si, cpumask_of(0));
147
148 dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
149
150 if (err) {
151 printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
152 si.cancelled = 1;
153 }
154
155out_resume:
156 if (!si.cancelled) {
157 xen_arch_resume();
158 xs_resume();
159 } else
160 xs_suspend_cancel();
161
162 dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
163
164 /* Make sure timer events get retriggered on all CPUs */
165 clock_was_set();
166
167out_thaw:
168 thaw_processes();
169out:
170 shutting_down = SHUTDOWN_INVALID;
171}
172#endif /* CONFIG_HIBERNATE_CALLBACKS */
173
174struct shutdown_handler {
175 const char *command;
176 void (*cb)(void);
177};
178
179static void do_poweroff(void)
180{
181 shutting_down = SHUTDOWN_POWEROFF;
182 orderly_poweroff(false);
183}
184
185static void do_reboot(void)
186{
187 shutting_down = SHUTDOWN_POWEROFF; /* ? */
188 ctrl_alt_del();
189}
190
191static void shutdown_handler(struct xenbus_watch *watch,
192 const char **vec, unsigned int len)
193{
194 char *str;
195 struct xenbus_transaction xbt;
196 int err;
197 static struct shutdown_handler handlers[] = {
198 { "poweroff", do_poweroff },
199 { "halt", do_poweroff },
200 { "reboot", do_reboot },
201#ifdef CONFIG_HIBERNATE_CALLBACKS
202 { "suspend", do_suspend },
203#endif
204 {NULL, NULL},
205 };
206 static struct shutdown_handler *handler;
207
208 if (shutting_down != SHUTDOWN_INVALID)
209 return;
210
211 again:
212 err = xenbus_transaction_start(&xbt);
213 if (err)
214 return;
215
216 str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
217 /* Ignore read errors and empty reads. */
218 if (XENBUS_IS_ERR_READ(str)) {
219 xenbus_transaction_end(xbt, 1);
220 return;
221 }
222
223 for (handler = &handlers[0]; handler->command; handler++) {
224 if (strcmp(str, handler->command) == 0)
225 break;
226 }
227
228 /* Only acknowledge commands which we are prepared to handle. */
229 if (handler->cb)
230 xenbus_write(xbt, "control", "shutdown", "");
231
232 err = xenbus_transaction_end(xbt, 0);
233 if (err == -EAGAIN) {
234 kfree(str);
235 goto again;
236 }
237
238 if (handler->cb) {
239 handler->cb();
240 } else {
241 printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
242 shutting_down = SHUTDOWN_INVALID;
243 }
244
245 kfree(str);
246}
247
248#ifdef CONFIG_MAGIC_SYSRQ
249static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
250 unsigned int len)
251{
252 char sysrq_key = '\0';
253 struct xenbus_transaction xbt;
254 int err;
255
256 again:
257 err = xenbus_transaction_start(&xbt);
258 if (err)
259 return;
260 if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
261 printk(KERN_ERR "Unable to read sysrq code in "
262 "control/sysrq\n");
263 xenbus_transaction_end(xbt, 1);
264 return;
265 }
266
267 if (sysrq_key != '\0')
268 xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
269
270 err = xenbus_transaction_end(xbt, 0);
271 if (err == -EAGAIN)
272 goto again;
273
274 if (sysrq_key != '\0')
275 handle_sysrq(sysrq_key);
276}
277
278static struct xenbus_watch sysrq_watch = {
279 .node = "control/sysrq",
280 .callback = sysrq_handler
281};
282#endif
283
284static struct xenbus_watch shutdown_watch = {
285 .node = "control/shutdown",
286 .callback = shutdown_handler
287};
288
289static int setup_shutdown_watcher(void)
290{
291 int err;
292
293 err = register_xenbus_watch(&shutdown_watch);
294 if (err) {
295 printk(KERN_ERR "Failed to set shutdown watcher\n");
296 return err;
297 }
298
299#ifdef CONFIG_MAGIC_SYSRQ
300 err = register_xenbus_watch(&sysrq_watch);
301 if (err) {
302 printk(KERN_ERR "Failed to set sysrq watcher\n");
303 return err;
304 }
305#endif
306
307 return 0;
308}
309
310static int shutdown_event(struct notifier_block *notifier,
311 unsigned long event,
312 void *data)
313{
314 setup_shutdown_watcher();
315 return NOTIFY_DONE;
316}
317
318int xen_setup_shutdown_event(void)
319{
320 static struct notifier_block xenstore_notifier = {
321 .notifier_call = shutdown_event
322 };
323
324 if (!xen_domain())
325 return -ENODEV;
326 register_xenstore_notifier(&xenstore_notifier);
327
328 return 0;
329}
330EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
331
332subsys_initcall(xen_setup_shutdown_event);