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