]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/staging/hv/hv_utils.c
Merge branch 'staging-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[karo-tx-linux.git] / drivers / staging / hv / hv_utils.c
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 #include <linux/kernel.h>
22 #include <linux/init.h>
23 #include <linux/module.h>
24 #include <linux/slab.h>
25 #include <linux/sysctl.h>
26 #include <linux/reboot.h>
27 #include <linux/dmi.h>
28 #include <linux/pci.h>
29
30 #include "logging.h"
31 #include "osd.h"
32 #include "vmbus.h"
33 #include "vmbus_packet_format.h"
34 #include "vmbus_channel_interface.h"
35 #include "version_info.h"
36 #include "channel.h"
37 #include "vmbus_private.h"
38 #include "vmbus_api.h"
39 #include "utils.h"
40
41
42 static void shutdown_onchannelcallback(void *context)
43 {
44         struct vmbus_channel *channel = context;
45         u8 *buf;
46         u32 buflen, recvlen;
47         u64 requestid;
48         u8  execute_shutdown = false;
49
50         struct shutdown_msg_data *shutdown_msg;
51
52         struct icmsg_hdr *icmsghdrp;
53         struct icmsg_negotiate *negop = NULL;
54
55         buflen = PAGE_SIZE;
56         buf = kmalloc(buflen, GFP_ATOMIC);
57
58         vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
59
60         if (recvlen > 0) {
61                 DPRINT_DBG(VMBUS, "shutdown packet: len=%d, requestid=%lld",
62                            recvlen, requestid);
63
64                 icmsghdrp = (struct icmsg_hdr *)&buf[
65                         sizeof(struct vmbuspipe_hdr)];
66
67                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
68                         prep_negotiate_resp(icmsghdrp, negop, buf);
69                 } else {
70                         shutdown_msg = (struct shutdown_msg_data *)&buf[
71                                 sizeof(struct vmbuspipe_hdr) +
72                                 sizeof(struct icmsg_hdr)];
73
74                         switch (shutdown_msg->flags) {
75                         case 0:
76                         case 1:
77                                 icmsghdrp->status = HV_S_OK;
78                                 execute_shutdown = true;
79
80                                 DPRINT_INFO(VMBUS, "Shutdown request received -"
81                                             " gracefull shutdown initiated");
82                                 break;
83                         default:
84                                 icmsghdrp->status = HV_E_FAIL;
85                                 execute_shutdown = false;
86
87                                 DPRINT_INFO(VMBUS, "Shutdown request received -"
88                                             " Invalid request");
89                                 break;
90                         };
91                 }
92
93                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
94                         | ICMSGHDRFLAG_RESPONSE;
95
96                 vmbus_sendpacket(channel, buf,
97                                        recvlen, requestid,
98                                        VmbusPacketTypeDataInBand, 0);
99         }
100
101         kfree(buf);
102
103         if (execute_shutdown == true)
104                 orderly_poweroff(false);
105 }
106
107 /*
108  * Set guest time to host UTC time.
109  */
110 static inline void do_adj_guesttime(u64 hosttime)
111 {
112         s64 host_tns;
113         struct timespec host_ts;
114
115         host_tns = (hosttime - WLTIMEDELTA) * 100;
116         host_ts = ns_to_timespec(host_tns);
117
118         do_settimeofday(&host_ts);
119 }
120
121 /*
122  * Synchronize time with host after reboot, restore, etc.
123  *
124  * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
125  * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
126  * message after the timesync channel is opened. Since the hv_utils module is
127  * loaded after hv_vmbus, the first message is usually missed. The other
128  * thing is, systime is automatically set to emulated hardware clock which may
129  * not be UTC time or in the same time zone. So, to override these effects, we
130  * use the first 50 time samples for initial system time setting.
131  */
132 static inline void adj_guesttime(u64 hosttime, u8 flags)
133 {
134         static s32 scnt = 50;
135
136         if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
137                 do_adj_guesttime(hosttime);
138                 return;
139         }
140
141         if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
142                 scnt--;
143                 do_adj_guesttime(hosttime);
144         }
145 }
146
147 /*
148  * Time Sync Channel message handler.
149  */
150 static void timesync_onchannelcallback(void *context)
151 {
152         struct vmbus_channel *channel = context;
153         u8 *buf;
154         u32 buflen, recvlen;
155         u64 requestid;
156         struct icmsg_hdr *icmsghdrp;
157         struct ictimesync_data *timedatap;
158
159         buflen = PAGE_SIZE;
160         buf = kmalloc(buflen, GFP_ATOMIC);
161
162         vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
163
164         if (recvlen > 0) {
165                 DPRINT_DBG(VMBUS, "timesync packet: recvlen=%d, requestid=%lld",
166                         recvlen, requestid);
167
168                 icmsghdrp = (struct icmsg_hdr *)&buf[
169                                 sizeof(struct vmbuspipe_hdr)];
170
171                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
172                         prep_negotiate_resp(icmsghdrp, NULL, buf);
173                 } else {
174                         timedatap = (struct ictimesync_data *)&buf[
175                                 sizeof(struct vmbuspipe_hdr) +
176                                 sizeof(struct icmsg_hdr)];
177                         adj_guesttime(timedatap->parenttime, timedatap->flags);
178                 }
179
180                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
181                         | ICMSGHDRFLAG_RESPONSE;
182
183                 vmbus_sendpacket(channel, buf,
184                                 recvlen, requestid,
185                                 VmbusPacketTypeDataInBand, 0);
186         }
187
188         kfree(buf);
189 }
190
191 /*
192  * Heartbeat functionality.
193  * Every two seconds, Hyper-V send us a heartbeat request message.
194  * we respond to this message, and Hyper-V knows we are alive.
195  */
196 static void heartbeat_onchannelcallback(void *context)
197 {
198         struct vmbus_channel *channel = context;
199         u8 *buf;
200         u32 buflen, recvlen;
201         u64 requestid;
202         struct icmsg_hdr *icmsghdrp;
203         struct heartbeat_msg_data *heartbeat_msg;
204
205         buflen = PAGE_SIZE;
206         buf = kmalloc(buflen, GFP_ATOMIC);
207
208         vmbus_recvpacket(channel, buf, buflen, &recvlen, &requestid);
209
210         if (recvlen > 0) {
211                 DPRINT_DBG(VMBUS, "heartbeat packet: len=%d, requestid=%lld",
212                            recvlen, requestid);
213
214                 icmsghdrp = (struct icmsg_hdr *)&buf[
215                                 sizeof(struct vmbuspipe_hdr)];
216
217                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
218                         prep_negotiate_resp(icmsghdrp, NULL, buf);
219                 } else {
220                         heartbeat_msg = (struct heartbeat_msg_data *)&buf[
221                                 sizeof(struct vmbuspipe_hdr) +
222                                 sizeof(struct icmsg_hdr)];
223
224                         DPRINT_DBG(VMBUS, "heartbeat seq = %lld",
225                                    heartbeat_msg->seq_num);
226
227                         heartbeat_msg->seq_num += 1;
228                 }
229
230                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
231                         | ICMSGHDRFLAG_RESPONSE;
232
233                 vmbus_sendpacket(channel, buf,
234                                        recvlen, requestid,
235                                        VmbusPacketTypeDataInBand, 0);
236         }
237
238         kfree(buf);
239 }
240
241 static const struct pci_device_id __initconst
242 hv_utils_pci_table[] __maybe_unused = {
243         { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
244         { 0 }
245 };
246 MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
247
248
249 static const struct dmi_system_id __initconst
250 hv_utils_dmi_table[] __maybe_unused  = {
251         {
252                 .ident = "Hyper-V",
253                 .matches = {
254                         DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
255                         DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
256                         DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
257                 },
258         },
259         { },
260 };
261 MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
262
263
264 static int __init init_hyperv_utils(void)
265 {
266         printk(KERN_INFO "Registering HyperV Utility Driver\n");
267
268         if (!dmi_check_system(hv_utils_dmi_table))
269                 return -ENODEV;
270
271         hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
272                 &shutdown_onchannelcallback;
273         hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
274
275         hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
276                 &timesync_onchannelcallback;
277         hv_cb_utils[HV_TIMESYNC_MSG].callback = &timesync_onchannelcallback;
278
279         hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
280                 &heartbeat_onchannelcallback;
281         hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
282
283         return 0;
284 }
285
286 static void exit_hyperv_utils(void)
287 {
288         printk(KERN_INFO "De-Registered HyperV Utility Driver\n");
289
290         hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
291                 &chn_cb_negotiate;
292         hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate;
293
294         hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
295                 &chn_cb_negotiate;
296         hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate;
297
298         hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
299                 &chn_cb_negotiate;
300         hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
301 }
302
303 module_init(init_hyperv_utils);
304 module_exit(exit_hyperv_utils);
305
306 MODULE_DESCRIPTION("Hyper-V Utilities");
307 MODULE_VERSION(HV_DRV_VERSION);
308 MODULE_LICENSE("GPL");