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