xref: /netbsd-src/sys/dev/hyperv/hvshutdown.c (revision 38df7c851b0829656bb3c12557b3151c28062a3a)
1*38df7c85Snonaka /*	$NetBSD: hvshutdown.c,v 1.2 2019/03/01 08:17:51 nonaka Exp $	*/
250517e57Snonaka 
350517e57Snonaka /*-
450517e57Snonaka  * Copyright (c) 2014,2016 Microsoft Corp.
550517e57Snonaka  * All rights reserved.
650517e57Snonaka  *
750517e57Snonaka  * Redistribution and use in source and binary forms, with or without
850517e57Snonaka  * modification, are permitted provided that the following conditions
950517e57Snonaka  * are met:
1050517e57Snonaka  * 1. Redistributions of source code must retain the above copyright
1150517e57Snonaka  *    notice unmodified, this list of conditions, and the following
1250517e57Snonaka  *    disclaimer.
1350517e57Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
1450517e57Snonaka  *    notice, this list of conditions and the following disclaimer in the
1550517e57Snonaka  *    documentation and/or other materials provided with the distribution.
1650517e57Snonaka  *
1750517e57Snonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1850517e57Snonaka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1950517e57Snonaka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2050517e57Snonaka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2150517e57Snonaka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2250517e57Snonaka  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2350517e57Snonaka  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2450517e57Snonaka  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2550517e57Snonaka  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2650517e57Snonaka  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2750517e57Snonaka  */
2850517e57Snonaka 
2950517e57Snonaka #include <sys/cdefs.h>
3050517e57Snonaka #ifdef __KERNEL_RCSID
31*38df7c85Snonaka __KERNEL_RCSID(0, "$NetBSD: hvshutdown.c,v 1.2 2019/03/01 08:17:51 nonaka Exp $");
3250517e57Snonaka #endif
3350517e57Snonaka #ifdef __FBSDID
3450517e57Snonaka __FBSDID("$FreeBSD: head/sys/dev/hyperv/utilities/vmbus_shutdown.c 310324 2016-12-20 09:46:14Z sephe $");
3550517e57Snonaka #endif
3650517e57Snonaka 
3750517e57Snonaka #include <sys/param.h>
3850517e57Snonaka #include <sys/systm.h>
3950517e57Snonaka #include <sys/device.h>
4050517e57Snonaka #include <sys/module.h>
4150517e57Snonaka #include <sys/pmf.h>
4250517e57Snonaka 
4350517e57Snonaka #include <dev/sysmon/sysmonvar.h>
4450517e57Snonaka #include <dev/sysmon/sysmon_taskq.h>
4550517e57Snonaka 
4650517e57Snonaka #include <dev/hyperv/vmbusvar.h>
4750517e57Snonaka #include <dev/hyperv/vmbusicreg.h>
4850517e57Snonaka #include <dev/hyperv/vmbusicvar.h>
4950517e57Snonaka 
5050517e57Snonaka #define VMBUS_SHUTDOWN_FWVER_MAJOR	3
5150517e57Snonaka #define VMBUS_SHUTDOWN_FWVER		\
5250517e57Snonaka 	    VMBUS_IC_VERSION(VMBUS_SHUTDOWN_FWVER_MAJOR, 0)
5350517e57Snonaka 
5450517e57Snonaka #define VMBUS_SHUTDOWN_MSGVER_MAJOR	3
5550517e57Snonaka #define VMBUS_SHUTDOWN_MSGVER		\
5650517e57Snonaka 	    VMBUS_IC_VERSION(VMBUS_SHUTDOWN_MSGVER_MAJOR, 0)
5750517e57Snonaka 
5850517e57Snonaka static int	hvshutdown_match(device_t, cfdata_t, void *);
5950517e57Snonaka static void	hvshutdown_attach(device_t, device_t, void *);
6050517e57Snonaka static int	hvshutdown_detach(device_t, int);
6150517e57Snonaka 
6250517e57Snonaka static void	hvshutdown_channel_cb(void *);
6350517e57Snonaka 
6450517e57Snonaka struct hvshutdown_softc {
6550517e57Snonaka 	struct vmbusic_softc	sc_vmbusic;
6650517e57Snonaka 
6750517e57Snonaka 	struct sysmon_pswitch	sc_smpsw;
6850517e57Snonaka };
6950517e57Snonaka 
7050517e57Snonaka CFATTACH_DECL_NEW(hvshutdown, sizeof(struct hvshutdown_softc),
7150517e57Snonaka     hvshutdown_match, hvshutdown_attach, hvshutdown_detach, NULL);
7250517e57Snonaka 
7350517e57Snonaka static int
hvshutdown_match(device_t parent,cfdata_t cf,void * aux)7450517e57Snonaka hvshutdown_match(device_t parent, cfdata_t cf, void *aux)
7550517e57Snonaka {
7650517e57Snonaka 	struct vmbus_attach_args *aa = aux;
7750517e57Snonaka 
7850517e57Snonaka 	return vmbusic_probe(aa, &hyperv_guid_shutdown);
7950517e57Snonaka }
8050517e57Snonaka 
8150517e57Snonaka static void
hvshutdown_attach(device_t parent,device_t self,void * aux)8250517e57Snonaka hvshutdown_attach(device_t parent, device_t self, void *aux)
8350517e57Snonaka {
8450517e57Snonaka 	struct hvshutdown_softc *sc = device_private(self);
8550517e57Snonaka 	struct vmbus_attach_args *aa = aux;
8650517e57Snonaka 	int error;
8750517e57Snonaka 
8850517e57Snonaka 	aprint_naive("\n");
89*38df7c85Snonaka 	aprint_normal(": Hyper-V Guest Shutdown Service\n");
9050517e57Snonaka 
9150517e57Snonaka 	error = vmbusic_attach(self, aa, hvshutdown_channel_cb);
9250517e57Snonaka 	if (error)
9350517e57Snonaka 		return;
9450517e57Snonaka 
9550517e57Snonaka 	(void) pmf_device_register(self, NULL, NULL);
9650517e57Snonaka 
9750517e57Snonaka 	sysmon_task_queue_init();
9850517e57Snonaka 
9950517e57Snonaka 	sc->sc_smpsw.smpsw_name = device_xname(self);
10050517e57Snonaka 	sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_POWER;
10150517e57Snonaka 	(void) sysmon_pswitch_register(&sc->sc_smpsw);
10250517e57Snonaka }
10350517e57Snonaka 
10450517e57Snonaka static int
hvshutdown_detach(device_t self,int flags)10550517e57Snonaka hvshutdown_detach(device_t self, int flags)
10650517e57Snonaka {
10750517e57Snonaka 	struct hvshutdown_softc *sc = device_private(self);
10850517e57Snonaka 	int error;
10950517e57Snonaka 
11050517e57Snonaka 	error = vmbusic_detach(self, flags);
11150517e57Snonaka 	if (error)
11250517e57Snonaka 		return error;
11350517e57Snonaka 
11450517e57Snonaka 	pmf_device_deregister(self);
11550517e57Snonaka 	sysmon_pswitch_unregister(&sc->sc_smpsw);
11650517e57Snonaka 
11750517e57Snonaka 	return 0;
11850517e57Snonaka }
11950517e57Snonaka 
12050517e57Snonaka static void
hvshutdown_do_shutdown(void * arg)12150517e57Snonaka hvshutdown_do_shutdown(void *arg)
12250517e57Snonaka {
12350517e57Snonaka 	struct hvshutdown_softc *sc = arg;
12450517e57Snonaka 
12550517e57Snonaka 	sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED);
12650517e57Snonaka }
12750517e57Snonaka 
12850517e57Snonaka static void
hvshutdown_channel_cb(void * arg)12950517e57Snonaka hvshutdown_channel_cb(void *arg)
13050517e57Snonaka {
13150517e57Snonaka 	struct hvshutdown_softc *sc = arg;
13250517e57Snonaka 	struct vmbusic_softc *vsc = &sc->sc_vmbusic;
13350517e57Snonaka 	struct vmbus_channel *ch = vsc->sc_chan;
13450517e57Snonaka 	struct vmbus_icmsg_hdr *hdr;
13550517e57Snonaka 	struct vmbus_icmsg_shutdown *msg;
13650517e57Snonaka 	uint64_t rid;
13750517e57Snonaka 	uint32_t rlen;
13850517e57Snonaka 	int error;
13950517e57Snonaka 	bool do_shutdown = false;
14050517e57Snonaka 
14150517e57Snonaka 	error = vmbus_channel_recv(ch, vsc->sc_buf, vsc->sc_buflen,
14250517e57Snonaka 	    &rlen, &rid, 0);
14350517e57Snonaka 	if (error || rlen == 0) {
14450517e57Snonaka 		if (error != EAGAIN) {
14550517e57Snonaka 			DPRINTF("%s: shutdown error=%d len=%u\n",
14650517e57Snonaka 			    device_xname(vsc->sc_dev), error, rlen);
14750517e57Snonaka 		}
14850517e57Snonaka 		return;
14950517e57Snonaka 	}
15050517e57Snonaka 	if (rlen < sizeof(*hdr)) {
15150517e57Snonaka 		DPRINTF("%s: shutdown short read len=%u\n",
15250517e57Snonaka 		    device_xname(vsc->sc_dev), rlen);
15350517e57Snonaka 		return;
15450517e57Snonaka 	}
15550517e57Snonaka 
15650517e57Snonaka 	hdr = (struct vmbus_icmsg_hdr *)vsc->sc_buf;
15750517e57Snonaka 	switch (hdr->ic_type) {
15850517e57Snonaka 	case VMBUS_ICMSG_TYPE_NEGOTIATE:
15950517e57Snonaka 		error = vmbusic_negotiate(vsc, hdr, &rlen, VMBUS_SHUTDOWN_FWVER,
16050517e57Snonaka 		    VMBUS_SHUTDOWN_MSGVER);
16150517e57Snonaka 		if (error)
16250517e57Snonaka 			return;
16350517e57Snonaka 		break;
16450517e57Snonaka 
16550517e57Snonaka 	case VMBUS_ICMSG_TYPE_SHUTDOWN:
16650517e57Snonaka 		if (rlen < VMBUS_ICMSG_SHUTDOWN_SIZE_MIN) {
16750517e57Snonaka 			DPRINTF("%s: invalid shutdown len=%u\n",
16850517e57Snonaka 			    device_xname(vsc->sc_dev), rlen);
16950517e57Snonaka 			return;
17050517e57Snonaka 		}
17150517e57Snonaka 
17250517e57Snonaka 		msg = (struct vmbus_icmsg_shutdown *)hdr;
17350517e57Snonaka 		if (msg->ic_haltflags == 0 || msg->ic_haltflags == 1) {
17450517e57Snonaka 			device_printf(vsc->sc_dev, "shutdown requested\n");
17550517e57Snonaka 			hdr->ic_status = VMBUS_ICMSG_STATUS_OK;
17650517e57Snonaka 			do_shutdown = true;
17750517e57Snonaka 		} else {
17850517e57Snonaka 			device_printf(vsc->sc_dev,
17950517e57Snonaka 			    "unknown shutdown flags 0x%08x\n",
18050517e57Snonaka 			    msg->ic_haltflags);
18150517e57Snonaka 			hdr->ic_status = VMBUS_ICMSG_STATUS_FAIL;
18250517e57Snonaka 		}
18350517e57Snonaka 		break;
18450517e57Snonaka 
18550517e57Snonaka 	default:
18650517e57Snonaka 		device_printf(vsc->sc_dev,
18750517e57Snonaka 		    "unhandled shutdown message type %u\n", hdr->ic_type);
18850517e57Snonaka 		return;
18950517e57Snonaka 	}
19050517e57Snonaka 
19150517e57Snonaka 	(void) vmbusic_sendresp(vsc, ch, vsc->sc_buf, rlen, rid);
19250517e57Snonaka 
19350517e57Snonaka 	if (do_shutdown)
19450517e57Snonaka 		sysmon_task_queue_sched(0, hvshutdown_do_shutdown, sc);
19550517e57Snonaka }
19650517e57Snonaka 
19750517e57Snonaka MODULE(MODULE_CLASS_DRIVER, hvshutdown, "vmbus");
19850517e57Snonaka 
19950517e57Snonaka #ifdef _MODULE
20050517e57Snonaka #include "ioconf.c"
20150517e57Snonaka #endif
20250517e57Snonaka 
20350517e57Snonaka static int
hvshutdown_modcmd(modcmd_t cmd,void * aux)20450517e57Snonaka hvshutdown_modcmd(modcmd_t cmd, void *aux)
20550517e57Snonaka {
20650517e57Snonaka 	int error = 0;
20750517e57Snonaka 
20850517e57Snonaka 	switch (cmd) {
20950517e57Snonaka 	case MODULE_CMD_INIT:
21050517e57Snonaka #ifdef _MODULE
21150517e57Snonaka 		error = config_init_component(cfdriver_ioconf_hvshutdown,
21250517e57Snonaka 		    cfattach_ioconf_hvshutdown, cfdata_ioconf_hvshutdown);
21350517e57Snonaka #endif
21450517e57Snonaka 		break;
21550517e57Snonaka 
21650517e57Snonaka 	case MODULE_CMD_FINI:
21750517e57Snonaka #ifdef _MODULE
21850517e57Snonaka 		error = config_fini_component(cfdriver_ioconf_hvshutdown,
21950517e57Snonaka 		    cfattach_ioconf_hvshutdown, cfdata_ioconf_hvshutdown);
22050517e57Snonaka #endif
22150517e57Snonaka 		break;
22250517e57Snonaka 
22350517e57Snonaka 	case MODULE_CMD_AUTOUNLOAD:
22450517e57Snonaka 		error = EBUSY;
22550517e57Snonaka 		break;
22650517e57Snonaka 
22750517e57Snonaka 	default:
22850517e57Snonaka 		error = ENOTTY;
22950517e57Snonaka 		break;
23050517e57Snonaka 	}
23150517e57Snonaka 
23250517e57Snonaka 	return error;
23350517e57Snonaka }
234