1ec9b2065SIsaku Yamahata /* 2ec9b2065SIsaku Yamahata * Handle extern requests for shutdown, reboot and sysrq 3ec9b2065SIsaku Yamahata */ 4283c0972SJoe Perches 5283c0972SJoe Perches #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt 6283c0972SJoe Perches 7ec9b2065SIsaku Yamahata #include <linux/kernel.h> 8ec9b2065SIsaku Yamahata #include <linux/err.h> 95a0e3ad6STejun Heo #include <linux/slab.h> 10ec9b2065SIsaku Yamahata #include <linux/reboot.h> 11ec9b2065SIsaku Yamahata #include <linux/sysrq.h> 120e91398fSJeremy Fitzhardinge #include <linux/stop_machine.h> 130e91398fSJeremy Fitzhardinge #include <linux/freezer.h> 1419234c08SRafael J. Wysocki #include <linux/syscore_ops.h> 1563c9744bSPaul Gortmaker #include <linux/export.h> 16ec9b2065SIsaku Yamahata 17016b6f5fSStefano Stabellini #include <xen/xen.h> 18ec9b2065SIsaku Yamahata #include <xen/xenbus.h> 190e91398fSJeremy Fitzhardinge #include <xen/grant_table.h> 200e91398fSJeremy Fitzhardinge #include <xen/events.h> 210e91398fSJeremy Fitzhardinge #include <xen/hvc-console.h> 220e91398fSJeremy Fitzhardinge #include <xen/xen-ops.h> 23ec9b2065SIsaku Yamahata 240e91398fSJeremy Fitzhardinge #include <asm/xen/hypercall.h> 250e91398fSJeremy Fitzhardinge #include <asm/xen/page.h> 26016b6f5fSStefano Stabellini #include <asm/xen/hypervisor.h> 270e91398fSJeremy Fitzhardinge 280e91398fSJeremy Fitzhardinge enum shutdown_state { 290e91398fSJeremy Fitzhardinge SHUTDOWN_INVALID = -1, 300e91398fSJeremy Fitzhardinge SHUTDOWN_POWEROFF = 0, 310e91398fSJeremy Fitzhardinge SHUTDOWN_SUSPEND = 2, 32ec9b2065SIsaku Yamahata /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only 330e91398fSJeremy Fitzhardinge report a crash, not be instructed to crash! 340e91398fSJeremy Fitzhardinge HALT is the same as POWEROFF, as far as we're concerned. The tools use 350e91398fSJeremy Fitzhardinge the distinction when we return the reason code to them. */ 360e91398fSJeremy Fitzhardinge SHUTDOWN_HALT = 4, 370e91398fSJeremy Fitzhardinge }; 38ec9b2065SIsaku Yamahata 39ec9b2065SIsaku Yamahata /* Ignore multiple shutdown requests. */ 400e91398fSJeremy Fitzhardinge static enum shutdown_state shutting_down = SHUTDOWN_INVALID; 410e91398fSJeremy Fitzhardinge 42ceb18029SIan Campbell struct suspend_info { 43ceb18029SIan Campbell int cancelled; 44ceb18029SIan Campbell }; 45ceb18029SIan Campbell 46cd979883SStanislaw Gruszka static RAW_NOTIFIER_HEAD(xen_resume_notifier); 47cd979883SStanislaw Gruszka 48cd979883SStanislaw Gruszka void xen_resume_notifier_register(struct notifier_block *nb) 49cd979883SStanislaw Gruszka { 50cd979883SStanislaw Gruszka raw_notifier_chain_register(&xen_resume_notifier, nb); 51cd979883SStanislaw Gruszka } 52cd979883SStanislaw Gruszka EXPORT_SYMBOL_GPL(xen_resume_notifier_register); 53cd979883SStanislaw Gruszka 54cd979883SStanislaw Gruszka void xen_resume_notifier_unregister(struct notifier_block *nb) 55cd979883SStanislaw Gruszka { 56cd979883SStanislaw Gruszka raw_notifier_chain_unregister(&xen_resume_notifier, nb); 57cd979883SStanislaw Gruszka } 58cd979883SStanislaw Gruszka EXPORT_SYMBOL_GPL(xen_resume_notifier_unregister); 59cd979883SStanislaw Gruszka 6065e053a7SStefano Stabellini #ifdef CONFIG_HIBERNATE_CALLBACKS 610e91398fSJeremy Fitzhardinge static int xen_suspend(void *data) 620e91398fSJeremy Fitzhardinge { 63ceb18029SIan Campbell struct suspend_info *si = data; 64359cdd3fSJeremy Fitzhardinge int err; 650e91398fSJeremy Fitzhardinge 660e91398fSJeremy Fitzhardinge BUG_ON(!irqs_disabled()); 670e91398fSJeremy Fitzhardinge 6819234c08SRafael J. Wysocki err = syscore_suspend(); 69770824bdSRafael J. Wysocki if (err) { 70283c0972SJoe Perches pr_err("%s: system core suspend failed: %d\n", __func__, err); 71770824bdSRafael J. Wysocki return err; 72770824bdSRafael J. Wysocki } 73359cdd3fSJeremy Fitzhardinge 74aa8532c3SDavid Vrabel gnttab_suspend(); 75aa8532c3SDavid Vrabel xen_arch_pre_suspend(); 760e91398fSJeremy Fitzhardinge 770e91398fSJeremy Fitzhardinge /* 780e91398fSJeremy Fitzhardinge * This hypercall returns 1 if suspend was cancelled 790e91398fSJeremy Fitzhardinge * or the domain was merely checkpointed, and 0 if it 800e91398fSJeremy Fitzhardinge * is resuming in a new domain. 810e91398fSJeremy Fitzhardinge */ 82aa8532c3SDavid Vrabel si->cancelled = HYPERVISOR_suspend(xen_pv_domain() 83aa8532c3SDavid Vrabel ? virt_to_mfn(xen_start_info) 84aa8532c3SDavid Vrabel : 0); 850e91398fSJeremy Fitzhardinge 86aa8532c3SDavid Vrabel xen_arch_post_suspend(si->cancelled); 87aa8532c3SDavid Vrabel gnttab_resume(); 880e91398fSJeremy Fitzhardinge 89ceb18029SIan Campbell if (!si->cancelled) { 900e91398fSJeremy Fitzhardinge xen_irq_resume(); 91ad55db9fSIsaku Yamahata xen_timer_resume(); 920e91398fSJeremy Fitzhardinge } 930e91398fSJeremy Fitzhardinge 9419234c08SRafael J. Wysocki syscore_resume(); 951e6fcf84SIan Campbell 960e91398fSJeremy Fitzhardinge return 0; 970e91398fSJeremy Fitzhardinge } 980e91398fSJeremy Fitzhardinge 990e91398fSJeremy Fitzhardinge static void do_suspend(void) 1000e91398fSJeremy Fitzhardinge { 1010e91398fSJeremy Fitzhardinge int err; 102ceb18029SIan Campbell struct suspend_info si; 1030e91398fSJeremy Fitzhardinge 1040e91398fSJeremy Fitzhardinge shutting_down = SHUTDOWN_SUSPEND; 1050e91398fSJeremy Fitzhardinge 1060e91398fSJeremy Fitzhardinge err = freeze_processes(); 1070e91398fSJeremy Fitzhardinge if (err) { 10872978b2fSRoss Lagerwall pr_err("%s: freeze processes failed %d\n", __func__, err); 1093fc1f1e2STejun Heo goto out; 1100e91398fSJeremy Fitzhardinge } 1110e91398fSJeremy Fitzhardinge 11272978b2fSRoss Lagerwall err = freeze_kernel_threads(); 11372978b2fSRoss Lagerwall if (err) { 11472978b2fSRoss Lagerwall pr_err("%s: freeze kernel threads failed %d\n", __func__, err); 11572978b2fSRoss Lagerwall goto out_thaw; 11672978b2fSRoss Lagerwall } 11772978b2fSRoss Lagerwall 118b3e96c0cSShriram Rajagopalan err = dpm_suspend_start(PMSG_FREEZE); 1190e91398fSJeremy Fitzhardinge if (err) { 120283c0972SJoe Perches pr_err("%s: dpm_suspend_start %d\n", __func__, err); 12165f63384SIan Campbell goto out_thaw; 1220e91398fSJeremy Fitzhardinge } 1230e91398fSJeremy Fitzhardinge 124c5cae661SIan Campbell printk(KERN_DEBUG "suspending xenstore...\n"); 125c5cae661SIan Campbell xs_suspend(); 126c5cae661SIan Campbell 127cf579dfbSRafael J. Wysocki err = dpm_suspend_end(PMSG_FREEZE); 1282ed8d2b3SRafael J. Wysocki if (err) { 129283c0972SJoe Perches pr_err("dpm_suspend_end failed: %d\n", err); 130186bab1cSKonrad Rzeszutek Wilk si.cancelled = 0; 13165f63384SIan Campbell goto out_resume; 1322ed8d2b3SRafael J. Wysocki } 1332ed8d2b3SRafael J. Wysocki 1342b953a5eSBoris Ostrovsky xen_arch_suspend(); 1352b953a5eSBoris Ostrovsky 136ceb18029SIan Campbell si.cancelled = 1; 137ceb18029SIan Campbell 138ceb18029SIan Campbell err = stop_machine(xen_suspend, &si, cpumask_of(0)); 139922cc38aSJeremy Fitzhardinge 1401b647823SDavid Vrabel /* Resume console as early as possible. */ 1411b647823SDavid Vrabel if (!si.cancelled) 1421b647823SDavid Vrabel xen_console_resume(); 1431b647823SDavid Vrabel 144cd979883SStanislaw Gruszka raw_notifier_call_chain(&xen_resume_notifier, 0, NULL); 145cd979883SStanislaw Gruszka 146cf579dfbSRafael J. Wysocki dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE); 147922cc38aSJeremy Fitzhardinge 1480e91398fSJeremy Fitzhardinge if (err) { 149283c0972SJoe Perches pr_err("failed to start xen_suspend: %d\n", err); 150ceb18029SIan Campbell si.cancelled = 1; 1510e91398fSJeremy Fitzhardinge } 1520e91398fSJeremy Fitzhardinge 153ad55db9fSIsaku Yamahata xen_arch_resume(); 1542b953a5eSBoris Ostrovsky 1552b953a5eSBoris Ostrovsky out_resume: 1562b953a5eSBoris Ostrovsky if (!si.cancelled) 157de5b31bdSIan Campbell xs_resume(); 1582b953a5eSBoris Ostrovsky else 159de5b31bdSIan Campbell xs_suspend_cancel(); 1600e91398fSJeremy Fitzhardinge 161b3e96c0cSShriram Rajagopalan dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE); 1620e91398fSJeremy Fitzhardinge 16365f63384SIan Campbell out_thaw: 1640e91398fSJeremy Fitzhardinge thaw_processes(); 16565f63384SIan Campbell out: 1660e91398fSJeremy Fitzhardinge shutting_down = SHUTDOWN_INVALID; 1670e91398fSJeremy Fitzhardinge } 1681f112ceeSRafael J. Wysocki #endif /* CONFIG_HIBERNATE_CALLBACKS */ 169ec9b2065SIsaku Yamahata 17055271723SIan Campbell struct shutdown_handler { 17155271723SIan Campbell const char *command; 17255271723SIan Campbell void (*cb)(void); 17355271723SIan Campbell }; 17455271723SIan Campbell 175eb47f712SKonrad Rzeszutek Wilk static int poweroff_nb(struct notifier_block *cb, unsigned long code, void *unused) 176eb47f712SKonrad Rzeszutek Wilk { 177eb47f712SKonrad Rzeszutek Wilk switch (code) { 178eb47f712SKonrad Rzeszutek Wilk case SYS_DOWN: 179eb47f712SKonrad Rzeszutek Wilk case SYS_HALT: 180eb47f712SKonrad Rzeszutek Wilk case SYS_POWER_OFF: 181eb47f712SKonrad Rzeszutek Wilk shutting_down = SHUTDOWN_POWEROFF; 182eb47f712SKonrad Rzeszutek Wilk default: 183eb47f712SKonrad Rzeszutek Wilk break; 184eb47f712SKonrad Rzeszutek Wilk } 185eb47f712SKonrad Rzeszutek Wilk return NOTIFY_DONE; 186eb47f712SKonrad Rzeszutek Wilk } 18755271723SIan Campbell static void do_poweroff(void) 18855271723SIan Campbell { 189eb47f712SKonrad Rzeszutek Wilk switch (system_state) { 190eb47f712SKonrad Rzeszutek Wilk case SYSTEM_BOOTING: 191eb47f712SKonrad Rzeszutek Wilk orderly_poweroff(true); 192eb47f712SKonrad Rzeszutek Wilk break; 193eb47f712SKonrad Rzeszutek Wilk case SYSTEM_RUNNING: 19455271723SIan Campbell orderly_poweroff(false); 195eb47f712SKonrad Rzeszutek Wilk break; 196eb47f712SKonrad Rzeszutek Wilk default: 197eb47f712SKonrad Rzeszutek Wilk /* Don't do it when we are halting/rebooting. */ 198eb47f712SKonrad Rzeszutek Wilk pr_info("Ignoring Xen toolstack shutdown.\n"); 199eb47f712SKonrad Rzeszutek Wilk break; 200eb47f712SKonrad Rzeszutek Wilk } 20155271723SIan Campbell } 20255271723SIan Campbell 20355271723SIan Campbell static void do_reboot(void) 20455271723SIan Campbell { 20555271723SIan Campbell shutting_down = SHUTDOWN_POWEROFF; /* ? */ 20655271723SIan Campbell ctrl_alt_del(); 20755271723SIan Campbell } 20855271723SIan Campbell 209ec9b2065SIsaku Yamahata static void shutdown_handler(struct xenbus_watch *watch, 210ec9b2065SIsaku Yamahata const char **vec, unsigned int len) 211ec9b2065SIsaku Yamahata { 212ec9b2065SIsaku Yamahata char *str; 213ec9b2065SIsaku Yamahata struct xenbus_transaction xbt; 214ec9b2065SIsaku Yamahata int err; 21555271723SIan Campbell static struct shutdown_handler handlers[] = { 21655271723SIan Campbell { "poweroff", do_poweroff }, 21755271723SIan Campbell { "halt", do_poweroff }, 21855271723SIan Campbell { "reboot", do_reboot }, 2191f112ceeSRafael J. Wysocki #ifdef CONFIG_HIBERNATE_CALLBACKS 22055271723SIan Campbell { "suspend", do_suspend }, 22155271723SIan Campbell #endif 22255271723SIan Campbell {NULL, NULL}, 22355271723SIan Campbell }; 22455271723SIan Campbell static struct shutdown_handler *handler; 225ec9b2065SIsaku Yamahata 226ec9b2065SIsaku Yamahata if (shutting_down != SHUTDOWN_INVALID) 227ec9b2065SIsaku Yamahata return; 228ec9b2065SIsaku Yamahata 229ec9b2065SIsaku Yamahata again: 230ec9b2065SIsaku Yamahata err = xenbus_transaction_start(&xbt); 231ec9b2065SIsaku Yamahata if (err) 232ec9b2065SIsaku Yamahata return; 233ec9b2065SIsaku Yamahata 234ec9b2065SIsaku Yamahata str = (char *)xenbus_read(xbt, "control", "shutdown", NULL); 235ec9b2065SIsaku Yamahata /* Ignore read errors and empty reads. */ 236ec9b2065SIsaku Yamahata if (XENBUS_IS_ERR_READ(str)) { 237ec9b2065SIsaku Yamahata xenbus_transaction_end(xbt, 1); 238ec9b2065SIsaku Yamahata return; 239ec9b2065SIsaku Yamahata } 240ec9b2065SIsaku Yamahata 24155271723SIan Campbell for (handler = &handlers[0]; handler->command; handler++) { 24255271723SIan Campbell if (strcmp(str, handler->command) == 0) 24355271723SIan Campbell break; 24455271723SIan Campbell } 24555271723SIan Campbell 24655271723SIan Campbell /* Only acknowledge commands which we are prepared to handle. */ 24755271723SIan Campbell if (handler->cb) 248ec9b2065SIsaku Yamahata xenbus_write(xbt, "control", "shutdown", ""); 249ec9b2065SIsaku Yamahata 250ec9b2065SIsaku Yamahata err = xenbus_transaction_end(xbt, 0); 251ec9b2065SIsaku Yamahata if (err == -EAGAIN) { 252ec9b2065SIsaku Yamahata kfree(str); 253ec9b2065SIsaku Yamahata goto again; 254ec9b2065SIsaku Yamahata } 255ec9b2065SIsaku Yamahata 25655271723SIan Campbell if (handler->cb) { 25755271723SIan Campbell handler->cb(); 2580e91398fSJeremy Fitzhardinge } else { 259283c0972SJoe Perches pr_info("Ignoring shutdown request: %s\n", str); 260ec9b2065SIsaku Yamahata shutting_down = SHUTDOWN_INVALID; 261ec9b2065SIsaku Yamahata } 262ec9b2065SIsaku Yamahata 263ec9b2065SIsaku Yamahata kfree(str); 264ec9b2065SIsaku Yamahata } 265ec9b2065SIsaku Yamahata 266f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ 267ec9b2065SIsaku Yamahata static void sysrq_handler(struct xenbus_watch *watch, const char **vec, 268ec9b2065SIsaku Yamahata unsigned int len) 269ec9b2065SIsaku Yamahata { 270ec9b2065SIsaku Yamahata char sysrq_key = '\0'; 271ec9b2065SIsaku Yamahata struct xenbus_transaction xbt; 272ec9b2065SIsaku Yamahata int err; 273ec9b2065SIsaku Yamahata 274ec9b2065SIsaku Yamahata again: 275ec9b2065SIsaku Yamahata err = xenbus_transaction_start(&xbt); 276ec9b2065SIsaku Yamahata if (err) 277ec9b2065SIsaku Yamahata return; 278ec9b2065SIsaku Yamahata if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { 279283c0972SJoe Perches pr_err("Unable to read sysrq code in control/sysrq\n"); 280ec9b2065SIsaku Yamahata xenbus_transaction_end(xbt, 1); 281ec9b2065SIsaku Yamahata return; 282ec9b2065SIsaku Yamahata } 283ec9b2065SIsaku Yamahata 284ec9b2065SIsaku Yamahata if (sysrq_key != '\0') 285ec9b2065SIsaku Yamahata xenbus_printf(xbt, "control", "sysrq", "%c", '\0'); 286ec9b2065SIsaku Yamahata 287ec9b2065SIsaku Yamahata err = xenbus_transaction_end(xbt, 0); 288ec9b2065SIsaku Yamahata if (err == -EAGAIN) 289ec9b2065SIsaku Yamahata goto again; 290ec9b2065SIsaku Yamahata 291ec9b2065SIsaku Yamahata if (sysrq_key != '\0') 292f335397dSDmitry Torokhov handle_sysrq(sysrq_key); 293ec9b2065SIsaku Yamahata } 294ec9b2065SIsaku Yamahata 295ec9b2065SIsaku Yamahata static struct xenbus_watch sysrq_watch = { 296ec9b2065SIsaku Yamahata .node = "control/sysrq", 297ec9b2065SIsaku Yamahata .callback = sysrq_handler 298ec9b2065SIsaku Yamahata }; 299f3bc3189SRandy Dunlap #endif 300f3bc3189SRandy Dunlap 301f3bc3189SRandy Dunlap static struct xenbus_watch shutdown_watch = { 302f3bc3189SRandy Dunlap .node = "control/shutdown", 303f3bc3189SRandy Dunlap .callback = shutdown_handler 304f3bc3189SRandy Dunlap }; 305ec9b2065SIsaku Yamahata 306eb47f712SKonrad Rzeszutek Wilk static struct notifier_block xen_reboot_nb = { 307eb47f712SKonrad Rzeszutek Wilk .notifier_call = poweroff_nb, 308eb47f712SKonrad Rzeszutek Wilk }; 309eb47f712SKonrad Rzeszutek Wilk 310ec9b2065SIsaku Yamahata static int setup_shutdown_watcher(void) 311ec9b2065SIsaku Yamahata { 312ec9b2065SIsaku Yamahata int err; 313ec9b2065SIsaku Yamahata 314ec9b2065SIsaku Yamahata err = register_xenbus_watch(&shutdown_watch); 315ec9b2065SIsaku Yamahata if (err) { 316283c0972SJoe Perches pr_err("Failed to set shutdown watcher\n"); 317ec9b2065SIsaku Yamahata return err; 318ec9b2065SIsaku Yamahata } 319ec9b2065SIsaku Yamahata 320eb47f712SKonrad Rzeszutek Wilk 321f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ 322ec9b2065SIsaku Yamahata err = register_xenbus_watch(&sysrq_watch); 323ec9b2065SIsaku Yamahata if (err) { 324283c0972SJoe Perches pr_err("Failed to set sysrq watcher\n"); 325ec9b2065SIsaku Yamahata return err; 326ec9b2065SIsaku Yamahata } 327f3bc3189SRandy Dunlap #endif 328ec9b2065SIsaku Yamahata 329ec9b2065SIsaku Yamahata return 0; 330ec9b2065SIsaku Yamahata } 331ec9b2065SIsaku Yamahata 332ec9b2065SIsaku Yamahata static int shutdown_event(struct notifier_block *notifier, 333ec9b2065SIsaku Yamahata unsigned long event, 334ec9b2065SIsaku Yamahata void *data) 335ec9b2065SIsaku Yamahata { 336ec9b2065SIsaku Yamahata setup_shutdown_watcher(); 337ec9b2065SIsaku Yamahata return NOTIFY_DONE; 338ec9b2065SIsaku Yamahata } 339ec9b2065SIsaku Yamahata 340016b6f5fSStefano Stabellini int xen_setup_shutdown_event(void) 341ec9b2065SIsaku Yamahata { 342ec9b2065SIsaku Yamahata static struct notifier_block xenstore_notifier = { 343ec9b2065SIsaku Yamahata .notifier_call = shutdown_event 344ec9b2065SIsaku Yamahata }; 345702d4eb9SStefano Stabellini 346702d4eb9SStefano Stabellini if (!xen_domain()) 347702d4eb9SStefano Stabellini return -ENODEV; 348ec9b2065SIsaku Yamahata register_xenstore_notifier(&xenstore_notifier); 349eb47f712SKonrad Rzeszutek Wilk register_reboot_notifier(&xen_reboot_nb); 350ec9b2065SIsaku Yamahata 351ec9b2065SIsaku Yamahata return 0; 352ec9b2065SIsaku Yamahata } 353183d03ccSStefano Stabellini EXPORT_SYMBOL_GPL(xen_setup_shutdown_event); 354ec9b2065SIsaku Yamahata 355702d4eb9SStefano Stabellini subsys_initcall(xen_setup_shutdown_event); 356