hv_util.c revision 255414
1250199Sgrehan/*- 2250199Sgrehan * Copyright (c) 2009-2012 Microsoft Corp. 3250199Sgrehan * Copyright (c) 2012 NetApp Inc. 4250199Sgrehan * Copyright (c) 2012 Citrix Inc. 5250199Sgrehan * All rights reserved. 6250199Sgrehan * 7250199Sgrehan * Redistribution and use in source and binary forms, with or without 8250199Sgrehan * modification, are permitted provided that the following conditions 9250199Sgrehan * are met: 10250199Sgrehan * 1. Redistributions of source code must retain the above copyright 11250199Sgrehan * notice unmodified, this list of conditions, and the following 12250199Sgrehan * disclaimer. 13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 14250199Sgrehan * notice, this list of conditions and the following disclaimer in the 15250199Sgrehan * documentation and/or other materials provided with the distribution. 16250199Sgrehan * 17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27250199Sgrehan */ 28250199Sgrehan 29250199Sgrehan/** 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 56250199Sgrehan/** 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, 83250199Sgrehan .callback = hv_heartbeat_cb, 84250199Sgrehan }, 85250199Sgrehan 86250199Sgrehan /* KVP (Key Value Pair) Service */ 87250199Sgrehan { .guid.data = {0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, 88250199Sgrehan 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6}, 89250199Sgrehan .name = "Hyper-V KVP Service\n", 90255414Sgrehan .enabled = TRUE, 91255414Sgrehan .init = hv_kvp_init, 92255414Sgrehan .callback = hv_kvp_callback, 93250199Sgrehan }, 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 102250199Sgrehanstruct hv_ictimesync_data { 103250199Sgrehan uint64_t parenttime; 104250199Sgrehan uint64_t childtime; 105250199Sgrehan uint64_t roundtriptime; 106255414Sgrehan uint8_t flags; 107250199Sgrehan} __packed; 108250199Sgrehan 109255414Sgrehanstatic int 110255414Sgrehanhv_timesync_init(hv_vmbus_service *serv) 111250199Sgrehan{ 112255414Sgrehan 113250199Sgrehan serv->work_queue = hv_work_queue_create("Time Sync"); 114250199Sgrehan if (serv->work_queue == NULL) 115255414Sgrehan return (ENOMEM); 116250199Sgrehan return (0); 117250199Sgrehan} 118250199Sgrehan 119250199Sgrehanstatic void 120250199Sgrehanhv_negotiate_version( 121250199Sgrehan struct hv_vmbus_icmsg_hdr* icmsghdrp, 122250199Sgrehan struct hv_vmbus_icmsg_negotiate* negop, 123250199Sgrehan uint8_t* buf) 124255414Sgrehan{ 125250199Sgrehan icmsghdrp->icmsgsize = 0x10; 126250199Sgrehan 127250199Sgrehan negop = (struct hv_vmbus_icmsg_negotiate *)&buf[ 128250199Sgrehan sizeof(struct hv_vmbus_pipe_hdr) + 129250199Sgrehan sizeof(struct hv_vmbus_icmsg_hdr)]; 130250199Sgrehan 131255414Sgrehan if (negop->icframe_vercnt >= 2 && 132250199Sgrehan negop->icversion_data[1].major == 3) { 133250199Sgrehan negop->icversion_data[0].major = 3; 134250199Sgrehan negop->icversion_data[0].minor = 0; 135250199Sgrehan negop->icversion_data[1].major = 3; 136250199Sgrehan negop->icversion_data[1].minor = 0; 137250199Sgrehan } else { 138250199Sgrehan negop->icversion_data[0].major = 1; 139250199Sgrehan negop->icversion_data[0].minor = 0; 140250199Sgrehan negop->icversion_data[1].major = 1; 141250199Sgrehan negop->icversion_data[1].minor = 0; 142250199Sgrehan } 143250199Sgrehan 144250199Sgrehan negop->icframe_vercnt = 1; 145250199Sgrehan negop->icmsg_vercnt = 1; 146250199Sgrehan} 147250199Sgrehan 148250199Sgrehan 149250199Sgrehan/** 150250199Sgrehan * Set host time based on time sync message from host 151250199Sgrehan */ 152250199Sgrehanstatic void 153250199Sgrehanhv_set_host_time(void *context) 154250199Sgrehan{ 155255414Sgrehan time_sync_data *time_msg = context; 156255414Sgrehan uint64_t hosttime = time_msg->data; 157255414Sgrehan struct timespec guest_ts, host_ts; 158255414Sgrehan uint64_t host_tns; 159255414Sgrehan int64_t diff; 160255414Sgrehan int error; 161250199Sgrehan 162250199Sgrehan host_tns = (hosttime - HV_WLTIMEDELTA) * 100; 163255414Sgrehan host_ts.tv_sec = (time_t)(host_tns/HV_NANO_SEC_PER_SEC); 164255414Sgrehan host_ts.tv_nsec = (long)(host_tns%HV_NANO_SEC_PER_SEC); 165250199Sgrehan 166255414Sgrehan nanotime(&guest_ts); 167255414Sgrehan 168255414Sgrehan diff = (int64_t)host_ts.tv_sec - (int64_t)guest_ts.tv_sec; 169250199Sgrehan 170255414Sgrehan /* 171255414Sgrehan * If host differs by 5 seconds then make the guest catch up 172255414Sgrehan */ 173255414Sgrehan if (diff > 5 || diff < -5) { 174255414Sgrehan error = kern_clock_settime(curthread, CLOCK_REALTIME, 175255414Sgrehan &host_ts); 176255414Sgrehan } 177255414Sgrehan 178255414Sgrehan /* 179255414Sgrehan * Free the hosttime that was allocated in hv_adj_guesttime() 180255414Sgrehan */ 181255414Sgrehan free(time_msg, M_DEVBUF); 182250199Sgrehan} 183250199Sgrehan 184250199Sgrehan/** 185250199Sgrehan * @brief Synchronize time with host after reboot, restore, etc. 186250199Sgrehan * 187250199Sgrehan * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. 188250199Sgrehan * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time 189250199Sgrehan * message after the timesync channel is opened. Since the hv_utils module is 190250199Sgrehan * loaded after hv_vmbus, the first message is usually missed. The other 191250199Sgrehan * thing is, systime is automatically set to emulated hardware clock which may 192250199Sgrehan * not be UTC time or in the same time zone. So, to override these effects, we 193250199Sgrehan * use the first 50 time samples for initial system time setting. 194250199Sgrehan */ 195250199Sgrehanstatic inline 196250199Sgrehanvoid hv_adj_guesttime(uint64_t hosttime, uint8_t flags) 197250199Sgrehan{ 198255414Sgrehan time_sync_data* time_msg; 199250199Sgrehan 200255414Sgrehan time_msg = malloc(sizeof(time_sync_data), M_DEVBUF, M_NOWAIT); 201255414Sgrehan 202255414Sgrehan if (time_msg == NULL) 203255414Sgrehan return; 204255414Sgrehan 205255414Sgrehan time_msg->data = hosttime; 206255414Sgrehan 207250199Sgrehan if ((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) { 208255414Sgrehan hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, 209255414Sgrehan hv_set_host_time, time_msg); 210255414Sgrehan } else if ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0) { 211255414Sgrehan hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, 212255414Sgrehan hv_set_host_time, time_msg); 213255414Sgrehan } else { 214255414Sgrehan free(time_msg, M_DEVBUF); 215250199Sgrehan } 216250199Sgrehan} 217250199Sgrehan 218250199Sgrehan/** 219250199Sgrehan * Time Sync Channel message handler 220250199Sgrehan */ 221250199Sgrehanstatic void 222250199Sgrehanhv_timesync_cb(void *context) 223250199Sgrehan{ 224250199Sgrehan hv_vmbus_channel* channel = context; 225250199Sgrehan hv_vmbus_icmsg_hdr* icmsghdrp; 226250199Sgrehan uint32_t recvlen; 227250199Sgrehan uint64_t requestId; 228250199Sgrehan int ret; 229250199Sgrehan uint8_t* time_buf; 230250199Sgrehan struct hv_ictimesync_data* timedatap; 231250199Sgrehan 232250199Sgrehan time_buf = receive_buffer[HV_TIME_SYNCH]; 233250199Sgrehan 234250199Sgrehan ret = hv_vmbus_channel_recv_packet(channel, time_buf, 235250199Sgrehan PAGE_SIZE, &recvlen, &requestId); 236250199Sgrehan 237250199Sgrehan if ((ret == 0) && recvlen > 0) { 238250199Sgrehan icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &time_buf[ 239250199Sgrehan sizeof(struct hv_vmbus_pipe_hdr)]; 240250199Sgrehan 241250199Sgrehan if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 242250199Sgrehan hv_negotiate_version(icmsghdrp, NULL, time_buf); 243250199Sgrehan } else { 244250199Sgrehan timedatap = (struct hv_ictimesync_data *) &time_buf[ 245250199Sgrehan sizeof(struct hv_vmbus_pipe_hdr) + 246250199Sgrehan sizeof(struct hv_vmbus_icmsg_hdr)]; 247250199Sgrehan hv_adj_guesttime(timedatap->parenttime, timedatap->flags); 248250199Sgrehan } 249250199Sgrehan 250250199Sgrehan icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION 251250199Sgrehan | HV_ICMSGHDRFLAG_RESPONSE; 252250199Sgrehan 253250199Sgrehan hv_vmbus_channel_send_packet(channel, time_buf, 254250199Sgrehan recvlen, requestId, 255250199Sgrehan HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 256250199Sgrehan } 257250199Sgrehan} 258250199Sgrehan 259250199Sgrehan/** 260250199Sgrehan * Shutdown 261250199Sgrehan */ 262250199Sgrehanstatic void 263250199Sgrehanhv_shutdown_cb(void *context) 264250199Sgrehan{ 265250199Sgrehan uint8_t* buf; 266250199Sgrehan hv_vmbus_channel* channel = context; 267250199Sgrehan uint8_t execute_shutdown = 0; 268250199Sgrehan hv_vmbus_icmsg_hdr* icmsghdrp; 269250199Sgrehan uint32_t recv_len; 270250199Sgrehan uint64_t request_id; 271250199Sgrehan int ret; 272250199Sgrehan hv_vmbus_shutdown_msg_data* shutdown_msg; 273250199Sgrehan 274250199Sgrehan buf = receive_buffer[HV_SHUT_DOWN]; 275250199Sgrehan 276250199Sgrehan ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, 277250199Sgrehan &recv_len, &request_id); 278250199Sgrehan 279250199Sgrehan if ((ret == 0) && recv_len > 0) { 280250199Sgrehan 281250199Sgrehan icmsghdrp = (struct hv_vmbus_icmsg_hdr *) 282250199Sgrehan &buf[sizeof(struct hv_vmbus_pipe_hdr)]; 283250199Sgrehan 284250199Sgrehan if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 285250199Sgrehan hv_negotiate_version(icmsghdrp, NULL, buf); 286250199Sgrehan 287250199Sgrehan } else { 288250199Sgrehan shutdown_msg = 289250199Sgrehan (struct hv_vmbus_shutdown_msg_data *) 290250199Sgrehan &buf[sizeof(struct hv_vmbus_pipe_hdr) + 291250199Sgrehan sizeof(struct hv_vmbus_icmsg_hdr)]; 292250199Sgrehan 293250199Sgrehan switch (shutdown_msg->flags) { 294250199Sgrehan case 0: 295250199Sgrehan case 1: 296250199Sgrehan icmsghdrp->status = HV_S_OK; 297250199Sgrehan execute_shutdown = 1; 298250199Sgrehan if(bootverbose) 299250199Sgrehan printf("Shutdown request received -" 300250199Sgrehan " graceful shutdown initiated\n"); 301250199Sgrehan break; 302250199Sgrehan default: 303250199Sgrehan icmsghdrp->status = HV_E_FAIL; 304250199Sgrehan execute_shutdown = 0; 305250199Sgrehan printf("Shutdown request received -" 306250199Sgrehan " Invalid request\n"); 307250199Sgrehan break; 308250199Sgrehan } 309250199Sgrehan } 310250199Sgrehan 311250199Sgrehan icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | 312250199Sgrehan HV_ICMSGHDRFLAG_RESPONSE; 313250199Sgrehan 314250199Sgrehan hv_vmbus_channel_send_packet(channel, buf, 315250199Sgrehan recv_len, request_id, 316250199Sgrehan HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 317250199Sgrehan } 318250199Sgrehan 319250199Sgrehan if (execute_shutdown) 320250199Sgrehan shutdown_nice(RB_POWEROFF); 321250199Sgrehan} 322250199Sgrehan 323250199Sgrehan/** 324250199Sgrehan * Process heartbeat message 325250199Sgrehan */ 326250199Sgrehanstatic void 327250199Sgrehanhv_heartbeat_cb(void *context) 328250199Sgrehan{ 329250199Sgrehan uint8_t* buf; 330250199Sgrehan hv_vmbus_channel* channel = context; 331250199Sgrehan uint32_t recvlen; 332250199Sgrehan uint64_t requestid; 333250199Sgrehan int ret; 334250199Sgrehan 335250199Sgrehan struct hv_vmbus_heartbeat_msg_data* heartbeat_msg; 336250199Sgrehan struct hv_vmbus_icmsg_hdr* icmsghdrp; 337250199Sgrehan 338250199Sgrehan buf = receive_buffer[HV_HEART_BEAT]; 339250199Sgrehan 340250199Sgrehan ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, &recvlen, 341250199Sgrehan &requestid); 342250199Sgrehan 343250199Sgrehan if ((ret == 0) && recvlen > 0) { 344250199Sgrehan 345250199Sgrehan icmsghdrp = (struct hv_vmbus_icmsg_hdr *) 346250199Sgrehan &buf[sizeof(struct hv_vmbus_pipe_hdr)]; 347250199Sgrehan 348250199Sgrehan if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 349250199Sgrehan hv_negotiate_version(icmsghdrp, NULL, buf); 350250199Sgrehan 351250199Sgrehan } else { 352250199Sgrehan heartbeat_msg = 353250199Sgrehan (struct hv_vmbus_heartbeat_msg_data *) 354250199Sgrehan &buf[sizeof(struct hv_vmbus_pipe_hdr) + 355250199Sgrehan sizeof(struct hv_vmbus_icmsg_hdr)]; 356250199Sgrehan 357250199Sgrehan heartbeat_msg->seq_num += 1; 358250199Sgrehan } 359250199Sgrehan 360250199Sgrehan icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | 361250199Sgrehan HV_ICMSGHDRFLAG_RESPONSE; 362250199Sgrehan 363250199Sgrehan hv_vmbus_channel_send_packet(channel, buf, recvlen, requestid, 364250199Sgrehan HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 365250199Sgrehan } 366250199Sgrehan} 367250199Sgrehan 368250199Sgrehan 369250199Sgrehanstatic int 370250199Sgrehanhv_util_probe(device_t dev) 371250199Sgrehan{ 372250199Sgrehan int i; 373250199Sgrehan int rtn_value = ENXIO; 374250199Sgrehan 375250199Sgrehan for (i = 0; i < HV_MAX_UTIL_SERVICES; i++) { 376250199Sgrehan const char *p = vmbus_get_type(dev); 377250199Sgrehan if (service_table[i].enabled && !memcmp(p, &service_table[i].guid, sizeof(hv_guid))) { 378250199Sgrehan device_set_softc(dev, (void *) (&service_table[i])); 379250199Sgrehan rtn_value = 0; 380250199Sgrehan } 381250199Sgrehan } 382250199Sgrehan 383250199Sgrehan return rtn_value; 384250199Sgrehan} 385250199Sgrehan 386250199Sgrehanstatic int 387250199Sgrehanhv_util_attach(device_t dev) 388250199Sgrehan{ 389250199Sgrehan struct hv_device* hv_dev; 390250199Sgrehan struct hv_vmbus_service* service; 391250199Sgrehan int ret; 392250199Sgrehan size_t receive_buffer_offset; 393250199Sgrehan 394250199Sgrehan hv_dev = vmbus_get_devctx(dev); 395250199Sgrehan service = device_get_softc(dev); 396250199Sgrehan receive_buffer_offset = service - &service_table[0]; 397250199Sgrehan device_printf(dev, "Hyper-V Service attaching: %s\n", service->name); 398250199Sgrehan receive_buffer[receive_buffer_offset] = 399255414Sgrehan malloc(4 * PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 400250199Sgrehan 401250199Sgrehan if (service->init != NULL) { 402250199Sgrehan ret = service->init(service); 403250199Sgrehan if (ret) { 404250199Sgrehan ret = ENODEV; 405250199Sgrehan goto error0; 406250199Sgrehan } 407250199Sgrehan } 408250199Sgrehan 409255414Sgrehan ret = hv_vmbus_channel_open(hv_dev->channel, 4 * PAGE_SIZE, 410255414Sgrehan 4 * PAGE_SIZE, NULL, 0, 411250199Sgrehan service->callback, hv_dev->channel); 412250199Sgrehan 413250199Sgrehan if (ret) 414250199Sgrehan goto error0; 415250199Sgrehan 416250199Sgrehan return (0); 417250199Sgrehan 418250199Sgrehan error0: 419250199Sgrehan 420250199Sgrehan free(receive_buffer[receive_buffer_offset], M_DEVBUF); 421250199Sgrehan receive_buffer[receive_buffer_offset] = NULL; 422250199Sgrehan 423250199Sgrehan return (ret); 424250199Sgrehan} 425250199Sgrehan 426250199Sgrehanstatic int 427250199Sgrehanhv_util_detach(device_t dev) 428250199Sgrehan{ 429250199Sgrehan struct hv_device* hv_dev; 430250199Sgrehan struct hv_vmbus_service* service; 431250199Sgrehan size_t receive_buffer_offset; 432250199Sgrehan 433250199Sgrehan hv_dev = vmbus_get_devctx(dev); 434250199Sgrehan 435250199Sgrehan hv_vmbus_channel_close(hv_dev->channel); 436250199Sgrehan service = device_get_softc(dev); 437250199Sgrehan receive_buffer_offset = service - &service_table[0]; 438250199Sgrehan 439250199Sgrehan if (service->work_queue != NULL) 440250199Sgrehan hv_work_queue_close(service->work_queue); 441250199Sgrehan 442250199Sgrehan free(receive_buffer[receive_buffer_offset], M_DEVBUF); 443250199Sgrehan receive_buffer[receive_buffer_offset] = NULL; 444250199Sgrehan 445250199Sgrehan return (0); 446250199Sgrehan} 447250199Sgrehan 448250199Sgrehanstatic void hv_util_init(void) 449250199Sgrehan{ 450250199Sgrehan} 451250199Sgrehan 452250199Sgrehanstatic int hv_util_modevent(module_t mod, int event, void *arg) 453250199Sgrehan{ 454250199Sgrehan switch (event) { 455250199Sgrehan case MOD_LOAD: 456250199Sgrehan break; 457250199Sgrehan case MOD_UNLOAD: 458250199Sgrehan break; 459250199Sgrehan default: 460250199Sgrehan break; 461250199Sgrehan } 462250199Sgrehan return (0); 463250199Sgrehan} 464250199Sgrehan 465250199Sgrehanstatic device_method_t util_methods[] = { 466250199Sgrehan /* Device interface */ 467250199Sgrehan DEVMETHOD(device_probe, hv_util_probe), 468250199Sgrehan DEVMETHOD(device_attach, hv_util_attach), 469250199Sgrehan DEVMETHOD(device_detach, hv_util_detach), 470250199Sgrehan DEVMETHOD(device_shutdown, bus_generic_shutdown), 471250199Sgrehan { 0, 0 } } 472250199Sgrehan; 473250199Sgrehan 474250199Sgrehanstatic driver_t util_driver = { "hyperv-utils", util_methods, 0 }; 475250199Sgrehan 476250199Sgrehanstatic devclass_t util_devclass; 477250199Sgrehan 478250199SgrehanDRIVER_MODULE(hv_utils, vmbus, util_driver, util_devclass, hv_util_modevent, 0); 479250199SgrehanMODULE_VERSION(hv_utils, 1); 480250199SgrehanMODULE_DEPEND(hv_utils, vmbus, 1, 1, 1); 481250199Sgrehan 482253869SgrehanSYSINIT(hv_util_initx, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE + 1, 483250199Sgrehan hv_util_init, NULL); 484