3 * Copyright (c) 2009, Microsoft Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307 USA.
19 * Haiyang Zhang <haiyangz@microsoft.com>
20 * Hank Janssen <hjanssen@microsoft.com>
24 #include <linux/kernel.h>
26 #include <linux/vmalloc.h>
29 #include "VmbusPrivate.h"
33 /* The one and only */
34 struct hv_context gHvContext = {
35 .SynICInitialized = false,
36 .HypercallPage = NULL,
37 .SignalEventParam = NULL,
38 .SignalEventBuffer = NULL,
45 HvQueryHypervisorPresence()
48 Query the cpuid for presense of windows hypervisor
52 HvQueryHypervisorPresence (
66 op = HvCpuIdFunctionVersionAndFeatures;
67 cpuid(op, &eax, &ebx, &ecx, &edx);
69 return (ecx & HV_PRESENT_BIT);
76 HvQueryHypervisorInfo()
79 Get version info of the windows hypervisor
83 HvQueryHypervisorInfo (
95 * Its assumed that this is called after confirming that Viridian
96 * is present. Query id and revision.
104 op = HvCpuIdFunctionHvVendorAndMaxFunction;
105 cpuid(op, &eax, &ebx, &ecx, &edx);
107 DPRINT_INFO(VMBUS, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c",
110 ((ebx >> 16) & 0xFF),
111 ((ebx >> 24) & 0xFF),
114 ((ecx >> 16) & 0xFF),
115 ((ecx >> 24) & 0xFF),
118 ((edx >> 16) & 0xFF),
119 ((edx >> 24) & 0xFF));
126 op = HvCpuIdFunctionHvInterface;
127 cpuid(op, &eax, &ebx, &ecx, &edx);
129 DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c",
132 ((eax >> 16) & 0xFF),
133 ((eax >> 24) & 0xFF));
135 if (maxLeaf >= HvCpuIdFunctionMsHvVersion) {
140 op = HvCpuIdFunctionMsHvVersion;
141 cpuid(op, &eax, &ebx, &ecx, &edx);
142 DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",
160 Invoke the specified hypercall
172 u64 inputAddress = (Input)? virt_to_phys(Input) : 0;
173 u64 outputAddress = (Output)? virt_to_phys(Output) : 0;
174 volatile void* hypercallPage = gHvContext.HypercallPage;
176 DPRINT_DBG(VMBUS, "Hypercall <control %llx input phys %llx virt %p output phys %llx virt %p hypercall %p>",
184 __asm__ __volatile__ ("mov %0, %%r8" : : "r" (outputAddress): "r8");
185 __asm__ __volatile__ ("call *%3" : "=a"(hvStatus): "c" (Control), "d" (inputAddress), "m" (hypercallPage));
187 DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatus);
193 u32 controlHi = Control >> 32;
194 u32 controlLo = Control & 0xFFFFFFFF;
197 u64 inputAddress = (Input) ? virt_to_phys(Input) : 0;
198 u32 inputAddressHi = inputAddress >> 32;
199 u32 inputAddressLo = inputAddress & 0xFFFFFFFF;
200 u64 outputAddress = (Output) ? virt_to_phys(Output) : 0;
201 u32 outputAddressHi = outputAddress >> 32;
202 u32 outputAddressLo = outputAddress & 0xFFFFFFFF;
203 volatile void* hypercallPage = gHvContext.HypercallPage;
205 DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>",
210 __asm__ __volatile__ ("call *%8" : "=d"(hvStatusHi), "=a"(hvStatusLo) : "d" (controlHi), "a" (controlLo), "b" (inputAddressHi), "c" (inputAddressLo), "D"(outputAddressHi), "S"(outputAddressLo), "m" (hypercallPage));
213 DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatusLo | ((u64)hvStatusHi << 32));
215 return (hvStatusLo | ((u64)hvStatusHi << 32));
225 Main initialization routine. This routine must be called
226 before any other routines in here are called
233 union hv_x64_msr_hypercall_contents hypercallMsr;
234 void *virtAddr = NULL;
238 memset(gHvContext.synICEventPage, 0, sizeof(void *) * MAX_NUM_CPUS);
239 memset(gHvContext.synICMessagePage, 0, sizeof(void *) * MAX_NUM_CPUS);
241 if (!HvQueryHypervisorPresence())
243 DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!");
247 DPRINT_INFO(VMBUS, "Windows hypervisor detected! Retrieving more info...");
249 maxLeaf = HvQueryHypervisorInfo();
250 /* HvQueryHypervisorFeatures(maxLeaf); */
252 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
253 rdmsrl(HV_X64_MSR_GUEST_OS_ID, gHvContext.GuestId);
254 if (gHvContext.GuestId == 0)
256 /* Write our OS info */
257 wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
259 gHvContext.GuestId = HV_LINUX_GUEST_ID;
262 /* See if the hypercall page is already set */
263 rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
264 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
266 /* Allocate the hypercall page memory */
267 /* virtAddr = osd_PageAlloc(1); */
268 virtAddr = osd_VirtualAllocExec(PAGE_SIZE);
272 DPRINT_ERR(VMBUS, "unable to allocate hypercall page!!");
276 hypercallMsr.Enable = 1;
277 /* hypercallMsr.GuestPhysicalAddress = virt_to_phys(virtAddr) >> PAGE_SHIFT; */
278 hypercallMsr.GuestPhysicalAddress = vmalloc_to_pfn(virtAddr);
279 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
281 /* Confirm that hypercall page did get setup. */
282 hypercallMsr.AsUINT64 = 0;
283 rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
284 if (!hypercallMsr.Enable)
286 DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
290 gHvContext.HypercallPage = virtAddr;
294 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", gHvContext.GuestId);
298 DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
299 gHvContext.HypercallPage,
300 (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
302 /* Setup the global signal event param for the signal event hypercall */
303 gHvContext.SignalEventBuffer = kmalloc(sizeof(struct hv_input_signal_event_buffer), GFP_KERNEL);
304 if (!gHvContext.SignalEventBuffer)
309 gHvContext.SignalEventParam = (struct hv_input_signal_event *)(ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer, HV_HYPERCALL_PARAM_ALIGN));
310 gHvContext.SignalEventParam->ConnectionId.Asu32 = 0;
311 gHvContext.SignalEventParam->ConnectionId.u.Id = VMBUS_EVENT_CONNECTION_ID;
312 gHvContext.SignalEventParam->FlagNumber = 0;
313 gHvContext.SignalEventParam->RsvdZ = 0;
315 /* DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId()); */
324 if (hypercallMsr.Enable)
326 hypercallMsr.AsUINT64 = 0;
327 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
345 Cleanup routine. This routine is called normally during driver unloading or exiting.
348 void HvCleanup (void)
350 union hv_x64_msr_hypercall_contents hypercallMsr;
354 if (gHvContext.SignalEventBuffer)
356 kfree(gHvContext.SignalEventBuffer);
357 gHvContext.SignalEventBuffer = NULL;
358 gHvContext.SignalEventParam = NULL;
361 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
363 if (gHvContext.HypercallPage)
365 hypercallMsr.AsUINT64 = 0;
366 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
367 vfree(gHvContext.HypercallPage);
368 gHvContext.HypercallPage = NULL;
383 Post a message using the hypervisor message IPC. This
384 involves a hypercall.
387 u16 HvPostMessage(union hv_connection_id connectionId,
388 enum hv_message_type messageType,
389 void *payload, size_t payloadSize)
391 struct alignedInput {
393 struct hv_input_post_message msg;
396 struct hv_input_post_message *alignedMsg;
400 if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
405 addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
412 alignedMsg = (struct hv_input_post_message *)(ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
414 alignedMsg->ConnectionId = connectionId;
415 alignedMsg->MessageType = messageType;
416 alignedMsg->PayloadSize = payloadSize;
417 memcpy((void*)alignedMsg->Payload, payload, payloadSize);
419 status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
433 Signal an event on the specified connection using the hypervisor event IPC. This
434 involves a hypercall.
437 u16 HvSignalEvent(void)
441 status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, NULL) & 0xFFFF;
453 Initialize the Synthethic Interrupt Controller. If it is already initialized by
454 another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
455 Otherwise, we create and initialize the message and event pages.
458 int HvSynicInit (u32 irqVector)
461 union hv_synic_simp simp;
462 union hv_synic_siefp siefp;
463 union hv_synic_sint sharedSint;
464 union hv_synic_scontrol sctrl;
470 if (!gHvContext.HypercallPage)
476 /* Check the version */
477 rdmsrl(HV_X64_MSR_SVERSION, version);
479 DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
481 /* TODO: Handle SMP */
482 if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
484 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
486 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
487 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
489 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
491 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
492 rdmsrl(HV_X64_MSR_GUEST_OS_ID, guestID);
493 if (guestID == HV_LINUX_GUEST_ID)
495 gHvContext.synICMessagePage[0] = phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT);
496 gHvContext.synICEventPage[0] = phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT);
500 DPRINT_ERR(VMBUS, "unknown guest id!!");
503 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
507 gHvContext.synICMessagePage[0] = osd_PageAlloc(1);
508 if (gHvContext.synICMessagePage[0] == NULL)
510 DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
514 gHvContext.synICEventPage[0] = osd_PageAlloc(1);
515 if (gHvContext.synICEventPage[0] == NULL)
517 DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
521 /* Setup the Synic's message page */
522 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
523 simp.SimpEnabled = 1;
524 simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
526 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
528 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
530 /* Setup the Synic's event page */
531 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
532 siefp.SiefpEnabled = 1;
533 siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
535 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
537 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
539 /* Setup the interception SINT. */
540 /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
541 /* interceptionSint.AsUINT64); */
543 /* Setup the shared SINT. */
544 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
546 sharedSint.AsUINT64 = 0;
547 sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
548 sharedSint.Masked = false;
549 sharedSint.AutoEoi = true;
551 DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
553 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
555 /* Enable the global synic bit */
556 rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
559 wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
561 gHvContext.SynICInitialized = true;
570 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
572 if (gHvContext.synICEventPage[0])
574 osd_PageFree(gHvContext.synICEventPage[0],1);
577 if (gHvContext.synICMessagePage[0])
579 osd_PageFree(gHvContext.synICMessagePage[0], 1);
595 Cleanup routine for HvSynicInit().
598 void HvSynicCleanup(void)
600 union hv_synic_sint sharedSint;
601 union hv_synic_simp simp;
602 union hv_synic_siefp siefp;
606 if (!gHvContext.SynICInitialized)
612 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
614 sharedSint.Masked = 1;
616 /* Disable the interrupt */
617 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
620 * Disable and free the resources only if we are running as
621 * native linux since in xenlinux, we are sharing the
622 * resources with the x2v shim
624 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
626 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
627 simp.SimpEnabled = 0;
628 simp.BaseSimpGpa = 0;
630 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
632 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
633 siefp.SiefpEnabled = 0;
634 siefp.BaseSiefpGpa = 0;
636 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
638 osd_PageFree(gHvContext.synICMessagePage[0], 1);
639 osd_PageFree(gHvContext.synICEventPage[0], 1);