xref: /openbmc/linux/drivers/xen/manage.c (revision 1e6fcf84)
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>
6ec9b2065SIsaku Yamahata #include <linux/reboot.h>
7ec9b2065SIsaku Yamahata #include <linux/sysrq.h>
80e91398fSJeremy Fitzhardinge #include <linux/stop_machine.h>
90e91398fSJeremy Fitzhardinge #include <linux/freezer.h>
10ec9b2065SIsaku Yamahata 
11ec9b2065SIsaku Yamahata #include <xen/xenbus.h>
120e91398fSJeremy Fitzhardinge #include <xen/grant_table.h>
130e91398fSJeremy Fitzhardinge #include <xen/events.h>
140e91398fSJeremy Fitzhardinge #include <xen/hvc-console.h>
150e91398fSJeremy Fitzhardinge #include <xen/xen-ops.h>
16ec9b2065SIsaku Yamahata 
170e91398fSJeremy Fitzhardinge #include <asm/xen/hypercall.h>
180e91398fSJeremy Fitzhardinge #include <asm/xen/page.h>
190e91398fSJeremy Fitzhardinge 
200e91398fSJeremy Fitzhardinge enum shutdown_state {
210e91398fSJeremy Fitzhardinge 	SHUTDOWN_INVALID = -1,
220e91398fSJeremy Fitzhardinge 	SHUTDOWN_POWEROFF = 0,
230e91398fSJeremy Fitzhardinge 	SHUTDOWN_SUSPEND = 2,
24ec9b2065SIsaku Yamahata 	/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
250e91398fSJeremy Fitzhardinge 	   report a crash, not be instructed to crash!
260e91398fSJeremy Fitzhardinge 	   HALT is the same as POWEROFF, as far as we're concerned.  The tools use
270e91398fSJeremy Fitzhardinge 	   the distinction when we return the reason code to them.  */
280e91398fSJeremy Fitzhardinge 	 SHUTDOWN_HALT = 4,
290e91398fSJeremy Fitzhardinge };
30ec9b2065SIsaku Yamahata 
31ec9b2065SIsaku Yamahata /* Ignore multiple shutdown requests. */
320e91398fSJeremy Fitzhardinge static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
330e91398fSJeremy Fitzhardinge 
34c7827728SJeremy Fitzhardinge #ifdef CONFIG_PM_SLEEP
350e91398fSJeremy Fitzhardinge static int xen_suspend(void *data)
360e91398fSJeremy Fitzhardinge {
370e91398fSJeremy Fitzhardinge 	int *cancelled = data;
38359cdd3fSJeremy Fitzhardinge 	int err;
390e91398fSJeremy Fitzhardinge 
400e91398fSJeremy Fitzhardinge 	BUG_ON(!irqs_disabled());
410e91398fSJeremy Fitzhardinge 
42359cdd3fSJeremy Fitzhardinge 	err = device_power_down(PMSG_SUSPEND);
43359cdd3fSJeremy Fitzhardinge 	if (err) {
44359cdd3fSJeremy Fitzhardinge 		printk(KERN_ERR "xen_suspend: device_power_down failed: %d\n",
45359cdd3fSJeremy Fitzhardinge 		       err);
46359cdd3fSJeremy Fitzhardinge 		return err;
47359cdd3fSJeremy Fitzhardinge 	}
48770824bdSRafael J. Wysocki 	err = sysdev_suspend(PMSG_SUSPEND);
49770824bdSRafael J. Wysocki 	if (err) {
50770824bdSRafael J. Wysocki 		printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
51770824bdSRafael J. Wysocki 			err);
52770824bdSRafael J. Wysocki 		device_power_up(PMSG_RESUME);
53770824bdSRafael J. Wysocki 		return err;
54770824bdSRafael J. Wysocki 	}
55359cdd3fSJeremy Fitzhardinge 
560e91398fSJeremy Fitzhardinge 	xen_mm_pin_all();
570e91398fSJeremy Fitzhardinge 	gnttab_suspend();
580e91398fSJeremy Fitzhardinge 	xen_pre_suspend();
590e91398fSJeremy Fitzhardinge 
600e91398fSJeremy Fitzhardinge 	/*
610e91398fSJeremy Fitzhardinge 	 * This hypercall returns 1 if suspend was cancelled
620e91398fSJeremy Fitzhardinge 	 * or the domain was merely checkpointed, and 0 if it
630e91398fSJeremy Fitzhardinge 	 * is resuming in a new domain.
640e91398fSJeremy Fitzhardinge 	 */
650e91398fSJeremy Fitzhardinge 	*cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
660e91398fSJeremy Fitzhardinge 
670e91398fSJeremy Fitzhardinge 	xen_post_suspend(*cancelled);
680e91398fSJeremy Fitzhardinge 	gnttab_resume();
690e91398fSJeremy Fitzhardinge 	xen_mm_unpin_all();
700e91398fSJeremy Fitzhardinge 
710e91398fSJeremy Fitzhardinge 	if (!*cancelled) {
720e91398fSJeremy Fitzhardinge 		xen_irq_resume();
730e91398fSJeremy Fitzhardinge 		xen_console_resume();
74ad55db9fSIsaku Yamahata 		xen_timer_resume();
750e91398fSJeremy Fitzhardinge 	}
760e91398fSJeremy Fitzhardinge 
771e6fcf84SIan Campbell 	sysdev_resume();
781e6fcf84SIan Campbell 	device_power_up(PMSG_RESUME);
791e6fcf84SIan Campbell 
800e91398fSJeremy Fitzhardinge 	return 0;
810e91398fSJeremy Fitzhardinge }
820e91398fSJeremy Fitzhardinge 
830e91398fSJeremy Fitzhardinge static void do_suspend(void)
840e91398fSJeremy Fitzhardinge {
850e91398fSJeremy Fitzhardinge 	int err;
860e91398fSJeremy Fitzhardinge 	int cancelled = 1;
870e91398fSJeremy Fitzhardinge 
880e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_SUSPEND;
890e91398fSJeremy Fitzhardinge 
900e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT
910e91398fSJeremy Fitzhardinge 	/* If the kernel is preemptible, we need to freeze all the processes
920e91398fSJeremy Fitzhardinge 	   to prevent them from being in the middle of a pagetable update
930e91398fSJeremy Fitzhardinge 	   during suspend. */
940e91398fSJeremy Fitzhardinge 	err = freeze_processes();
950e91398fSJeremy Fitzhardinge 	if (err) {
960e91398fSJeremy Fitzhardinge 		printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
970e91398fSJeremy Fitzhardinge 		return;
980e91398fSJeremy Fitzhardinge 	}
990e91398fSJeremy Fitzhardinge #endif
1000e91398fSJeremy Fitzhardinge 
1010e91398fSJeremy Fitzhardinge 	err = device_suspend(PMSG_SUSPEND);
1020e91398fSJeremy Fitzhardinge 	if (err) {
1030e91398fSJeremy Fitzhardinge 		printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
1040e91398fSJeremy Fitzhardinge 		goto out;
1050e91398fSJeremy Fitzhardinge 	}
1060e91398fSJeremy Fitzhardinge 
1070e91398fSJeremy Fitzhardinge 	printk("suspending xenbus...\n");
1080e91398fSJeremy Fitzhardinge 	/* XXX use normal device tree? */
1090e91398fSJeremy Fitzhardinge 	xenbus_suspend();
1100e91398fSJeremy Fitzhardinge 
111f7df8ed1SRusty Russell 	err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
1120e91398fSJeremy Fitzhardinge 	if (err) {
1130e91398fSJeremy Fitzhardinge 		printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
1140e91398fSJeremy Fitzhardinge 		goto out;
1150e91398fSJeremy Fitzhardinge 	}
1160e91398fSJeremy Fitzhardinge 
117ad55db9fSIsaku Yamahata 	if (!cancelled) {
118ad55db9fSIsaku Yamahata 		xen_arch_resume();
1190e91398fSJeremy Fitzhardinge 		xenbus_resume();
120ad55db9fSIsaku Yamahata 	} else
1210e91398fSJeremy Fitzhardinge 		xenbus_suspend_cancel();
1220e91398fSJeremy Fitzhardinge 
12355ca089eSStephen Rothwell 	device_resume(PMSG_RESUME);
1240e91398fSJeremy Fitzhardinge 
125359cdd3fSJeremy Fitzhardinge 	/* Make sure timer events get retriggered on all CPUs */
126359cdd3fSJeremy Fitzhardinge 	clock_was_set();
1270e91398fSJeremy Fitzhardinge out:
1280e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT
1290e91398fSJeremy Fitzhardinge 	thaw_processes();
1300e91398fSJeremy Fitzhardinge #endif
1310e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_INVALID;
1320e91398fSJeremy Fitzhardinge }
133c7827728SJeremy Fitzhardinge #endif	/* CONFIG_PM_SLEEP */
134ec9b2065SIsaku Yamahata 
135ec9b2065SIsaku Yamahata static void shutdown_handler(struct xenbus_watch *watch,
136ec9b2065SIsaku Yamahata 			     const char **vec, unsigned int len)
137ec9b2065SIsaku Yamahata {
138ec9b2065SIsaku Yamahata 	char *str;
139ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
140ec9b2065SIsaku Yamahata 	int err;
141ec9b2065SIsaku Yamahata 
142ec9b2065SIsaku Yamahata 	if (shutting_down != SHUTDOWN_INVALID)
143ec9b2065SIsaku Yamahata 		return;
144ec9b2065SIsaku Yamahata 
145ec9b2065SIsaku Yamahata  again:
146ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
147ec9b2065SIsaku Yamahata 	if (err)
148ec9b2065SIsaku Yamahata 		return;
149ec9b2065SIsaku Yamahata 
150ec9b2065SIsaku Yamahata 	str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
151ec9b2065SIsaku Yamahata 	/* Ignore read errors and empty reads. */
152ec9b2065SIsaku Yamahata 	if (XENBUS_IS_ERR_READ(str)) {
153ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
154ec9b2065SIsaku Yamahata 		return;
155ec9b2065SIsaku Yamahata 	}
156ec9b2065SIsaku Yamahata 
157ec9b2065SIsaku Yamahata 	xenbus_write(xbt, "control", "shutdown", "");
158ec9b2065SIsaku Yamahata 
159ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
160ec9b2065SIsaku Yamahata 	if (err == -EAGAIN) {
161ec9b2065SIsaku Yamahata 		kfree(str);
162ec9b2065SIsaku Yamahata 		goto again;
163ec9b2065SIsaku Yamahata 	}
164ec9b2065SIsaku Yamahata 
165ec9b2065SIsaku Yamahata 	if (strcmp(str, "poweroff") == 0 ||
1660e91398fSJeremy Fitzhardinge 	    strcmp(str, "halt") == 0) {
1670e91398fSJeremy Fitzhardinge 		shutting_down = SHUTDOWN_POWEROFF;
168ec9b2065SIsaku Yamahata 		orderly_poweroff(false);
1690e91398fSJeremy Fitzhardinge 	} else if (strcmp(str, "reboot") == 0) {
1700e91398fSJeremy Fitzhardinge 		shutting_down = SHUTDOWN_POWEROFF; /* ? */
171ec9b2065SIsaku Yamahata 		ctrl_alt_del();
1720e91398fSJeremy Fitzhardinge #ifdef CONFIG_PM_SLEEP
1730e91398fSJeremy Fitzhardinge 	} else if (strcmp(str, "suspend") == 0) {
1740e91398fSJeremy Fitzhardinge 		do_suspend();
1750e91398fSJeremy Fitzhardinge #endif
1760e91398fSJeremy Fitzhardinge 	} else {
177ec9b2065SIsaku Yamahata 		printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
178ec9b2065SIsaku Yamahata 		shutting_down = SHUTDOWN_INVALID;
179ec9b2065SIsaku Yamahata 	}
180ec9b2065SIsaku Yamahata 
181ec9b2065SIsaku Yamahata 	kfree(str);
182ec9b2065SIsaku Yamahata }
183ec9b2065SIsaku Yamahata 
184ec9b2065SIsaku Yamahata static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
185ec9b2065SIsaku Yamahata 			  unsigned int len)
186ec9b2065SIsaku Yamahata {
187ec9b2065SIsaku Yamahata 	char sysrq_key = '\0';
188ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
189ec9b2065SIsaku Yamahata 	int err;
190ec9b2065SIsaku Yamahata 
191ec9b2065SIsaku Yamahata  again:
192ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
193ec9b2065SIsaku Yamahata 	if (err)
194ec9b2065SIsaku Yamahata 		return;
195ec9b2065SIsaku Yamahata 	if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
196ec9b2065SIsaku Yamahata 		printk(KERN_ERR "Unable to read sysrq code in "
197ec9b2065SIsaku Yamahata 		       "control/sysrq\n");
198ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
199ec9b2065SIsaku Yamahata 		return;
200ec9b2065SIsaku Yamahata 	}
201ec9b2065SIsaku Yamahata 
202ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
203ec9b2065SIsaku Yamahata 		xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
204ec9b2065SIsaku Yamahata 
205ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
206ec9b2065SIsaku Yamahata 	if (err == -EAGAIN)
207ec9b2065SIsaku Yamahata 		goto again;
208ec9b2065SIsaku Yamahata 
209ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
210ec9b2065SIsaku Yamahata 		handle_sysrq(sysrq_key, NULL);
211ec9b2065SIsaku Yamahata }
212ec9b2065SIsaku Yamahata 
213ec9b2065SIsaku Yamahata static struct xenbus_watch shutdown_watch = {
214ec9b2065SIsaku Yamahata 	.node = "control/shutdown",
215ec9b2065SIsaku Yamahata 	.callback = shutdown_handler
216ec9b2065SIsaku Yamahata };
217ec9b2065SIsaku Yamahata 
218ec9b2065SIsaku Yamahata static struct xenbus_watch sysrq_watch = {
219ec9b2065SIsaku Yamahata 	.node = "control/sysrq",
220ec9b2065SIsaku Yamahata 	.callback = sysrq_handler
221ec9b2065SIsaku Yamahata };
222ec9b2065SIsaku Yamahata 
223ec9b2065SIsaku Yamahata static int setup_shutdown_watcher(void)
224ec9b2065SIsaku Yamahata {
225ec9b2065SIsaku Yamahata 	int err;
226ec9b2065SIsaku Yamahata 
227ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&shutdown_watch);
228ec9b2065SIsaku Yamahata 	if (err) {
229ec9b2065SIsaku Yamahata 		printk(KERN_ERR "Failed to set shutdown watcher\n");
230ec9b2065SIsaku Yamahata 		return err;
231ec9b2065SIsaku Yamahata 	}
232ec9b2065SIsaku Yamahata 
233ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&sysrq_watch);
234ec9b2065SIsaku Yamahata 	if (err) {
235ec9b2065SIsaku Yamahata 		printk(KERN_ERR "Failed to set sysrq watcher\n");
236ec9b2065SIsaku Yamahata 		return err;
237ec9b2065SIsaku Yamahata 	}
238ec9b2065SIsaku Yamahata 
239ec9b2065SIsaku Yamahata 	return 0;
240ec9b2065SIsaku Yamahata }
241ec9b2065SIsaku Yamahata 
242ec9b2065SIsaku Yamahata static int shutdown_event(struct notifier_block *notifier,
243ec9b2065SIsaku Yamahata 			  unsigned long event,
244ec9b2065SIsaku Yamahata 			  void *data)
245ec9b2065SIsaku Yamahata {
246ec9b2065SIsaku Yamahata 	setup_shutdown_watcher();
247ec9b2065SIsaku Yamahata 	return NOTIFY_DONE;
248ec9b2065SIsaku Yamahata }
249ec9b2065SIsaku Yamahata 
250ec9b2065SIsaku Yamahata static int __init setup_shutdown_event(void)
251ec9b2065SIsaku Yamahata {
252ec9b2065SIsaku Yamahata 	static struct notifier_block xenstore_notifier = {
253ec9b2065SIsaku Yamahata 		.notifier_call = shutdown_event
254ec9b2065SIsaku Yamahata 	};
255ec9b2065SIsaku Yamahata 	register_xenstore_notifier(&xenstore_notifier);
256ec9b2065SIsaku Yamahata 
257ec9b2065SIsaku Yamahata 	return 0;
258ec9b2065SIsaku Yamahata }
259ec9b2065SIsaku Yamahata 
260ec9b2065SIsaku Yamahata subsys_initcall(setup_shutdown_event);
261