xref: /openbmc/linux/drivers/xen/manage.c (revision aa8532c3)
1ec9b2065SIsaku Yamahata /*
2ec9b2065SIsaku Yamahata  * Handle extern requests for shutdown, reboot and sysrq
3ec9b2065SIsaku Yamahata  */
4283c0972SJoe Perches 
5283c0972SJoe Perches #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt
6283c0972SJoe Perches 
7ec9b2065SIsaku Yamahata #include <linux/kernel.h>
8ec9b2065SIsaku Yamahata #include <linux/err.h>
95a0e3ad6STejun Heo #include <linux/slab.h>
10ec9b2065SIsaku Yamahata #include <linux/reboot.h>
11ec9b2065SIsaku Yamahata #include <linux/sysrq.h>
120e91398fSJeremy Fitzhardinge #include <linux/stop_machine.h>
130e91398fSJeremy Fitzhardinge #include <linux/freezer.h>
1419234c08SRafael J. Wysocki #include <linux/syscore_ops.h>
1563c9744bSPaul Gortmaker #include <linux/export.h>
16ec9b2065SIsaku Yamahata 
17016b6f5fSStefano Stabellini #include <xen/xen.h>
18ec9b2065SIsaku Yamahata #include <xen/xenbus.h>
190e91398fSJeremy Fitzhardinge #include <xen/grant_table.h>
200e91398fSJeremy Fitzhardinge #include <xen/events.h>
210e91398fSJeremy Fitzhardinge #include <xen/hvc-console.h>
220e91398fSJeremy Fitzhardinge #include <xen/xen-ops.h>
23ec9b2065SIsaku Yamahata 
240e91398fSJeremy Fitzhardinge #include <asm/xen/hypercall.h>
250e91398fSJeremy Fitzhardinge #include <asm/xen/page.h>
26016b6f5fSStefano Stabellini #include <asm/xen/hypervisor.h>
270e91398fSJeremy Fitzhardinge 
280e91398fSJeremy Fitzhardinge enum shutdown_state {
290e91398fSJeremy Fitzhardinge 	SHUTDOWN_INVALID = -1,
300e91398fSJeremy Fitzhardinge 	SHUTDOWN_POWEROFF = 0,
310e91398fSJeremy Fitzhardinge 	SHUTDOWN_SUSPEND = 2,
32ec9b2065SIsaku Yamahata 	/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
330e91398fSJeremy Fitzhardinge 	   report a crash, not be instructed to crash!
340e91398fSJeremy Fitzhardinge 	   HALT is the same as POWEROFF, as far as we're concerned.  The tools use
350e91398fSJeremy Fitzhardinge 	   the distinction when we return the reason code to them.  */
360e91398fSJeremy Fitzhardinge 	 SHUTDOWN_HALT = 4,
370e91398fSJeremy Fitzhardinge };
38ec9b2065SIsaku Yamahata 
39ec9b2065SIsaku Yamahata /* Ignore multiple shutdown requests. */
400e91398fSJeremy Fitzhardinge static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
410e91398fSJeremy Fitzhardinge 
42ceb18029SIan Campbell struct suspend_info {
43ceb18029SIan Campbell 	int cancelled;
44ceb18029SIan Campbell };
45ceb18029SIan Campbell 
46cd979883SStanislaw Gruszka static RAW_NOTIFIER_HEAD(xen_resume_notifier);
47cd979883SStanislaw Gruszka 
48cd979883SStanislaw Gruszka void xen_resume_notifier_register(struct notifier_block *nb)
49cd979883SStanislaw Gruszka {
50cd979883SStanislaw Gruszka 	raw_notifier_chain_register(&xen_resume_notifier, nb);
51cd979883SStanislaw Gruszka }
52cd979883SStanislaw Gruszka EXPORT_SYMBOL_GPL(xen_resume_notifier_register);
53cd979883SStanislaw Gruszka 
54cd979883SStanislaw Gruszka void xen_resume_notifier_unregister(struct notifier_block *nb)
55cd979883SStanislaw Gruszka {
56cd979883SStanislaw Gruszka 	raw_notifier_chain_unregister(&xen_resume_notifier, nb);
57cd979883SStanislaw Gruszka }
58cd979883SStanislaw Gruszka EXPORT_SYMBOL_GPL(xen_resume_notifier_unregister);
59cd979883SStanislaw Gruszka 
6065e053a7SStefano Stabellini #ifdef CONFIG_HIBERNATE_CALLBACKS
610e91398fSJeremy Fitzhardinge static int xen_suspend(void *data)
620e91398fSJeremy Fitzhardinge {
63ceb18029SIan Campbell 	struct suspend_info *si = data;
64359cdd3fSJeremy Fitzhardinge 	int err;
650e91398fSJeremy Fitzhardinge 
660e91398fSJeremy Fitzhardinge 	BUG_ON(!irqs_disabled());
670e91398fSJeremy Fitzhardinge 
6819234c08SRafael J. Wysocki 	err = syscore_suspend();
69770824bdSRafael J. Wysocki 	if (err) {
70283c0972SJoe Perches 		pr_err("%s: system core suspend failed: %d\n", __func__, err);
71770824bdSRafael J. Wysocki 		return err;
72770824bdSRafael J. Wysocki 	}
73359cdd3fSJeremy Fitzhardinge 
74aa8532c3SDavid Vrabel 	gnttab_suspend();
75aa8532c3SDavid Vrabel 	xen_arch_pre_suspend();
760e91398fSJeremy Fitzhardinge 
770e91398fSJeremy Fitzhardinge 	/*
780e91398fSJeremy Fitzhardinge 	 * This hypercall returns 1 if suspend was cancelled
790e91398fSJeremy Fitzhardinge 	 * or the domain was merely checkpointed, and 0 if it
800e91398fSJeremy Fitzhardinge 	 * is resuming in a new domain.
810e91398fSJeremy Fitzhardinge 	 */
82aa8532c3SDavid Vrabel 	si->cancelled = HYPERVISOR_suspend(xen_pv_domain()
83aa8532c3SDavid Vrabel                                            ? virt_to_mfn(xen_start_info)
84aa8532c3SDavid Vrabel                                            : 0);
850e91398fSJeremy Fitzhardinge 
86aa8532c3SDavid Vrabel 	xen_arch_post_suspend(si->cancelled);
87aa8532c3SDavid Vrabel 	gnttab_resume();
880e91398fSJeremy Fitzhardinge 
89ceb18029SIan Campbell 	if (!si->cancelled) {
900e91398fSJeremy Fitzhardinge 		xen_irq_resume();
910e91398fSJeremy Fitzhardinge 		xen_console_resume();
92ad55db9fSIsaku Yamahata 		xen_timer_resume();
930e91398fSJeremy Fitzhardinge 	}
940e91398fSJeremy Fitzhardinge 
9519234c08SRafael J. Wysocki 	syscore_resume();
961e6fcf84SIan Campbell 
970e91398fSJeremy Fitzhardinge 	return 0;
980e91398fSJeremy Fitzhardinge }
990e91398fSJeremy Fitzhardinge 
1000e91398fSJeremy Fitzhardinge static void do_suspend(void)
1010e91398fSJeremy Fitzhardinge {
1020e91398fSJeremy Fitzhardinge 	int err;
103ceb18029SIan Campbell 	struct suspend_info si;
1040e91398fSJeremy Fitzhardinge 
1050e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_SUSPEND;
1060e91398fSJeremy Fitzhardinge 
1070e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT
1080e91398fSJeremy Fitzhardinge 	/* If the kernel is preemptible, we need to freeze all the processes
1090e91398fSJeremy Fitzhardinge 	   to prevent them from being in the middle of a pagetable update
1100e91398fSJeremy Fitzhardinge 	   during suspend. */
1110e91398fSJeremy Fitzhardinge 	err = freeze_processes();
1120e91398fSJeremy Fitzhardinge 	if (err) {
113283c0972SJoe Perches 		pr_err("%s: freeze failed %d\n", __func__, err);
1143fc1f1e2STejun Heo 		goto out;
1150e91398fSJeremy Fitzhardinge 	}
1160e91398fSJeremy Fitzhardinge #endif
1170e91398fSJeremy Fitzhardinge 
118b3e96c0cSShriram Rajagopalan 	err = dpm_suspend_start(PMSG_FREEZE);
1190e91398fSJeremy Fitzhardinge 	if (err) {
120283c0972SJoe Perches 		pr_err("%s: dpm_suspend_start %d\n", __func__, err);
12165f63384SIan Campbell 		goto out_thaw;
1220e91398fSJeremy Fitzhardinge 	}
1230e91398fSJeremy Fitzhardinge 
124c5cae661SIan Campbell 	printk(KERN_DEBUG "suspending xenstore...\n");
125c5cae661SIan Campbell 	xs_suspend();
126c5cae661SIan Campbell 
127cf579dfbSRafael J. Wysocki 	err = dpm_suspend_end(PMSG_FREEZE);
1282ed8d2b3SRafael J. Wysocki 	if (err) {
129283c0972SJoe Perches 		pr_err("dpm_suspend_end failed: %d\n", err);
130186bab1cSKonrad Rzeszutek Wilk 		si.cancelled = 0;
13165f63384SIan Campbell 		goto out_resume;
1322ed8d2b3SRafael J. Wysocki 	}
1332ed8d2b3SRafael J. Wysocki 
134ceb18029SIan Campbell 	si.cancelled = 1;
135ceb18029SIan Campbell 
136ceb18029SIan Campbell 	err = stop_machine(xen_suspend, &si, cpumask_of(0));
137922cc38aSJeremy Fitzhardinge 
138cd979883SStanislaw Gruszka 	raw_notifier_call_chain(&xen_resume_notifier, 0, NULL);
139cd979883SStanislaw Gruszka 
140cf579dfbSRafael J. Wysocki 	dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
141922cc38aSJeremy Fitzhardinge 
1420e91398fSJeremy Fitzhardinge 	if (err) {
143283c0972SJoe Perches 		pr_err("failed to start xen_suspend: %d\n", err);
144ceb18029SIan Campbell 		si.cancelled = 1;
1450e91398fSJeremy Fitzhardinge 	}
1460e91398fSJeremy Fitzhardinge 
147c5cae661SIan Campbell out_resume:
148ceb18029SIan Campbell 	if (!si.cancelled) {
149ad55db9fSIsaku Yamahata 		xen_arch_resume();
150de5b31bdSIan Campbell 		xs_resume();
151ad55db9fSIsaku Yamahata 	} else
152de5b31bdSIan Campbell 		xs_suspend_cancel();
1530e91398fSJeremy Fitzhardinge 
154b3e96c0cSShriram Rajagopalan 	dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
1550e91398fSJeremy Fitzhardinge 
15665f63384SIan Campbell out_thaw:
1570e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT
1580e91398fSJeremy Fitzhardinge 	thaw_processes();
15965f63384SIan Campbell out:
1603fc1f1e2STejun Heo #endif
1610e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_INVALID;
1620e91398fSJeremy Fitzhardinge }
1631f112ceeSRafael J. Wysocki #endif	/* CONFIG_HIBERNATE_CALLBACKS */
164ec9b2065SIsaku Yamahata 
16555271723SIan Campbell struct shutdown_handler {
16655271723SIan Campbell 	const char *command;
16755271723SIan Campbell 	void (*cb)(void);
16855271723SIan Campbell };
16955271723SIan Campbell 
170eb47f712SKonrad Rzeszutek Wilk static int poweroff_nb(struct notifier_block *cb, unsigned long code, void *unused)
171eb47f712SKonrad Rzeszutek Wilk {
172eb47f712SKonrad Rzeszutek Wilk 	switch (code) {
173eb47f712SKonrad Rzeszutek Wilk 	case SYS_DOWN:
174eb47f712SKonrad Rzeszutek Wilk 	case SYS_HALT:
175eb47f712SKonrad Rzeszutek Wilk 	case SYS_POWER_OFF:
176eb47f712SKonrad Rzeszutek Wilk 		shutting_down = SHUTDOWN_POWEROFF;
177eb47f712SKonrad Rzeszutek Wilk 	default:
178eb47f712SKonrad Rzeszutek Wilk 		break;
179eb47f712SKonrad Rzeszutek Wilk 	}
180eb47f712SKonrad Rzeszutek Wilk 	return NOTIFY_DONE;
181eb47f712SKonrad Rzeszutek Wilk }
18255271723SIan Campbell static void do_poweroff(void)
18355271723SIan Campbell {
184eb47f712SKonrad Rzeszutek Wilk 	switch (system_state) {
185eb47f712SKonrad Rzeszutek Wilk 	case SYSTEM_BOOTING:
186eb47f712SKonrad Rzeszutek Wilk 		orderly_poweroff(true);
187eb47f712SKonrad Rzeszutek Wilk 		break;
188eb47f712SKonrad Rzeszutek Wilk 	case SYSTEM_RUNNING:
18955271723SIan Campbell 		orderly_poweroff(false);
190eb47f712SKonrad Rzeszutek Wilk 		break;
191eb47f712SKonrad Rzeszutek Wilk 	default:
192eb47f712SKonrad Rzeszutek Wilk 		/* Don't do it when we are halting/rebooting. */
193eb47f712SKonrad Rzeszutek Wilk 		pr_info("Ignoring Xen toolstack shutdown.\n");
194eb47f712SKonrad Rzeszutek Wilk 		break;
195eb47f712SKonrad Rzeszutek Wilk 	}
19655271723SIan Campbell }
19755271723SIan Campbell 
19855271723SIan Campbell static void do_reboot(void)
19955271723SIan Campbell {
20055271723SIan Campbell 	shutting_down = SHUTDOWN_POWEROFF; /* ? */
20155271723SIan Campbell 	ctrl_alt_del();
20255271723SIan Campbell }
20355271723SIan Campbell 
204ec9b2065SIsaku Yamahata static void shutdown_handler(struct xenbus_watch *watch,
205ec9b2065SIsaku Yamahata 			     const char **vec, unsigned int len)
206ec9b2065SIsaku Yamahata {
207ec9b2065SIsaku Yamahata 	char *str;
208ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
209ec9b2065SIsaku Yamahata 	int err;
21055271723SIan Campbell 	static struct shutdown_handler handlers[] = {
21155271723SIan Campbell 		{ "poweroff",	do_poweroff },
21255271723SIan Campbell 		{ "halt",	do_poweroff },
21355271723SIan Campbell 		{ "reboot",	do_reboot   },
2141f112ceeSRafael J. Wysocki #ifdef CONFIG_HIBERNATE_CALLBACKS
21555271723SIan Campbell 		{ "suspend",	do_suspend  },
21655271723SIan Campbell #endif
21755271723SIan Campbell 		{NULL, NULL},
21855271723SIan Campbell 	};
21955271723SIan Campbell 	static struct shutdown_handler *handler;
220ec9b2065SIsaku Yamahata 
221ec9b2065SIsaku Yamahata 	if (shutting_down != SHUTDOWN_INVALID)
222ec9b2065SIsaku Yamahata 		return;
223ec9b2065SIsaku Yamahata 
224ec9b2065SIsaku Yamahata  again:
225ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
226ec9b2065SIsaku Yamahata 	if (err)
227ec9b2065SIsaku Yamahata 		return;
228ec9b2065SIsaku Yamahata 
229ec9b2065SIsaku Yamahata 	str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
230ec9b2065SIsaku Yamahata 	/* Ignore read errors and empty reads. */
231ec9b2065SIsaku Yamahata 	if (XENBUS_IS_ERR_READ(str)) {
232ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
233ec9b2065SIsaku Yamahata 		return;
234ec9b2065SIsaku Yamahata 	}
235ec9b2065SIsaku Yamahata 
23655271723SIan Campbell 	for (handler = &handlers[0]; handler->command; handler++) {
23755271723SIan Campbell 		if (strcmp(str, handler->command) == 0)
23855271723SIan Campbell 			break;
23955271723SIan Campbell 	}
24055271723SIan Campbell 
24155271723SIan Campbell 	/* Only acknowledge commands which we are prepared to handle. */
24255271723SIan Campbell 	if (handler->cb)
243ec9b2065SIsaku Yamahata 		xenbus_write(xbt, "control", "shutdown", "");
244ec9b2065SIsaku Yamahata 
245ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
246ec9b2065SIsaku Yamahata 	if (err == -EAGAIN) {
247ec9b2065SIsaku Yamahata 		kfree(str);
248ec9b2065SIsaku Yamahata 		goto again;
249ec9b2065SIsaku Yamahata 	}
250ec9b2065SIsaku Yamahata 
25155271723SIan Campbell 	if (handler->cb) {
25255271723SIan Campbell 		handler->cb();
2530e91398fSJeremy Fitzhardinge 	} else {
254283c0972SJoe Perches 		pr_info("Ignoring shutdown request: %s\n", str);
255ec9b2065SIsaku Yamahata 		shutting_down = SHUTDOWN_INVALID;
256ec9b2065SIsaku Yamahata 	}
257ec9b2065SIsaku Yamahata 
258ec9b2065SIsaku Yamahata 	kfree(str);
259ec9b2065SIsaku Yamahata }
260ec9b2065SIsaku Yamahata 
261f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ
262ec9b2065SIsaku Yamahata static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
263ec9b2065SIsaku Yamahata 			  unsigned int len)
264ec9b2065SIsaku Yamahata {
265ec9b2065SIsaku Yamahata 	char sysrq_key = '\0';
266ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
267ec9b2065SIsaku Yamahata 	int err;
268ec9b2065SIsaku Yamahata 
269ec9b2065SIsaku Yamahata  again:
270ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
271ec9b2065SIsaku Yamahata 	if (err)
272ec9b2065SIsaku Yamahata 		return;
273ec9b2065SIsaku Yamahata 	if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
274283c0972SJoe Perches 		pr_err("Unable to read sysrq code in control/sysrq\n");
275ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
276ec9b2065SIsaku Yamahata 		return;
277ec9b2065SIsaku Yamahata 	}
278ec9b2065SIsaku Yamahata 
279ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
280ec9b2065SIsaku Yamahata 		xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
281ec9b2065SIsaku Yamahata 
282ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
283ec9b2065SIsaku Yamahata 	if (err == -EAGAIN)
284ec9b2065SIsaku Yamahata 		goto again;
285ec9b2065SIsaku Yamahata 
286ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
287f335397dSDmitry Torokhov 		handle_sysrq(sysrq_key);
288ec9b2065SIsaku Yamahata }
289ec9b2065SIsaku Yamahata 
290ec9b2065SIsaku Yamahata static struct xenbus_watch sysrq_watch = {
291ec9b2065SIsaku Yamahata 	.node = "control/sysrq",
292ec9b2065SIsaku Yamahata 	.callback = sysrq_handler
293ec9b2065SIsaku Yamahata };
294f3bc3189SRandy Dunlap #endif
295f3bc3189SRandy Dunlap 
296f3bc3189SRandy Dunlap static struct xenbus_watch shutdown_watch = {
297f3bc3189SRandy Dunlap 	.node = "control/shutdown",
298f3bc3189SRandy Dunlap 	.callback = shutdown_handler
299f3bc3189SRandy Dunlap };
300ec9b2065SIsaku Yamahata 
301eb47f712SKonrad Rzeszutek Wilk static struct notifier_block xen_reboot_nb = {
302eb47f712SKonrad Rzeszutek Wilk 	.notifier_call = poweroff_nb,
303eb47f712SKonrad Rzeszutek Wilk };
304eb47f712SKonrad Rzeszutek Wilk 
305ec9b2065SIsaku Yamahata static int setup_shutdown_watcher(void)
306ec9b2065SIsaku Yamahata {
307ec9b2065SIsaku Yamahata 	int err;
308ec9b2065SIsaku Yamahata 
309ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&shutdown_watch);
310ec9b2065SIsaku Yamahata 	if (err) {
311283c0972SJoe Perches 		pr_err("Failed to set shutdown watcher\n");
312ec9b2065SIsaku Yamahata 		return err;
313ec9b2065SIsaku Yamahata 	}
314ec9b2065SIsaku Yamahata 
315eb47f712SKonrad Rzeszutek Wilk 
316f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ
317ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&sysrq_watch);
318ec9b2065SIsaku Yamahata 	if (err) {
319283c0972SJoe Perches 		pr_err("Failed to set sysrq watcher\n");
320ec9b2065SIsaku Yamahata 		return err;
321ec9b2065SIsaku Yamahata 	}
322f3bc3189SRandy Dunlap #endif
323ec9b2065SIsaku Yamahata 
324ec9b2065SIsaku Yamahata 	return 0;
325ec9b2065SIsaku Yamahata }
326ec9b2065SIsaku Yamahata 
327ec9b2065SIsaku Yamahata static int shutdown_event(struct notifier_block *notifier,
328ec9b2065SIsaku Yamahata 			  unsigned long event,
329ec9b2065SIsaku Yamahata 			  void *data)
330ec9b2065SIsaku Yamahata {
331ec9b2065SIsaku Yamahata 	setup_shutdown_watcher();
332ec9b2065SIsaku Yamahata 	return NOTIFY_DONE;
333ec9b2065SIsaku Yamahata }
334ec9b2065SIsaku Yamahata 
335016b6f5fSStefano Stabellini int xen_setup_shutdown_event(void)
336ec9b2065SIsaku Yamahata {
337ec9b2065SIsaku Yamahata 	static struct notifier_block xenstore_notifier = {
338ec9b2065SIsaku Yamahata 		.notifier_call = shutdown_event
339ec9b2065SIsaku Yamahata 	};
340702d4eb9SStefano Stabellini 
341702d4eb9SStefano Stabellini 	if (!xen_domain())
342702d4eb9SStefano Stabellini 		return -ENODEV;
343ec9b2065SIsaku Yamahata 	register_xenstore_notifier(&xenstore_notifier);
344eb47f712SKonrad Rzeszutek Wilk 	register_reboot_notifier(&xen_reboot_nb);
345ec9b2065SIsaku Yamahata 
346ec9b2065SIsaku Yamahata 	return 0;
347ec9b2065SIsaku Yamahata }
348183d03ccSStefano Stabellini EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
349ec9b2065SIsaku Yamahata 
350702d4eb9SStefano Stabellini subsys_initcall(xen_setup_shutdown_event);
351