hv_util.c revision 255414
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 /* KVP (Key Value Pair) Service */ 87 { .guid.data = {0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, 88 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6}, 89 .name = "Hyper-V KVP Service\n", 90 .enabled = TRUE, 91 .init = hv_kvp_init, 92 .callback = hv_kvp_callback, 93 }, 94}; 95 96/* 97 * Receive buffer pointers. There is one buffer per utility service. The 98 * buffer is allocated during attach(). 99 */ 100uint8_t *receive_buffer[HV_MAX_UTIL_SERVICES]; 101 102struct hv_ictimesync_data { 103 uint64_t parenttime; 104 uint64_t childtime; 105 uint64_t roundtriptime; 106 uint8_t flags; 107} __packed; 108 109static int 110hv_timesync_init(hv_vmbus_service *serv) 111{ 112 113 serv->work_queue = hv_work_queue_create("Time Sync"); 114 if (serv->work_queue == NULL) 115 return (ENOMEM); 116 return (0); 117} 118 119static void 120hv_negotiate_version( 121 struct hv_vmbus_icmsg_hdr* icmsghdrp, 122 struct hv_vmbus_icmsg_negotiate* negop, 123 uint8_t* buf) 124{ 125 icmsghdrp->icmsgsize = 0x10; 126 127 negop = (struct hv_vmbus_icmsg_negotiate *)&buf[ 128 sizeof(struct hv_vmbus_pipe_hdr) + 129 sizeof(struct hv_vmbus_icmsg_hdr)]; 130 131 if (negop->icframe_vercnt >= 2 && 132 negop->icversion_data[1].major == 3) { 133 negop->icversion_data[0].major = 3; 134 negop->icversion_data[0].minor = 0; 135 negop->icversion_data[1].major = 3; 136 negop->icversion_data[1].minor = 0; 137 } else { 138 negop->icversion_data[0].major = 1; 139 negop->icversion_data[0].minor = 0; 140 negop->icversion_data[1].major = 1; 141 negop->icversion_data[1].minor = 0; 142 } 143 144 negop->icframe_vercnt = 1; 145 negop->icmsg_vercnt = 1; 146} 147 148 149/** 150 * Set host time based on time sync message from host 151 */ 152static void 153hv_set_host_time(void *context) 154{ 155 time_sync_data *time_msg = context; 156 uint64_t hosttime = time_msg->data; 157 struct timespec guest_ts, host_ts; 158 uint64_t host_tns; 159 int64_t diff; 160 int error; 161 162 host_tns = (hosttime - HV_WLTIMEDELTA) * 100; 163 host_ts.tv_sec = (time_t)(host_tns/HV_NANO_SEC_PER_SEC); 164 host_ts.tv_nsec = (long)(host_tns%HV_NANO_SEC_PER_SEC); 165 166 nanotime(&guest_ts); 167 168 diff = (int64_t)host_ts.tv_sec - (int64_t)guest_ts.tv_sec; 169 170 /* 171 * If host differs by 5 seconds then make the guest catch up 172 */ 173 if (diff > 5 || diff < -5) { 174 error = kern_clock_settime(curthread, CLOCK_REALTIME, 175 &host_ts); 176 } 177 178 /* 179 * Free the hosttime that was allocated in hv_adj_guesttime() 180 */ 181 free(time_msg, M_DEVBUF); 182} 183 184/** 185 * @brief Synchronize time with host after reboot, restore, etc. 186 * 187 * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. 188 * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time 189 * message after the timesync channel is opened. Since the hv_utils module is 190 * loaded after hv_vmbus, the first message is usually missed. The other 191 * thing is, systime is automatically set to emulated hardware clock which may 192 * not be UTC time or in the same time zone. So, to override these effects, we 193 * use the first 50 time samples for initial system time setting. 194 */ 195static inline 196void hv_adj_guesttime(uint64_t hosttime, uint8_t flags) 197{ 198 time_sync_data* time_msg; 199 200 time_msg = malloc(sizeof(time_sync_data), M_DEVBUF, M_NOWAIT); 201 202 if (time_msg == NULL) 203 return; 204 205 time_msg->data = hosttime; 206 207 if ((flags & HV_ICTIMESYNCFLAG_SYNC) != 0) { 208 hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, 209 hv_set_host_time, time_msg); 210 } else if ((flags & HV_ICTIMESYNCFLAG_SAMPLE) != 0) { 211 hv_queue_work_item(service_table[HV_TIME_SYNCH].work_queue, 212 hv_set_host_time, time_msg); 213 } else { 214 free(time_msg, M_DEVBUF); 215 } 216} 217 218/** 219 * Time Sync Channel message handler 220 */ 221static void 222hv_timesync_cb(void *context) 223{ 224 hv_vmbus_channel* channel = context; 225 hv_vmbus_icmsg_hdr* icmsghdrp; 226 uint32_t recvlen; 227 uint64_t requestId; 228 int ret; 229 uint8_t* time_buf; 230 struct hv_ictimesync_data* timedatap; 231 232 time_buf = receive_buffer[HV_TIME_SYNCH]; 233 234 ret = hv_vmbus_channel_recv_packet(channel, time_buf, 235 PAGE_SIZE, &recvlen, &requestId); 236 237 if ((ret == 0) && recvlen > 0) { 238 icmsghdrp = (struct hv_vmbus_icmsg_hdr *) &time_buf[ 239 sizeof(struct hv_vmbus_pipe_hdr)]; 240 241 if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 242 hv_negotiate_version(icmsghdrp, NULL, time_buf); 243 } else { 244 timedatap = (struct hv_ictimesync_data *) &time_buf[ 245 sizeof(struct hv_vmbus_pipe_hdr) + 246 sizeof(struct hv_vmbus_icmsg_hdr)]; 247 hv_adj_guesttime(timedatap->parenttime, timedatap->flags); 248 } 249 250 icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION 251 | HV_ICMSGHDRFLAG_RESPONSE; 252 253 hv_vmbus_channel_send_packet(channel, time_buf, 254 recvlen, requestId, 255 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 256 } 257} 258 259/** 260 * Shutdown 261 */ 262static void 263hv_shutdown_cb(void *context) 264{ 265 uint8_t* buf; 266 hv_vmbus_channel* channel = context; 267 uint8_t execute_shutdown = 0; 268 hv_vmbus_icmsg_hdr* icmsghdrp; 269 uint32_t recv_len; 270 uint64_t request_id; 271 int ret; 272 hv_vmbus_shutdown_msg_data* shutdown_msg; 273 274 buf = receive_buffer[HV_SHUT_DOWN]; 275 276 ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, 277 &recv_len, &request_id); 278 279 if ((ret == 0) && recv_len > 0) { 280 281 icmsghdrp = (struct hv_vmbus_icmsg_hdr *) 282 &buf[sizeof(struct hv_vmbus_pipe_hdr)]; 283 284 if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 285 hv_negotiate_version(icmsghdrp, NULL, buf); 286 287 } else { 288 shutdown_msg = 289 (struct hv_vmbus_shutdown_msg_data *) 290 &buf[sizeof(struct hv_vmbus_pipe_hdr) + 291 sizeof(struct hv_vmbus_icmsg_hdr)]; 292 293 switch (shutdown_msg->flags) { 294 case 0: 295 case 1: 296 icmsghdrp->status = HV_S_OK; 297 execute_shutdown = 1; 298 if(bootverbose) 299 printf("Shutdown request received -" 300 " graceful shutdown initiated\n"); 301 break; 302 default: 303 icmsghdrp->status = HV_E_FAIL; 304 execute_shutdown = 0; 305 printf("Shutdown request received -" 306 " Invalid request\n"); 307 break; 308 } 309 } 310 311 icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | 312 HV_ICMSGHDRFLAG_RESPONSE; 313 314 hv_vmbus_channel_send_packet(channel, buf, 315 recv_len, request_id, 316 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 317 } 318 319 if (execute_shutdown) 320 shutdown_nice(RB_POWEROFF); 321} 322 323/** 324 * Process heartbeat message 325 */ 326static void 327hv_heartbeat_cb(void *context) 328{ 329 uint8_t* buf; 330 hv_vmbus_channel* channel = context; 331 uint32_t recvlen; 332 uint64_t requestid; 333 int ret; 334 335 struct hv_vmbus_heartbeat_msg_data* heartbeat_msg; 336 struct hv_vmbus_icmsg_hdr* icmsghdrp; 337 338 buf = receive_buffer[HV_HEART_BEAT]; 339 340 ret = hv_vmbus_channel_recv_packet(channel, buf, PAGE_SIZE, &recvlen, 341 &requestid); 342 343 if ((ret == 0) && recvlen > 0) { 344 345 icmsghdrp = (struct hv_vmbus_icmsg_hdr *) 346 &buf[sizeof(struct hv_vmbus_pipe_hdr)]; 347 348 if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { 349 hv_negotiate_version(icmsghdrp, NULL, buf); 350 351 } else { 352 heartbeat_msg = 353 (struct hv_vmbus_heartbeat_msg_data *) 354 &buf[sizeof(struct hv_vmbus_pipe_hdr) + 355 sizeof(struct hv_vmbus_icmsg_hdr)]; 356 357 heartbeat_msg->seq_num += 1; 358 } 359 360 icmsghdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | 361 HV_ICMSGHDRFLAG_RESPONSE; 362 363 hv_vmbus_channel_send_packet(channel, buf, recvlen, requestid, 364 HV_VMBUS_PACKET_TYPE_DATA_IN_BAND, 0); 365 } 366} 367 368 369static int 370hv_util_probe(device_t dev) 371{ 372 int i; 373 int rtn_value = ENXIO; 374 375 for (i = 0; i < HV_MAX_UTIL_SERVICES; i++) { 376 const char *p = vmbus_get_type(dev); 377 if (service_table[i].enabled && !memcmp(p, &service_table[i].guid, sizeof(hv_guid))) { 378 device_set_softc(dev, (void *) (&service_table[i])); 379 rtn_value = 0; 380 } 381 } 382 383 return rtn_value; 384} 385 386static int 387hv_util_attach(device_t dev) 388{ 389 struct hv_device* hv_dev; 390 struct hv_vmbus_service* service; 391 int ret; 392 size_t receive_buffer_offset; 393 394 hv_dev = vmbus_get_devctx(dev); 395 service = device_get_softc(dev); 396 receive_buffer_offset = service - &service_table[0]; 397 device_printf(dev, "Hyper-V Service attaching: %s\n", service->name); 398 receive_buffer[receive_buffer_offset] = 399 malloc(4 * PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 400 401 if (service->init != NULL) { 402 ret = service->init(service); 403 if (ret) { 404 ret = ENODEV; 405 goto error0; 406 } 407 } 408 409 ret = hv_vmbus_channel_open(hv_dev->channel, 4 * PAGE_SIZE, 410 4 * PAGE_SIZE, NULL, 0, 411 service->callback, hv_dev->channel); 412 413 if (ret) 414 goto error0; 415 416 return (0); 417 418 error0: 419 420 free(receive_buffer[receive_buffer_offset], M_DEVBUF); 421 receive_buffer[receive_buffer_offset] = NULL; 422 423 return (ret); 424} 425 426static int 427hv_util_detach(device_t dev) 428{ 429 struct hv_device* hv_dev; 430 struct hv_vmbus_service* service; 431 size_t receive_buffer_offset; 432 433 hv_dev = vmbus_get_devctx(dev); 434 435 hv_vmbus_channel_close(hv_dev->channel); 436 service = device_get_softc(dev); 437 receive_buffer_offset = service - &service_table[0]; 438 439 if (service->work_queue != NULL) 440 hv_work_queue_close(service->work_queue); 441 442 free(receive_buffer[receive_buffer_offset], M_DEVBUF); 443 receive_buffer[receive_buffer_offset] = NULL; 444 445 return (0); 446} 447 448static void hv_util_init(void) 449{ 450} 451 452static int hv_util_modevent(module_t mod, int event, void *arg) 453{ 454 switch (event) { 455 case MOD_LOAD: 456 break; 457 case MOD_UNLOAD: 458 break; 459 default: 460 break; 461 } 462 return (0); 463} 464 465static device_method_t util_methods[] = { 466 /* Device interface */ 467 DEVMETHOD(device_probe, hv_util_probe), 468 DEVMETHOD(device_attach, hv_util_attach), 469 DEVMETHOD(device_detach, hv_util_detach), 470 DEVMETHOD(device_shutdown, bus_generic_shutdown), 471 { 0, 0 } } 472; 473 474static driver_t util_driver = { "hyperv-utils", util_methods, 0 }; 475 476static devclass_t util_devclass; 477 478DRIVER_MODULE(hv_utils, vmbus, util_driver, util_devclass, hv_util_modevent, 0); 479MODULE_VERSION(hv_utils, 1); 480MODULE_DEPEND(hv_utils, vmbus, 1, 1, 1); 481 482SYSINIT(hv_util_initx, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE + 1, 483 hv_util_init, NULL); 484