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(); 910e91398fSJeremy Fitzhardinge xen_console_resume(); 92ad55db9fSIsaku Yamahata xen_timer_resume(); 930e91398fSJeremy Fitzhardinge } 940e91398fSJeremy Fitzhardinge 9519234c08SRafael J. Wysocki syscore_resume(); 961e6fcf84SIan Campbell 970e91398fSJeremy Fitzhardinge return 0; 980e91398fSJeremy Fitzhardinge } 990e91398fSJeremy Fitzhardinge 1000e91398fSJeremy Fitzhardinge static void do_suspend(void) 1010e91398fSJeremy Fitzhardinge { 1020e91398fSJeremy Fitzhardinge int err; 103ceb18029SIan Campbell struct suspend_info si; 1040e91398fSJeremy Fitzhardinge 1050e91398fSJeremy Fitzhardinge shutting_down = SHUTDOWN_SUSPEND; 1060e91398fSJeremy Fitzhardinge 1070e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT 1080e91398fSJeremy Fitzhardinge /* If the kernel is preemptible, we need to freeze all the processes 1090e91398fSJeremy Fitzhardinge to prevent them from being in the middle of a pagetable update 1100e91398fSJeremy Fitzhardinge during suspend. */ 1110e91398fSJeremy Fitzhardinge err = freeze_processes(); 1120e91398fSJeremy Fitzhardinge if (err) { 113283c0972SJoe Perches pr_err("%s: freeze failed %d\n", __func__, err); 1143fc1f1e2STejun Heo goto out; 1150e91398fSJeremy Fitzhardinge } 1160e91398fSJeremy Fitzhardinge #endif 1170e91398fSJeremy Fitzhardinge 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 134ceb18029SIan Campbell si.cancelled = 1; 135ceb18029SIan Campbell 136ceb18029SIan Campbell err = stop_machine(xen_suspend, &si, cpumask_of(0)); 137922cc38aSJeremy Fitzhardinge 138cd979883SStanislaw Gruszka raw_notifier_call_chain(&xen_resume_notifier, 0, NULL); 139cd979883SStanislaw Gruszka 140cf579dfbSRafael J. Wysocki dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE); 141922cc38aSJeremy Fitzhardinge 1420e91398fSJeremy Fitzhardinge if (err) { 143283c0972SJoe Perches pr_err("failed to start xen_suspend: %d\n", err); 144ceb18029SIan Campbell si.cancelled = 1; 1450e91398fSJeremy Fitzhardinge } 1460e91398fSJeremy Fitzhardinge 147c5cae661SIan Campbell out_resume: 148ceb18029SIan Campbell if (!si.cancelled) { 149ad55db9fSIsaku Yamahata xen_arch_resume(); 150de5b31bdSIan Campbell xs_resume(); 151ad55db9fSIsaku Yamahata } else 152de5b31bdSIan Campbell xs_suspend_cancel(); 1530e91398fSJeremy Fitzhardinge 154b3e96c0cSShriram Rajagopalan dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE); 1550e91398fSJeremy Fitzhardinge 15665f63384SIan Campbell out_thaw: 1570e91398fSJeremy Fitzhardinge #ifdef CONFIG_PREEMPT 1580e91398fSJeremy Fitzhardinge thaw_processes(); 15965f63384SIan Campbell out: 1603fc1f1e2STejun Heo #endif 1610e91398fSJeremy Fitzhardinge shutting_down = SHUTDOWN_INVALID; 1620e91398fSJeremy Fitzhardinge } 1631f112ceeSRafael J. Wysocki #endif /* CONFIG_HIBERNATE_CALLBACKS */ 164ec9b2065SIsaku Yamahata 16555271723SIan Campbell struct shutdown_handler { 16655271723SIan Campbell const char *command; 16755271723SIan Campbell void (*cb)(void); 16855271723SIan Campbell }; 16955271723SIan Campbell 170eb47f712SKonrad Rzeszutek Wilk static int poweroff_nb(struct notifier_block *cb, unsigned long code, void *unused) 171eb47f712SKonrad Rzeszutek Wilk { 172eb47f712SKonrad Rzeszutek Wilk switch (code) { 173eb47f712SKonrad Rzeszutek Wilk case SYS_DOWN: 174eb47f712SKonrad Rzeszutek Wilk case SYS_HALT: 175eb47f712SKonrad Rzeszutek Wilk case SYS_POWER_OFF: 176eb47f712SKonrad Rzeszutek Wilk shutting_down = SHUTDOWN_POWEROFF; 177eb47f712SKonrad Rzeszutek Wilk default: 178eb47f712SKonrad Rzeszutek Wilk break; 179eb47f712SKonrad Rzeszutek Wilk } 180eb47f712SKonrad Rzeszutek Wilk return NOTIFY_DONE; 181eb47f712SKonrad Rzeszutek Wilk } 18255271723SIan Campbell static void do_poweroff(void) 18355271723SIan Campbell { 184eb47f712SKonrad Rzeszutek Wilk switch (system_state) { 185eb47f712SKonrad Rzeszutek Wilk case SYSTEM_BOOTING: 186eb47f712SKonrad Rzeszutek Wilk orderly_poweroff(true); 187eb47f712SKonrad Rzeszutek Wilk break; 188eb47f712SKonrad Rzeszutek Wilk case SYSTEM_RUNNING: 18955271723SIan Campbell orderly_poweroff(false); 190eb47f712SKonrad Rzeszutek Wilk break; 191eb47f712SKonrad Rzeszutek Wilk default: 192eb47f712SKonrad Rzeszutek Wilk /* Don't do it when we are halting/rebooting. */ 193eb47f712SKonrad Rzeszutek Wilk pr_info("Ignoring Xen toolstack shutdown.\n"); 194eb47f712SKonrad Rzeszutek Wilk break; 195eb47f712SKonrad Rzeszutek Wilk } 19655271723SIan Campbell } 19755271723SIan Campbell 19855271723SIan Campbell static void do_reboot(void) 19955271723SIan Campbell { 20055271723SIan Campbell shutting_down = SHUTDOWN_POWEROFF; /* ? */ 20155271723SIan Campbell ctrl_alt_del(); 20255271723SIan Campbell } 20355271723SIan Campbell 204ec9b2065SIsaku Yamahata static void shutdown_handler(struct xenbus_watch *watch, 205ec9b2065SIsaku Yamahata const char **vec, unsigned int len) 206ec9b2065SIsaku Yamahata { 207ec9b2065SIsaku Yamahata char *str; 208ec9b2065SIsaku Yamahata struct xenbus_transaction xbt; 209ec9b2065SIsaku Yamahata int err; 21055271723SIan Campbell static struct shutdown_handler handlers[] = { 21155271723SIan Campbell { "poweroff", do_poweroff }, 21255271723SIan Campbell { "halt", do_poweroff }, 21355271723SIan Campbell { "reboot", do_reboot }, 2141f112ceeSRafael J. Wysocki #ifdef CONFIG_HIBERNATE_CALLBACKS 21555271723SIan Campbell { "suspend", do_suspend }, 21655271723SIan Campbell #endif 21755271723SIan Campbell {NULL, NULL}, 21855271723SIan Campbell }; 21955271723SIan Campbell static struct shutdown_handler *handler; 220ec9b2065SIsaku Yamahata 221ec9b2065SIsaku Yamahata if (shutting_down != SHUTDOWN_INVALID) 222ec9b2065SIsaku Yamahata return; 223ec9b2065SIsaku Yamahata 224ec9b2065SIsaku Yamahata again: 225ec9b2065SIsaku Yamahata err = xenbus_transaction_start(&xbt); 226ec9b2065SIsaku Yamahata if (err) 227ec9b2065SIsaku Yamahata return; 228ec9b2065SIsaku Yamahata 229ec9b2065SIsaku Yamahata str = (char *)xenbus_read(xbt, "control", "shutdown", NULL); 230ec9b2065SIsaku Yamahata /* Ignore read errors and empty reads. */ 231ec9b2065SIsaku Yamahata if (XENBUS_IS_ERR_READ(str)) { 232ec9b2065SIsaku Yamahata xenbus_transaction_end(xbt, 1); 233ec9b2065SIsaku Yamahata return; 234ec9b2065SIsaku Yamahata } 235ec9b2065SIsaku Yamahata 23655271723SIan Campbell for (handler = &handlers[0]; handler->command; handler++) { 23755271723SIan Campbell if (strcmp(str, handler->command) == 0) 23855271723SIan Campbell break; 23955271723SIan Campbell } 24055271723SIan Campbell 24155271723SIan Campbell /* Only acknowledge commands which we are prepared to handle. */ 24255271723SIan Campbell if (handler->cb) 243ec9b2065SIsaku Yamahata xenbus_write(xbt, "control", "shutdown", ""); 244ec9b2065SIsaku Yamahata 245ec9b2065SIsaku Yamahata err = xenbus_transaction_end(xbt, 0); 246ec9b2065SIsaku Yamahata if (err == -EAGAIN) { 247ec9b2065SIsaku Yamahata kfree(str); 248ec9b2065SIsaku Yamahata goto again; 249ec9b2065SIsaku Yamahata } 250ec9b2065SIsaku Yamahata 25155271723SIan Campbell if (handler->cb) { 25255271723SIan Campbell handler->cb(); 2530e91398fSJeremy Fitzhardinge } else { 254283c0972SJoe Perches pr_info("Ignoring shutdown request: %s\n", str); 255ec9b2065SIsaku Yamahata shutting_down = SHUTDOWN_INVALID; 256ec9b2065SIsaku Yamahata } 257ec9b2065SIsaku Yamahata 258ec9b2065SIsaku Yamahata kfree(str); 259ec9b2065SIsaku Yamahata } 260ec9b2065SIsaku Yamahata 261f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ 262ec9b2065SIsaku Yamahata static void sysrq_handler(struct xenbus_watch *watch, const char **vec, 263ec9b2065SIsaku Yamahata unsigned int len) 264ec9b2065SIsaku Yamahata { 265ec9b2065SIsaku Yamahata char sysrq_key = '\0'; 266ec9b2065SIsaku Yamahata struct xenbus_transaction xbt; 267ec9b2065SIsaku Yamahata int err; 268ec9b2065SIsaku Yamahata 269ec9b2065SIsaku Yamahata again: 270ec9b2065SIsaku Yamahata err = xenbus_transaction_start(&xbt); 271ec9b2065SIsaku Yamahata if (err) 272ec9b2065SIsaku Yamahata return; 273ec9b2065SIsaku Yamahata if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { 274283c0972SJoe Perches pr_err("Unable to read sysrq code in control/sysrq\n"); 275ec9b2065SIsaku Yamahata xenbus_transaction_end(xbt, 1); 276ec9b2065SIsaku Yamahata return; 277ec9b2065SIsaku Yamahata } 278ec9b2065SIsaku Yamahata 279ec9b2065SIsaku Yamahata if (sysrq_key != '\0') 280ec9b2065SIsaku Yamahata xenbus_printf(xbt, "control", "sysrq", "%c", '\0'); 281ec9b2065SIsaku Yamahata 282ec9b2065SIsaku Yamahata err = xenbus_transaction_end(xbt, 0); 283ec9b2065SIsaku Yamahata if (err == -EAGAIN) 284ec9b2065SIsaku Yamahata goto again; 285ec9b2065SIsaku Yamahata 286ec9b2065SIsaku Yamahata if (sysrq_key != '\0') 287f335397dSDmitry Torokhov handle_sysrq(sysrq_key); 288ec9b2065SIsaku Yamahata } 289ec9b2065SIsaku Yamahata 290ec9b2065SIsaku Yamahata static struct xenbus_watch sysrq_watch = { 291ec9b2065SIsaku Yamahata .node = "control/sysrq", 292ec9b2065SIsaku Yamahata .callback = sysrq_handler 293ec9b2065SIsaku Yamahata }; 294f3bc3189SRandy Dunlap #endif 295f3bc3189SRandy Dunlap 296f3bc3189SRandy Dunlap static struct xenbus_watch shutdown_watch = { 297f3bc3189SRandy Dunlap .node = "control/shutdown", 298f3bc3189SRandy Dunlap .callback = shutdown_handler 299f3bc3189SRandy Dunlap }; 300ec9b2065SIsaku Yamahata 301eb47f712SKonrad Rzeszutek Wilk static struct notifier_block xen_reboot_nb = { 302eb47f712SKonrad Rzeszutek Wilk .notifier_call = poweroff_nb, 303eb47f712SKonrad Rzeszutek Wilk }; 304eb47f712SKonrad Rzeszutek Wilk 305ec9b2065SIsaku Yamahata static int setup_shutdown_watcher(void) 306ec9b2065SIsaku Yamahata { 307ec9b2065SIsaku Yamahata int err; 308ec9b2065SIsaku Yamahata 309ec9b2065SIsaku Yamahata err = register_xenbus_watch(&shutdown_watch); 310ec9b2065SIsaku Yamahata if (err) { 311283c0972SJoe Perches pr_err("Failed to set shutdown watcher\n"); 312ec9b2065SIsaku Yamahata return err; 313ec9b2065SIsaku Yamahata } 314ec9b2065SIsaku Yamahata 315eb47f712SKonrad Rzeszutek Wilk 316f3bc3189SRandy Dunlap #ifdef CONFIG_MAGIC_SYSRQ 317ec9b2065SIsaku Yamahata err = register_xenbus_watch(&sysrq_watch); 318ec9b2065SIsaku Yamahata if (err) { 319283c0972SJoe Perches pr_err("Failed to set sysrq watcher\n"); 320ec9b2065SIsaku Yamahata return err; 321ec9b2065SIsaku Yamahata } 322f3bc3189SRandy Dunlap #endif 323ec9b2065SIsaku Yamahata 324ec9b2065SIsaku Yamahata return 0; 325ec9b2065SIsaku Yamahata } 326ec9b2065SIsaku Yamahata 327ec9b2065SIsaku Yamahata static int shutdown_event(struct notifier_block *notifier, 328ec9b2065SIsaku Yamahata unsigned long event, 329ec9b2065SIsaku Yamahata void *data) 330ec9b2065SIsaku Yamahata { 331ec9b2065SIsaku Yamahata setup_shutdown_watcher(); 332ec9b2065SIsaku Yamahata return NOTIFY_DONE; 333ec9b2065SIsaku Yamahata } 334ec9b2065SIsaku Yamahata 335016b6f5fSStefano Stabellini int xen_setup_shutdown_event(void) 336ec9b2065SIsaku Yamahata { 337ec9b2065SIsaku Yamahata static struct notifier_block xenstore_notifier = { 338ec9b2065SIsaku Yamahata .notifier_call = shutdown_event 339ec9b2065SIsaku Yamahata }; 340702d4eb9SStefano Stabellini 341702d4eb9SStefano Stabellini if (!xen_domain()) 342702d4eb9SStefano Stabellini return -ENODEV; 343ec9b2065SIsaku Yamahata register_xenstore_notifier(&xenstore_notifier); 344eb47f712SKonrad Rzeszutek Wilk register_reboot_notifier(&xen_reboot_nb); 345ec9b2065SIsaku Yamahata 346ec9b2065SIsaku Yamahata return 0; 347ec9b2065SIsaku Yamahata } 348183d03ccSStefano Stabellini EXPORT_SYMBOL_GPL(xen_setup_shutdown_event); 349ec9b2065SIsaku Yamahata 350702d4eb9SStefano Stabellini subsys_initcall(xen_setup_shutdown_event); 351