xref: /openbmc/linux/drivers/xen/manage.c (revision c7827728)
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 
420e91398fSJeremy Fitzhardinge 	load_cr3(swapper_pg_dir);
430e91398fSJeremy Fitzhardinge 
44359cdd3fSJeremy Fitzhardinge 	err = device_power_down(PMSG_SUSPEND);
45359cdd3fSJeremy Fitzhardinge 	if (err) {
46359cdd3fSJeremy Fitzhardinge 		printk(KERN_ERR "xen_suspend: device_power_down failed: %d\n",
47359cdd3fSJeremy Fitzhardinge 		       err);
48359cdd3fSJeremy Fitzhardinge 		return err;
49359cdd3fSJeremy Fitzhardinge 	}
50359cdd3fSJeremy Fitzhardinge 
510e91398fSJeremy Fitzhardinge 	xen_mm_pin_all();
520e91398fSJeremy Fitzhardinge 	gnttab_suspend();
530e91398fSJeremy Fitzhardinge 	xen_pre_suspend();
540e91398fSJeremy Fitzhardinge 
550e91398fSJeremy Fitzhardinge 	/*
560e91398fSJeremy Fitzhardinge 	 * This hypercall returns 1 if suspend was cancelled
570e91398fSJeremy Fitzhardinge 	 * or the domain was merely checkpointed, and 0 if it
580e91398fSJeremy Fitzhardinge 	 * is resuming in a new domain.
590e91398fSJeremy Fitzhardinge 	 */
600e91398fSJeremy Fitzhardinge 	*cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
610e91398fSJeremy Fitzhardinge 
620e91398fSJeremy Fitzhardinge 	xen_post_suspend(*cancelled);
630e91398fSJeremy Fitzhardinge 	gnttab_resume();
640e91398fSJeremy Fitzhardinge 	xen_mm_unpin_all();
650e91398fSJeremy Fitzhardinge 
66359cdd3fSJeremy Fitzhardinge 	device_power_up();
67359cdd3fSJeremy Fitzhardinge 
680e91398fSJeremy Fitzhardinge 	if (!*cancelled) {
690e91398fSJeremy Fitzhardinge 		xen_irq_resume();
700e91398fSJeremy Fitzhardinge 		xen_console_resume();
710e91398fSJeremy Fitzhardinge 	}
720e91398fSJeremy Fitzhardinge 
730e91398fSJeremy Fitzhardinge 	return 0;
740e91398fSJeremy Fitzhardinge }
750e91398fSJeremy Fitzhardinge 
760e91398fSJeremy Fitzhardinge static void do_suspend(void)
770e91398fSJeremy Fitzhardinge {
780e91398fSJeremy Fitzhardinge 	int err;
790e91398fSJeremy Fitzhardinge 	int cancelled = 1;
800e91398fSJeremy Fitzhardinge 
810e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_SUSPEND;
820e91398fSJeremy Fitzhardinge 
830e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT
840e91398fSJeremy Fitzhardinge 	/* If the kernel is preemptible, we need to freeze all the processes
850e91398fSJeremy Fitzhardinge 	   to prevent them from being in the middle of a pagetable update
860e91398fSJeremy Fitzhardinge 	   during suspend. */
870e91398fSJeremy Fitzhardinge 	err = freeze_processes();
880e91398fSJeremy Fitzhardinge 	if (err) {
890e91398fSJeremy Fitzhardinge 		printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
900e91398fSJeremy Fitzhardinge 		return;
910e91398fSJeremy Fitzhardinge 	}
920e91398fSJeremy Fitzhardinge #endif
930e91398fSJeremy Fitzhardinge 
940e91398fSJeremy Fitzhardinge 	err = device_suspend(PMSG_SUSPEND);
950e91398fSJeremy Fitzhardinge 	if (err) {
960e91398fSJeremy Fitzhardinge 		printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
970e91398fSJeremy Fitzhardinge 		goto out;
980e91398fSJeremy Fitzhardinge 	}
990e91398fSJeremy Fitzhardinge 
1000e91398fSJeremy Fitzhardinge 	printk("suspending xenbus...\n");
1010e91398fSJeremy Fitzhardinge 	/* XXX use normal device tree? */
1020e91398fSJeremy Fitzhardinge 	xenbus_suspend();
1030e91398fSJeremy Fitzhardinge 
1040e91398fSJeremy Fitzhardinge 	err = stop_machine_run(xen_suspend, &cancelled, 0);
1050e91398fSJeremy Fitzhardinge 	if (err) {
1060e91398fSJeremy Fitzhardinge 		printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
1070e91398fSJeremy Fitzhardinge 		goto out;
1080e91398fSJeremy Fitzhardinge 	}
1090e91398fSJeremy Fitzhardinge 
1100e91398fSJeremy Fitzhardinge 	if (!cancelled)
1110e91398fSJeremy Fitzhardinge 		xenbus_resume();
1120e91398fSJeremy Fitzhardinge 	else
1130e91398fSJeremy Fitzhardinge 		xenbus_suspend_cancel();
1140e91398fSJeremy Fitzhardinge 
1150e91398fSJeremy Fitzhardinge 	device_resume();
1160e91398fSJeremy Fitzhardinge 
117359cdd3fSJeremy Fitzhardinge 	/* Make sure timer events get retriggered on all CPUs */
118359cdd3fSJeremy Fitzhardinge 	clock_was_set();
1190e91398fSJeremy Fitzhardinge out:
1200e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT
1210e91398fSJeremy Fitzhardinge 	thaw_processes();
1220e91398fSJeremy Fitzhardinge #endif
1230e91398fSJeremy Fitzhardinge 	shutting_down = SHUTDOWN_INVALID;
1240e91398fSJeremy Fitzhardinge }
125c7827728SJeremy Fitzhardinge #endif	/* CONFIG_PM_SLEEP */
126ec9b2065SIsaku Yamahata 
127ec9b2065SIsaku Yamahata static void shutdown_handler(struct xenbus_watch *watch,
128ec9b2065SIsaku Yamahata 			     const char **vec, unsigned int len)
129ec9b2065SIsaku Yamahata {
130ec9b2065SIsaku Yamahata 	char *str;
131ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
132ec9b2065SIsaku Yamahata 	int err;
133ec9b2065SIsaku Yamahata 
134ec9b2065SIsaku Yamahata 	if (shutting_down != SHUTDOWN_INVALID)
135ec9b2065SIsaku Yamahata 		return;
136ec9b2065SIsaku Yamahata 
137ec9b2065SIsaku Yamahata  again:
138ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
139ec9b2065SIsaku Yamahata 	if (err)
140ec9b2065SIsaku Yamahata 		return;
141ec9b2065SIsaku Yamahata 
142ec9b2065SIsaku Yamahata 	str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
143ec9b2065SIsaku Yamahata 	/* Ignore read errors and empty reads. */
144ec9b2065SIsaku Yamahata 	if (XENBUS_IS_ERR_READ(str)) {
145ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
146ec9b2065SIsaku Yamahata 		return;
147ec9b2065SIsaku Yamahata 	}
148ec9b2065SIsaku Yamahata 
149ec9b2065SIsaku Yamahata 	xenbus_write(xbt, "control", "shutdown", "");
150ec9b2065SIsaku Yamahata 
151ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
152ec9b2065SIsaku Yamahata 	if (err == -EAGAIN) {
153ec9b2065SIsaku Yamahata 		kfree(str);
154ec9b2065SIsaku Yamahata 		goto again;
155ec9b2065SIsaku Yamahata 	}
156ec9b2065SIsaku Yamahata 
157ec9b2065SIsaku Yamahata 	if (strcmp(str, "poweroff") == 0 ||
1580e91398fSJeremy Fitzhardinge 	    strcmp(str, "halt") == 0) {
1590e91398fSJeremy Fitzhardinge 		shutting_down = SHUTDOWN_POWEROFF;
160ec9b2065SIsaku Yamahata 		orderly_poweroff(false);
1610e91398fSJeremy Fitzhardinge 	} else if (strcmp(str, "reboot") == 0) {
1620e91398fSJeremy Fitzhardinge 		shutting_down = SHUTDOWN_POWEROFF; /* ? */
163ec9b2065SIsaku Yamahata 		ctrl_alt_del();
1640e91398fSJeremy Fitzhardinge #ifdef CONFIG_PM_SLEEP
1650e91398fSJeremy Fitzhardinge 	} else if (strcmp(str, "suspend") == 0) {
1660e91398fSJeremy Fitzhardinge 		do_suspend();
1670e91398fSJeremy Fitzhardinge #endif
1680e91398fSJeremy Fitzhardinge 	} else {
169ec9b2065SIsaku Yamahata 		printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
170ec9b2065SIsaku Yamahata 		shutting_down = SHUTDOWN_INVALID;
171ec9b2065SIsaku Yamahata 	}
172ec9b2065SIsaku Yamahata 
173ec9b2065SIsaku Yamahata 	kfree(str);
174ec9b2065SIsaku Yamahata }
175ec9b2065SIsaku Yamahata 
176ec9b2065SIsaku Yamahata static void sysrq_handler(struct xenbus_watch *watch, const char **vec,
177ec9b2065SIsaku Yamahata 			  unsigned int len)
178ec9b2065SIsaku Yamahata {
179ec9b2065SIsaku Yamahata 	char sysrq_key = '\0';
180ec9b2065SIsaku Yamahata 	struct xenbus_transaction xbt;
181ec9b2065SIsaku Yamahata 	int err;
182ec9b2065SIsaku Yamahata 
183ec9b2065SIsaku Yamahata  again:
184ec9b2065SIsaku Yamahata 	err = xenbus_transaction_start(&xbt);
185ec9b2065SIsaku Yamahata 	if (err)
186ec9b2065SIsaku Yamahata 		return;
187ec9b2065SIsaku Yamahata 	if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
188ec9b2065SIsaku Yamahata 		printk(KERN_ERR "Unable to read sysrq code in "
189ec9b2065SIsaku Yamahata 		       "control/sysrq\n");
190ec9b2065SIsaku Yamahata 		xenbus_transaction_end(xbt, 1);
191ec9b2065SIsaku Yamahata 		return;
192ec9b2065SIsaku Yamahata 	}
193ec9b2065SIsaku Yamahata 
194ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
195ec9b2065SIsaku Yamahata 		xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
196ec9b2065SIsaku Yamahata 
197ec9b2065SIsaku Yamahata 	err = xenbus_transaction_end(xbt, 0);
198ec9b2065SIsaku Yamahata 	if (err == -EAGAIN)
199ec9b2065SIsaku Yamahata 		goto again;
200ec9b2065SIsaku Yamahata 
201ec9b2065SIsaku Yamahata 	if (sysrq_key != '\0')
202ec9b2065SIsaku Yamahata 		handle_sysrq(sysrq_key, NULL);
203ec9b2065SIsaku Yamahata }
204ec9b2065SIsaku Yamahata 
205ec9b2065SIsaku Yamahata static struct xenbus_watch shutdown_watch = {
206ec9b2065SIsaku Yamahata 	.node = "control/shutdown",
207ec9b2065SIsaku Yamahata 	.callback = shutdown_handler
208ec9b2065SIsaku Yamahata };
209ec9b2065SIsaku Yamahata 
210ec9b2065SIsaku Yamahata static struct xenbus_watch sysrq_watch = {
211ec9b2065SIsaku Yamahata 	.node = "control/sysrq",
212ec9b2065SIsaku Yamahata 	.callback = sysrq_handler
213ec9b2065SIsaku Yamahata };
214ec9b2065SIsaku Yamahata 
215ec9b2065SIsaku Yamahata static int setup_shutdown_watcher(void)
216ec9b2065SIsaku Yamahata {
217ec9b2065SIsaku Yamahata 	int err;
218ec9b2065SIsaku Yamahata 
219ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&shutdown_watch);
220ec9b2065SIsaku Yamahata 	if (err) {
221ec9b2065SIsaku Yamahata 		printk(KERN_ERR "Failed to set shutdown watcher\n");
222ec9b2065SIsaku Yamahata 		return err;
223ec9b2065SIsaku Yamahata 	}
224ec9b2065SIsaku Yamahata 
225ec9b2065SIsaku Yamahata 	err = register_xenbus_watch(&sysrq_watch);
226ec9b2065SIsaku Yamahata 	if (err) {
227ec9b2065SIsaku Yamahata 		printk(KERN_ERR "Failed to set sysrq watcher\n");
228ec9b2065SIsaku Yamahata 		return err;
229ec9b2065SIsaku Yamahata 	}
230ec9b2065SIsaku Yamahata 
231ec9b2065SIsaku Yamahata 	return 0;
232ec9b2065SIsaku Yamahata }
233ec9b2065SIsaku Yamahata 
234ec9b2065SIsaku Yamahata static int shutdown_event(struct notifier_block *notifier,
235ec9b2065SIsaku Yamahata 			  unsigned long event,
236ec9b2065SIsaku Yamahata 			  void *data)
237ec9b2065SIsaku Yamahata {
238ec9b2065SIsaku Yamahata 	setup_shutdown_watcher();
239ec9b2065SIsaku Yamahata 	return NOTIFY_DONE;
240ec9b2065SIsaku Yamahata }
241ec9b2065SIsaku Yamahata 
242ec9b2065SIsaku Yamahata static int __init setup_shutdown_event(void)
243ec9b2065SIsaku Yamahata {
244ec9b2065SIsaku Yamahata 	static struct notifier_block xenstore_notifier = {
245ec9b2065SIsaku Yamahata 		.notifier_call = shutdown_event
246ec9b2065SIsaku Yamahata 	};
247ec9b2065SIsaku Yamahata 	register_xenstore_notifier(&xenstore_notifier);
248ec9b2065SIsaku Yamahata 
249ec9b2065SIsaku Yamahata 	return 0;
250ec9b2065SIsaku Yamahata }
251ec9b2065SIsaku Yamahata 
252ec9b2065SIsaku Yamahata subsys_initcall(setup_shutdown_event);
253