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