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