xref: /openbmc/linux/drivers/xen/manage.c (revision 702d4eb9)
1ec9b2065SIsaku Yamahata /*
2ec9b2065SIsaku Yamahata  * Handle extern requests for shutdown, reboot and sysrq
3ec9b2065SIsaku Yamahata  */
4ec9b2065SIsaku Yamahata #include <linux/kernel.h>
5ec9b2065SIsaku Yamahata #include <linux/err.h>
65a0e3ad6STejun Heo #include <linux/slab.h>
7ec9b2065SIsaku Yamahata #include <linux/reboot.h>
8ec9b2065SIsaku Yamahata #include <linux/sysrq.h>
90e91398fSJeremy Fitzhardinge #include <linux/stop_machine.h>
100e91398fSJeremy Fitzhardinge #include <linux/freezer.h>
11ec9b2065SIsaku Yamahata 
12016b6f5fSStefano Stabellini #include <xen/xen.h>
13ec9b2065SIsaku Yamahata #include <xen/xenbus.h>
140e91398fSJeremy Fitzhardinge #include <xen/grant_table.h>
150e91398fSJeremy Fitzhardinge #include <xen/events.h>
160e91398fSJeremy Fitzhardinge #include <xen/hvc-console.h>
170e91398fSJeremy Fitzhardinge #include <xen/xen-ops.h>
18ec9b2065SIsaku Yamahata 
190e91398fSJeremy Fitzhardinge #include <asm/xen/hypercall.h>
200e91398fSJeremy Fitzhardinge #include <asm/xen/page.h>
21016b6f5fSStefano Stabellini #include <asm/xen/hypervisor.h>
220e91398fSJeremy Fitzhardinge 
230e91398fSJeremy Fitzhardinge enum shutdown_state {
240e91398fSJeremy Fitzhardinge 	SHUTDOWN_INVALID = -1,
250e91398fSJeremy Fitzhardinge 	SHUTDOWN_POWEROFF = 0,
260e91398fSJeremy Fitzhardinge 	SHUTDOWN_SUSPEND = 2,
27ec9b2065SIsaku Yamahata 	/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
280e91398fSJeremy Fitzhardinge 	   report a crash, not be instructed to crash!
290e91398fSJeremy Fitzhardinge 	   HALT is the same as POWEROFF, as far as we're concerned.  The tools use
300e91398fSJeremy Fitzhardinge 	   the distinction when we return the reason code to them.  */
310e91398fSJeremy Fitzhardinge 	 SHUTDOWN_HALT = 4,
320e91398fSJeremy Fitzhardinge };
33ec9b2065SIsaku Yamahata 
34ec9b2065SIsaku Yamahata /* Ignore multiple shutdown requests. */
350e91398fSJeremy Fitzhardinge static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
360e91398fSJeremy Fitzhardinge 
37c7827728SJeremy Fitzhardinge #ifdef CONFIG_PM_SLEEP
38016b6f5fSStefano Stabellini static int xen_hvm_suspend(void *data)
39016b6f5fSStefano Stabellini {
408dd38383SIan Campbell 	int err;
41016b6f5fSStefano Stabellini 	struct sched_shutdown r = { .reason = SHUTDOWN_suspend };
42016b6f5fSStefano Stabellini 	int *cancelled = data;
43016b6f5fSStefano Stabellini 
44016b6f5fSStefano Stabellini 	BUG_ON(!irqs_disabled());
45016b6f5fSStefano Stabellini 
468dd38383SIan Campbell 	err = sysdev_suspend(PMSG_SUSPEND);
478dd38383SIan Campbell 	if (err) {
488dd38383SIan Campbell 		printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n",
498dd38383SIan Campbell 		       err);
508dd38383SIan Campbell 		return err;
518dd38383SIan Campbell 	}
528dd38383SIan Campbell 
53016b6f5fSStefano Stabellini 	*cancelled = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r);
54016b6f5fSStefano Stabellini 
55016b6f5fSStefano Stabellini 	xen_hvm_post_suspend(*cancelled);
56016b6f5fSStefano Stabellini 	gnttab_resume();
57016b6f5fSStefano Stabellini 
58016b6f5fSStefano Stabellini 	if (!*cancelled) {
59016b6f5fSStefano Stabellini 		xen_irq_resume();
606411fe69SStefano Stabellini 		xen_console_resume();
61016b6f5fSStefano Stabellini 		xen_timer_resume();
62016b6f5fSStefano Stabellini 	}
63016b6f5fSStefano Stabellini 
648dd38383SIan Campbell 	sysdev_resume();
658dd38383SIan Campbell 
66016b6f5fSStefano Stabellini 	return 0;
67016b6f5fSStefano Stabellini }
68016b6f5fSStefano Stabellini 
690e91398fSJeremy Fitzhardinge static int xen_suspend(void *data)
700e91398fSJeremy Fitzhardinge {
71359cdd3fSJeremy Fitzhardinge 	int err;
72016b6f5fSStefano Stabellini 	int *cancelled = data;
730e91398fSJeremy Fitzhardinge 
740e91398fSJeremy Fitzhardinge 	BUG_ON(!irqs_disabled());
750e91398fSJeremy Fitzhardinge 
76770824bdSRafael J. Wysocki 	err = sysdev_suspend(PMSG_SUSPEND);
77770824bdSRafael J. Wysocki 	if (err) {
78770824bdSRafael J. Wysocki 		printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
79770824bdSRafael J. Wysocki 			err);
80770824bdSRafael J. Wysocki 		return err;
81770824bdSRafael J. Wysocki 	}
82359cdd3fSJeremy Fitzhardinge 
830e91398fSJeremy Fitzhardinge 	xen_mm_pin_all();
840e91398fSJeremy Fitzhardinge 	gnttab_suspend();
850e91398fSJeremy Fitzhardinge 	xen_pre_suspend();
860e91398fSJeremy Fitzhardinge 
870e91398fSJeremy Fitzhardinge 	/*
880e91398fSJeremy Fitzhardinge 	 * This hypercall returns 1 if suspend was cancelled
890e91398fSJeremy Fitzhardinge 	 * or the domain was merely checkpointed, and 0 if it
900e91398fSJeremy Fitzhardinge 	 * is resuming in a new domain.
910e91398fSJeremy Fitzhardinge 	 */
920e91398fSJeremy Fitzhardinge 	*cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
930e91398fSJeremy Fitzhardinge 
940e91398fSJeremy Fitzhardinge 	xen_post_suspend(*cancelled);
950e91398fSJeremy Fitzhardinge 	gnttab_resume();
960e91398fSJeremy Fitzhardinge 	xen_mm_unpin_all();
970e91398fSJeremy Fitzhardinge 
980e91398fSJeremy Fitzhardinge 	if (!*cancelled) {
990e91398fSJeremy Fitzhardinge 		xen_irq_resume();
1000e91398fSJeremy Fitzhardinge 		xen_console_resume();
101ad55db9fSIsaku Yamahata 		xen_timer_resume();
1020e91398fSJeremy Fitzhardinge 	}
1030e91398fSJeremy Fitzhardinge 
1041e6fcf84SIan Campbell 	sysdev_resume();
1051e6fcf84SIan Campbell 
1060e91398fSJeremy Fitzhardinge 	return 0;
1070e91398fSJeremy Fitzhardinge }
1080e91398fSJeremy Fitzhardinge 
1090e91398fSJeremy Fitzhardinge static void do_suspend(void)
1100e91398fSJeremy Fitzhardinge {
1110e91398fSJeremy Fitzhardinge 	int err;
1120e91398fSJeremy Fitzhardinge 	int cancelled = 1;
1130e91398fSJeremy Fitzhardinge 
1140e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_SUSPEND;
1150e91398fSJeremy Fitzhardinge 
1160e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT
1170e91398fSJeremy Fitzhardinge 	/* If the kernel is preemptible, we need to freeze all the processes
1180e91398fSJeremy Fitzhardinge 	   to prevent them from being in the middle of a pagetable update
1190e91398fSJeremy Fitzhardinge 	   during suspend. */
1200e91398fSJeremy Fitzhardinge 	err = freeze_processes();
1210e91398fSJeremy Fitzhardinge 	if (err) {
1220e91398fSJeremy Fitzhardinge 		printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
1233fc1f1e2STejun Heo 		goto out;
1240e91398fSJeremy Fitzhardinge 	}
1250e91398fSJeremy Fitzhardinge #endif
1260e91398fSJeremy Fitzhardinge 
127d1616302SAlan Stern 	err = dpm_suspend_start(PMSG_SUSPEND);
1280e91398fSJeremy Fitzhardinge 	if (err) {
129d1616302SAlan Stern 		printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
13065f63384SIan Campbell 		goto out_thaw;
1310e91398fSJeremy Fitzhardinge 	}
1320e91398fSJeremy Fitzhardinge 
133c5cae661SIan Campbell 	printk(KERN_DEBUG "suspending xenstore...\n");
134c5cae661SIan Campbell 	xs_suspend();
135c5cae661SIan Campbell 
136d1616302SAlan Stern 	err = dpm_suspend_noirq(PMSG_SUSPEND);
1372ed8d2b3SRafael J. Wysocki 	if (err) {
138d1616302SAlan Stern 		printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
13965f63384SIan Campbell 		goto out_resume;
1402ed8d2b3SRafael J. Wysocki 	}
1412ed8d2b3SRafael J. Wysocki 
142016b6f5fSStefano Stabellini 	if (xen_hvm_domain())
143016b6f5fSStefano Stabellini 		err = stop_machine(xen_hvm_suspend, &cancelled, cpumask_of(0));
144016b6f5fSStefano Stabellini 	else
145f7df8ed1SRusty Russell 		err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
146922cc38aSJeremy Fitzhardinge 
147922cc38aSJeremy Fitzhardinge 	dpm_resume_noirq(PMSG_RESUME);
148922cc38aSJeremy Fitzhardinge 
1490e91398fSJeremy Fitzhardinge 	if (err) {
1500e91398fSJeremy Fitzhardinge 		printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
15165f63384SIan Campbell 		cancelled = 1;
1520e91398fSJeremy Fitzhardinge 	}
1530e91398fSJeremy Fitzhardinge 
154c5cae661SIan Campbell out_resume:
155ad55db9fSIsaku Yamahata 	if (!cancelled) {
156ad55db9fSIsaku Yamahata 		xen_arch_resume();
157de5b31bdSIan Campbell 		xs_resume();
158ad55db9fSIsaku Yamahata 	} else
159de5b31bdSIan Campbell 		xs_suspend_cancel();
1600e91398fSJeremy Fitzhardinge 
161d1616302SAlan Stern 	dpm_resume_end(PMSG_RESUME);
1620e91398fSJeremy Fitzhardinge 
163359cdd3fSJeremy Fitzhardinge 	/* Make sure timer events get retriggered on all CPUs */
164359cdd3fSJeremy Fitzhardinge 	clock_was_set();
16565f63384SIan Campbell 
16665f63384SIan Campbell out_thaw:
1670e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT
1680e91398fSJeremy Fitzhardinge 	thaw_processes();
16965f63384SIan Campbell out:
1703fc1f1e2STejun Heo #endif
1710e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_INVALID;
1720e91398fSJeremy Fitzhardinge }
173c7827728SJeremy Fitzhardinge #endif	/* CONFIG_PM_SLEEP */
174ec9b2065SIsaku Yamahata 
175ec9b2065SIsaku Yamahata static void shutdown_handler(struct xenbus_watch *watch,
176ec9b2065SIsaku Yamahata 			     const char **vec, unsigned int len)
177ec9b2065SIsaku Yamahata {
178ec9b2065SIsaku Yamahata 	char *str;
179ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
180ec9b2065SIsaku Yamahata 	int err;
181ec9b2065SIsaku Yamahata 
182ec9b2065SIsaku Yamahata 	if (shutting_down != SHUTDOWN_INVALID)
183ec9b2065SIsaku Yamahata 		return;
184ec9b2065SIsaku Yamahata 
185ec9b2065SIsaku Yamahata  again:
186ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
187ec9b2065SIsaku Yamahata 	if (err)
188ec9b2065SIsaku Yamahata 		return;
189ec9b2065SIsaku Yamahata 
190ec9b2065SIsaku Yamahata 	str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
191ec9b2065SIsaku Yamahata 	/* Ignore read errors and empty reads. */
192ec9b2065SIsaku Yamahata 	if (XENBUS_IS_ERR_READ(str)) {
193ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
194ec9b2065SIsaku Yamahata 		return;
195ec9b2065SIsaku Yamahata 	}
196ec9b2065SIsaku Yamahata 
197ec9b2065SIsaku Yamahata 	xenbus_write(xbt, "control", "shutdown", "");
198ec9b2065SIsaku Yamahata 
199ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
200ec9b2065SIsaku Yamahata 	if (err == -EAGAIN) {
201ec9b2065SIsaku Yamahata 		kfree(str);
202ec9b2065SIsaku Yamahata 		goto again;
203ec9b2065SIsaku Yamahata 	}
204ec9b2065SIsaku Yamahata 
205ec9b2065SIsaku Yamahata 	if (strcmp(str, "poweroff") == 0 ||
2060e91398fSJeremy Fitzhardinge 	    strcmp(str, "halt") == 0) {
2070e91398fSJeremy Fitzhardinge 		shutting_down = SHUTDOWN_POWEROFF;
208ec9b2065SIsaku Yamahata 		orderly_poweroff(false);
2090e91398fSJeremy Fitzhardinge 	} else if (strcmp(str, "reboot") == 0) {
2100e91398fSJeremy Fitzhardinge 		shutting_down = SHUTDOWN_POWEROFF; /* ? */
211ec9b2065SIsaku Yamahata 		ctrl_alt_del();
2120e91398fSJeremy Fitzhardinge #ifdef CONFIG_PM_SLEEP
2130e91398fSJeremy Fitzhardinge 	} else if (strcmp(str, "suspend") == 0) {
2140e91398fSJeremy Fitzhardinge 		do_suspend();
2150e91398fSJeremy Fitzhardinge #endif
2160e91398fSJeremy Fitzhardinge 	} else {
217ec9b2065SIsaku Yamahata 		printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
218ec9b2065SIsaku Yamahata 		shutting_down = SHUTDOWN_INVALID;
219ec9b2065SIsaku Yamahata 	}
220ec9b2065SIsaku Yamahata 
221ec9b2065SIsaku Yamahata 	kfree(str);
222ec9b2065SIsaku Yamahata }
223ec9b2065SIsaku Yamahata 
224f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ
225ec9b2065SIsaku Yamahata static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
226ec9b2065SIsaku Yamahata 			  unsigned int len)
227ec9b2065SIsaku Yamahata {
228ec9b2065SIsaku Yamahata 	char sysrq_key = '\0';
229ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
230ec9b2065SIsaku Yamahata 	int err;
231ec9b2065SIsaku Yamahata 
232ec9b2065SIsaku Yamahata  again:
233ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
234ec9b2065SIsaku Yamahata 	if (err)
235ec9b2065SIsaku Yamahata 		return;
236ec9b2065SIsaku Yamahata 	if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
237ec9b2065SIsaku Yamahata 		printk(KERN_ERR "Unable to read sysrq code in "
238ec9b2065SIsaku Yamahata 		       "control/sysrq\n");
239ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
240ec9b2065SIsaku Yamahata 		return;
241ec9b2065SIsaku Yamahata 	}
242ec9b2065SIsaku Yamahata 
243ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
244ec9b2065SIsaku Yamahata 		xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
245ec9b2065SIsaku Yamahata 
246ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
247ec9b2065SIsaku Yamahata 	if (err == -EAGAIN)
248ec9b2065SIsaku Yamahata 		goto again;
249ec9b2065SIsaku Yamahata 
250ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
251f335397dSDmitry Torokhov 		handle_sysrq(sysrq_key);
252ec9b2065SIsaku Yamahata }
253ec9b2065SIsaku Yamahata 
254ec9b2065SIsaku Yamahata static struct xenbus_watch sysrq_watch = {
255ec9b2065SIsaku Yamahata 	.node = "control/sysrq",
256ec9b2065SIsaku Yamahata 	.callback = sysrq_handler
257ec9b2065SIsaku Yamahata };
258f3bc3189SRandy Dunlap #endif
259f3bc3189SRandy Dunlap 
260f3bc3189SRandy Dunlap static struct xenbus_watch shutdown_watch = {
261f3bc3189SRandy Dunlap 	.node = "control/shutdown",
262f3bc3189SRandy Dunlap 	.callback = shutdown_handler
263f3bc3189SRandy Dunlap };
264ec9b2065SIsaku Yamahata 
265ec9b2065SIsaku Yamahata static int setup_shutdown_watcher(void)
266ec9b2065SIsaku Yamahata {
267ec9b2065SIsaku Yamahata 	int err;
268ec9b2065SIsaku Yamahata 
269ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&shutdown_watch);
270ec9b2065SIsaku Yamahata 	if (err) {
271ec9b2065SIsaku Yamahata 		printk(KERN_ERR "Failed to set shutdown watcher\n");
272ec9b2065SIsaku Yamahata 		return err;
273ec9b2065SIsaku Yamahata 	}
274ec9b2065SIsaku Yamahata 
275f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ
276ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&sysrq_watch);
277ec9b2065SIsaku Yamahata 	if (err) {
278ec9b2065SIsaku Yamahata 		printk(KERN_ERR "Failed to set sysrq watcher\n");
279ec9b2065SIsaku Yamahata 		return err;
280ec9b2065SIsaku Yamahata 	}
281f3bc3189SRandy Dunlap #endif
282ec9b2065SIsaku Yamahata 
283ec9b2065SIsaku Yamahata 	return 0;
284ec9b2065SIsaku Yamahata }
285ec9b2065SIsaku Yamahata 
286ec9b2065SIsaku Yamahata static int shutdown_event(struct notifier_block *notifier,
287ec9b2065SIsaku Yamahata 			  unsigned long event,
288ec9b2065SIsaku Yamahata 			  void *data)
289ec9b2065SIsaku Yamahata {
290ec9b2065SIsaku Yamahata 	setup_shutdown_watcher();
291ec9b2065SIsaku Yamahata 	return NOTIFY_DONE;
292ec9b2065SIsaku Yamahata }
293ec9b2065SIsaku Yamahata 
294016b6f5fSStefano Stabellini int xen_setup_shutdown_event(void)
295ec9b2065SIsaku Yamahata {
296ec9b2065SIsaku Yamahata 	static struct notifier_block xenstore_notifier = {
297ec9b2065SIsaku Yamahata 		.notifier_call = shutdown_event
298ec9b2065SIsaku Yamahata 	};
299702d4eb9SStefano Stabellini 
300702d4eb9SStefano Stabellini 	if (!xen_domain())
301702d4eb9SStefano Stabellini 		return -ENODEV;
302ec9b2065SIsaku Yamahata 	register_xenstore_notifier(&xenstore_notifier);
303ec9b2065SIsaku Yamahata 
304ec9b2065SIsaku Yamahata 	return 0;
305ec9b2065SIsaku Yamahata }
306183d03ccSStefano Stabellini EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
307ec9b2065SIsaku Yamahata 
308702d4eb9SStefano Stabellini subsys_initcall(xen_setup_shutdown_event);
309