#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/bio.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/disk.h>
#include <sys/fcntl.h>
#include <sys/filedesc.h>
#include <sys/kdb.h>
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/rman.h>
#include <sys/sched.h>
#include <sys/taskqueue.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/sched.h>
#include <sys/smp.h>
#include <sys/eventhandler.h>
#include <sys/timetc.h>
#include <sys/power.h>
#include <geom/geom.h>
#include <machine/_inttypes.h>
#if defined(__amd64__) || defined(__i386__)
#include <machine/intr_machdep.h>
#include <x86/apicvar.h>
#endif
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <xen/xen-os.h>
#include <xen/blkif.h>
#include <xen/evtchn.h>
#include <xen/gnttab.h>
#include <xen/xen_intr.h>
#include <xen/hvm.h>
#include <contrib/xen/event_channel.h>
#include <contrib/xen/grant_table.h>
#include <xen/xenbus/xenbusvar.h>
bool xen_suspend_cancelled;
typedef void (xctrl_shutdown_handler_t)(void);
static xctrl_shutdown_handler_t xctrl_poweroff;
static xctrl_shutdown_handler_t xctrl_reboot;
static xctrl_shutdown_handler_t xctrl_suspend;
static xctrl_shutdown_handler_t xctrl_crash;
struct xctrl_shutdown_reason {
const char *name;
xctrl_shutdown_handler_t *handler;
};
static const struct xctrl_shutdown_reason xctrl_shutdown_reasons[] = {
{ "poweroff", xctrl_poweroff },
{ "reboot", xctrl_reboot },
{ "suspend", xctrl_suspend },
{ "crash", xctrl_crash },
{ "halt", xctrl_poweroff },
};
struct xctrl_softc {
struct xs_watch xctrl_watch;
};
static void
xctrl_poweroff(void)
{
shutdown_nice(RB_POWEROFF|RB_HALT);
}
static void
xctrl_reboot(void)
{
shutdown_nice(0);
}
#if !defined(__amd64__) && !defined(__i386__)
static void
xctrl_suspend(void)
{
printf("WARNING: xen/control: Suspend not supported!\n");
}
#else
static void
xctrl_suspend(void)
{
#ifdef SMP
cpuset_t cpu_suspend_map;
#endif
EVENTHANDLER_INVOKE(power_suspend_early, POWER_STYPE_SUSPEND_TO_MEM);
xs_lock();
stop_all_proc();
xs_unlock();
suspend_all_fs();
EVENTHANDLER_INVOKE(power_suspend, POWER_STYPE_SUSPEND_TO_MEM);
#ifdef EARLY_AP_STARTUP
MPASS(mp_ncpus == 1 || smp_started);
thread_lock(curthread);
sched_bind(curthread, 0);
thread_unlock(curthread);
#else
if (smp_started) {
thread_lock(curthread);
sched_bind(curthread, 0);
thread_unlock(curthread);
}
#endif
KASSERT((PCPU_GET(cpuid) == 0), ("Not running on CPU#0"));
bus_topo_lock();
if (DEVICE_SUSPEND(root_bus) != 0) {
bus_topo_unlock();
printf("%s: device_suspend failed\n", __func__);
return;
}
#ifdef SMP
#ifdef EARLY_AP_STARTUP
cpu_suspend_map = all_cpus;
CPU_CLR(PCPU_GET(cpuid), &cpu_suspend_map);
if (!CPU_EMPTY(&cpu_suspend_map))
suspend_cpus(cpu_suspend_map);
#else
CPU_ZERO(&cpu_suspend_map);
if (smp_started) {
cpu_suspend_map = all_cpus;
CPU_CLR(PCPU_GET(cpuid), &cpu_suspend_map);
if (!CPU_EMPTY(&cpu_suspend_map))
suspend_cpus(cpu_suspend_map);
}
#endif
#endif
disable_intr();
intr_suspend();
xen_hvm_suspend();
xen_suspend_cancelled = !!HYPERVISOR_suspend(0);
if (!xen_suspend_cancelled) {
xen_hvm_resume(false);
}
intr_resume(xen_suspend_cancelled != 0);
enable_intr();
if (!xen_suspend_cancelled) {
gnttab_resume(NULL);
}
#ifdef SMP
if (!CPU_EMPTY(&cpu_suspend_map)) {
resume_cpus(cpu_suspend_map);
#if defined(__amd64__) || defined(__i386__)
lapic_ipi_vectored(IPI_BITMAP_VECTOR, APIC_IPI_DEST_ALL);
#endif
}
#endif
DEVICE_RESUME(root_bus);
bus_topo_unlock();
timecounter->tc_get_timecount(timecounter);
inittodr(time_second);
#ifdef EARLY_AP_STARTUP
thread_lock(curthread);
sched_unbind(curthread);
thread_unlock(curthread);
#else
if (smp_started) {
thread_lock(curthread);
sched_unbind(curthread);
thread_unlock(curthread);
}
#endif
resume_all_fs();
resume_all_proc();
EVENTHANDLER_INVOKE(power_resume, POWER_STYPE_SUSPEND_TO_MEM);
if (bootverbose)
printf("System resumed after suspension\n");
}
#endif
static void
xctrl_crash(void)
{
panic("Xen directed crash");
}
static void
xctrl_shutdown_final(void *arg, int howto)
{
if (KERNEL_PANICKED())
HYPERVISOR_shutdown(SHUTDOWN_crash);
else if ((howto & RB_POWEROFF) != 0)
HYPERVISOR_shutdown(SHUTDOWN_poweroff);
else if ((howto & RB_HALT) == 0)
HYPERVISOR_shutdown(SHUTDOWN_reboot);
}
static void
xctrl_on_watch_event(struct xs_watch *watch, const char **vec, unsigned int len)
{
const struct xctrl_shutdown_reason *reason;
const struct xctrl_shutdown_reason *last_reason;
char *result;
int error;
int result_len;
error = xs_read(XST_NIL, "control", "shutdown",
&result_len, (void **)&result);
if (error != 0 || result_len == 0)
return;
error = xs_write(XST_NIL, "control", "shutdown", "");
if (error != 0)
printf("unable to ack shutdown request, proceeding anyway\n");
reason = xctrl_shutdown_reasons;
last_reason = reason + nitems(xctrl_shutdown_reasons);
while (reason < last_reason) {
if (!strcmp(result, reason->name)) {
reason->handler();
break;
}
reason++;
}
free(result, M_XENSTORE);
}
static void
notify_support(void)
{
xs_write(XST_NIL, "control", "feature-poweroff", "1");
xs_write(XST_NIL, "control", "feature-reboot", "1");
xs_write(XST_NIL, "control", "feature-suspend", "1");
}
static void
xctrl_identify(driver_t *driver, device_t parent)
{
BUS_ADD_CHILD(parent, 0, driver->name, 0);
}
static int
xctrl_probe(device_t dev)
{
device_set_desc(dev, "Xen Control Device");
return (BUS_PROBE_NOWILDCARD);
}
static int
xctrl_attach(device_t dev)
{
struct xctrl_softc *xctrl;
xctrl = device_get_softc(dev);
xctrl->xctrl_watch.node = "control/shutdown";
xctrl->xctrl_watch.callback = xctrl_on_watch_event;
xctrl->xctrl_watch.callback_data = (uintptr_t)xctrl;
xctrl->xctrl_watch.max_pending = 1;
xs_register_watch(&xctrl->xctrl_watch);
EVENTHANDLER_REGISTER(shutdown_final, xctrl_shutdown_final, NULL,
SHUTDOWN_PRI_LAST);
notify_support();
return (0);
}
static int
xctrl_detach(device_t dev)
{
struct xctrl_softc *xctrl;
xctrl = device_get_softc(dev);
xs_unregister_watch(&xctrl->xctrl_watch);
return (0);
}
static int
xctrl_resume(device_t dev)
{
notify_support();
return (0);
}
static device_method_t xctrl_methods[] = {
DEVMETHOD(device_identify, xctrl_identify),
DEVMETHOD(device_probe, xctrl_probe),
DEVMETHOD(device_attach, xctrl_attach),
DEVMETHOD(device_detach, xctrl_detach),
DEVMETHOD(device_resume, xctrl_resume),
DEVMETHOD_END
};
DEFINE_CLASS_0(xctrl, xctrl_driver, xctrl_methods, sizeof(struct xctrl_softc));
DRIVER_MODULE(xctrl, xenstore, xctrl_driver, NULL, NULL);