2
* Copyright (c) 2010, Microsoft Corporation.
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.
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
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.
18
* Haiyang Zhang <haiyangz@microsoft.com>
19
* Hank Janssen <hjanssen@microsoft.com>
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>
33
#include "vmbus_packet_format.h"
34
#include "vmbus_channel_interface.h"
35
#include "version_info.h"
37
#include "vmbus_private.h"
38
#include "vmbus_api.h"
41
static u8 *shut_txf_buf;
42
static u8 *time_txf_buf;
43
static u8 *hbeat_txf_buf;
45
static void shutdown_onchannelcallback(void *context)
47
struct vmbus_channel *channel = context;
50
u8 execute_shutdown = false;
52
struct shutdown_msg_data *shutdown_msg;
54
struct icmsg_hdr *icmsghdrp;
55
struct icmsg_negotiate *negop = NULL;
57
vmbus_recvpacket(channel, shut_txf_buf,
58
PAGE_SIZE, &recvlen, &requestid);
61
DPRINT_DBG(VMBUS, "shutdown packet: len=%d, requestid=%lld",
64
icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[
65
sizeof(struct vmbuspipe_hdr)];
67
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
68
prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf);
71
(struct shutdown_msg_data *)&shut_txf_buf[
72
sizeof(struct vmbuspipe_hdr) +
73
sizeof(struct icmsg_hdr)];
75
switch (shutdown_msg->flags) {
78
icmsghdrp->status = HV_S_OK;
79
execute_shutdown = true;
81
DPRINT_INFO(VMBUS, "Shutdown request received -"
82
" gracefull shutdown initiated");
85
icmsghdrp->status = HV_E_FAIL;
86
execute_shutdown = false;
88
DPRINT_INFO(VMBUS, "Shutdown request received -"
94
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
95
| ICMSGHDRFLAG_RESPONSE;
97
vmbus_sendpacket(channel, shut_txf_buf,
99
VmbusPacketTypeDataInBand, 0);
102
if (execute_shutdown == true)
103
orderly_poweroff(false);
107
* Set guest time to host UTC time.
109
static inline void do_adj_guesttime(u64 hosttime)
112
struct timespec host_ts;
114
host_tns = (hosttime - WLTIMEDELTA) * 100;
115
host_ts = ns_to_timespec(host_tns);
117
do_settimeofday(&host_ts);
121
* Synchronize time with host after reboot, restore, etc.
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.
131
static inline void adj_guesttime(u64 hosttime, u8 flags)
133
static s32 scnt = 50;
135
if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
136
do_adj_guesttime(hosttime);
140
if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
142
do_adj_guesttime(hosttime);
147
* Time Sync Channel message handler.
149
static void timesync_onchannelcallback(void *context)
151
struct vmbus_channel *channel = context;
154
struct icmsg_hdr *icmsghdrp;
155
struct ictimesync_data *timedatap;
157
vmbus_recvpacket(channel, time_txf_buf,
158
PAGE_SIZE, &recvlen, &requestid);
161
DPRINT_DBG(VMBUS, "timesync packet: recvlen=%d, requestid=%lld",
164
icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
165
sizeof(struct vmbuspipe_hdr)];
167
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
168
prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf);
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);
176
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
177
| ICMSGHDRFLAG_RESPONSE;
179
vmbus_sendpacket(channel, time_txf_buf,
181
VmbusPacketTypeDataInBand, 0);
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.
190
static void heartbeat_onchannelcallback(void *context)
192
struct vmbus_channel *channel = context;
195
struct icmsg_hdr *icmsghdrp;
196
struct heartbeat_msg_data *heartbeat_msg;
198
vmbus_recvpacket(channel, hbeat_txf_buf,
199
PAGE_SIZE, &recvlen, &requestid);
202
DPRINT_DBG(VMBUS, "heartbeat packet: len=%d, requestid=%lld",
205
icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
206
sizeof(struct vmbuspipe_hdr)];
208
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
209
prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf);
212
(struct heartbeat_msg_data *)&hbeat_txf_buf[
213
sizeof(struct vmbuspipe_hdr) +
214
sizeof(struct icmsg_hdr)];
216
DPRINT_DBG(VMBUS, "heartbeat seq = %lld",
217
heartbeat_msg->seq_num);
219
heartbeat_msg->seq_num += 1;
222
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
223
| ICMSGHDRFLAG_RESPONSE;
225
vmbus_sendpacket(channel, hbeat_txf_buf,
227
VmbusPacketTypeDataInBand, 0);
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 */
236
MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
239
static const struct dmi_system_id __initconst
240
hv_utils_dmi_table[] __maybe_unused = {
244
DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
245
DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
246
DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
251
MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
254
static int __init init_hyperv_utils(void)
256
printk(KERN_INFO "Registering HyperV Utility Driver\n");
258
if (!dmi_check_system(hv_utils_dmi_table))
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);
265
if (!shut_txf_buf || !time_txf_buf || !hbeat_txf_buf) {
267
"Unable to allocate memory for receive buffer\n");
270
kfree(hbeat_txf_buf);
274
hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback =
275
&shutdown_onchannelcallback;
276
hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
278
hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback =
279
×ync_onchannelcallback;
280
hv_cb_utils[HV_TIMESYNC_MSG].callback = ×ync_onchannelcallback;
282
hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback =
283
&heartbeat_onchannelcallback;
284
hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
289
static void exit_hyperv_utils(void)
291
printk(KERN_INFO "De-Registered HyperV Utility Driver\n");
293
hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback =
295
hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate;
297
hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback =
299
hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate;
301
hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback =
303
hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
307
kfree(hbeat_txf_buf);
310
module_init(init_hyperv_utils);
311
module_exit(exit_hyperv_utils);
313
MODULE_DESCRIPTION("Hyper-V Utilities");
314
MODULE_VERSION(HV_DRV_VERSION);
315
MODULE_LICENSE("GPL");