Merge branch 'usb-linus' 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
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                 icmsghdrp = (struct icmsg_hdr *)&buf[
218                                 sizeof(struct vmbuspipe_hdr)];
219
220                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
221                         prep_negotiate_resp(icmsghdrp, NULL, buf);
222                 } else {
223                         heartbeat_msg = (struct heartbeat_msg_data *)&buf[
224                                 sizeof(struct vmbuspipe_hdr) +
225                                 sizeof(struct icmsg_hdr)];
226
227                         DPRINT_DBG(VMBUS, "heartbeat seq = %lld",
228                                    heartbeat_msg->seq_num);
229
230                         heartbeat_msg->seq_num += 1;
231                 }
232
233                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
234                         | ICMSGHDRFLAG_RESPONSE;
235
236                 vmbus_sendpacket(channel, buf,
237                                        recvlen, requestid,
238                                        VmbusPacketTypeDataInBand, 0);
239         }
240
241         kfree(buf);
242 }
243
244 static const struct pci_device_id __initconst
245 hv_utils_pci_table[] __maybe_unused = {
246         { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
247         { 0 }
248 };
249 MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
250
251
252 static const struct dmi_system_id __initconst
253 hv_utils_dmi_table[] __maybe_unused  = {
254         {
255                 .ident = "Hyper-V",
256                 .matches = {
257                         DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
258                         DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
259                         DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
260                 },
261         },
262         { },
263 };
264 MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
265
266
267 static int __init init_hyperv_utils(void)
268 {
269         printk(KERN_INFO "Registering HyperV Utility Driver\n");
270
271         if (!dmi_check_system(hv_utils_dmi_table))
272                 return -ENODEV;
273
274         hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
275                 &shutdown_onchannelcallback;
276         hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
277
278         hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
279                 &timesync_onchannelcallback;
280         hv_cb_utils[HV_TIMESYNC_MSG].callback = &timesync_onchannelcallback;
281
282         hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
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->OnChannelCallback =
294                 &chn_cb_negotiate;
295         hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate;
296
297         hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
298                 &chn_cb_negotiate;
299         hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate;
300
301         hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
302                 &chn_cb_negotiate;
303         hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
304 }
305
306 module_init(init_hyperv_utils);
307 module_exit(exit_hyperv_utils);
308
309 MODULE_DESCRIPTION("Hyper-V Utilities");
310 MODULE_VERSION(HV_DRV_VERSION);
311 MODULE_LICENSE("GPL");