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/vmalloc.h>
26 #include "include/logging.h"
27 #include "VmbusPrivate.h"
31 /* The one and only */
32 HV_CONTEXT gHvContext
={
33 .SynICInitialized
= false,
34 .HypercallPage
= NULL
,
35 .SignalEventParam
= NULL
,
36 .SignalEventBuffer
= NULL
,
43 HvQueryHypervisorPresence()
46 Query the cpuid for presense of windows hypervisor
50 HvQueryHypervisorPresence (
64 op
= HvCpuIdFunctionVersionAndFeatures
;
65 cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
67 return (ecx
& HV_PRESENT_BIT
);
74 HvQueryHypervisorInfo()
77 Get version info of the windows hypervisor
81 HvQueryHypervisorInfo (
93 * Its assumed that this is called after confirming that Viridian
94 * is present. Query id and revision.
102 op
= HvCpuIdFunctionHvVendorAndMaxFunction
;
103 cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
105 DPRINT_INFO(VMBUS
, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c",
108 ((ebx
>> 16) & 0xFF),
109 ((ebx
>> 24) & 0xFF),
112 ((ecx
>> 16) & 0xFF),
113 ((ecx
>> 24) & 0xFF),
116 ((edx
>> 16) & 0xFF),
117 ((edx
>> 24) & 0xFF));
124 op
= HvCpuIdFunctionHvInterface
;
125 cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
127 DPRINT_INFO(VMBUS
, "Interface ID: %c%c%c%c",
130 ((eax
>> 16) & 0xFF),
131 ((eax
>> 24) & 0xFF));
133 if (maxLeaf
>= HvCpuIdFunctionMsHvVersion
) {
138 op
= HvCpuIdFunctionMsHvVersion
;
139 cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
140 DPRINT_INFO(VMBUS
, "OS Build:%d-%d.%d-%d-%d.%d",
158 Invoke the specified hypercall
170 u64 inputAddress
= (Input
)? virt_to_phys(Input
) : 0;
171 u64 outputAddress
= (Output
)? virt_to_phys(Output
) : 0;
172 volatile void* hypercallPage
= gHvContext
.HypercallPage
;
174 DPRINT_DBG(VMBUS
, "Hypercall <control %llx input phys %llx virt %p output phys %llx virt %p hypercall %p>",
182 __asm__
__volatile__ ("mov %0, %%r8" : : "r" (outputAddress
): "r8");
183 __asm__
__volatile__ ("call *%3" : "=a"(hvStatus
): "c" (Control
), "d" (inputAddress
), "m" (hypercallPage
));
185 DPRINT_DBG(VMBUS
, "Hypercall <return %llx>", hvStatus
);
191 u32 controlHi
= Control
>> 32;
192 u32 controlLo
= Control
& 0xFFFFFFFF;
195 u64 inputAddress
= (Input
) ? virt_to_phys(Input
) : 0;
196 u32 inputAddressHi
= inputAddress
>> 32;
197 u32 inputAddressLo
= inputAddress
& 0xFFFFFFFF;
198 u64 outputAddress
= (Output
) ? virt_to_phys(Output
) : 0;
199 u32 outputAddressHi
= outputAddress
>> 32;
200 u32 outputAddressLo
= outputAddress
& 0xFFFFFFFF;
201 volatile void* hypercallPage
= gHvContext
.HypercallPage
;
203 DPRINT_DBG(VMBUS
, "Hypercall <control %llx input %p output %p>",
208 __asm__
__volatile__ ("call *%8" : "=d"(hvStatusHi
), "=a"(hvStatusLo
) : "d" (controlHi
), "a" (controlLo
), "b" (inputAddressHi
), "c" (inputAddressLo
), "D"(outputAddressHi
), "S"(outputAddressLo
), "m" (hypercallPage
));
211 DPRINT_DBG(VMBUS
, "Hypercall <return %llx>", hvStatusLo
| ((u64
)hvStatusHi
<< 32));
213 return (hvStatusLo
| ((u64
)hvStatusHi
<< 32));
223 Main initialization routine. This routine must be called
224 before any other routines in here are called
234 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr
;
235 void *virtAddr
= NULL
;
239 memset(gHvContext
.synICEventPage
, 0, sizeof(void *) * MAX_NUM_CPUS
);
240 memset(gHvContext
.synICMessagePage
, 0, sizeof(void *) * MAX_NUM_CPUS
);
242 if (!HvQueryHypervisorPresence())
244 DPRINT_ERR(VMBUS
, "No Windows hypervisor detected!!");
248 DPRINT_INFO(VMBUS
, "Windows hypervisor detected! Retrieving more info...");
250 maxLeaf
= HvQueryHypervisorInfo();
251 /* HvQueryHypervisorFeatures(maxLeaf); */
253 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
254 gHvContext
.GuestId
= ReadMsr(HV_X64_MSR_GUEST_OS_ID
);
256 if (gHvContext
.GuestId
== 0)
258 /* Write our OS info */
259 WriteMsr(HV_X64_MSR_GUEST_OS_ID
, HV_LINUX_GUEST_ID
);
261 gHvContext
.GuestId
= HV_LINUX_GUEST_ID
;
264 /* See if the hypercall page is already set */
265 hypercallMsr
.AsUINT64
= ReadMsr(HV_X64_MSR_HYPERCALL
);
267 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
269 /* Allocate the hypercall page memory */
270 /* virtAddr = PageAlloc(1); */
271 virtAddr
= VirtualAllocExec(PAGE_SIZE
);
275 DPRINT_ERR(VMBUS
, "unable to allocate hypercall page!!");
279 hypercallMsr
.Enable
= 1;
280 /* hypercallMsr.GuestPhysicalAddress = virt_to_phys(virtAddr) >> PAGE_SHIFT; */
281 hypercallMsr
.GuestPhysicalAddress
= vmalloc_to_pfn(virtAddr
);
282 WriteMsr(HV_X64_MSR_HYPERCALL
, hypercallMsr
.AsUINT64
);
284 /* Confirm that hypercall page did get setup. */
285 hypercallMsr
.AsUINT64
= 0;
286 hypercallMsr
.AsUINT64
= ReadMsr(HV_X64_MSR_HYPERCALL
);
288 if (!hypercallMsr
.Enable
)
290 DPRINT_ERR(VMBUS
, "unable to set hypercall page!!");
294 gHvContext
.HypercallPage
= virtAddr
;
298 DPRINT_ERR(VMBUS
, "Unknown guest id (0x%llx)!!", gHvContext
.GuestId
);
302 DPRINT_INFO(VMBUS
, "Hypercall page VA=%p, PA=0x%0llx",
303 gHvContext
.HypercallPage
,
304 (u64
)hypercallMsr
.GuestPhysicalAddress
<< PAGE_SHIFT
);
306 /* Setup the global signal event param for the signal event hypercall */
307 gHvContext
.SignalEventBuffer
= kmalloc(sizeof(HV_INPUT_SIGNAL_EVENT_BUFFER
), GFP_KERNEL
);
308 if (!gHvContext
.SignalEventBuffer
)
313 gHvContext
.SignalEventParam
= (PHV_INPUT_SIGNAL_EVENT
)(ALIGN_UP((unsigned long)gHvContext
.SignalEventBuffer
, HV_HYPERCALL_PARAM_ALIGN
));
314 gHvContext
.SignalEventParam
->ConnectionId
.Asu32
= 0;
315 gHvContext
.SignalEventParam
->ConnectionId
.u
.Id
= VMBUS_EVENT_CONNECTION_ID
;
316 gHvContext
.SignalEventParam
->FlagNumber
= 0;
317 gHvContext
.SignalEventParam
->RsvdZ
= 0;
319 /* DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId()); */
328 if (hypercallMsr
.Enable
)
330 hypercallMsr
.AsUINT64
= 0;
331 WriteMsr(HV_X64_MSR_HYPERCALL
, hypercallMsr
.AsUINT64
);
349 Cleanup routine. This routine is called normally during driver unloading or exiting.
357 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr
;
361 if (gHvContext
.SignalEventBuffer
)
363 kfree(gHvContext
.SignalEventBuffer
);
364 gHvContext
.SignalEventBuffer
= NULL
;
365 gHvContext
.SignalEventParam
= NULL
;
368 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
370 if (gHvContext
.HypercallPage
)
372 hypercallMsr
.AsUINT64
= 0;
373 WriteMsr(HV_X64_MSR_HYPERCALL
, hypercallMsr
.AsUINT64
);
374 vfree(gHvContext
.HypercallPage
);
375 gHvContext
.HypercallPage
= NULL
;
390 Post a message using the hypervisor message IPC. This
391 involves a hypercall.
396 HV_CONNECTION_ID connectionId
,
397 HV_MESSAGE_TYPE messageType
,
402 struct alignedInput
{
404 HV_INPUT_POST_MESSAGE msg
;
407 PHV_INPUT_POST_MESSAGE alignedMsg
;
411 if (payloadSize
> HV_MESSAGE_PAYLOAD_BYTE_COUNT
)
416 addr
= (unsigned long)kmalloc(sizeof(struct alignedInput
), GFP_ATOMIC
);
423 alignedMsg
= (PHV_INPUT_POST_MESSAGE
)(ALIGN_UP(addr
, HV_HYPERCALL_PARAM_ALIGN
));
425 alignedMsg
->ConnectionId
= connectionId
;
426 alignedMsg
->MessageType
= messageType
;
427 alignedMsg
->PayloadSize
= payloadSize
;
428 memcpy((void*)alignedMsg
->Payload
, payload
, payloadSize
);
430 status
= HvDoHypercall(HvCallPostMessage
, alignedMsg
, NULL
) & 0xFFFF;
444 Signal an event on the specified connection using the hypervisor event IPC. This
445 involves a hypercall.
453 status
= HvDoHypercall(HvCallSignalEvent
, gHvContext
.SignalEventParam
, NULL
) & 0xFFFF;
465 Initialize the Synthethic Interrupt Controller. If it is already initialized by
466 another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
467 Otherwise, we create and initialize the message and event pages.
477 HV_SYNIC_SIEFP siefp
;
478 HV_SYNIC_SINT sharedSint
;
479 HV_SYNIC_SCONTROL sctrl
;
485 if (!gHvContext
.HypercallPage
)
491 /* Check the version */
492 version
= ReadMsr(HV_X64_MSR_SVERSION
);
494 DPRINT_INFO(VMBUS
, "SynIC version: %llx", version
);
496 /* TODO: Handle SMP */
497 if (gHvContext
.GuestId
== HV_XENLINUX_GUEST_ID
)
499 DPRINT_INFO(VMBUS
, "Skipping SIMP and SIEFP setup since it is already set.");
501 simp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIMP
);
502 siefp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIEFP
);
504 DPRINT_DBG(VMBUS
, "Simp: %llx, Sifep: %llx", simp
.AsUINT64
, siefp
.AsUINT64
);
506 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
507 guestID
= ReadMsr(HV_X64_MSR_GUEST_OS_ID
);
509 if (guestID
== HV_LINUX_GUEST_ID
)
511 gHvContext
.synICMessagePage
[0] = phys_to_virt(simp
.BaseSimpGpa
<< PAGE_SHIFT
);
512 gHvContext
.synICEventPage
[0] = phys_to_virt(siefp
.BaseSiefpGpa
<< PAGE_SHIFT
);
516 DPRINT_ERR(VMBUS
, "unknown guest id!!");
519 DPRINT_DBG(VMBUS
, "MAPPED: Simp: %p, Sifep: %p", gHvContext
.synICMessagePage
[0], gHvContext
.synICEventPage
[0]);
523 gHvContext
.synICMessagePage
[0] = PageAlloc(1);
524 if (gHvContext
.synICMessagePage
[0] == NULL
)
526 DPRINT_ERR(VMBUS
, "unable to allocate SYNIC message page!!");
530 gHvContext
.synICEventPage
[0] = PageAlloc(1);
531 if (gHvContext
.synICEventPage
[0] == NULL
)
533 DPRINT_ERR(VMBUS
, "unable to allocate SYNIC event page!!");
537 /* Setup the Synic's message page */
538 simp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIMP
);
539 simp
.SimpEnabled
= 1;
540 simp
.BaseSimpGpa
= virt_to_phys(gHvContext
.synICMessagePage
[0]) >> PAGE_SHIFT
;
542 DPRINT_DBG(VMBUS
, "HV_X64_MSR_SIMP msr set to: %llx", simp
.AsUINT64
);
544 WriteMsr(HV_X64_MSR_SIMP
, simp
.AsUINT64
);
546 /* Setup the Synic's event page */
547 siefp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIEFP
);
548 siefp
.SiefpEnabled
= 1;
549 siefp
.BaseSiefpGpa
= virt_to_phys(gHvContext
.synICEventPage
[0]) >> PAGE_SHIFT
;
551 DPRINT_DBG(VMBUS
, "HV_X64_MSR_SIEFP msr set to: %llx", siefp
.AsUINT64
);
553 WriteMsr(HV_X64_MSR_SIEFP
, siefp
.AsUINT64
);
555 /* Setup the interception SINT. */
556 /* WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
557 /* interceptionSint.AsUINT64); */
559 /* Setup the shared SINT. */
560 sharedSint
.AsUINT64
= ReadMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
);
562 sharedSint
.AsUINT64
= 0;
563 sharedSint
.Vector
= irqVector
; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
564 sharedSint
.Masked
= false;
565 sharedSint
.AutoEoi
= true;
567 DPRINT_DBG(VMBUS
, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint
.AsUINT64
);
569 WriteMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
, sharedSint
.AsUINT64
);
571 /* Enable the global synic bit */
572 sctrl
.AsUINT64
= ReadMsr(HV_X64_MSR_SCONTROL
);
575 WriteMsr(HV_X64_MSR_SCONTROL
, sctrl
.AsUINT64
);
577 gHvContext
.SynICInitialized
= true;
586 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
588 if (gHvContext
.synICEventPage
[0])
590 PageFree(gHvContext
.synICEventPage
[0],1);
593 if (gHvContext
.synICMessagePage
[0])
595 PageFree(gHvContext
.synICMessagePage
[0], 1);
611 Cleanup routine for HvSynicInit().
619 HV_SYNIC_SINT sharedSint
;
621 HV_SYNIC_SIEFP siefp
;
625 if (!gHvContext
.SynICInitialized
)
631 sharedSint
.AsUINT64
= ReadMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
);
633 sharedSint
.Masked
= 1;
635 /* Disable the interrupt */
636 WriteMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
, sharedSint
.AsUINT64
);
639 * Disable and free the resources only if we are running as
640 * native linux since in xenlinux, we are sharing the
641 * resources with the x2v shim
643 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
645 simp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIMP
);
646 simp
.SimpEnabled
= 0;
647 simp
.BaseSimpGpa
= 0;
649 WriteMsr(HV_X64_MSR_SIMP
, simp
.AsUINT64
);
651 siefp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIEFP
);
652 siefp
.SiefpEnabled
= 0;
653 siefp
.BaseSiefpGpa
= 0;
655 WriteMsr(HV_X64_MSR_SIEFP
, siefp
.AsUINT64
);
657 PageFree(gHvContext
.synICMessagePage
[0], 1);
658 PageFree(gHvContext
.synICEventPage
[0], 1);