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 42static 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 VmbusChannelRecvPacket(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 VmbusChannelSendPacket(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 */ 110static 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 */ 132static 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 */ 150static 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 VmbusChannelRecvPacket(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 VmbusChannelSendPacket(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 */ 196static 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 VmbusChannelRecvPacket(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 VmbusChannelSendPacket(channel, buf, 237 recvlen, requestid, 238 VmbusPacketTypeDataInBand, 0); 239 } 240 241 kfree(buf); 242} 243 244static const struct pci_device_id __initconst 245hv_utils_pci_table[] __maybe_unused = { 246 { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */ 247 { 0 } 248}; 249MODULE_DEVICE_TABLE(pci, hv_utils_pci_table); 250 251 252static const struct dmi_system_id __initconst 253hv_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}; 264MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table); 265 266 267static 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 ×ync_onchannelcallback; 280 hv_cb_utils[HV_TIMESYNC_MSG].callback = ×ync_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 289static 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 306module_init(init_hyperv_utils); 307module_exit(exit_hyperv_utils); 308 309MODULE_DESCRIPTION("Hyper-V Utilities"); 310MODULE_VERSION(HV_DRV_VERSION); 311MODULE_LICENSE("GPL"); 312