xref: /openbmc/linux/drivers/hv/hv_util.c (revision e6c81cce)
1 /*
2  * Copyright (c) 2010, Microsoft Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Authors:
18  *   Haiyang Zhang <haiyangz@microsoft.com>
19  *   Hank Janssen  <hjanssen@microsoft.com>
20  */
21 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22 
23 #include <linux/kernel.h>
24 #include <linux/init.h>
25 #include <linux/module.h>
26 #include <linux/slab.h>
27 #include <linux/sysctl.h>
28 #include <linux/reboot.h>
29 #include <linux/hyperv.h>
30 
31 #include "hyperv_vmbus.h"
32 
33 #define SD_MAJOR	3
34 #define SD_MINOR	0
35 #define SD_VERSION	(SD_MAJOR << 16 | SD_MINOR)
36 
37 #define SD_WS2008_MAJOR		1
38 #define SD_WS2008_VERSION	(SD_WS2008_MAJOR << 16 | SD_MINOR)
39 
40 #define TS_MAJOR	3
41 #define TS_MINOR	0
42 #define TS_VERSION	(TS_MAJOR << 16 | TS_MINOR)
43 
44 #define TS_WS2008_MAJOR		1
45 #define TS_WS2008_VERSION	(TS_WS2008_MAJOR << 16 | TS_MINOR)
46 
47 #define HB_MAJOR	3
48 #define HB_MINOR 0
49 #define HB_VERSION	(HB_MAJOR << 16 | HB_MINOR)
50 
51 #define HB_WS2008_MAJOR	1
52 #define HB_WS2008_VERSION	(HB_WS2008_MAJOR << 16 | HB_MINOR)
53 
54 static int sd_srv_version;
55 static int ts_srv_version;
56 static int hb_srv_version;
57 static int util_fw_version;
58 
59 static void shutdown_onchannelcallback(void *context);
60 static struct hv_util_service util_shutdown = {
61 	.util_cb = shutdown_onchannelcallback,
62 };
63 
64 static void timesync_onchannelcallback(void *context);
65 static struct hv_util_service util_timesynch = {
66 	.util_cb = timesync_onchannelcallback,
67 };
68 
69 static void heartbeat_onchannelcallback(void *context);
70 static struct hv_util_service util_heartbeat = {
71 	.util_cb = heartbeat_onchannelcallback,
72 };
73 
74 static struct hv_util_service util_kvp = {
75 	.util_cb = hv_kvp_onchannelcallback,
76 	.util_init = hv_kvp_init,
77 	.util_deinit = hv_kvp_deinit,
78 };
79 
80 static struct hv_util_service util_vss = {
81 	.util_cb = hv_vss_onchannelcallback,
82 	.util_init = hv_vss_init,
83 	.util_deinit = hv_vss_deinit,
84 };
85 
86 static struct hv_util_service util_fcopy = {
87 	.util_cb = hv_fcopy_onchannelcallback,
88 	.util_init = hv_fcopy_init,
89 	.util_deinit = hv_fcopy_deinit,
90 };
91 
92 static void perform_shutdown(struct work_struct *dummy)
93 {
94 	orderly_poweroff(true);
95 }
96 
97 /*
98  * Perform the shutdown operation in a thread context.
99  */
100 static DECLARE_WORK(shutdown_work, perform_shutdown);
101 
102 static void shutdown_onchannelcallback(void *context)
103 {
104 	struct vmbus_channel *channel = context;
105 	u32 recvlen;
106 	u64 requestid;
107 	bool execute_shutdown = false;
108 	u8  *shut_txf_buf = util_shutdown.recv_buffer;
109 
110 	struct shutdown_msg_data *shutdown_msg;
111 
112 	struct icmsg_hdr *icmsghdrp;
113 	struct icmsg_negotiate *negop = NULL;
114 
115 	vmbus_recvpacket(channel, shut_txf_buf,
116 			 PAGE_SIZE, &recvlen, &requestid);
117 
118 	if (recvlen > 0) {
119 		icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[
120 			sizeof(struct vmbuspipe_hdr)];
121 
122 		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
123 			vmbus_prep_negotiate_resp(icmsghdrp, negop,
124 					shut_txf_buf, util_fw_version,
125 					sd_srv_version);
126 		} else {
127 			shutdown_msg =
128 				(struct shutdown_msg_data *)&shut_txf_buf[
129 					sizeof(struct vmbuspipe_hdr) +
130 					sizeof(struct icmsg_hdr)];
131 
132 			switch (shutdown_msg->flags) {
133 			case 0:
134 			case 1:
135 				icmsghdrp->status = HV_S_OK;
136 				execute_shutdown = true;
137 
138 				pr_info("Shutdown request received -"
139 					    " graceful shutdown initiated\n");
140 				break;
141 			default:
142 				icmsghdrp->status = HV_E_FAIL;
143 				execute_shutdown = false;
144 
145 				pr_info("Shutdown request received -"
146 					    " Invalid request\n");
147 				break;
148 			}
149 		}
150 
151 		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
152 			| ICMSGHDRFLAG_RESPONSE;
153 
154 		vmbus_sendpacket(channel, shut_txf_buf,
155 				       recvlen, requestid,
156 				       VM_PKT_DATA_INBAND, 0);
157 	}
158 
159 	if (execute_shutdown == true)
160 		schedule_work(&shutdown_work);
161 }
162 
163 /*
164  * Set guest time to host UTC time.
165  */
166 static inline void do_adj_guesttime(u64 hosttime)
167 {
168 	s64 host_tns;
169 	struct timespec host_ts;
170 
171 	host_tns = (hosttime - WLTIMEDELTA) * 100;
172 	host_ts = ns_to_timespec(host_tns);
173 
174 	do_settimeofday(&host_ts);
175 }
176 
177 /*
178  * Set the host time in a process context.
179  */
180 
181 struct adj_time_work {
182 	struct work_struct work;
183 	u64	host_time;
184 };
185 
186 static void hv_set_host_time(struct work_struct *work)
187 {
188 	struct adj_time_work	*wrk;
189 
190 	wrk = container_of(work, struct adj_time_work, work);
191 	do_adj_guesttime(wrk->host_time);
192 	kfree(wrk);
193 }
194 
195 /*
196  * Synchronize time with host after reboot, restore, etc.
197  *
198  * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
199  * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
200  * message after the timesync channel is opened. Since the hv_utils module is
201  * loaded after hv_vmbus, the first message is usually missed. The other
202  * thing is, systime is automatically set to emulated hardware clock which may
203  * not be UTC time or in the same time zone. So, to override these effects, we
204  * use the first 50 time samples for initial system time setting.
205  */
206 static inline void adj_guesttime(u64 hosttime, u8 flags)
207 {
208 	struct adj_time_work    *wrk;
209 	static s32 scnt = 50;
210 
211 	wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC);
212 	if (wrk == NULL)
213 		return;
214 
215 	wrk->host_time = hosttime;
216 	if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
217 		INIT_WORK(&wrk->work, hv_set_host_time);
218 		schedule_work(&wrk->work);
219 		return;
220 	}
221 
222 	if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
223 		scnt--;
224 		INIT_WORK(&wrk->work, hv_set_host_time);
225 		schedule_work(&wrk->work);
226 	} else
227 		kfree(wrk);
228 }
229 
230 /*
231  * Time Sync Channel message handler.
232  */
233 static void timesync_onchannelcallback(void *context)
234 {
235 	struct vmbus_channel *channel = context;
236 	u32 recvlen;
237 	u64 requestid;
238 	struct icmsg_hdr *icmsghdrp;
239 	struct ictimesync_data *timedatap;
240 	u8 *time_txf_buf = util_timesynch.recv_buffer;
241 	struct icmsg_negotiate *negop = NULL;
242 
243 	vmbus_recvpacket(channel, time_txf_buf,
244 			 PAGE_SIZE, &recvlen, &requestid);
245 
246 	if (recvlen > 0) {
247 		icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
248 				sizeof(struct vmbuspipe_hdr)];
249 
250 		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
251 			vmbus_prep_negotiate_resp(icmsghdrp, negop,
252 						time_txf_buf,
253 						util_fw_version,
254 						ts_srv_version);
255 		} else {
256 			timedatap = (struct ictimesync_data *)&time_txf_buf[
257 				sizeof(struct vmbuspipe_hdr) +
258 				sizeof(struct icmsg_hdr)];
259 			adj_guesttime(timedatap->parenttime, timedatap->flags);
260 		}
261 
262 		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
263 			| ICMSGHDRFLAG_RESPONSE;
264 
265 		vmbus_sendpacket(channel, time_txf_buf,
266 				recvlen, requestid,
267 				VM_PKT_DATA_INBAND, 0);
268 	}
269 }
270 
271 /*
272  * Heartbeat functionality.
273  * Every two seconds, Hyper-V send us a heartbeat request message.
274  * we respond to this message, and Hyper-V knows we are alive.
275  */
276 static void heartbeat_onchannelcallback(void *context)
277 {
278 	struct vmbus_channel *channel = context;
279 	u32 recvlen;
280 	u64 requestid;
281 	struct icmsg_hdr *icmsghdrp;
282 	struct heartbeat_msg_data *heartbeat_msg;
283 	u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
284 	struct icmsg_negotiate *negop = NULL;
285 
286 	vmbus_recvpacket(channel, hbeat_txf_buf,
287 			 PAGE_SIZE, &recvlen, &requestid);
288 
289 	if (recvlen > 0) {
290 		icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
291 				sizeof(struct vmbuspipe_hdr)];
292 
293 		if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
294 			vmbus_prep_negotiate_resp(icmsghdrp, negop,
295 				hbeat_txf_buf, util_fw_version,
296 				hb_srv_version);
297 		} else {
298 			heartbeat_msg =
299 				(struct heartbeat_msg_data *)&hbeat_txf_buf[
300 					sizeof(struct vmbuspipe_hdr) +
301 					sizeof(struct icmsg_hdr)];
302 
303 			heartbeat_msg->seq_num += 1;
304 		}
305 
306 		icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
307 			| ICMSGHDRFLAG_RESPONSE;
308 
309 		vmbus_sendpacket(channel, hbeat_txf_buf,
310 				       recvlen, requestid,
311 				       VM_PKT_DATA_INBAND, 0);
312 	}
313 }
314 
315 static int util_probe(struct hv_device *dev,
316 			const struct hv_vmbus_device_id *dev_id)
317 {
318 	struct hv_util_service *srv =
319 		(struct hv_util_service *)dev_id->driver_data;
320 	int ret;
321 
322 	srv->recv_buffer = kmalloc(PAGE_SIZE * 4, GFP_KERNEL);
323 	if (!srv->recv_buffer)
324 		return -ENOMEM;
325 	if (srv->util_init) {
326 		ret = srv->util_init(srv);
327 		if (ret) {
328 			ret = -ENODEV;
329 			goto error1;
330 		}
331 	}
332 
333 	/*
334 	 * The set of services managed by the util driver are not performance
335 	 * critical and do not need batched reading. Furthermore, some services
336 	 * such as KVP can only handle one message from the host at a time.
337 	 * Turn off batched reading for all util drivers before we open the
338 	 * channel.
339 	 */
340 
341 	set_channel_read_state(dev->channel, false);
342 
343 	hv_set_drvdata(dev, srv);
344 
345 	/*
346 	 * Based on the host; initialize the framework and
347 	 * service version numbers we will negotiate.
348 	 */
349 	switch (vmbus_proto_version) {
350 	case (VERSION_WS2008):
351 		util_fw_version = UTIL_WS2K8_FW_VERSION;
352 		sd_srv_version = SD_WS2008_VERSION;
353 		ts_srv_version = TS_WS2008_VERSION;
354 		hb_srv_version = HB_WS2008_VERSION;
355 		break;
356 
357 	default:
358 		util_fw_version = UTIL_FW_VERSION;
359 		sd_srv_version = SD_VERSION;
360 		ts_srv_version = TS_VERSION;
361 		hb_srv_version = HB_VERSION;
362 	}
363 
364 	ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
365 			srv->util_cb, dev->channel);
366 	if (ret)
367 		goto error;
368 
369 	return 0;
370 
371 error:
372 	if (srv->util_deinit)
373 		srv->util_deinit();
374 error1:
375 	kfree(srv->recv_buffer);
376 	return ret;
377 }
378 
379 static int util_remove(struct hv_device *dev)
380 {
381 	struct hv_util_service *srv = hv_get_drvdata(dev);
382 
383 	if (srv->util_deinit)
384 		srv->util_deinit();
385 	vmbus_close(dev->channel);
386 	kfree(srv->recv_buffer);
387 
388 	return 0;
389 }
390 
391 static const struct hv_vmbus_device_id id_table[] = {
392 	/* Shutdown guid */
393 	{ HV_SHUTDOWN_GUID,
394 	  .driver_data = (unsigned long)&util_shutdown
395 	},
396 	/* Time synch guid */
397 	{ HV_TS_GUID,
398 	  .driver_data = (unsigned long)&util_timesynch
399 	},
400 	/* Heartbeat guid */
401 	{ HV_HEART_BEAT_GUID,
402 	  .driver_data = (unsigned long)&util_heartbeat
403 	},
404 	/* KVP guid */
405 	{ HV_KVP_GUID,
406 	  .driver_data = (unsigned long)&util_kvp
407 	},
408 	/* VSS GUID */
409 	{ HV_VSS_GUID,
410 	  .driver_data = (unsigned long)&util_vss
411 	},
412 	/* File copy GUID */
413 	{ HV_FCOPY_GUID,
414 	  .driver_data = (unsigned long)&util_fcopy
415 	},
416 	{ },
417 };
418 
419 MODULE_DEVICE_TABLE(vmbus, id_table);
420 
421 /* The one and only one */
422 static  struct hv_driver util_drv = {
423 	.name = "hv_util",
424 	.id_table = id_table,
425 	.probe =  util_probe,
426 	.remove =  util_remove,
427 };
428 
429 static int __init init_hyperv_utils(void)
430 {
431 	pr_info("Registering HyperV Utility Driver\n");
432 
433 	return vmbus_driver_register(&util_drv);
434 }
435 
436 static void exit_hyperv_utils(void)
437 {
438 	pr_info("De-Registered HyperV Utility Driver\n");
439 
440 	vmbus_driver_unregister(&util_drv);
441 }
442 
443 module_init(init_hyperv_utils);
444 module_exit(exit_hyperv_utils);
445 
446 MODULE_DESCRIPTION("Hyper-V Utilities");
447 MODULE_LICENSE("GPL");
448