hv.c revision 5a0e3ad6af8660be21ca98a971cd00f331318c05
1/* 2 * Copyright (c) 2009, 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 */ 22#include <linux/kernel.h> 23#include <linux/mm.h> 24#include <linux/slab.h> 25#include <linux/vmalloc.h> 26#include "osd.h" 27#include "logging.h" 28#include "VmbusPrivate.h" 29 30/* The one and only */ 31struct hv_context gHvContext = { 32 .SynICInitialized = false, 33 .HypercallPage = NULL, 34 .SignalEventParam = NULL, 35 .SignalEventBuffer = NULL, 36}; 37 38/** 39 * HvQueryHypervisorPresence - Query the cpuid for presense of windows hypervisor 40 */ 41static int HvQueryHypervisorPresence(void) 42{ 43 unsigned int eax; 44 unsigned int ebx; 45 unsigned int ecx; 46 unsigned int edx; 47 unsigned int op; 48 49 eax = 0; 50 ebx = 0; 51 ecx = 0; 52 edx = 0; 53 op = HvCpuIdFunctionVersionAndFeatures; 54 cpuid(op, &eax, &ebx, &ecx, &edx); 55 56 return ecx & HV_PRESENT_BIT; 57} 58 59/** 60 * HvQueryHypervisorInfo - Get version info of the windows hypervisor 61 */ 62static int HvQueryHypervisorInfo(void) 63{ 64 unsigned int eax; 65 unsigned int ebx; 66 unsigned int ecx; 67 unsigned int edx; 68 unsigned int maxLeaf; 69 unsigned int op; 70 71 /* 72 * Its assumed that this is called after confirming that Viridian 73 * is present. Query id and revision. 74 */ 75 eax = 0; 76 ebx = 0; 77 ecx = 0; 78 edx = 0; 79 op = HvCpuIdFunctionHvVendorAndMaxFunction; 80 cpuid(op, &eax, &ebx, &ecx, &edx); 81 82 DPRINT_INFO(VMBUS, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c", 83 (ebx & 0xFF), 84 ((ebx >> 8) & 0xFF), 85 ((ebx >> 16) & 0xFF), 86 ((ebx >> 24) & 0xFF), 87 (ecx & 0xFF), 88 ((ecx >> 8) & 0xFF), 89 ((ecx >> 16) & 0xFF), 90 ((ecx >> 24) & 0xFF), 91 (edx & 0xFF), 92 ((edx >> 8) & 0xFF), 93 ((edx >> 16) & 0xFF), 94 ((edx >> 24) & 0xFF)); 95 96 maxLeaf = eax; 97 eax = 0; 98 ebx = 0; 99 ecx = 0; 100 edx = 0; 101 op = HvCpuIdFunctionHvInterface; 102 cpuid(op, &eax, &ebx, &ecx, &edx); 103 104 DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c", 105 (eax & 0xFF), 106 ((eax >> 8) & 0xFF), 107 ((eax >> 16) & 0xFF), 108 ((eax >> 24) & 0xFF)); 109 110 if (maxLeaf >= HvCpuIdFunctionMsHvVersion) { 111 eax = 0; 112 ebx = 0; 113 ecx = 0; 114 edx = 0; 115 op = HvCpuIdFunctionMsHvVersion; 116 cpuid(op, &eax, &ebx, &ecx, &edx); 117 DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",\ 118 eax, 119 ebx >> 16, 120 ebx & 0xFFFF, 121 ecx, 122 edx >> 24, 123 edx & 0xFFFFFF); 124 } 125 return maxLeaf; 126} 127 128/** 129 * HvDoHypercall - Invoke the specified hypercall 130 */ 131static u64 HvDoHypercall(u64 Control, void *Input, void *Output) 132{ 133#ifdef CONFIG_X86_64 134 u64 hvStatus = 0; 135 u64 inputAddress = (Input) ? virt_to_phys(Input) : 0; 136 u64 outputAddress = (Output) ? virt_to_phys(Output) : 0; 137 volatile void *hypercallPage = gHvContext.HypercallPage; 138 139 DPRINT_DBG(VMBUS, "Hypercall <control %llx input phys %llx virt %p " 140 "output phys %llx virt %p hypercall %p>", 141 Control, inputAddress, Input, 142 outputAddress, Output, hypercallPage); 143 144 __asm__ __volatile__("mov %0, %%r8" : : "r" (outputAddress) : "r8"); 145 __asm__ __volatile__("call *%3" : "=a" (hvStatus) : 146 "c" (Control), "d" (inputAddress), 147 "m" (hypercallPage)); 148 149 DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatus); 150 151 return hvStatus; 152 153#else 154 155 u32 controlHi = Control >> 32; 156 u32 controlLo = Control & 0xFFFFFFFF; 157 u32 hvStatusHi = 1; 158 u32 hvStatusLo = 1; 159 u64 inputAddress = (Input) ? virt_to_phys(Input) : 0; 160 u32 inputAddressHi = inputAddress >> 32; 161 u32 inputAddressLo = inputAddress & 0xFFFFFFFF; 162 u64 outputAddress = (Output) ? virt_to_phys(Output) : 0; 163 u32 outputAddressHi = outputAddress >> 32; 164 u32 outputAddressLo = outputAddress & 0xFFFFFFFF; 165 volatile void *hypercallPage = gHvContext.HypercallPage; 166 167 DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>", 168 Control, Input, Output); 169 170 __asm__ __volatile__ ("call *%8" : "=d"(hvStatusHi), 171 "=a"(hvStatusLo) : "d" (controlHi), 172 "a" (controlLo), "b" (inputAddressHi), 173 "c" (inputAddressLo), "D"(outputAddressHi), 174 "S"(outputAddressLo), "m" (hypercallPage)); 175 176 DPRINT_DBG(VMBUS, "Hypercall <return %llx>", 177 hvStatusLo | ((u64)hvStatusHi << 32)); 178 179 return hvStatusLo | ((u64)hvStatusHi << 32); 180#endif /* !x86_64 */ 181} 182 183/** 184 * HvInit - Main initialization routine. 185 * 186 * This routine must be called before any other routines in here are called 187 */ 188int HvInit(void) 189{ 190 int ret = 0; 191 int maxLeaf; 192 union hv_x64_msr_hypercall_contents hypercallMsr; 193 void *virtAddr = NULL; 194 195 DPRINT_ENTER(VMBUS); 196 197 memset(gHvContext.synICEventPage, 0, sizeof(void *) * MAX_NUM_CPUS); 198 memset(gHvContext.synICMessagePage, 0, sizeof(void *) * MAX_NUM_CPUS); 199 200 if (!HvQueryHypervisorPresence()) { 201 DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!"); 202 goto Cleanup; 203 } 204 205 DPRINT_INFO(VMBUS, 206 "Windows hypervisor detected! Retrieving more info..."); 207 208 maxLeaf = HvQueryHypervisorInfo(); 209 /* HvQueryHypervisorFeatures(maxLeaf); */ 210 211 /* 212 * We only support running on top of Hyper-V 213 */ 214 rdmsrl(HV_X64_MSR_GUEST_OS_ID, gHvContext.GuestId); 215 216 if (gHvContext.GuestId != 0) { 217 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", 218 gHvContext.GuestId); 219 goto Cleanup; 220 } 221 222 /* Write our OS info */ 223 wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID); 224 gHvContext.GuestId = HV_LINUX_GUEST_ID; 225 226 /* See if the hypercall page is already set */ 227 rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); 228 229 /* 230 * Allocate the hypercall page memory 231 * virtAddr = osd_PageAlloc(1); 232 */ 233 virtAddr = osd_VirtualAllocExec(PAGE_SIZE); 234 235 if (!virtAddr) { 236 DPRINT_ERR(VMBUS, 237 "unable to allocate hypercall page!!"); 238 goto Cleanup; 239 } 240 241 hypercallMsr.Enable = 1; 242 243 hypercallMsr.GuestPhysicalAddress = vmalloc_to_pfn(virtAddr); 244 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); 245 246 /* Confirm that hypercall page did get setup. */ 247 hypercallMsr.AsUINT64 = 0; 248 rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); 249 250 if (!hypercallMsr.Enable) { 251 DPRINT_ERR(VMBUS, "unable to set hypercall page!!"); 252 goto Cleanup; 253 } 254 255 gHvContext.HypercallPage = virtAddr; 256 257 DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx", 258 gHvContext.HypercallPage, 259 (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT); 260 261 /* Setup the global signal event param for the signal event hypercall */ 262 gHvContext.SignalEventBuffer = 263 kmalloc(sizeof(struct hv_input_signal_event_buffer), 264 GFP_KERNEL); 265 if (!gHvContext.SignalEventBuffer) 266 goto Cleanup; 267 268 gHvContext.SignalEventParam = 269 (struct hv_input_signal_event *) 270 (ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer, 271 HV_HYPERCALL_PARAM_ALIGN)); 272 gHvContext.SignalEventParam->ConnectionId.Asu32 = 0; 273 gHvContext.SignalEventParam->ConnectionId.u.Id = 274 VMBUS_EVENT_CONNECTION_ID; 275 gHvContext.SignalEventParam->FlagNumber = 0; 276 gHvContext.SignalEventParam->RsvdZ = 0; 277 278 DPRINT_EXIT(VMBUS); 279 280 return ret; 281 282Cleanup: 283 if (virtAddr) { 284 if (hypercallMsr.Enable) { 285 hypercallMsr.AsUINT64 = 0; 286 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); 287 } 288 289 vfree(virtAddr); 290 } 291 ret = -1; 292 DPRINT_EXIT(VMBUS); 293 294 return ret; 295} 296 297/** 298 * HvCleanup - Cleanup routine. 299 * 300 * This routine is called normally during driver unloading or exiting. 301 */ 302void HvCleanup(void) 303{ 304 union hv_x64_msr_hypercall_contents hypercallMsr; 305 306 DPRINT_ENTER(VMBUS); 307 308 if (gHvContext.SignalEventBuffer) { 309 gHvContext.SignalEventBuffer = NULL; 310 gHvContext.SignalEventParam = NULL; 311 kfree(gHvContext.SignalEventBuffer); 312 } 313 314 if (gHvContext.HypercallPage) { 315 hypercallMsr.AsUINT64 = 0; 316 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64); 317 vfree(gHvContext.HypercallPage); 318 gHvContext.HypercallPage = NULL; 319 } 320 321 DPRINT_EXIT(VMBUS); 322} 323 324/** 325 * HvPostMessage - Post a message using the hypervisor message IPC. 326 * 327 * This involves a hypercall. 328 */ 329u16 HvPostMessage(union hv_connection_id connectionId, 330 enum hv_message_type messageType, 331 void *payload, size_t payloadSize) 332{ 333 struct alignedInput { 334 u64 alignment8; 335 struct hv_input_post_message msg; 336 }; 337 338 struct hv_input_post_message *alignedMsg; 339 u16 status; 340 unsigned long addr; 341 342 if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT) 343 return -1; 344 345 addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC); 346 if (!addr) 347 return -1; 348 349 alignedMsg = (struct hv_input_post_message *) 350 (ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN)); 351 352 alignedMsg->ConnectionId = connectionId; 353 alignedMsg->MessageType = messageType; 354 alignedMsg->PayloadSize = payloadSize; 355 memcpy((void *)alignedMsg->Payload, payload, payloadSize); 356 357 status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF; 358 359 kfree((void *)addr); 360 361 return status; 362} 363 364 365/** 366 * HvSignalEvent - Signal an event on the specified connection using the hypervisor event IPC. 367 * 368 * This involves a hypercall. 369 */ 370u16 HvSignalEvent(void) 371{ 372 u16 status; 373 374 status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, 375 NULL) & 0xFFFF; 376 return status; 377} 378 379/** 380 * HvSynicInit - Initialize the Synthethic Interrupt Controller. 381 * 382 * If it is already initialized by another entity (ie x2v shim), we need to 383 * retrieve the initialized message and event pages. Otherwise, we create and 384 * initialize the message and event pages. 385 */ 386void HvSynicInit(void *irqarg) 387{ 388 u64 version; 389 union hv_synic_simp simp; 390 union hv_synic_siefp siefp; 391 union hv_synic_sint sharedSint; 392 union hv_synic_scontrol sctrl; 393 394 u32 irqVector = *((u32 *)(irqarg)); 395 int cpu = smp_processor_id(); 396 397 DPRINT_ENTER(VMBUS); 398 399 if (!gHvContext.HypercallPage) { 400 DPRINT_EXIT(VMBUS); 401 return; 402 } 403 404 /* Check the version */ 405 rdmsrl(HV_X64_MSR_SVERSION, version); 406 407 DPRINT_INFO(VMBUS, "SynIC version: %llx", version); 408 409 gHvContext.synICMessagePage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); 410 411 if (gHvContext.synICMessagePage[cpu] == NULL) { 412 DPRINT_ERR(VMBUS, 413 "unable to allocate SYNIC message page!!"); 414 goto Cleanup; 415 } 416 417 gHvContext.synICEventPage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC); 418 419 if (gHvContext.synICEventPage[cpu] == NULL) { 420 DPRINT_ERR(VMBUS, 421 "unable to allocate SYNIC event page!!"); 422 goto Cleanup; 423 } 424 425 /* Setup the Synic's message page */ 426 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); 427 simp.SimpEnabled = 1; 428 simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[cpu]) 429 >> PAGE_SHIFT; 430 431 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64); 432 433 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); 434 435 /* Setup the Synic's event page */ 436 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); 437 siefp.SiefpEnabled = 1; 438 siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[cpu]) 439 >> PAGE_SHIFT; 440 441 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64); 442 443 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); 444 445 /* Setup the interception SINT. */ 446 /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */ 447 /* interceptionSint.AsUINT64); */ 448 449 /* Setup the shared SINT. */ 450 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); 451 452 sharedSint.AsUINT64 = 0; 453 sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */ 454 sharedSint.Masked = false; 455 sharedSint.AutoEoi = true; 456 457 DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", 458 sharedSint.AsUINT64); 459 460 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); 461 462 /* Enable the global synic bit */ 463 rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64); 464 sctrl.Enable = 1; 465 466 wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64); 467 468 gHvContext.SynICInitialized = true; 469 470 DPRINT_EXIT(VMBUS); 471 472 return; 473 474Cleanup: 475 if (gHvContext.synICEventPage[cpu]) 476 osd_PageFree(gHvContext.synICEventPage[cpu], 1); 477 478 if (gHvContext.synICMessagePage[cpu]) 479 osd_PageFree(gHvContext.synICMessagePage[cpu], 1); 480 481 DPRINT_EXIT(VMBUS); 482 return; 483} 484 485/** 486 * HvSynicCleanup - Cleanup routine for HvSynicInit(). 487 */ 488void HvSynicCleanup(void *arg) 489{ 490 union hv_synic_sint sharedSint; 491 union hv_synic_simp simp; 492 union hv_synic_siefp siefp; 493 int cpu = smp_processor_id(); 494 495 DPRINT_ENTER(VMBUS); 496 497 if (!gHvContext.SynICInitialized) { 498 DPRINT_EXIT(VMBUS); 499 return; 500 } 501 502 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); 503 504 sharedSint.Masked = 1; 505 506 /* Need to correctly cleanup in the case of SMP!!! */ 507 /* Disable the interrupt */ 508 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64); 509 510 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); 511 simp.SimpEnabled = 0; 512 simp.BaseSimpGpa = 0; 513 514 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64); 515 516 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); 517 siefp.SiefpEnabled = 0; 518 siefp.BaseSiefpGpa = 0; 519 520 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64); 521 522 osd_PageFree(gHvContext.synICMessagePage[cpu], 1); 523 osd_PageFree(gHvContext.synICEventPage[cpu], 1); 524 525 DPRINT_EXIT(VMBUS); 526} 527