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>
25 #include "include/logging.h"
26 #include "VmbusPrivate.h"
30 /* The one and only */
31 HV_CONTEXT gHvContext
={
32 .SynICInitialized
= false,
33 .HypercallPage
= NULL
,
34 .SignalEventParam
= NULL
,
35 .SignalEventBuffer
= NULL
,
42 HvQueryHypervisorPresence()
45 Query the cpuid for presense of windows hypervisor
49 HvQueryHypervisorPresence (
63 op
= HvCpuIdFunctionVersionAndFeatures
;
64 do_cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
66 return (ecx
& HV_PRESENT_BIT
);
73 HvQueryHypervisorInfo()
76 Get version info of the windows hypervisor
80 HvQueryHypervisorInfo (
92 * Its assumed that this is called after confirming that Viridian
93 * is present. Query id and revision.
101 op
= HvCpuIdFunctionHvVendorAndMaxFunction
;
102 do_cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
104 DPRINT_INFO(VMBUS
, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c",
107 ((ebx
>> 16) & 0xFF),
108 ((ebx
>> 24) & 0xFF),
111 ((ecx
>> 16) & 0xFF),
112 ((ecx
>> 24) & 0xFF),
115 ((edx
>> 16) & 0xFF),
116 ((edx
>> 24) & 0xFF));
123 op
= HvCpuIdFunctionHvInterface
;
124 do_cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
126 DPRINT_INFO(VMBUS
, "Interface ID: %c%c%c%c",
129 ((eax
>> 16) & 0xFF),
130 ((eax
>> 24) & 0xFF));
132 if (maxLeaf
>= HvCpuIdFunctionMsHvVersion
) {
137 op
= HvCpuIdFunctionMsHvVersion
;
138 do_cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
139 DPRINT_INFO(VMBUS
, "OS Build:%d-%d.%d-%d-%d.%d",
157 Invoke the specified hypercall
169 u64 inputAddress
= (Input
)? GetPhysicalAddress(Input
) : 0;
170 u64 outputAddress
= (Output
)? GetPhysicalAddress(Output
) : 0;
171 volatile void* hypercallPage
= gHvContext
.HypercallPage
;
173 DPRINT_DBG(VMBUS
, "Hypercall <control %llx input phys %llx virt %p output phys %llx virt %p hypercall %p>",
181 __asm__
__volatile__ ("mov %0, %%r8" : : "r" (outputAddress
): "r8");
182 __asm__
__volatile__ ("call *%3" : "=a"(hvStatus
): "c" (Control
), "d" (inputAddress
), "m" (hypercallPage
));
184 DPRINT_DBG(VMBUS
, "Hypercall <return %llx>", hvStatus
);
190 u32 controlHi
= Control
>> 32;
191 u32 controlLo
= Control
& 0xFFFFFFFF;
194 u64 inputAddress
= (Input
) ? GetPhysicalAddress(Input
) : 0;
195 u32 inputAddressHi
= inputAddress
>> 32;
196 u32 inputAddressLo
= inputAddress
& 0xFFFFFFFF;
197 u64 outputAddress
= (Output
) ?GetPhysicalAddress(Output
) : 0;
198 u32 outputAddressHi
= outputAddress
>> 32;
199 u32 outputAddressLo
= outputAddress
& 0xFFFFFFFF;
200 volatile void* hypercallPage
= gHvContext
.HypercallPage
;
202 DPRINT_DBG(VMBUS
, "Hypercall <control %llx input %p output %p>",
207 __asm__
__volatile__ ("call *%8" : "=d"(hvStatusHi
), "=a"(hvStatusLo
) : "d" (controlHi
), "a" (controlLo
), "b" (inputAddressHi
), "c" (inputAddressLo
), "D"(outputAddressHi
), "S"(outputAddressLo
), "m" (hypercallPage
));
210 DPRINT_DBG(VMBUS
, "Hypercall <return %llx>", hvStatusLo
| ((u64
)hvStatusHi
<< 32));
212 return (hvStatusLo
| ((u64
)hvStatusHi
<< 32));
222 Main initialization routine. This routine must be called
223 before any other routines in here are called
233 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr
;
235 unsigned long physAddr
=0;
239 memset(gHvContext
.synICEventPage
, 0, sizeof(HANDLE
)*MAX_NUM_CPUS
);
240 memset(gHvContext
.synICMessagePage
, 0, sizeof(HANDLE
)*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 = Logical2PhysicalAddr(virtAddr) >> PAGE_SHIFT; */
281 hypercallMsr
.GuestPhysicalAddress
= Virtual2Physical(virtAddr
) >> PAGE_SHIFT
;
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
);
334 VirtualFree(virtAddr
);
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 VirtualFree(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
, 0) & 0xFFFF;
444 Signal an event on the specified connection using the hypervisor event IPC. This
445 involves a hypercall.
454 status
= HvDoHypercall(HvCallSignalEvent
, gHvContext
.SignalEventParam
, 0) & 0xFFFF;
466 Initialize the Synthethic Interrupt Controller. If it is already initialized by
467 another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
468 Otherwise, we create and initialize the message and event pages.
478 HV_SYNIC_SIEFP siefp
;
479 HV_SYNIC_SINT sharedSint
;
480 HV_SYNIC_SCONTROL sctrl
;
486 if (!gHvContext
.HypercallPage
)
492 /* Check the version */
493 version
= ReadMsr(HV_X64_MSR_SVERSION
);
495 DPRINT_INFO(VMBUS
, "SynIC version: %llx", version
);
497 /* TODO: Handle SMP */
498 if (gHvContext
.GuestId
== HV_XENLINUX_GUEST_ID
)
500 DPRINT_INFO(VMBUS
, "Skipping SIMP and SIEFP setup since it is already set.");
502 simp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIMP
);
503 siefp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIEFP
);
505 DPRINT_DBG(VMBUS
, "Simp: %llx, Sifep: %llx", simp
.AsUINT64
, siefp
.AsUINT64
);
507 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
508 guestID
= ReadMsr(HV_X64_MSR_GUEST_OS_ID
);
510 if (guestID
== HV_LINUX_GUEST_ID
)
512 gHvContext
.synICMessagePage
[0] = GetVirtualAddress(simp
.BaseSimpGpa
<< PAGE_SHIFT
);
513 gHvContext
.synICEventPage
[0] = GetVirtualAddress(siefp
.BaseSiefpGpa
<< PAGE_SHIFT
);
517 DPRINT_ERR(VMBUS
, "unknown guest id!!");
520 DPRINT_DBG(VMBUS
, "MAPPED: Simp: %p, Sifep: %p", gHvContext
.synICMessagePage
[0], gHvContext
.synICEventPage
[0]);
524 gHvContext
.synICMessagePage
[0] = PageAlloc(1);
525 if (gHvContext
.synICMessagePage
[0] == NULL
)
527 DPRINT_ERR(VMBUS
, "unable to allocate SYNIC message page!!");
531 gHvContext
.synICEventPage
[0] = PageAlloc(1);
532 if (gHvContext
.synICEventPage
[0] == NULL
)
534 DPRINT_ERR(VMBUS
, "unable to allocate SYNIC event page!!");
538 /* Setup the Synic's message page */
539 simp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIMP
);
540 simp
.SimpEnabled
= 1;
541 simp
.BaseSimpGpa
= GetPhysicalAddress(gHvContext
.synICMessagePage
[0]) >> PAGE_SHIFT
;
543 DPRINT_DBG(VMBUS
, "HV_X64_MSR_SIMP msr set to: %llx", simp
.AsUINT64
);
545 WriteMsr(HV_X64_MSR_SIMP
, simp
.AsUINT64
);
547 /* Setup the Synic's event page */
548 siefp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIEFP
);
549 siefp
.SiefpEnabled
= 1;
550 siefp
.BaseSiefpGpa
= GetPhysicalAddress(gHvContext
.synICEventPage
[0]) >> PAGE_SHIFT
;
552 DPRINT_DBG(VMBUS
, "HV_X64_MSR_SIEFP msr set to: %llx", siefp
.AsUINT64
);
554 WriteMsr(HV_X64_MSR_SIEFP
, siefp
.AsUINT64
);
556 /* Setup the interception SINT. */
557 /* WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
558 /* interceptionSint.AsUINT64); */
560 /* Setup the shared SINT. */
561 sharedSint
.AsUINT64
= ReadMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
);
563 sharedSint
.AsUINT64
= 0;
564 sharedSint
.Vector
= irqVector
; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
565 sharedSint
.Masked
= false;
566 sharedSint
.AutoEoi
= true;
568 DPRINT_DBG(VMBUS
, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint
.AsUINT64
);
570 WriteMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
, sharedSint
.AsUINT64
);
572 /* Enable the global synic bit */
573 sctrl
.AsUINT64
= ReadMsr(HV_X64_MSR_SCONTROL
);
576 WriteMsr(HV_X64_MSR_SCONTROL
, sctrl
.AsUINT64
);
578 gHvContext
.SynICInitialized
= true;
587 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
589 if (gHvContext
.synICEventPage
[0])
591 PageFree(gHvContext
.synICEventPage
[0],1);
594 if (gHvContext
.synICMessagePage
[0])
596 PageFree(gHvContext
.synICMessagePage
[0], 1);
612 Cleanup routine for HvSynicInit().
620 HV_SYNIC_SINT sharedSint
;
622 HV_SYNIC_SIEFP siefp
;
626 if (!gHvContext
.SynICInitialized
)
632 sharedSint
.AsUINT64
= ReadMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
);
634 sharedSint
.Masked
= 1;
636 /* Disable the interrupt */
637 WriteMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
, sharedSint
.AsUINT64
);
640 * Disable and free the resources only if we are running as
641 * native linux since in xenlinux, we are sharing the
642 * resources with the x2v shim
644 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
646 simp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIMP
);
647 simp
.SimpEnabled
= 0;
648 simp
.BaseSimpGpa
= 0;
650 WriteMsr(HV_X64_MSR_SIMP
, simp
.AsUINT64
);
652 siefp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIEFP
);
653 siefp
.SiefpEnabled
= 0;
654 siefp
.BaseSiefpGpa
= 0;
656 WriteMsr(HV_X64_MSR_SIEFP
, siefp
.AsUINT64
);
658 PageFree(gHvContext
.synICMessagePage
[0], 1);
659 PageFree(gHvContext
.synICEventPage
[0], 1);