1/*- 2 * Copyright (c) 2009-2012 Microsoft Corp. 3 * Copyright (c) 2012 NetApp Inc. 4 * Copyright (c) 2012 Citrix Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29/** 30 * A common driver for all hyper-V util services. 31 */ 32 33#include <sys/param.h> 34#include <sys/kernel.h> 35#include <sys/bus.h> 36#include <sys/malloc.h> 37#include <sys/module.h> 38#include <sys/reboot.h> 39#include <sys/timetc.h> 40#include <sys/syscallsubr.h> 41 42#include <dev/hyperv/include/hyperv.h> 43#include "hv_kvp.h" 44 45/* Time Sync data */ 46typedef struct { 47 uint64_t data; 48} time_sync_data; 49 50static void hv_shutdown_cb(void *context); 51static void hv_heartbeat_cb(void *context); 52static void hv_timesync_cb(void *context); 53 54static int hv_timesync_init(hv_vmbus_service *serv); 55 56/** 57 * Note: GUID codes below are predefined by the host hypervisor 58 * (Hyper-V and Azure)interface and required for correct operation. 59 */ 60hv_vmbus_service service_table[] = { 61 /* Shutdown Service */ 62 { .guid.data = {0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, 63 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB}, 64 .name = "Hyper-V Shutdown Service\n", 65 .enabled = TRUE, 66 .callback = hv_shutdown_cb, 67 }, 68 69 /* Time Synch Service */ 70 { .guid.data = {0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, 71 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf}, 72 .name = "Hyper-V Time Synch Service\n", 73 .enabled = TRUE, 74 .init = hv_timesync_init, 75 .callback = hv_timesync_cb, 76 }, 77 78 /* Heartbeat Service */ 79 { .guid.data = {0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, 80 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d}, 81 .name = "Hyper-V Heartbeat Service\n", 82 .enabled = TRUE, 83 .callback = hv_heartbeat_cb, 84 }, 85}; 86 87/* 88 * Receive buffer pointers. There is one buffer per utility service. The 89 * buffer is allocated during attach(). 90 */ 91uint8_t *receive_buffer[HV_MAX_UTIL_SERVICES]; 92 93struct hv_ictimesync_data { 94 uint64_t parenttime; 95 uint64_t childtime; 96 uint64_t roundtriptime; 97 uint8_t flags; 98} __packed; 99 100static int 101hv_timesync_init(hv_vmbus_service *serv) 102{ 103 104 serv->work_queue = hv_work_queue_create("Time Sync"); 105 if (serv->work_queue == NULL) 106 return (ENOMEM); 107 return (0); 108} 109 110static void 111hv_negotiate_version( 112 struct hv_vmbus_icmsg_hdr* icmsghdrp, 113 struct hv_vmbus_icmsg_negotiate* negop, 114 uint8_t* buf) 115{ 116 icmsghdrp->icmsgsize = 0x10; 117 118 negop = (struct hv_vmbus_icmsg_negotiate *)&buf[ 119 sizeof(struct hv_vmbus_pipe_hdr) + 120 sizeof(struct hv_vmbus_icmsg_hdr)]; 121 122 if (negop->icframe_vercnt >= 2 && 123 negop->icversion_data[1].major == 3) { 124 negop->icversion_data[0].major = 3; 125 negop->icversion_data[0].minor = 0; 126 negop->icversion_data[1].major = 3; 127 negop->icversion_data[1].minor = 0; 128 } else { 129 negop->icversion_data[0].major = 1; 130 negop->icversion_data[0].minor = 0; 131 negop->icversion_data[1].major = 1; 132 negop->icversion_data[1].minor = 0; 133 } 134 135 negop->icframe_vercnt = 1; 136 negop->icmsg_vercnt = 1; 137} 138 139 140/** 141 * Set host time based on time sync message from host 142 */ 143static void 144hv_set_host_time(void *context) 145{ 146 time_sync_data *time_msg = context; 147 uint64_t hosttime = time_msg->data; 148 struct timespec guest_ts, host_ts; 149 uint64_t host_tns; 150 int64_t diff; 151 int error; 152 153 host_tns = (hosttime - HV_WLTIMEDELTA) * 100; 154 host_ts.tv_sec = (time_t)(host_tns/HV_NANO_SEC_PER_SEC); 155 host_ts.tv_nsec = (long)(host_tns%HV_NANO_SEC_PER_SEC); 156 157 nanotime(&guest_ts); 158 159 diff = (int64_t)host_ts.tv_sec - (int64_t)guest_ts.tv_sec; 160 161 /* 162 * If host differs by 5 seconds then make the guest catch up 163 */ 164 if (diff > 5 || diff < -5) { 165 error = kern_clock_settime(curthread, CLOCK_REALTIME, 166 &host_ts); 167 } 168 169 /* 170 * Free the hosttime that was allocated in hv_adj_guesttime() 171 */ 172 free(time_msg, M_DEVBUF); 173} 174 175/** 176 * @brief Synchronize time with host after reboot, restore, etc. 177 * 178 * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. 179 * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time 180 * message after the timesync channel is opened. Since the hv_utils module is 181 * loaded after hv_vmbus, the first message is usually missed. The other 182 * thing is, systime is automatically set to emulated hardware clock which may 183 * not be UTC time or in the same time zone. So, to override these effects, we 184 * use the first 50 time samples for initial system time setting. 185 */ 186static inline 187void hv_adj_guesttime(uint64_t hosttime, uint8_t flags) 188{ 189 time_sync_data* time_msg; 190 191 time_msg = malloc(sizeof(time_sync_data), M_DEVBUF, M_NOWAIT); 192 193 if (time_msg == NULL) 194 return; 195 196 time_msg->data = hosttime; 197 198 if ((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) { 199 hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, 200 hv_set_host_time, time_msg); 201 } else if ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0) { 202 hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, 203 hv_set_host_time, time_msg); 204 } else { 205 free(time_msg, M_DEVBUF); 206 } 207} 208 209/** 210 * Time Sync Channel message handler 211 */ 212static void 213hv_timesync_cb(void *context) 214{ 215 hv_vmbus_channel* channel = context; 216 hv_vmbus_icmsg_hdr* icmsghdrp; 217 uint32_t recvlen; 218 uint64_t requestId; 219 int ret; 220 uint8_t* time_buf; 221 struct hv_ictimesync_data* timedatap; 222 223 time_buf = receive_buffer[HV_TIME_SYNCH]; 224 225 ret = hv_vmbus_channel_recv_packet(channel, time_buf, 226 PAGE_SIZE, &recvlen, &requestId); 227 228 if ((ret == 0) && recvlen > 0) { 229 icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &time_buf[ 230 sizeof(struct hv_vmbus_pipe_hdr)]; 231 232 if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 233 hv_negotiate_version(icmsghdrp, NULL, time_buf); 234 } else { 235 timedatap = (struct hv_ictimesync_data *) &time_buf[ 236 sizeof(struct hv_vmbus_pipe_hdr) + 237 sizeof(struct hv_vmbus_icmsg_hdr)]; 238 hv_adj_guesttime(timedatap->parenttime, timedatap->flags); 239 } 240 241 icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION 242 | HV_ICMSGHDRFLAG_RESPONSE; 243 244 hv_vmbus_channel_send_packet(channel, time_buf, 245 recvlen, requestId, 246 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 247 } 248} 249 250/** 251 * Shutdown 252 */ 253static void 254hv_shutdown_cb(void *context) 255{ 256 uint8_t* buf; 257 hv_vmbus_channel* channel = context; 258 uint8_t execute_shutdown = 0; 259 hv_vmbus_icmsg_hdr* icmsghdrp; 260 uint32_t recv_len; 261 uint64_t request_id; 262 int ret; 263 hv_vmbus_shutdown_msg_data* shutdown_msg; 264 265 buf = receive_buffer[HV_SHUT_DOWN]; 266 267 ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, 268 &recv_len, &request_id); 269 270 if ((ret == 0) && recv_len > 0) { 271 272 icmsghdrp = (struct hv_vmbus_icmsg_hdr *) 273 &buf[sizeof(struct hv_vmbus_pipe_hdr)]; 274 275 if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 276 hv_negotiate_version(icmsghdrp, NULL, buf); 277 278 } else { 279 shutdown_msg = 280 (struct hv_vmbus_shutdown_msg_data *) 281 &buf[sizeof(struct hv_vmbus_pipe_hdr) + 282 sizeof(struct hv_vmbus_icmsg_hdr)]; 283 284 switch (shutdown_msg->flags) { 285 case 0: 286 case 1: 287 icmsghdrp->status = HV_S_OK; 288 execute_shutdown = 1; 289 if(bootverbose) 290 printf("Shutdown request received -" 291 " graceful shutdown initiated\n"); 292 break; 293 default: 294 icmsghdrp->status = HV_E_FAIL; 295 execute_shutdown = 0; 296 printf("Shutdown request received -" 297 " Invalid request\n"); 298 break; 299 } 300 } 301 302 icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | 303 HV_ICMSGHDRFLAG_RESPONSE; 304 305 hv_vmbus_channel_send_packet(channel, buf, 306 recv_len, request_id, 307 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 308 } 309 310 if (execute_shutdown) 311 shutdown_nice(RB_POWEROFF); 312} 313 314/** 315 * Process heartbeat message 316 */ 317static void 318hv_heartbeat_cb(void *context) 319{ 320 uint8_t* buf; 321 hv_vmbus_channel* channel = context; 322 uint32_t recvlen; 323 uint64_t requestid; 324 int ret; 325 326 struct hv_vmbus_heartbeat_msg_data* heartbeat_msg; 327 struct hv_vmbus_icmsg_hdr* icmsghdrp; 328 329 buf = receive_buffer[HV_HEART_BEAT]; 330 331 ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, &recvlen, 332 &requestid); 333 334 if ((ret == 0) && recvlen > 0) { 335 336 icmsghdrp = (struct hv_vmbus_icmsg_hdr *) 337 &buf[sizeof(struct hv_vmbus_pipe_hdr)]; 338 339 if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 340 hv_negotiate_version(icmsghdrp, NULL, buf); 341 342 } else { 343 heartbeat_msg = 344 (struct hv_vmbus_heartbeat_msg_data *) 345 &buf[sizeof(struct hv_vmbus_pipe_hdr) + 346 sizeof(struct hv_vmbus_icmsg_hdr)]; 347 348 heartbeat_msg->seq_num += 1; 349 } 350 351 icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | 352 HV_ICMSGHDRFLAG_RESPONSE; 353 354 hv_vmbus_channel_send_packet(channel, buf, recvlen, requestid, 355 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 356 } 357} 358 359 360static int 361hv_util_probe(device_t dev) 362{ 363 int i; 364 int rtn_value = ENXIO; 365 366 for (i = 0; i < HV_MAX_UTIL_SERVICES; i++) { 367 const char *p = vmbus_get_type(dev); 368 if (service_table[i].enabled && !memcmp(p, &service_table[i].guid, sizeof(hv_guid))) { 369 device_set_softc(dev, (void *) (&service_table[i])); 370 rtn_value = 0; 371 } 372 } 373 374 return rtn_value; 375} 376 377static int 378hv_util_attach(device_t dev) 379{ 380 struct hv_device* hv_dev; 381 struct hv_vmbus_service* service; 382 int ret; 383 size_t receive_buffer_offset; 384 385 hv_dev = vmbus_get_devctx(dev); 386 service = device_get_softc(dev); 387 receive_buffer_offset = service - &service_table[0]; 388 device_printf(dev, "Hyper-V Service attaching: %s\n", service->name); 389 receive_buffer[receive_buffer_offset] = 390 malloc(4 * PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 391 392 if (service->init != NULL) { 393 ret = service->init(service); 394 if (ret) { 395 ret = ENODEV; 396 goto error0; 397 } 398 } 399 400 ret = hv_vmbus_channel_open(hv_dev->channel, 4 * PAGE_SIZE, 401 4 * PAGE_SIZE, NULL, 0, 402 service->callback, hv_dev->channel); 403 404 if (ret) 405 goto error0; 406 407 return (0); 408 409 error0: 410 411 free(receive_buffer[receive_buffer_offset], M_DEVBUF); 412 receive_buffer[receive_buffer_offset] = NULL; 413 414 return (ret); 415} 416 417static int 418hv_util_detach(device_t dev) 419{ 420 struct hv_device* hv_dev; 421 struct hv_vmbus_service* service; 422 size_t receive_buffer_offset; 423 424 hv_dev = vmbus_get_devctx(dev); 425 426 hv_vmbus_channel_close(hv_dev->channel); 427 service = device_get_softc(dev); 428 receive_buffer_offset = service - &service_table[0]; 429 430 if (service->work_queue != NULL) 431 hv_work_queue_close(service->work_queue); 432 433 free(receive_buffer[receive_buffer_offset], M_DEVBUF); 434 receive_buffer[receive_buffer_offset] = NULL; 435 436 return (0); 437} 438 439static void hv_util_init(void) 440{ 441} 442 443static int hv_util_modevent(module_t mod, int event, void *arg) 444{ 445 switch (event) { 446 case MOD_LOAD: 447 break; 448 case MOD_UNLOAD: 449 break; 450 default: 451 break; 452 } 453 return (0); 454} 455 456static device_method_t util_methods[] = { 457 /* Device interface */ 458 DEVMETHOD(device_probe, hv_util_probe), 459 DEVMETHOD(device_attach, hv_util_attach), 460 DEVMETHOD(device_detach, hv_util_detach), 461 DEVMETHOD(device_shutdown, bus_generic_shutdown), 462 { 0, 0 } } 463; 464 465static driver_t util_driver = { "hyperv-utils", util_methods, 0 }; 466 467static devclass_t util_devclass; 468 469DRIVER_MODULE(hv_utils, vmbus, util_driver, util_devclass, hv_util_modevent, 0); 470MODULE_VERSION(hv_utils, 1); 471MODULE_DEPEND(hv_utils, vmbus, 1, 1, 1); 472 473SYSINIT(hv_util_initx, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE + 1, 474 hv_util_init, NULL); 475