xref: /openbmc/linux/drivers/xen/manage.c (revision a9fd60e2)
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>
22a9fd60e2SJulien Grall #include <xen/page.h>
230e91398fSJeremy Fitzhardinge #include <xen/xen-ops.h>
24ec9b2065SIsaku Yamahata 
250e91398fSJeremy Fitzhardinge #include <asm/xen/hypercall.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();
91ad55db9fSIsaku Yamahata 		xen_timer_resume();
920e91398fSJeremy Fitzhardinge 	}
930e91398fSJeremy Fitzhardinge 
9419234c08SRafael J. Wysocki 	syscore_resume();
951e6fcf84SIan Campbell 
960e91398fSJeremy Fitzhardinge 	return 0;
970e91398fSJeremy Fitzhardinge }
980e91398fSJeremy Fitzhardinge 
990e91398fSJeremy Fitzhardinge static void do_suspend(void)
1000e91398fSJeremy Fitzhardinge {
1010e91398fSJeremy Fitzhardinge 	int err;
102ceb18029SIan Campbell 	struct suspend_info si;
1030e91398fSJeremy Fitzhardinge 
1040e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_SUSPEND;
1050e91398fSJeremy Fitzhardinge 
1060e91398fSJeremy Fitzhardinge 	err = freeze_processes();
1070e91398fSJeremy Fitzhardinge 	if (err) {
10872978b2fSRoss Lagerwall 		pr_err("%s: freeze processes failed %d\n", __func__, err);
1093fc1f1e2STejun Heo 		goto out;
1100e91398fSJeremy Fitzhardinge 	}
1110e91398fSJeremy Fitzhardinge 
11272978b2fSRoss Lagerwall 	err = freeze_kernel_threads();
11372978b2fSRoss Lagerwall 	if (err) {
11472978b2fSRoss Lagerwall 		pr_err("%s: freeze kernel threads failed %d\n", __func__, err);
11572978b2fSRoss Lagerwall 		goto out_thaw;
11672978b2fSRoss Lagerwall 	}
11772978b2fSRoss Lagerwall 
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 
1342b953a5eSBoris Ostrovsky 	xen_arch_suspend();
1352b953a5eSBoris Ostrovsky 
136ceb18029SIan Campbell 	si.cancelled = 1;
137ceb18029SIan Campbell 
138ceb18029SIan Campbell 	err = stop_machine(xen_suspend, &si, cpumask_of(0));
139922cc38aSJeremy Fitzhardinge 
1401b647823SDavid Vrabel 	/* Resume console as early as possible. */
1411b647823SDavid Vrabel 	if (!si.cancelled)
1421b647823SDavid Vrabel 		xen_console_resume();
1431b647823SDavid Vrabel 
144cd979883SStanislaw Gruszka 	raw_notifier_call_chain(&xen_resume_notifier, 0, NULL);
145cd979883SStanislaw Gruszka 
146cf579dfbSRafael J. Wysocki 	dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
147922cc38aSJeremy Fitzhardinge 
1480e91398fSJeremy Fitzhardinge 	if (err) {
149283c0972SJoe Perches 		pr_err("failed to start xen_suspend: %d\n", err);
150ceb18029SIan Campbell 		si.cancelled = 1;
1510e91398fSJeremy Fitzhardinge 	}
1520e91398fSJeremy Fitzhardinge 
153ad55db9fSIsaku Yamahata 	xen_arch_resume();
1542b953a5eSBoris Ostrovsky 
1552b953a5eSBoris Ostrovsky out_resume:
1562b953a5eSBoris Ostrovsky 	if (!si.cancelled)
157de5b31bdSIan Campbell 		xs_resume();
1582b953a5eSBoris Ostrovsky 	else
159de5b31bdSIan Campbell 		xs_suspend_cancel();
1600e91398fSJeremy Fitzhardinge 
161b3e96c0cSShriram Rajagopalan 	dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
1620e91398fSJeremy Fitzhardinge 
16365f63384SIan Campbell out_thaw:
1640e91398fSJeremy Fitzhardinge 	thaw_processes();
16565f63384SIan Campbell out:
1660e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_INVALID;
1670e91398fSJeremy Fitzhardinge }
1681f112ceeSRafael J. Wysocki #endif	/* CONFIG_HIBERNATE_CALLBACKS */
169ec9b2065SIsaku Yamahata 
17055271723SIan Campbell struct shutdown_handler {
17155271723SIan Campbell 	const char *command;
17255271723SIan Campbell 	void (*cb)(void);
17355271723SIan Campbell };
17455271723SIan Campbell 
175eb47f712SKonrad Rzeszutek Wilk static int poweroff_nb(struct notifier_block *cb, unsigned long code, void *unused)
176eb47f712SKonrad Rzeszutek Wilk {
177eb47f712SKonrad Rzeszutek Wilk 	switch (code) {
178eb47f712SKonrad Rzeszutek Wilk 	case SYS_DOWN:
179eb47f712SKonrad Rzeszutek Wilk 	case SYS_HALT:
180eb47f712SKonrad Rzeszutek Wilk 	case SYS_POWER_OFF:
181eb47f712SKonrad Rzeszutek Wilk 		shutting_down = SHUTDOWN_POWEROFF;
182eb47f712SKonrad Rzeszutek Wilk 	default:
183eb47f712SKonrad Rzeszutek Wilk 		break;
184eb47f712SKonrad Rzeszutek Wilk 	}
185eb47f712SKonrad Rzeszutek Wilk 	return NOTIFY_DONE;
186eb47f712SKonrad Rzeszutek Wilk }
18755271723SIan Campbell static void do_poweroff(void)
18855271723SIan Campbell {
189eb47f712SKonrad Rzeszutek Wilk 	switch (system_state) {
190eb47f712SKonrad Rzeszutek Wilk 	case SYSTEM_BOOTING:
191eb47f712SKonrad Rzeszutek Wilk 		orderly_poweroff(true);
192eb47f712SKonrad Rzeszutek Wilk 		break;
193eb47f712SKonrad Rzeszutek Wilk 	case SYSTEM_RUNNING:
19455271723SIan Campbell 		orderly_poweroff(false);
195eb47f712SKonrad Rzeszutek Wilk 		break;
196eb47f712SKonrad Rzeszutek Wilk 	default:
197eb47f712SKonrad Rzeszutek Wilk 		/* Don't do it when we are halting/rebooting. */
198eb47f712SKonrad Rzeszutek Wilk 		pr_info("Ignoring Xen toolstack shutdown.\n");
199eb47f712SKonrad Rzeszutek Wilk 		break;
200eb47f712SKonrad Rzeszutek Wilk 	}
20155271723SIan Campbell }
20255271723SIan Campbell 
20355271723SIan Campbell static void do_reboot(void)
20455271723SIan Campbell {
20555271723SIan Campbell 	shutting_down = SHUTDOWN_POWEROFF; /* ? */
20655271723SIan Campbell 	ctrl_alt_del();
20755271723SIan Campbell }
20855271723SIan Campbell 
209ec9b2065SIsaku Yamahata static void shutdown_handler(struct xenbus_watch *watch,
210ec9b2065SIsaku Yamahata 			     const char **vec, unsigned int len)
211ec9b2065SIsaku Yamahata {
212ec9b2065SIsaku Yamahata 	char *str;
213ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
214ec9b2065SIsaku Yamahata 	int err;
21555271723SIan Campbell 	static struct shutdown_handler handlers[] = {
21655271723SIan Campbell 		{ "poweroff",	do_poweroff },
21755271723SIan Campbell 		{ "halt",	do_poweroff },
21855271723SIan Campbell 		{ "reboot",	do_reboot   },
2191f112ceeSRafael J. Wysocki #ifdef CONFIG_HIBERNATE_CALLBACKS
22055271723SIan Campbell 		{ "suspend",	do_suspend  },
22155271723SIan Campbell #endif
22255271723SIan Campbell 		{NULL, NULL},
22355271723SIan Campbell 	};
22455271723SIan Campbell 	static struct shutdown_handler *handler;
225ec9b2065SIsaku Yamahata 
226ec9b2065SIsaku Yamahata 	if (shutting_down != SHUTDOWN_INVALID)
227ec9b2065SIsaku Yamahata 		return;
228ec9b2065SIsaku Yamahata 
229ec9b2065SIsaku Yamahata  again:
230ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
231ec9b2065SIsaku Yamahata 	if (err)
232ec9b2065SIsaku Yamahata 		return;
233ec9b2065SIsaku Yamahata 
234ec9b2065SIsaku Yamahata 	str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
235ec9b2065SIsaku Yamahata 	/* Ignore read errors and empty reads. */
236ec9b2065SIsaku Yamahata 	if (XENBUS_IS_ERR_READ(str)) {
237ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
238ec9b2065SIsaku Yamahata 		return;
239ec9b2065SIsaku Yamahata 	}
240ec9b2065SIsaku Yamahata 
24155271723SIan Campbell 	for (handler = &handlers[0]; handler->command; handler++) {
24255271723SIan Campbell 		if (strcmp(str, handler->command) == 0)
24355271723SIan Campbell 			break;
24455271723SIan Campbell 	}
24555271723SIan Campbell 
24655271723SIan Campbell 	/* Only acknowledge commands which we are prepared to handle. */
24755271723SIan Campbell 	if (handler->cb)
248ec9b2065SIsaku Yamahata 		xenbus_write(xbt, "control", "shutdown", "");
249ec9b2065SIsaku Yamahata 
250ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
251ec9b2065SIsaku Yamahata 	if (err == -EAGAIN) {
252ec9b2065SIsaku Yamahata 		kfree(str);
253ec9b2065SIsaku Yamahata 		goto again;
254ec9b2065SIsaku Yamahata 	}
255ec9b2065SIsaku Yamahata 
25655271723SIan Campbell 	if (handler->cb) {
25755271723SIan Campbell 		handler->cb();
2580e91398fSJeremy Fitzhardinge 	} else {
259283c0972SJoe Perches 		pr_info("Ignoring shutdown request: %s\n", str);
260ec9b2065SIsaku Yamahata 		shutting_down = SHUTDOWN_INVALID;
261ec9b2065SIsaku Yamahata 	}
262ec9b2065SIsaku Yamahata 
263ec9b2065SIsaku Yamahata 	kfree(str);
264ec9b2065SIsaku Yamahata }
265ec9b2065SIsaku Yamahata 
266f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ
267ec9b2065SIsaku Yamahata static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
268ec9b2065SIsaku Yamahata 			  unsigned int len)
269ec9b2065SIsaku Yamahata {
270ec9b2065SIsaku Yamahata 	char sysrq_key = '\0';
271ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
272ec9b2065SIsaku Yamahata 	int err;
273ec9b2065SIsaku Yamahata 
274ec9b2065SIsaku Yamahata  again:
275ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
276ec9b2065SIsaku Yamahata 	if (err)
277ec9b2065SIsaku Yamahata 		return;
278ec9b2065SIsaku Yamahata 	if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
279283c0972SJoe Perches 		pr_err("Unable to read sysrq code in control/sysrq\n");
280ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
281ec9b2065SIsaku Yamahata 		return;
282ec9b2065SIsaku Yamahata 	}
283ec9b2065SIsaku Yamahata 
284ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
285ec9b2065SIsaku Yamahata 		xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
286ec9b2065SIsaku Yamahata 
287ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
288ec9b2065SIsaku Yamahata 	if (err == -EAGAIN)
289ec9b2065SIsaku Yamahata 		goto again;
290ec9b2065SIsaku Yamahata 
291ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
292f335397dSDmitry Torokhov 		handle_sysrq(sysrq_key);
293ec9b2065SIsaku Yamahata }
294ec9b2065SIsaku Yamahata 
295ec9b2065SIsaku Yamahata static struct xenbus_watch sysrq_watch = {
296ec9b2065SIsaku Yamahata 	.node = "control/sysrq",
297ec9b2065SIsaku Yamahata 	.callback = sysrq_handler
298ec9b2065SIsaku Yamahata };
299f3bc3189SRandy Dunlap #endif
300f3bc3189SRandy Dunlap 
301f3bc3189SRandy Dunlap static struct xenbus_watch shutdown_watch = {
302f3bc3189SRandy Dunlap 	.node = "control/shutdown",
303f3bc3189SRandy Dunlap 	.callback = shutdown_handler
304f3bc3189SRandy Dunlap };
305ec9b2065SIsaku Yamahata 
306eb47f712SKonrad Rzeszutek Wilk static struct notifier_block xen_reboot_nb = {
307eb47f712SKonrad Rzeszutek Wilk 	.notifier_call = poweroff_nb,
308eb47f712SKonrad Rzeszutek Wilk };
309eb47f712SKonrad Rzeszutek Wilk 
310ec9b2065SIsaku Yamahata static int setup_shutdown_watcher(void)
311ec9b2065SIsaku Yamahata {
312ec9b2065SIsaku Yamahata 	int err;
313ec9b2065SIsaku Yamahata 
314ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&shutdown_watch);
315ec9b2065SIsaku Yamahata 	if (err) {
316283c0972SJoe Perches 		pr_err("Failed to set shutdown watcher\n");
317ec9b2065SIsaku Yamahata 		return err;
318ec9b2065SIsaku Yamahata 	}
319ec9b2065SIsaku Yamahata 
320eb47f712SKonrad Rzeszutek Wilk 
321f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ
322ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&sysrq_watch);
323ec9b2065SIsaku Yamahata 	if (err) {
324283c0972SJoe Perches 		pr_err("Failed to set sysrq watcher\n");
325ec9b2065SIsaku Yamahata 		return err;
326ec9b2065SIsaku Yamahata 	}
327f3bc3189SRandy Dunlap #endif
328ec9b2065SIsaku Yamahata 
329ec9b2065SIsaku Yamahata 	return 0;
330ec9b2065SIsaku Yamahata }
331ec9b2065SIsaku Yamahata 
332ec9b2065SIsaku Yamahata static int shutdown_event(struct notifier_block *notifier,
333ec9b2065SIsaku Yamahata 			  unsigned long event,
334ec9b2065SIsaku Yamahata 			  void *data)
335ec9b2065SIsaku Yamahata {
336ec9b2065SIsaku Yamahata 	setup_shutdown_watcher();
337ec9b2065SIsaku Yamahata 	return NOTIFY_DONE;
338ec9b2065SIsaku Yamahata }
339ec9b2065SIsaku Yamahata 
340016b6f5fSStefano Stabellini int xen_setup_shutdown_event(void)
341ec9b2065SIsaku Yamahata {
342ec9b2065SIsaku Yamahata 	static struct notifier_block xenstore_notifier = {
343ec9b2065SIsaku Yamahata 		.notifier_call = shutdown_event
344ec9b2065SIsaku Yamahata 	};
345702d4eb9SStefano Stabellini 
346702d4eb9SStefano Stabellini 	if (!xen_domain())
347702d4eb9SStefano Stabellini 		return -ENODEV;
348ec9b2065SIsaku Yamahata 	register_xenstore_notifier(&xenstore_notifier);
349eb47f712SKonrad Rzeszutek Wilk 	register_reboot_notifier(&xen_reboot_nb);
350ec9b2065SIsaku Yamahata 
351ec9b2065SIsaku Yamahata 	return 0;
352ec9b2065SIsaku Yamahata }
353183d03ccSStefano Stabellini EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
354ec9b2065SIsaku Yamahata 
355702d4eb9SStefano Stabellini subsys_initcall(xen_setup_shutdown_event);
356