xref: /netbsd/sys/dev/hyperv/hvshutdown.c (revision 8fd434d0)
1*8fd434d0Snonaka /*	$NetBSD: hvshutdown.c,v 1.2 2019/03/01 08:17:51 nonaka Exp $	*/
2afdb7e40Snonaka 
3afdb7e40Snonaka /*-
4afdb7e40Snonaka  * Copyright (c) 2014,2016 Microsoft Corp.
5afdb7e40Snonaka  * All rights reserved.
6afdb7e40Snonaka  *
7afdb7e40Snonaka  * Redistribution and use in source and binary forms, with or without
8afdb7e40Snonaka  * modification, are permitted provided that the following conditions
9afdb7e40Snonaka  * are met:
10afdb7e40Snonaka  * 1. Redistributions of source code must retain the above copyright
11afdb7e40Snonaka  *    notice unmodified, this list of conditions, and the following
12afdb7e40Snonaka  *    disclaimer.
13afdb7e40Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
14afdb7e40Snonaka  *    notice, this list of conditions and the following disclaimer in the
15afdb7e40Snonaka  *    documentation and/or other materials provided with the distribution.
16afdb7e40Snonaka  *
17afdb7e40Snonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18afdb7e40Snonaka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19afdb7e40Snonaka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20afdb7e40Snonaka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21afdb7e40Snonaka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22afdb7e40Snonaka  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23afdb7e40Snonaka  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24afdb7e40Snonaka  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25afdb7e40Snonaka  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26afdb7e40Snonaka  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27afdb7e40Snonaka  */
28afdb7e40Snonaka 
29afdb7e40Snonaka #include <sys/cdefs.h>
30afdb7e40Snonaka #ifdef __KERNEL_RCSID
31*8fd434d0Snonaka __KERNEL_RCSID(0, "$NetBSD: hvshutdown.c,v 1.2 2019/03/01 08:17:51 nonaka Exp $");
32afdb7e40Snonaka #endif
33afdb7e40Snonaka #ifdef __FBSDID
34afdb7e40Snonaka __FBSDID("$FreeBSD: head/sys/dev/hyperv/utilities/vmbus_shutdown.c 310324 2016-12-20 09:46:14Z sephe $");
35afdb7e40Snonaka #endif
36afdb7e40Snonaka 
37afdb7e40Snonaka #include <sys/param.h>
38afdb7e40Snonaka #include <sys/systm.h>
39afdb7e40Snonaka #include <sys/device.h>
40afdb7e40Snonaka #include <sys/module.h>
41afdb7e40Snonaka #include <sys/pmf.h>
42afdb7e40Snonaka 
43afdb7e40Snonaka #include <dev/sysmon/sysmonvar.h>
44afdb7e40Snonaka #include <dev/sysmon/sysmon_taskq.h>
45afdb7e40Snonaka 
46afdb7e40Snonaka #include <dev/hyperv/vmbusvar.h>
47afdb7e40Snonaka #include <dev/hyperv/vmbusicreg.h>
48afdb7e40Snonaka #include <dev/hyperv/vmbusicvar.h>
49afdb7e40Snonaka 
50afdb7e40Snonaka #define VMBUS_SHUTDOWN_FWVER_MAJOR	3
51afdb7e40Snonaka #define VMBUS_SHUTDOWN_FWVER		\
52afdb7e40Snonaka 	    VMBUS_IC_VERSION(VMBUS_SHUTDOWN_FWVER_MAJOR, 0)
53afdb7e40Snonaka 
54afdb7e40Snonaka #define VMBUS_SHUTDOWN_MSGVER_MAJOR	3
55afdb7e40Snonaka #define VMBUS_SHUTDOWN_MSGVER		\
56afdb7e40Snonaka 	    VMBUS_IC_VERSION(VMBUS_SHUTDOWN_MSGVER_MAJOR, 0)
57afdb7e40Snonaka 
58afdb7e40Snonaka static int	hvshutdown_match(device_t, cfdata_t, void *);
59afdb7e40Snonaka static void	hvshutdown_attach(device_t, device_t, void *);
60afdb7e40Snonaka static int	hvshutdown_detach(device_t, int);
61afdb7e40Snonaka 
62afdb7e40Snonaka static void	hvshutdown_channel_cb(void *);
63afdb7e40Snonaka 
64afdb7e40Snonaka struct hvshutdown_softc {
65afdb7e40Snonaka 	struct vmbusic_softc	sc_vmbusic;
66afdb7e40Snonaka 
67afdb7e40Snonaka 	struct sysmon_pswitch	sc_smpsw;
68afdb7e40Snonaka };
69afdb7e40Snonaka 
70afdb7e40Snonaka CFATTACH_DECL_NEW(hvshutdown, sizeof(struct hvshutdown_softc),
71afdb7e40Snonaka     hvshutdown_match, hvshutdown_attach, hvshutdown_detach, NULL);
72afdb7e40Snonaka 
73afdb7e40Snonaka static int
hvshutdown_match(device_t parent,cfdata_t cf,void * aux)74afdb7e40Snonaka hvshutdown_match(device_t parent, cfdata_t cf, void *aux)
75afdb7e40Snonaka {
76afdb7e40Snonaka 	struct vmbus_attach_args *aa = aux;
77afdb7e40Snonaka 
78afdb7e40Snonaka 	return vmbusic_probe(aa, &hyperv_guid_shutdown);
79afdb7e40Snonaka }
80afdb7e40Snonaka 
81afdb7e40Snonaka static void
hvshutdown_attach(device_t parent,device_t self,void * aux)82afdb7e40Snonaka hvshutdown_attach(device_t parent, device_t self, void *aux)
83afdb7e40Snonaka {
84afdb7e40Snonaka 	struct hvshutdown_softc *sc = device_private(self);
85afdb7e40Snonaka 	struct vmbus_attach_args *aa = aux;
86afdb7e40Snonaka 	int error;
87afdb7e40Snonaka 
88afdb7e40Snonaka 	aprint_naive("\n");
89*8fd434d0Snonaka 	aprint_normal(": Hyper-V Guest Shutdown Service\n");
90afdb7e40Snonaka 
91afdb7e40Snonaka 	error = vmbusic_attach(self, aa, hvshutdown_channel_cb);
92afdb7e40Snonaka 	if (error)
93afdb7e40Snonaka 		return;
94afdb7e40Snonaka 
95afdb7e40Snonaka 	(void) pmf_device_register(self, NULL, NULL);
96afdb7e40Snonaka 
97afdb7e40Snonaka 	sysmon_task_queue_init();
98afdb7e40Snonaka 
99afdb7e40Snonaka 	sc->sc_smpsw.smpsw_name = device_xname(self);
100afdb7e40Snonaka 	sc->sc_smpsw.smpsw_type = PSWITCH_TYPE_POWER;
101afdb7e40Snonaka 	(void) sysmon_pswitch_register(&sc->sc_smpsw);
102afdb7e40Snonaka }
103afdb7e40Snonaka 
104afdb7e40Snonaka static int
hvshutdown_detach(device_t self,int flags)105afdb7e40Snonaka hvshutdown_detach(device_t self, int flags)
106afdb7e40Snonaka {
107afdb7e40Snonaka 	struct hvshutdown_softc *sc = device_private(self);
108afdb7e40Snonaka 	int error;
109afdb7e40Snonaka 
110afdb7e40Snonaka 	error = vmbusic_detach(self, flags);
111afdb7e40Snonaka 	if (error)
112afdb7e40Snonaka 		return error;
113afdb7e40Snonaka 
114afdb7e40Snonaka 	pmf_device_deregister(self);
115afdb7e40Snonaka 	sysmon_pswitch_unregister(&sc->sc_smpsw);
116afdb7e40Snonaka 
117afdb7e40Snonaka 	return 0;
118afdb7e40Snonaka }
119afdb7e40Snonaka 
120afdb7e40Snonaka static void
hvshutdown_do_shutdown(void * arg)121afdb7e40Snonaka hvshutdown_do_shutdown(void *arg)
122afdb7e40Snonaka {
123afdb7e40Snonaka 	struct hvshutdown_softc *sc = arg;
124afdb7e40Snonaka 
125afdb7e40Snonaka 	sysmon_pswitch_event(&sc->sc_smpsw, PSWITCH_EVENT_PRESSED);
126afdb7e40Snonaka }
127afdb7e40Snonaka 
128afdb7e40Snonaka static void
hvshutdown_channel_cb(void * arg)129afdb7e40Snonaka hvshutdown_channel_cb(void *arg)
130afdb7e40Snonaka {
131afdb7e40Snonaka 	struct hvshutdown_softc *sc = arg;
132afdb7e40Snonaka 	struct vmbusic_softc *vsc = &sc->sc_vmbusic;
133afdb7e40Snonaka 	struct vmbus_channel *ch = vsc->sc_chan;
134afdb7e40Snonaka 	struct vmbus_icmsg_hdr *hdr;
135afdb7e40Snonaka 	struct vmbus_icmsg_shutdown *msg;
136afdb7e40Snonaka 	uint64_t rid;
137afdb7e40Snonaka 	uint32_t rlen;
138afdb7e40Snonaka 	int error;
139afdb7e40Snonaka 	bool do_shutdown = false;
140afdb7e40Snonaka 
141afdb7e40Snonaka 	error = vmbus_channel_recv(ch, vsc->sc_buf, vsc->sc_buflen,
142afdb7e40Snonaka 	    &rlen, &rid, 0);
143afdb7e40Snonaka 	if (error || rlen == 0) {
144afdb7e40Snonaka 		if (error != EAGAIN) {
145afdb7e40Snonaka 			DPRINTF("%s: shutdown error=%d len=%u\n",
146afdb7e40Snonaka 			    device_xname(vsc->sc_dev), error, rlen);
147afdb7e40Snonaka 		}
148afdb7e40Snonaka 		return;
149afdb7e40Snonaka 	}
150afdb7e40Snonaka 	if (rlen < sizeof(*hdr)) {
151afdb7e40Snonaka 		DPRINTF("%s: shutdown short read len=%u\n",
152afdb7e40Snonaka 		    device_xname(vsc->sc_dev), rlen);
153afdb7e40Snonaka 		return;
154afdb7e40Snonaka 	}
155afdb7e40Snonaka 
156afdb7e40Snonaka 	hdr = (struct vmbus_icmsg_hdr *)vsc->sc_buf;
157afdb7e40Snonaka 	switch (hdr->ic_type) {
158afdb7e40Snonaka 	case VMBUS_ICMSG_TYPE_NEGOTIATE:
159afdb7e40Snonaka 		error = vmbusic_negotiate(vsc, hdr, &rlen, VMBUS_SHUTDOWN_FWVER,
160afdb7e40Snonaka 		    VMBUS_SHUTDOWN_MSGVER);
161afdb7e40Snonaka 		if (error)
162afdb7e40Snonaka 			return;
163afdb7e40Snonaka 		break;
164afdb7e40Snonaka 
165afdb7e40Snonaka 	case VMBUS_ICMSG_TYPE_SHUTDOWN:
166afdb7e40Snonaka 		if (rlen < VMBUS_ICMSG_SHUTDOWN_SIZE_MIN) {
167afdb7e40Snonaka 			DPRINTF("%s: invalid shutdown len=%u\n",
168afdb7e40Snonaka 			    device_xname(vsc->sc_dev), rlen);
169afdb7e40Snonaka 			return;
170afdb7e40Snonaka 		}
171afdb7e40Snonaka 
172afdb7e40Snonaka 		msg = (struct vmbus_icmsg_shutdown *)hdr;
173afdb7e40Snonaka 		if (msg->ic_haltflags == 0 || msg->ic_haltflags == 1) {
174afdb7e40Snonaka 			device_printf(vsc->sc_dev, "shutdown requested\n");
175afdb7e40Snonaka 			hdr->ic_status = VMBUS_ICMSG_STATUS_OK;
176afdb7e40Snonaka 			do_shutdown = true;
177afdb7e40Snonaka 		} else {
178afdb7e40Snonaka 			device_printf(vsc->sc_dev,
179afdb7e40Snonaka 			    "unknown shutdown flags 0x%08x\n",
180afdb7e40Snonaka 			    msg->ic_haltflags);
181afdb7e40Snonaka 			hdr->ic_status = VMBUS_ICMSG_STATUS_FAIL;
182afdb7e40Snonaka 		}
183afdb7e40Snonaka 		break;
184afdb7e40Snonaka 
185afdb7e40Snonaka 	default:
186afdb7e40Snonaka 		device_printf(vsc->sc_dev,
187afdb7e40Snonaka 		    "unhandled shutdown message type %u\n", hdr->ic_type);
188afdb7e40Snonaka 		return;
189afdb7e40Snonaka 	}
190afdb7e40Snonaka 
191afdb7e40Snonaka 	(void) vmbusic_sendresp(vsc, ch, vsc->sc_buf, rlen, rid);
192afdb7e40Snonaka 
193afdb7e40Snonaka 	if (do_shutdown)
194afdb7e40Snonaka 		sysmon_task_queue_sched(0, hvshutdown_do_shutdown, sc);
195afdb7e40Snonaka }
196afdb7e40Snonaka 
197afdb7e40Snonaka MODULE(MODULE_CLASS_DRIVER, hvshutdown, "vmbus");
198afdb7e40Snonaka 
199afdb7e40Snonaka #ifdef _MODULE
200afdb7e40Snonaka #include "ioconf.c"
201afdb7e40Snonaka #endif
202afdb7e40Snonaka 
203afdb7e40Snonaka static int
hvshutdown_modcmd(modcmd_t cmd,void * aux)204afdb7e40Snonaka hvshutdown_modcmd(modcmd_t cmd, void *aux)
205afdb7e40Snonaka {
206afdb7e40Snonaka 	int error = 0;
207afdb7e40Snonaka 
208afdb7e40Snonaka 	switch (cmd) {
209afdb7e40Snonaka 	case MODULE_CMD_INIT:
210afdb7e40Snonaka #ifdef _MODULE
211afdb7e40Snonaka 		error = config_init_component(cfdriver_ioconf_hvshutdown,
212afdb7e40Snonaka 		    cfattach_ioconf_hvshutdown, cfdata_ioconf_hvshutdown);
213afdb7e40Snonaka #endif
214afdb7e40Snonaka 		break;
215afdb7e40Snonaka 
216afdb7e40Snonaka 	case MODULE_CMD_FINI:
217afdb7e40Snonaka #ifdef _MODULE
218afdb7e40Snonaka 		error = config_fini_component(cfdriver_ioconf_hvshutdown,
219afdb7e40Snonaka 		    cfattach_ioconf_hvshutdown, cfdata_ioconf_hvshutdown);
220afdb7e40Snonaka #endif
221afdb7e40Snonaka 		break;
222afdb7e40Snonaka 
223afdb7e40Snonaka 	case MODULE_CMD_AUTOUNLOAD:
224afdb7e40Snonaka 		error = EBUSY;
225afdb7e40Snonaka 		break;
226afdb7e40Snonaka 
227afdb7e40Snonaka 	default:
228afdb7e40Snonaka 		error = ENOTTY;
229afdb7e40Snonaka 		break;
230afdb7e40Snonaka 	}
231afdb7e40Snonaka 
232afdb7e40Snonaka 	return error;
233afdb7e40Snonaka }
234