18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Handle extern requests for shutdown, reboot and sysrq 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/reboot.h> 128c2ecf20Sopenharmony_ci#include <linux/sysrq.h> 138c2ecf20Sopenharmony_ci#include <linux/stop_machine.h> 148c2ecf20Sopenharmony_ci#include <linux/freezer.h> 158c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 168c2ecf20Sopenharmony_ci#include <linux/export.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <xen/xen.h> 198c2ecf20Sopenharmony_ci#include <xen/xenbus.h> 208c2ecf20Sopenharmony_ci#include <xen/grant_table.h> 218c2ecf20Sopenharmony_ci#include <xen/events.h> 228c2ecf20Sopenharmony_ci#include <xen/hvc-console.h> 238c2ecf20Sopenharmony_ci#include <xen/page.h> 248c2ecf20Sopenharmony_ci#include <xen/xen-ops.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <asm/xen/hypercall.h> 278c2ecf20Sopenharmony_ci#include <asm/xen/hypervisor.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cienum shutdown_state { 308c2ecf20Sopenharmony_ci SHUTDOWN_INVALID = -1, 318c2ecf20Sopenharmony_ci SHUTDOWN_POWEROFF = 0, 328c2ecf20Sopenharmony_ci SHUTDOWN_SUSPEND = 2, 338c2ecf20Sopenharmony_ci /* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only 348c2ecf20Sopenharmony_ci report a crash, not be instructed to crash! 358c2ecf20Sopenharmony_ci HALT is the same as POWEROFF, as far as we're concerned. The tools use 368c2ecf20Sopenharmony_ci the distinction when we return the reason code to them. */ 378c2ecf20Sopenharmony_ci SHUTDOWN_HALT = 4, 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Ignore multiple shutdown requests. */ 418c2ecf20Sopenharmony_cistatic enum shutdown_state shutting_down = SHUTDOWN_INVALID; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct suspend_info { 448c2ecf20Sopenharmony_ci int cancelled; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic RAW_NOTIFIER_HEAD(xen_resume_notifier); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_civoid xen_resume_notifier_register(struct notifier_block *nb) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci raw_notifier_chain_register(&xen_resume_notifier, nb); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_resume_notifier_register); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_civoid xen_resume_notifier_unregister(struct notifier_block *nb) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci raw_notifier_chain_unregister(&xen_resume_notifier, nb); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_resume_notifier_unregister); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#ifdef CONFIG_HIBERNATE_CALLBACKS 628c2ecf20Sopenharmony_cistatic int xen_suspend(void *data) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct suspend_info *si = data; 658c2ecf20Sopenharmony_ci int err; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci BUG_ON(!irqs_disabled()); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci err = syscore_suspend(); 708c2ecf20Sopenharmony_ci if (err) { 718c2ecf20Sopenharmony_ci pr_err("%s: system core suspend failed: %d\n", __func__, err); 728c2ecf20Sopenharmony_ci return err; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci gnttab_suspend(); 768c2ecf20Sopenharmony_ci xen_manage_runstate_time(-1); 778c2ecf20Sopenharmony_ci xen_arch_pre_suspend(); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci si->cancelled = HYPERVISOR_suspend(xen_pv_domain() 808c2ecf20Sopenharmony_ci ? virt_to_gfn(xen_start_info) 818c2ecf20Sopenharmony_ci : 0); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci xen_arch_post_suspend(si->cancelled); 848c2ecf20Sopenharmony_ci xen_manage_runstate_time(si->cancelled ? 1 : 0); 858c2ecf20Sopenharmony_ci gnttab_resume(); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (!si->cancelled) { 888c2ecf20Sopenharmony_ci xen_irq_resume(); 898c2ecf20Sopenharmony_ci xen_timer_resume(); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci syscore_resume(); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void do_suspend(void) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int err; 1008c2ecf20Sopenharmony_ci struct suspend_info si; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci shutting_down = SHUTDOWN_SUSPEND; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci err = freeze_processes(); 1058c2ecf20Sopenharmony_ci if (err) { 1068c2ecf20Sopenharmony_ci pr_err("%s: freeze processes failed %d\n", __func__, err); 1078c2ecf20Sopenharmony_ci goto out; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci err = freeze_kernel_threads(); 1118c2ecf20Sopenharmony_ci if (err) { 1128c2ecf20Sopenharmony_ci pr_err("%s: freeze kernel threads failed %d\n", __func__, err); 1138c2ecf20Sopenharmony_ci goto out_thaw; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci err = dpm_suspend_start(PMSG_FREEZE); 1178c2ecf20Sopenharmony_ci if (err) { 1188c2ecf20Sopenharmony_ci pr_err("%s: dpm_suspend_start %d\n", __func__, err); 1198c2ecf20Sopenharmony_ci goto out_thaw; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci printk(KERN_DEBUG "suspending xenstore...\n"); 1238c2ecf20Sopenharmony_ci xs_suspend(); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci err = dpm_suspend_end(PMSG_FREEZE); 1268c2ecf20Sopenharmony_ci if (err) { 1278c2ecf20Sopenharmony_ci pr_err("dpm_suspend_end failed: %d\n", err); 1288c2ecf20Sopenharmony_ci si.cancelled = 0; 1298c2ecf20Sopenharmony_ci goto out_resume; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci xen_arch_suspend(); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci si.cancelled = 1; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci err = stop_machine(xen_suspend, &si, cpumask_of(0)); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Resume console as early as possible. */ 1398c2ecf20Sopenharmony_ci if (!si.cancelled) 1408c2ecf20Sopenharmony_ci xen_console_resume(); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci raw_notifier_call_chain(&xen_resume_notifier, 0, NULL); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (err) { 1478c2ecf20Sopenharmony_ci pr_err("failed to start xen_suspend: %d\n", err); 1488c2ecf20Sopenharmony_ci si.cancelled = 1; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci xen_arch_resume(); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciout_resume: 1548c2ecf20Sopenharmony_ci if (!si.cancelled) 1558c2ecf20Sopenharmony_ci xs_resume(); 1568c2ecf20Sopenharmony_ci else 1578c2ecf20Sopenharmony_ci xs_suspend_cancel(); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciout_thaw: 1628c2ecf20Sopenharmony_ci thaw_processes(); 1638c2ecf20Sopenharmony_ciout: 1648c2ecf20Sopenharmony_ci shutting_down = SHUTDOWN_INVALID; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci#endif /* CONFIG_HIBERNATE_CALLBACKS */ 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistruct shutdown_handler { 1698c2ecf20Sopenharmony_ci#define SHUTDOWN_CMD_SIZE 11 1708c2ecf20Sopenharmony_ci const char command[SHUTDOWN_CMD_SIZE]; 1718c2ecf20Sopenharmony_ci bool flag; 1728c2ecf20Sopenharmony_ci void (*cb)(void); 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int poweroff_nb(struct notifier_block *cb, unsigned long code, void *unused) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci switch (code) { 1788c2ecf20Sopenharmony_ci case SYS_DOWN: 1798c2ecf20Sopenharmony_ci case SYS_HALT: 1808c2ecf20Sopenharmony_ci case SYS_POWER_OFF: 1818c2ecf20Sopenharmony_ci shutting_down = SHUTDOWN_POWEROFF; 1828c2ecf20Sopenharmony_ci default: 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_cistatic void do_poweroff(void) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci switch (system_state) { 1908c2ecf20Sopenharmony_ci case SYSTEM_BOOTING: 1918c2ecf20Sopenharmony_ci case SYSTEM_SCHEDULING: 1928c2ecf20Sopenharmony_ci orderly_poweroff(true); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci case SYSTEM_RUNNING: 1958c2ecf20Sopenharmony_ci orderly_poweroff(false); 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci default: 1988c2ecf20Sopenharmony_ci /* Don't do it when we are halting/rebooting. */ 1998c2ecf20Sopenharmony_ci pr_info("Ignoring Xen toolstack shutdown.\n"); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void do_reboot(void) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci shutting_down = SHUTDOWN_POWEROFF; /* ? */ 2078c2ecf20Sopenharmony_ci ctrl_alt_del(); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic struct shutdown_handler shutdown_handlers[] = { 2118c2ecf20Sopenharmony_ci { "poweroff", true, do_poweroff }, 2128c2ecf20Sopenharmony_ci { "halt", false, do_poweroff }, 2138c2ecf20Sopenharmony_ci { "reboot", true, do_reboot }, 2148c2ecf20Sopenharmony_ci#ifdef CONFIG_HIBERNATE_CALLBACKS 2158c2ecf20Sopenharmony_ci { "suspend", true, do_suspend }, 2168c2ecf20Sopenharmony_ci#endif 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void shutdown_handler(struct xenbus_watch *watch, 2208c2ecf20Sopenharmony_ci const char *path, const char *token) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci char *str; 2238c2ecf20Sopenharmony_ci struct xenbus_transaction xbt; 2248c2ecf20Sopenharmony_ci int err; 2258c2ecf20Sopenharmony_ci int idx; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (shutting_down != SHUTDOWN_INVALID) 2288c2ecf20Sopenharmony_ci return; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci again: 2318c2ecf20Sopenharmony_ci err = xenbus_transaction_start(&xbt); 2328c2ecf20Sopenharmony_ci if (err) 2338c2ecf20Sopenharmony_ci return; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci str = (char *)xenbus_read(xbt, "control", "shutdown", NULL); 2368c2ecf20Sopenharmony_ci /* Ignore read errors and empty reads. */ 2378c2ecf20Sopenharmony_ci if (XENBUS_IS_ERR_READ(str)) { 2388c2ecf20Sopenharmony_ci xenbus_transaction_end(xbt, 1); 2398c2ecf20Sopenharmony_ci return; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(shutdown_handlers); idx++) { 2438c2ecf20Sopenharmony_ci if (strcmp(str, shutdown_handlers[idx].command) == 0) 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Only acknowledge commands which we are prepared to handle. */ 2488c2ecf20Sopenharmony_ci if (idx < ARRAY_SIZE(shutdown_handlers)) 2498c2ecf20Sopenharmony_ci xenbus_write(xbt, "control", "shutdown", ""); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci err = xenbus_transaction_end(xbt, 0); 2528c2ecf20Sopenharmony_ci if (err == -EAGAIN) { 2538c2ecf20Sopenharmony_ci kfree(str); 2548c2ecf20Sopenharmony_ci goto again; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (idx < ARRAY_SIZE(shutdown_handlers)) { 2588c2ecf20Sopenharmony_ci shutdown_handlers[idx].cb(); 2598c2ecf20Sopenharmony_ci } else { 2608c2ecf20Sopenharmony_ci pr_info("Ignoring shutdown request: %s\n", str); 2618c2ecf20Sopenharmony_ci shutting_down = SHUTDOWN_INVALID; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci kfree(str); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci#ifdef CONFIG_MAGIC_SYSRQ 2688c2ecf20Sopenharmony_cistatic void sysrq_handler(struct xenbus_watch *watch, const char *path, 2698c2ecf20Sopenharmony_ci const char *token) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci char sysrq_key = '\0'; 2728c2ecf20Sopenharmony_ci struct xenbus_transaction xbt; 2738c2ecf20Sopenharmony_ci int err; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci again: 2768c2ecf20Sopenharmony_ci err = xenbus_transaction_start(&xbt); 2778c2ecf20Sopenharmony_ci if (err) 2788c2ecf20Sopenharmony_ci return; 2798c2ecf20Sopenharmony_ci err = xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key); 2808c2ecf20Sopenharmony_ci if (err < 0) { 2818c2ecf20Sopenharmony_ci /* 2828c2ecf20Sopenharmony_ci * The Xenstore watch fires directly after registering it and 2838c2ecf20Sopenharmony_ci * after a suspend/resume cycle. So ENOENT is no error but 2848c2ecf20Sopenharmony_ci * might happen in those cases. ERANGE is observed when we get 2858c2ecf20Sopenharmony_ci * an empty value (''), this happens when we acknowledge the 2868c2ecf20Sopenharmony_ci * request by writing '\0' below. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci if (err != -ENOENT && err != -ERANGE) 2898c2ecf20Sopenharmony_ci pr_err("Error %d reading sysrq code in control/sysrq\n", 2908c2ecf20Sopenharmony_ci err); 2918c2ecf20Sopenharmony_ci xenbus_transaction_end(xbt, 1); 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (sysrq_key != '\0') { 2968c2ecf20Sopenharmony_ci err = xenbus_printf(xbt, "control", "sysrq", "%c", '\0'); 2978c2ecf20Sopenharmony_ci if (err) { 2988c2ecf20Sopenharmony_ci pr_err("%s: Error %d writing sysrq in control/sysrq\n", 2998c2ecf20Sopenharmony_ci __func__, err); 3008c2ecf20Sopenharmony_ci xenbus_transaction_end(xbt, 1); 3018c2ecf20Sopenharmony_ci return; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci err = xenbus_transaction_end(xbt, 0); 3068c2ecf20Sopenharmony_ci if (err == -EAGAIN) 3078c2ecf20Sopenharmony_ci goto again; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (sysrq_key != '\0') 3108c2ecf20Sopenharmony_ci handle_sysrq(sysrq_key); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic struct xenbus_watch sysrq_watch = { 3148c2ecf20Sopenharmony_ci .node = "control/sysrq", 3158c2ecf20Sopenharmony_ci .callback = sysrq_handler 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci#endif 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic struct xenbus_watch shutdown_watch = { 3208c2ecf20Sopenharmony_ci .node = "control/shutdown", 3218c2ecf20Sopenharmony_ci .callback = shutdown_handler 3228c2ecf20Sopenharmony_ci}; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic struct notifier_block xen_reboot_nb = { 3258c2ecf20Sopenharmony_ci .notifier_call = poweroff_nb, 3268c2ecf20Sopenharmony_ci}; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int setup_shutdown_watcher(void) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci int err; 3318c2ecf20Sopenharmony_ci int idx; 3328c2ecf20Sopenharmony_ci#define FEATURE_PATH_SIZE (SHUTDOWN_CMD_SIZE + sizeof("feature-")) 3338c2ecf20Sopenharmony_ci char node[FEATURE_PATH_SIZE]; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci err = register_xenbus_watch(&shutdown_watch); 3368c2ecf20Sopenharmony_ci if (err) { 3378c2ecf20Sopenharmony_ci pr_err("Failed to set shutdown watcher\n"); 3388c2ecf20Sopenharmony_ci return err; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci#ifdef CONFIG_MAGIC_SYSRQ 3438c2ecf20Sopenharmony_ci err = register_xenbus_watch(&sysrq_watch); 3448c2ecf20Sopenharmony_ci if (err) { 3458c2ecf20Sopenharmony_ci pr_err("Failed to set sysrq watcher\n"); 3468c2ecf20Sopenharmony_ci return err; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci#endif 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(shutdown_handlers); idx++) { 3518c2ecf20Sopenharmony_ci if (!shutdown_handlers[idx].flag) 3528c2ecf20Sopenharmony_ci continue; 3538c2ecf20Sopenharmony_ci snprintf(node, FEATURE_PATH_SIZE, "feature-%s", 3548c2ecf20Sopenharmony_ci shutdown_handlers[idx].command); 3558c2ecf20Sopenharmony_ci err = xenbus_printf(XBT_NIL, "control", node, "%u", 1); 3568c2ecf20Sopenharmony_ci if (err) { 3578c2ecf20Sopenharmony_ci pr_err("%s: Error %d writing %s\n", __func__, 3588c2ecf20Sopenharmony_ci err, node); 3598c2ecf20Sopenharmony_ci return err; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int shutdown_event(struct notifier_block *notifier, 3678c2ecf20Sopenharmony_ci unsigned long event, 3688c2ecf20Sopenharmony_ci void *data) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci setup_shutdown_watcher(); 3718c2ecf20Sopenharmony_ci return NOTIFY_DONE; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ciint xen_setup_shutdown_event(void) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci static struct notifier_block xenstore_notifier = { 3778c2ecf20Sopenharmony_ci .notifier_call = shutdown_event 3788c2ecf20Sopenharmony_ci }; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (!xen_domain()) 3818c2ecf20Sopenharmony_ci return -ENODEV; 3828c2ecf20Sopenharmony_ci register_xenstore_notifier(&xenstore_notifier); 3838c2ecf20Sopenharmony_ci register_reboot_notifier(&xen_reboot_nb); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_setup_shutdown_event); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cisubsys_initcall(xen_setup_shutdown_event); 390