1250199Sgrehan/*- 2272322Sdelphij * Copyright (c) 2014 Microsoft Corp. 3250199Sgrehan * All rights reserved. 4250199Sgrehan * 5250199Sgrehan * Redistribution and use in source and binary forms, with or without 6250199Sgrehan * modification, are permitted provided that the following conditions 7250199Sgrehan * are met: 8250199Sgrehan * 1. Redistributions of source code must retain the above copyright 9250199Sgrehan * notice unmodified, this list of conditions, and the following 10250199Sgrehan * disclaimer. 11250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 12250199Sgrehan * notice, this list of conditions and the following disclaimer in the 13250199Sgrehan * documentation and/or other materials provided with the distribution. 14250199Sgrehan * 15250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25272322Sdelphij * 26272322Sdelphij * $FreeBSD$ 27250199Sgrehan */ 28250199Sgrehan 29272322Sdelphij/* 30250199Sgrehan * A common driver for all hyper-V util services. 31250199Sgrehan */ 32250199Sgrehan 33250199Sgrehan#include <sys/param.h> 34250199Sgrehan#include <sys/kernel.h> 35250199Sgrehan#include <sys/bus.h> 36250199Sgrehan#include <sys/malloc.h> 37250199Sgrehan#include <sys/module.h> 38250199Sgrehan#include <sys/reboot.h> 39250199Sgrehan#include <sys/timetc.h> 40255414Sgrehan#include <sys/syscallsubr.h> 41250199Sgrehan 42250199Sgrehan#include <dev/hyperv/include/hyperv.h> 43255414Sgrehan#include "hv_kvp.h" 44250199Sgrehan 45255414Sgrehan/* Time Sync data */ 46255414Sgrehantypedef struct { 47255414Sgrehan uint64_t data; 48255414Sgrehan} time_sync_data; 49250199Sgrehan 50250199Sgrehanstatic void hv_shutdown_cb(void *context); 51250199Sgrehanstatic void hv_heartbeat_cb(void *context); 52250199Sgrehanstatic void hv_timesync_cb(void *context); 53250199Sgrehan 54250199Sgrehanstatic int hv_timesync_init(hv_vmbus_service *serv); 55250199Sgrehan 56272322Sdelphij/* 57250199Sgrehan * Note: GUID codes below are predefined by the host hypervisor 58250199Sgrehan * (Hyper-V and Azure)interface and required for correct operation. 59250199Sgrehan */ 60255414Sgrehanhv_vmbus_service service_table[] = { 61250199Sgrehan /* Shutdown Service */ 62250199Sgrehan { .guid.data = {0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, 63250199Sgrehan 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB}, 64250199Sgrehan .name = "Hyper-V Shutdown Service\n", 65250199Sgrehan .enabled = TRUE, 66250199Sgrehan .callback = hv_shutdown_cb, 67250199Sgrehan }, 68250199Sgrehan 69250199Sgrehan /* Time Synch Service */ 70250199Sgrehan { .guid.data = {0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, 71250199Sgrehan 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf}, 72250199Sgrehan .name = "Hyper-V Time Synch Service\n", 73250199Sgrehan .enabled = TRUE, 74250199Sgrehan .init = hv_timesync_init, 75250199Sgrehan .callback = hv_timesync_cb, 76250199Sgrehan }, 77250199Sgrehan 78250199Sgrehan /* Heartbeat Service */ 79250199Sgrehan { .guid.data = {0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, 80250199Sgrehan 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d}, 81250199Sgrehan .name = "Hyper-V Heartbeat Service\n", 82250199Sgrehan .enabled = TRUE, 83272322Sdelphij .callback = hv_heartbeat_cb, 84250199Sgrehan }, 85272322Sdelphij 86272322Sdelphij /* KVP (Key Value Pair) Service */ 87272322Sdelphij { .guid.data = {0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, 88272322Sdelphij 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6}, 89272322Sdelphij .name = "Hyper-V KVP Service\n", 90272322Sdelphij .enabled = TRUE, 91272322Sdelphij .init = hv_kvp_init, 92272322Sdelphij .callback = hv_kvp_callback, 93272322Sdelphij }, 94250199Sgrehan}; 95250199Sgrehan 96255414Sgrehan/* 97255414Sgrehan * Receive buffer pointers. There is one buffer per utility service. The 98250199Sgrehan * buffer is allocated during attach(). 99250199Sgrehan */ 100255414Sgrehanuint8_t *receive_buffer[HV_MAX_UTIL_SERVICES]; 101250199Sgrehan 102272322Sdelphijstatic boolean_t destroyed_kvp = FALSE; 103272322Sdelphij 104250199Sgrehanstruct hv_ictimesync_data { 105250199Sgrehan uint64_t parenttime; 106250199Sgrehan uint64_t childtime; 107250199Sgrehan uint64_t roundtriptime; 108255414Sgrehan uint8_t flags; 109250199Sgrehan} __packed; 110250199Sgrehan 111255414Sgrehanstatic int 112255414Sgrehanhv_timesync_init(hv_vmbus_service *serv) 113250199Sgrehan{ 114255414Sgrehan 115250199Sgrehan serv->work_queue = hv_work_queue_create("Time Sync"); 116250199Sgrehan if (serv->work_queue == NULL) 117255414Sgrehan return (ENOMEM); 118250199Sgrehan return (0); 119250199Sgrehan} 120250199Sgrehan 121250199Sgrehanstatic void 122250199Sgrehanhv_negotiate_version( 123250199Sgrehan struct hv_vmbus_icmsg_hdr* icmsghdrp, 124250199Sgrehan struct hv_vmbus_icmsg_negotiate* negop, 125250199Sgrehan uint8_t* buf) 126255414Sgrehan{ 127250199Sgrehan icmsghdrp->icmsgsize = 0x10; 128250199Sgrehan 129250199Sgrehan negop = (struct hv_vmbus_icmsg_negotiate *)&buf[ 130250199Sgrehan sizeof(struct hv_vmbus_pipe_hdr) + 131250199Sgrehan sizeof(struct hv_vmbus_icmsg_hdr)]; 132250199Sgrehan 133255414Sgrehan if (negop->icframe_vercnt >= 2 && 134250199Sgrehan negop->icversion_data[1].major == 3) { 135250199Sgrehan negop->icversion_data[0].major = 3; 136250199Sgrehan negop->icversion_data[0].minor = 0; 137250199Sgrehan negop->icversion_data[1].major = 3; 138250199Sgrehan negop->icversion_data[1].minor = 0; 139250199Sgrehan } else { 140250199Sgrehan negop->icversion_data[0].major = 1; 141250199Sgrehan negop->icversion_data[0].minor = 0; 142250199Sgrehan negop->icversion_data[1].major = 1; 143250199Sgrehan negop->icversion_data[1].minor = 0; 144250199Sgrehan } 145250199Sgrehan 146250199Sgrehan negop->icframe_vercnt = 1; 147250199Sgrehan negop->icmsg_vercnt = 1; 148250199Sgrehan} 149250199Sgrehan 150250199Sgrehan 151250199Sgrehan/** 152250199Sgrehan * Set host time based on time sync message from host 153250199Sgrehan */ 154250199Sgrehanstatic void 155250199Sgrehanhv_set_host_time(void *context) 156250199Sgrehan{ 157272322Sdelphij time_sync_data* time_msg = (time_sync_data*) context; 158255414Sgrehan uint64_t hosttime = time_msg->data; 159255414Sgrehan struct timespec guest_ts, host_ts; 160255414Sgrehan uint64_t host_tns; 161255414Sgrehan int64_t diff; 162255414Sgrehan int error; 163250199Sgrehan 164250199Sgrehan host_tns = (hosttime - HV_WLTIMEDELTA) * 100; 165255414Sgrehan host_ts.tv_sec = (time_t)(host_tns/HV_NANO_SEC_PER_SEC); 166255414Sgrehan host_ts.tv_nsec = (long)(host_tns%HV_NANO_SEC_PER_SEC); 167250199Sgrehan 168255414Sgrehan nanotime(&guest_ts); 169255414Sgrehan 170255414Sgrehan diff = (int64_t)host_ts.tv_sec - (int64_t)guest_ts.tv_sec; 171250199Sgrehan 172255414Sgrehan /* 173255414Sgrehan * If host differs by 5 seconds then make the guest catch up 174255414Sgrehan */ 175255414Sgrehan if (diff > 5 || diff < -5) { 176255414Sgrehan error = kern_clock_settime(curthread, CLOCK_REALTIME, 177255414Sgrehan &host_ts); 178255414Sgrehan } 179255414Sgrehan 180255414Sgrehan /* 181255414Sgrehan * Free the hosttime that was allocated in hv_adj_guesttime() 182255414Sgrehan */ 183255414Sgrehan free(time_msg, M_DEVBUF); 184250199Sgrehan} 185250199Sgrehan 186250199Sgrehan/** 187250199Sgrehan * @brief Synchronize time with host after reboot, restore, etc. 188250199Sgrehan * 189250199Sgrehan * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. 190250199Sgrehan * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time 191250199Sgrehan * message after the timesync channel is opened. Since the hv_utils module is 192250199Sgrehan * loaded after hv_vmbus, the first message is usually missed. The other 193250199Sgrehan * thing is, systime is automatically set to emulated hardware clock which may 194250199Sgrehan * not be UTC time or in the same time zone. So, to override these effects, we 195250199Sgrehan * use the first 50 time samples for initial system time setting. 196250199Sgrehan */ 197250199Sgrehanstatic inline 198250199Sgrehanvoid hv_adj_guesttime(uint64_t hosttime, uint8_t flags) 199250199Sgrehan{ 200255414Sgrehan time_sync_data* time_msg; 201250199Sgrehan 202255414Sgrehan time_msg = malloc(sizeof(time_sync_data), M_DEVBUF, M_NOWAIT); 203255414Sgrehan 204255414Sgrehan if (time_msg == NULL) 205255414Sgrehan return; 206255414Sgrehan 207255414Sgrehan time_msg->data = hosttime; 208255414Sgrehan 209250199Sgrehan if ((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) { 210255414Sgrehan hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, 211255414Sgrehan hv_set_host_time, time_msg); 212255414Sgrehan } else if ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0) { 213255414Sgrehan hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, 214255414Sgrehan hv_set_host_time, time_msg); 215255414Sgrehan } else { 216255414Sgrehan free(time_msg, M_DEVBUF); 217250199Sgrehan } 218250199Sgrehan} 219250199Sgrehan 220250199Sgrehan/** 221250199Sgrehan * Time Sync Channel message handler 222250199Sgrehan */ 223250199Sgrehanstatic void 224250199Sgrehanhv_timesync_cb(void *context) 225250199Sgrehan{ 226250199Sgrehan hv_vmbus_channel* channel = context; 227250199Sgrehan hv_vmbus_icmsg_hdr* icmsghdrp; 228250199Sgrehan uint32_t recvlen; 229250199Sgrehan uint64_t requestId; 230250199Sgrehan int ret; 231250199Sgrehan uint8_t* time_buf; 232250199Sgrehan struct hv_ictimesync_data* timedatap; 233250199Sgrehan 234250199Sgrehan time_buf = receive_buffer[HV_TIME_SYNCH]; 235250199Sgrehan 236250199Sgrehan ret = hv_vmbus_channel_recv_packet(channel, time_buf, 237250199Sgrehan PAGE_SIZE, &recvlen, &requestId); 238250199Sgrehan 239250199Sgrehan if ((ret == 0) && recvlen > 0) { 240250199Sgrehan icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &time_buf[ 241250199Sgrehan sizeof(struct hv_vmbus_pipe_hdr)]; 242250199Sgrehan 243250199Sgrehan if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 244250199Sgrehan hv_negotiate_version(icmsghdrp, NULL, time_buf); 245250199Sgrehan } else { 246250199Sgrehan timedatap = (struct hv_ictimesync_data *) &time_buf[ 247250199Sgrehan sizeof(struct hv_vmbus_pipe_hdr) + 248250199Sgrehan sizeof(struct hv_vmbus_icmsg_hdr)]; 249250199Sgrehan hv_adj_guesttime(timedatap->parenttime, timedatap->flags); 250250199Sgrehan } 251250199Sgrehan 252250199Sgrehan icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION 253250199Sgrehan | HV_ICMSGHDRFLAG_RESPONSE; 254250199Sgrehan 255250199Sgrehan hv_vmbus_channel_send_packet(channel, time_buf, 256250199Sgrehan recvlen, requestId, 257250199Sgrehan HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 258250199Sgrehan } 259250199Sgrehan} 260250199Sgrehan 261250199Sgrehan/** 262250199Sgrehan * Shutdown 263250199Sgrehan */ 264250199Sgrehanstatic void 265250199Sgrehanhv_shutdown_cb(void *context) 266250199Sgrehan{ 267272322Sdelphij uint8_t* buf; 268250199Sgrehan hv_vmbus_channel* channel = context; 269272322Sdelphij uint8_t execute_shutdown = 0; 270250199Sgrehan hv_vmbus_icmsg_hdr* icmsghdrp; 271272322Sdelphij uint32_t recv_len; 272272322Sdelphij uint64_t request_id; 273250199Sgrehan int ret; 274250199Sgrehan hv_vmbus_shutdown_msg_data* shutdown_msg; 275250199Sgrehan 276250199Sgrehan buf = receive_buffer[HV_SHUT_DOWN]; 277250199Sgrehan 278250199Sgrehan ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, 279250199Sgrehan &recv_len, &request_id); 280250199Sgrehan 281250199Sgrehan if ((ret == 0) && recv_len > 0) { 282250199Sgrehan 283250199Sgrehan icmsghdrp = (struct hv_vmbus_icmsg_hdr *) 284250199Sgrehan &buf[sizeof(struct hv_vmbus_pipe_hdr)]; 285250199Sgrehan 286250199Sgrehan if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 287250199Sgrehan hv_negotiate_version(icmsghdrp, NULL, buf); 288250199Sgrehan 289250199Sgrehan } else { 290250199Sgrehan shutdown_msg = 291250199Sgrehan (struct hv_vmbus_shutdown_msg_data *) 292250199Sgrehan &buf[sizeof(struct hv_vmbus_pipe_hdr) + 293250199Sgrehan sizeof(struct hv_vmbus_icmsg_hdr)]; 294250199Sgrehan 295250199Sgrehan switch (shutdown_msg->flags) { 296250199Sgrehan case 0: 297250199Sgrehan case 1: 298250199Sgrehan icmsghdrp->status = HV_S_OK; 299250199Sgrehan execute_shutdown = 1; 300250199Sgrehan if(bootverbose) 301250199Sgrehan printf("Shutdown request received -" 302250199Sgrehan " graceful shutdown initiated\n"); 303250199Sgrehan break; 304250199Sgrehan default: 305250199Sgrehan icmsghdrp->status = HV_E_FAIL; 306250199Sgrehan execute_shutdown = 0; 307250199Sgrehan printf("Shutdown request received -" 308250199Sgrehan " Invalid request\n"); 309250199Sgrehan break; 310250199Sgrehan } 311250199Sgrehan } 312250199Sgrehan 313250199Sgrehan icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | 314250199Sgrehan HV_ICMSGHDRFLAG_RESPONSE; 315250199Sgrehan 316250199Sgrehan hv_vmbus_channel_send_packet(channel, buf, 317250199Sgrehan recv_len, request_id, 318250199Sgrehan HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 319250199Sgrehan } 320250199Sgrehan 321250199Sgrehan if (execute_shutdown) 322250199Sgrehan shutdown_nice(RB_POWEROFF); 323250199Sgrehan} 324250199Sgrehan 325250199Sgrehan/** 326250199Sgrehan * Process heartbeat message 327250199Sgrehan */ 328250199Sgrehanstatic void 329250199Sgrehanhv_heartbeat_cb(void *context) 330250199Sgrehan{ 331250199Sgrehan uint8_t* buf; 332250199Sgrehan hv_vmbus_channel* channel = context; 333250199Sgrehan uint32_t recvlen; 334250199Sgrehan uint64_t requestid; 335250199Sgrehan int ret; 336250199Sgrehan 337250199Sgrehan struct hv_vmbus_heartbeat_msg_data* heartbeat_msg; 338250199Sgrehan struct hv_vmbus_icmsg_hdr* icmsghdrp; 339250199Sgrehan 340250199Sgrehan buf = receive_buffer[HV_HEART_BEAT]; 341250199Sgrehan 342250199Sgrehan ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, &recvlen, 343250199Sgrehan &requestid); 344250199Sgrehan 345250199Sgrehan if ((ret == 0) && recvlen > 0) { 346250199Sgrehan 347250199Sgrehan icmsghdrp = (struct hv_vmbus_icmsg_hdr *) 348250199Sgrehan &buf[sizeof(struct hv_vmbus_pipe_hdr)]; 349250199Sgrehan 350250199Sgrehan if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 351250199Sgrehan hv_negotiate_version(icmsghdrp, NULL, buf); 352250199Sgrehan 353250199Sgrehan } else { 354250199Sgrehan heartbeat_msg = 355250199Sgrehan (struct hv_vmbus_heartbeat_msg_data *) 356250199Sgrehan &buf[sizeof(struct hv_vmbus_pipe_hdr) + 357250199Sgrehan sizeof(struct hv_vmbus_icmsg_hdr)]; 358250199Sgrehan 359250199Sgrehan heartbeat_msg->seq_num += 1; 360250199Sgrehan } 361250199Sgrehan 362250199Sgrehan icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | 363250199Sgrehan HV_ICMSGHDRFLAG_RESPONSE; 364250199Sgrehan 365250199Sgrehan hv_vmbus_channel_send_packet(channel, buf, recvlen, requestid, 366250199Sgrehan HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 367250199Sgrehan } 368250199Sgrehan} 369250199Sgrehan 370250199Sgrehan 371250199Sgrehanstatic int 372250199Sgrehanhv_util_probe(device_t dev) 373250199Sgrehan{ 374250199Sgrehan int i; 375250199Sgrehan int rtn_value = ENXIO; 376250199Sgrehan 377250199Sgrehan for (i = 0; i < HV_MAX_UTIL_SERVICES; i++) { 378250199Sgrehan const char *p = vmbus_get_type(dev); 379250199Sgrehan if (service_table[i].enabled && !memcmp(p, &service_table[i].guid, sizeof(hv_guid))) { 380250199Sgrehan device_set_softc(dev, (void *) (&service_table[i])); 381273580Sdelphij rtn_value = BUS_PROBE_DEFAULT; 382250199Sgrehan } 383250199Sgrehan } 384250199Sgrehan 385250199Sgrehan return rtn_value; 386250199Sgrehan} 387250199Sgrehan 388250199Sgrehanstatic int 389250199Sgrehanhv_util_attach(device_t dev) 390250199Sgrehan{ 391250199Sgrehan struct hv_device* hv_dev; 392250199Sgrehan struct hv_vmbus_service* service; 393250199Sgrehan int ret; 394250199Sgrehan size_t receive_buffer_offset; 395250199Sgrehan 396250199Sgrehan hv_dev = vmbus_get_devctx(dev); 397250199Sgrehan service = device_get_softc(dev); 398250199Sgrehan receive_buffer_offset = service - &service_table[0]; 399250199Sgrehan device_printf(dev, "Hyper-V Service attaching: %s\n", service->name); 400250199Sgrehan receive_buffer[receive_buffer_offset] = 401255414Sgrehan malloc(4 * PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 402250199Sgrehan 403250199Sgrehan if (service->init != NULL) { 404250199Sgrehan ret = service->init(service); 405250199Sgrehan if (ret) { 406250199Sgrehan ret = ENODEV; 407250199Sgrehan goto error0; 408250199Sgrehan } 409250199Sgrehan } 410250199Sgrehan 411283280Swhu /* 412283280Swhu * These services are not performance critical and do not need 413283280Swhu * batched reading. Furthermore, some services such as KVP can 414283280Swhu * only handle one message from the host at a time. 415283280Swhu * Turn off batched reading for all util drivers before we open the 416283280Swhu * channel. 417283280Swhu */ 418283280Swhu hv_set_channel_read_state(hv_dev->channel, FALSE); 419283280Swhu 420255414Sgrehan ret = hv_vmbus_channel_open(hv_dev->channel, 4 * PAGE_SIZE, 421255414Sgrehan 4 * PAGE_SIZE, NULL, 0, 422250199Sgrehan service->callback, hv_dev->channel); 423250199Sgrehan 424250199Sgrehan if (ret) 425250199Sgrehan goto error0; 426250199Sgrehan 427250199Sgrehan return (0); 428250199Sgrehan 429250199Sgrehan error0: 430250199Sgrehan 431250199Sgrehan free(receive_buffer[receive_buffer_offset], M_DEVBUF); 432250199Sgrehan receive_buffer[receive_buffer_offset] = NULL; 433250199Sgrehan 434250199Sgrehan return (ret); 435250199Sgrehan} 436250199Sgrehan 437250199Sgrehanstatic int 438250199Sgrehanhv_util_detach(device_t dev) 439250199Sgrehan{ 440250199Sgrehan struct hv_device* hv_dev; 441250199Sgrehan struct hv_vmbus_service* service; 442250199Sgrehan size_t receive_buffer_offset; 443250199Sgrehan 444272322Sdelphij if (!destroyed_kvp) { 445272322Sdelphij hv_kvp_deinit(); 446272322Sdelphij destroyed_kvp = TRUE; 447272322Sdelphij } 448272322Sdelphij 449250199Sgrehan hv_dev = vmbus_get_devctx(dev); 450250199Sgrehan 451250199Sgrehan hv_vmbus_channel_close(hv_dev->channel); 452250199Sgrehan service = device_get_softc(dev); 453250199Sgrehan receive_buffer_offset = service - &service_table[0]; 454250199Sgrehan 455250199Sgrehan if (service->work_queue != NULL) 456250199Sgrehan hv_work_queue_close(service->work_queue); 457250199Sgrehan 458250199Sgrehan free(receive_buffer[receive_buffer_offset], M_DEVBUF); 459250199Sgrehan receive_buffer[receive_buffer_offset] = NULL; 460250199Sgrehan return (0); 461250199Sgrehan} 462250199Sgrehan 463272322Sdelphijstatic void 464272322Sdelphijhv_util_init(void) 465250199Sgrehan{ 466250199Sgrehan} 467250199Sgrehan 468272322Sdelphijstatic int 469272322Sdelphijhv_util_modevent(module_t mod, int event, void *arg) 470250199Sgrehan{ 471250199Sgrehan switch (event) { 472250199Sgrehan case MOD_LOAD: 473250199Sgrehan break; 474250199Sgrehan case MOD_UNLOAD: 475272322Sdelphij break; 476250199Sgrehan default: 477250199Sgrehan break; 478250199Sgrehan } 479250199Sgrehan return (0); 480250199Sgrehan} 481250199Sgrehan 482250199Sgrehanstatic device_method_t util_methods[] = { 483250199Sgrehan /* Device interface */ 484250199Sgrehan DEVMETHOD(device_probe, hv_util_probe), 485250199Sgrehan DEVMETHOD(device_attach, hv_util_attach), 486250199Sgrehan DEVMETHOD(device_detach, hv_util_detach), 487250199Sgrehan DEVMETHOD(device_shutdown, bus_generic_shutdown), 488250199Sgrehan { 0, 0 } } 489250199Sgrehan; 490250199Sgrehan 491250199Sgrehanstatic driver_t util_driver = { "hyperv-utils", util_methods, 0 }; 492250199Sgrehan 493250199Sgrehanstatic devclass_t util_devclass; 494250199Sgrehan 495250199SgrehanDRIVER_MODULE(hv_utils, vmbus, util_driver, util_devclass, hv_util_modevent, 0); 496250199SgrehanMODULE_VERSION(hv_utils, 1); 497250199SgrehanMODULE_DEPEND(hv_utils, vmbus, 1, 1, 1); 498250199Sgrehan 499253869SgrehanSYSINIT(hv_util_initx, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE + 1, 500250199Sgrehan hv_util_init, NULL); 501