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