13e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge/*
23e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge * Handle extern requests for shutdown, reboot and sysrq
33e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge */
43e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge#include <linux/kernel.h>
53e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge#include <linux/err.h>
65a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
73e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge#include <linux/reboot.h>
83e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge#include <linux/sysrq.h>
90e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#include <linux/stop_machine.h>
100e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#include <linux/freezer.h>
1119234c0819da0e043a02710488dfd9b242b42ebaRafael J. Wysocki#include <linux/syscore_ops.h>
1263c9744b9a53b8113b6d33ca361452b28f2ec391Paul Gortmaker#include <linux/export.h>
133e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
14016b6f5fe8398b0291cece60b749d7c930a2e09cStefano Stabellini#include <xen/xen.h>
153e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge#include <xen/xenbus.h>
160e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#include <xen/grant_table.h>
170e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#include <xen/events.h>
180e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#include <xen/hvc-console.h>
190e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#include <xen/xen-ops.h>
203e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
210e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#include <asm/xen/hypercall.h>
220e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#include <asm/xen/page.h>
23016b6f5fe8398b0291cece60b749d7c930a2e09cStefano Stabellini#include <asm/xen/hypervisor.h>
240e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
250e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardingeenum shutdown_state {
260e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	SHUTDOWN_INVALID = -1,
270e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	SHUTDOWN_POWEROFF = 0,
280e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	SHUTDOWN_SUSPEND = 2,
290e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only
300e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	   report a crash, not be instructed to crash!
310e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	   HALT is the same as POWEROFF, as far as we're concerned.  The tools use
320e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	   the distinction when we return the reason code to them.  */
330e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	 SHUTDOWN_HALT = 4,
340e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge};
353e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
363e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge/* Ignore multiple shutdown requests. */
370e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardingestatic enum shutdown_state shutting_down = SHUTDOWN_INVALID;
380e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
39ceb180294790c8a6a437533488616f6b591b49d0Ian Campbellstruct suspend_info {
40ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell	int cancelled;
4136b401e2c2788c7b4881115ddbbff603fe4cf78dIan Campbell	unsigned long arg; /* extra hypercall argument */
4255fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell	void (*pre)(void);
4355fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell	void (*post)(int cancelled);
44ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell};
45ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell
4607af38102fc4f260cc5a2418ec833707f53cdf70Ian Campbellstatic void xen_hvm_post_suspend(int cancelled)
4782043bb60d24d2897074905c94be5a53071e8913Ian Campbell{
4807af38102fc4f260cc5a2418ec833707f53cdf70Ian Campbell	xen_arch_hvm_post_suspend(cancelled);
4982043bb60d24d2897074905c94be5a53071e8913Ian Campbell	gnttab_resume();
5082043bb60d24d2897074905c94be5a53071e8913Ian Campbell}
5182043bb60d24d2897074905c94be5a53071e8913Ian Campbell
5282043bb60d24d2897074905c94be5a53071e8913Ian Campbellstatic void xen_pre_suspend(void)
5382043bb60d24d2897074905c94be5a53071e8913Ian Campbell{
5482043bb60d24d2897074905c94be5a53071e8913Ian Campbell	xen_mm_pin_all();
5582043bb60d24d2897074905c94be5a53071e8913Ian Campbell	gnttab_suspend();
5607af38102fc4f260cc5a2418ec833707f53cdf70Ian Campbell	xen_arch_pre_suspend();
5782043bb60d24d2897074905c94be5a53071e8913Ian Campbell}
5882043bb60d24d2897074905c94be5a53071e8913Ian Campbell
5907af38102fc4f260cc5a2418ec833707f53cdf70Ian Campbellstatic void xen_post_suspend(int cancelled)
6082043bb60d24d2897074905c94be5a53071e8913Ian Campbell{
6107af38102fc4f260cc5a2418ec833707f53cdf70Ian Campbell	xen_arch_post_suspend(cancelled);
6282043bb60d24d2897074905c94be5a53071e8913Ian Campbell	gnttab_resume();
6382043bb60d24d2897074905c94be5a53071e8913Ian Campbell	xen_mm_unpin_all();
6482043bb60d24d2897074905c94be5a53071e8913Ian Campbell}
6582043bb60d24d2897074905c94be5a53071e8913Ian Campbell
661f112cee07b314e244ee9e71d9c1e6950dc13327Rafael J. Wysocki#ifdef CONFIG_HIBERNATE_CALLBACKS
67016b6f5fe8398b0291cece60b749d7c930a2e09cStefano Stabellinistatic int xen_suspend(void *data)
68016b6f5fe8398b0291cece60b749d7c930a2e09cStefano Stabellini{
69ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell	struct suspend_info *si = data;
70359cdd3f866b6219a6729e313faf2221397f3278Jeremy Fitzhardinge	int err;
710e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
720e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	BUG_ON(!irqs_disabled());
730e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
742e711c04dbbf7a7732a3f7073b1fc285d12b369dRafael J. Wysocki	err = syscore_suspend();
75770824bdc421ff58a64db608294323571c949f4cRafael J. Wysocki	if (err) {
7619234c0819da0e043a02710488dfd9b242b42ebaRafael J. Wysocki		printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n",
77770824bdc421ff58a64db608294323571c949f4cRafael J. Wysocki			err);
78770824bdc421ff58a64db608294323571c949f4cRafael J. Wysocki		return err;
79770824bdc421ff58a64db608294323571c949f4cRafael J. Wysocki	}
80359cdd3f866b6219a6729e313faf2221397f3278Jeremy Fitzhardinge
8155fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell	if (si->pre)
8255fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell		si->pre();
830e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
840e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	/*
850e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	 * This hypercall returns 1 if suspend was cancelled
860e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	 * or the domain was merely checkpointed, and 0 if it
870e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	 * is resuming in a new domain.
880e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	 */
8936b401e2c2788c7b4881115ddbbff603fe4cf78dIan Campbell	si->cancelled = HYPERVISOR_suspend(si->arg);
900e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
9155fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell	if (si->post)
9255fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell		si->post(si->cancelled);
930e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
94ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell	if (!si->cancelled) {
950e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge		xen_irq_resume();
960e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge		xen_console_resume();
97ad55db9fed6d6cd09333045945cb03ba2c070085Isaku Yamahata		xen_timer_resume();
980e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	}
990e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
10019234c0819da0e043a02710488dfd9b242b42ebaRafael J. Wysocki	syscore_resume();
1011e6fcf840e11ceff8a656a678c6e4b0560a98e08Ian Campbell
1020e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	return 0;
1030e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge}
1040e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
1050e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardingestatic void do_suspend(void)
1060e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge{
1070e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	int err;
108ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell	struct suspend_info si;
1090e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
1100e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	shutting_down = SHUTDOWN_SUSPEND;
1110e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
1120e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#ifdef CONFIG_PREEMPT
1130e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	/* If the kernel is preemptible, we need to freeze all the processes
1140e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	   to prevent them from being in the middle of a pagetable update
1150e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	   during suspend. */
1160e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	err = freeze_processes();
1170e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	if (err) {
1180e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge		printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
1193fc1f1e27a5b807791d72e5d992aa33b668a6626Tejun Heo		goto out;
1200e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	}
1210e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#endif
1220e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
123b3e96c0c756211e805c6941d4a6e5f6e1995cb6bShriram Rajagopalan	err = dpm_suspend_start(PMSG_FREEZE);
1240e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	if (err) {
125d161630297a20802d01c55847bfcba85d2118a9fAlan Stern		printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
12665f63384b391bf4d384327d8a7c6de9860290b5cIan Campbell		goto out_thaw;
1270e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	}
1280e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
129c5cae661d6cf808b6984762f763261adf35f3eb7Ian Campbell	printk(KERN_DEBUG "suspending xenstore...\n");
130c5cae661d6cf808b6984762f763261adf35f3eb7Ian Campbell	xs_suspend();
131c5cae661d6cf808b6984762f763261adf35f3eb7Ian Campbell
132cf579dfb82550e34de7ccf3ef090d8b834ccd3a9Rafael J. Wysocki	err = dpm_suspend_end(PMSG_FREEZE);
1332ed8d2b3a81bdbb0418301628ccdb008ac9f40b7Rafael J. Wysocki	if (err) {
134cf579dfb82550e34de7ccf3ef090d8b834ccd3a9Rafael J. Wysocki		printk(KERN_ERR "dpm_suspend_end failed: %d\n", err);
135186bab1ce04f99153b7eeb3348438b654c24c24bKonrad Rzeszutek Wilk		si.cancelled = 0;
13665f63384b391bf4d384327d8a7c6de9860290b5cIan Campbell		goto out_resume;
1372ed8d2b3a81bdbb0418301628ccdb008ac9f40b7Rafael J. Wysocki	}
1382ed8d2b3a81bdbb0418301628ccdb008ac9f40b7Rafael J. Wysocki
139ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell	si.cancelled = 1;
140ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell
14155fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell	if (xen_hvm_domain()) {
14236b401e2c2788c7b4881115ddbbff603fe4cf78dIan Campbell		si.arg = 0UL;
14355fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell		si.pre = NULL;
14455fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell		si.post = &xen_hvm_post_suspend;
14555fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell	} else {
14636b401e2c2788c7b4881115ddbbff603fe4cf78dIan Campbell		si.arg = virt_to_mfn(xen_start_info);
14755fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell		si.pre = &xen_pre_suspend;
14855fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell		si.post = &xen_post_suspend;
14955fb4acef7089a6d4d93ed8caae6c258d06cfaf7Ian Campbell	}
15036b401e2c2788c7b4881115ddbbff603fe4cf78dIan Campbell
151b056b6a0144de90707cd22cf7b4f60bf69c86d59Ian Campbell	err = stop_machine(xen_suspend, &si, cpumask_of(0));
152922cc38ab71d1360978e65207e4a4f4988987127Jeremy Fitzhardinge
153cf579dfb82550e34de7ccf3ef090d8b834ccd3a9Rafael J. Wysocki	dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
154922cc38ab71d1360978e65207e4a4f4988987127Jeremy Fitzhardinge
1550e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	if (err) {
1560e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge		printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
157ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell		si.cancelled = 1;
1580e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	}
1590e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
160c5cae661d6cf808b6984762f763261adf35f3eb7Ian Campbellout_resume:
161ceb180294790c8a6a437533488616f6b591b49d0Ian Campbell	if (!si.cancelled) {
162ad55db9fed6d6cd09333045945cb03ba2c070085Isaku Yamahata		xen_arch_resume();
163de5b31bd47de7e6f41be2e271318dbc8f1af354dIan Campbell		xs_resume();
164ad55db9fed6d6cd09333045945cb03ba2c070085Isaku Yamahata	} else
165de5b31bd47de7e6f41be2e271318dbc8f1af354dIan Campbell		xs_suspend_cancel();
1660e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
167b3e96c0c756211e805c6941d4a6e5f6e1995cb6bShriram Rajagopalan	dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE);
1680e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge
169359cdd3f866b6219a6729e313faf2221397f3278Jeremy Fitzhardinge	/* Make sure timer events get retriggered on all CPUs */
170359cdd3f866b6219a6729e313faf2221397f3278Jeremy Fitzhardinge	clock_was_set();
17165f63384b391bf4d384327d8a7c6de9860290b5cIan Campbell
17265f63384b391bf4d384327d8a7c6de9860290b5cIan Campbellout_thaw:
1730e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge#ifdef CONFIG_PREEMPT
1740e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	thaw_processes();
17565f63384b391bf4d384327d8a7c6de9860290b5cIan Campbellout:
1763fc1f1e27a5b807791d72e5d992aa33b668a6626Tejun Heo#endif
1770e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	shutting_down = SHUTDOWN_INVALID;
1780e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge}
1791f112cee07b314e244ee9e71d9c1e6950dc13327Rafael J. Wysocki#endif	/* CONFIG_HIBERNATE_CALLBACKS */
1803e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
181552717231e50b478dfd19d63fd97879476ae051dIan Campbellstruct shutdown_handler {
182552717231e50b478dfd19d63fd97879476ae051dIan Campbell	const char *command;
183552717231e50b478dfd19d63fd97879476ae051dIan Campbell	void (*cb)(void);
184552717231e50b478dfd19d63fd97879476ae051dIan Campbell};
185552717231e50b478dfd19d63fd97879476ae051dIan Campbell
186552717231e50b478dfd19d63fd97879476ae051dIan Campbellstatic void do_poweroff(void)
187552717231e50b478dfd19d63fd97879476ae051dIan Campbell{
188552717231e50b478dfd19d63fd97879476ae051dIan Campbell	shutting_down = SHUTDOWN_POWEROFF;
189552717231e50b478dfd19d63fd97879476ae051dIan Campbell	orderly_poweroff(false);
190552717231e50b478dfd19d63fd97879476ae051dIan Campbell}
191552717231e50b478dfd19d63fd97879476ae051dIan Campbell
192552717231e50b478dfd19d63fd97879476ae051dIan Campbellstatic void do_reboot(void)
193552717231e50b478dfd19d63fd97879476ae051dIan Campbell{
194552717231e50b478dfd19d63fd97879476ae051dIan Campbell	shutting_down = SHUTDOWN_POWEROFF; /* ? */
195552717231e50b478dfd19d63fd97879476ae051dIan Campbell	ctrl_alt_del();
196552717231e50b478dfd19d63fd97879476ae051dIan Campbell}
197552717231e50b478dfd19d63fd97879476ae051dIan Campbell
1983e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardingestatic void shutdown_handler(struct xenbus_watch *watch,
1993e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge			     const char **vec, unsigned int len)
2003e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge{
2013e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	char *str;
2023e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	struct xenbus_transaction xbt;
2033e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	int err;
204552717231e50b478dfd19d63fd97879476ae051dIan Campbell	static struct shutdown_handler handlers[] = {
205552717231e50b478dfd19d63fd97879476ae051dIan Campbell		{ "poweroff",	do_poweroff },
206552717231e50b478dfd19d63fd97879476ae051dIan Campbell		{ "halt",	do_poweroff },
207552717231e50b478dfd19d63fd97879476ae051dIan Campbell		{ "reboot",	do_reboot   },
2081f112cee07b314e244ee9e71d9c1e6950dc13327Rafael J. Wysocki#ifdef CONFIG_HIBERNATE_CALLBACKS
209552717231e50b478dfd19d63fd97879476ae051dIan Campbell		{ "suspend",	do_suspend  },
210552717231e50b478dfd19d63fd97879476ae051dIan Campbell#endif
211552717231e50b478dfd19d63fd97879476ae051dIan Campbell		{NULL, NULL},
212552717231e50b478dfd19d63fd97879476ae051dIan Campbell	};
213552717231e50b478dfd19d63fd97879476ae051dIan Campbell	static struct shutdown_handler *handler;
2143e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2153e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (shutting_down != SHUTDOWN_INVALID)
2163e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		return;
2173e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2183e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge again:
2193e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	err = xenbus_transaction_start(&xbt);
2203e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (err)
2213e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		return;
2223e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2233e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	str = (char *)xenbus_read(xbt, "control", "shutdown", NULL);
2243e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	/* Ignore read errors and empty reads. */
2253e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (XENBUS_IS_ERR_READ(str)) {
2263e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		xenbus_transaction_end(xbt, 1);
2273e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		return;
2283e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	}
2293e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
230552717231e50b478dfd19d63fd97879476ae051dIan Campbell	for (handler = &handlers[0]; handler->command; handler++) {
231552717231e50b478dfd19d63fd97879476ae051dIan Campbell		if (strcmp(str, handler->command) == 0)
232552717231e50b478dfd19d63fd97879476ae051dIan Campbell			break;
233552717231e50b478dfd19d63fd97879476ae051dIan Campbell	}
234552717231e50b478dfd19d63fd97879476ae051dIan Campbell
235552717231e50b478dfd19d63fd97879476ae051dIan Campbell	/* Only acknowledge commands which we are prepared to handle. */
236552717231e50b478dfd19d63fd97879476ae051dIan Campbell	if (handler->cb)
237552717231e50b478dfd19d63fd97879476ae051dIan Campbell		xenbus_write(xbt, "control", "shutdown", "");
2383e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2393e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	err = xenbus_transaction_end(xbt, 0);
2403e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (err == -EAGAIN) {
2413e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		kfree(str);
2423e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		goto again;
2433e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	}
2443e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
245552717231e50b478dfd19d63fd97879476ae051dIan Campbell	if (handler->cb) {
246552717231e50b478dfd19d63fd97879476ae051dIan Campbell		handler->cb();
2470e91398f2a5d4eb6b07df8115917d0d1cf3e9b58Jeremy Fitzhardinge	} else {
2483e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		printk(KERN_INFO "Ignoring shutdown request: %s\n", str);
2493e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		shutting_down = SHUTDOWN_INVALID;
2503e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	}
2513e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2523e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	kfree(str);
2533e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge}
2543e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
255f3bc3189a001ec85c7b1119ad4aa5e39eea0f05eRandy Dunlap#ifdef CONFIG_MAGIC_SYSRQ
2563e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardingestatic void sysrq_handler(struct xenbus_watch *watch, const char **vec,
2573e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge			  unsigned int len)
2583e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge{
2593e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	char sysrq_key = '\0';
2603e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	struct xenbus_transaction xbt;
2613e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	int err;
2623e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2633e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge again:
2643e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	err = xenbus_transaction_start(&xbt);
2653e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (err)
2663e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		return;
2673e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
2683e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		printk(KERN_ERR "Unable to read sysrq code in "
2693e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		       "control/sysrq\n");
2703e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		xenbus_transaction_end(xbt, 1);
2713e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		return;
2723e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	}
2733e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2743e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (sysrq_key != '\0')
2753e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		xenbus_printf(xbt, "control", "sysrq", "%c", '\0');
2763e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2773e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	err = xenbus_transaction_end(xbt, 0);
2783e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (err == -EAGAIN)
2793e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		goto again;
2803e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2813e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (sysrq_key != '\0')
282f335397d177c906256ee1bba28e8c49e8ec63817Dmitry Torokhov		handle_sysrq(sysrq_key);
2833e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge}
2843e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2853e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardingestatic struct xenbus_watch sysrq_watch = {
2863e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	.node = "control/sysrq",
2873e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	.callback = sysrq_handler
2883e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge};
289f3bc3189a001ec85c7b1119ad4aa5e39eea0f05eRandy Dunlap#endif
290f3bc3189a001ec85c7b1119ad4aa5e39eea0f05eRandy Dunlap
291f3bc3189a001ec85c7b1119ad4aa5e39eea0f05eRandy Dunlapstatic struct xenbus_watch shutdown_watch = {
292f3bc3189a001ec85c7b1119ad4aa5e39eea0f05eRandy Dunlap	.node = "control/shutdown",
293f3bc3189a001ec85c7b1119ad4aa5e39eea0f05eRandy Dunlap	.callback = shutdown_handler
294f3bc3189a001ec85c7b1119ad4aa5e39eea0f05eRandy Dunlap};
2953e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
2963e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardingestatic int setup_shutdown_watcher(void)
2973e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge{
2983e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	int err;
2993e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
3003e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	err = register_xenbus_watch(&shutdown_watch);
3013e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (err) {
3023e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		printk(KERN_ERR "Failed to set shutdown watcher\n");
3033e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		return err;
3043e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	}
3053e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
306f3bc3189a001ec85c7b1119ad4aa5e39eea0f05eRandy Dunlap#ifdef CONFIG_MAGIC_SYSRQ
3073e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	err = register_xenbus_watch(&sysrq_watch);
3083e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	if (err) {
3093e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		printk(KERN_ERR "Failed to set sysrq watcher\n");
3103e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		return err;
3113e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	}
312f3bc3189a001ec85c7b1119ad4aa5e39eea0f05eRandy Dunlap#endif
3133e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
3143e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	return 0;
3153e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge}
3163e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
3173e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardingestatic int shutdown_event(struct notifier_block *notifier,
3183e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge			  unsigned long event,
3193e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge			  void *data)
3203e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge{
3213e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	setup_shutdown_watcher();
3223e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	return NOTIFY_DONE;
3233e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge}
3243e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
325016b6f5fe8398b0291cece60b749d7c930a2e09cStefano Stabelliniint xen_setup_shutdown_event(void)
3263e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge{
3273e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	static struct notifier_block xenstore_notifier = {
3283e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge		.notifier_call = shutdown_event
3293e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	};
330702d4eb9b3de4398ab99cf0a4e799e552c7ab756Stefano Stabellini
331702d4eb9b3de4398ab99cf0a4e799e552c7ab756Stefano Stabellini	if (!xen_domain())
332702d4eb9b3de4398ab99cf0a4e799e552c7ab756Stefano Stabellini		return -ENODEV;
3333e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	register_xenstore_notifier(&xenstore_notifier);
3343e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
3353e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge	return 0;
3363e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge}
337183d03cc4ff39e0f0d952c09aa96d0abfd6e0c3cStefano StabelliniEXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
3383e2b8fbeec8f005672f2a2e862fb9c26a0bafedcJeremy Fitzhardinge
339702d4eb9b3de4398ab99cf0a4e799e552c7ab756Stefano Stabellinisubsys_initcall(xen_setup_shutdown_event);
340