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"
33 HV_CONTEXT gHvContext
={
34 .SynICInitialized
= FALSE
,
35 .HypercallPage
= NULL
,
36 .SignalEventParam
= NULL
,
37 .SignalEventBuffer
= NULL
,
44 HvQueryHypervisorPresence()
47 Query the cpuid for presense of windows hypervisor
51 HvQueryHypervisorPresence (
65 op
= HvCpuIdFunctionVersionAndFeatures
;
66 do_cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
68 return (ecx
& HV_PRESENT_BIT
);
75 HvQueryHypervisorInfo()
78 Get version info of the windows hypervisor
82 HvQueryHypervisorInfo (
94 // Its assumed that this is called after confirming that Viridian is present.
95 // Query id and revision.
102 op
= HvCpuIdFunctionHvVendorAndMaxFunction
;
103 do_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 do_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 do_cpuid(op
, &eax
, &ebx
, &ecx
, &edx
);
140 DPRINT_INFO(VMBUS
, "OS Build:%d-%d.%d-%d-%d.%d",
158 Invoke the specified hypercall
170 UINT64 inputAddress
= (Input
)? GetPhysicalAddress(Input
) : 0;
171 UINT64 outputAddress
= (Output
)? GetPhysicalAddress(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 UINT32 controlHi
= Control
>> 32;
192 UINT32 controlLo
= Control
& 0xFFFFFFFF;
193 UINT32 hvStatusHi
= 1;
194 UINT32 hvStatusLo
= 1;
195 UINT64 inputAddress
= (Input
) ? GetPhysicalAddress(Input
) : 0;
196 UINT32 inputAddressHi
= inputAddress
>> 32;
197 UINT32 inputAddressLo
= inputAddress
& 0xFFFFFFFF;
198 UINT64 outputAddress
= (Output
) ?GetPhysicalAddress(Output
) : 0;
199 UINT32 outputAddressHi
= outputAddress
>> 32;
200 UINT32 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
| ((UINT64
)hvStatusHi
<< 32));
213 return (hvStatusLo
| ((UINT64
)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
;
236 ULONG_PTR physAddr
=0;
240 memset(gHvContext
.synICEventPage
, 0, sizeof(HANDLE
)*MAX_NUM_CPUS
);
241 memset(gHvContext
.synICMessagePage
, 0, sizeof(HANDLE
)*MAX_NUM_CPUS
);
243 if (!HvQueryHypervisorPresence())
245 DPRINT_ERR(VMBUS
, "No Windows hypervisor detected!!");
249 DPRINT_INFO(VMBUS
, "Windows hypervisor detected! Retrieving more info...");
251 maxLeaf
= HvQueryHypervisorInfo();
252 //HvQueryHypervisorFeatures(maxLeaf);
254 // Determine if we are running on xenlinux (ie x2v shim) or native linux
255 gHvContext
.GuestId
= ReadMsr(HV_X64_MSR_GUEST_OS_ID
);
257 if (gHvContext
.GuestId
== 0)
260 WriteMsr(HV_X64_MSR_GUEST_OS_ID
, HV_LINUX_GUEST_ID
);
262 gHvContext
.GuestId
= HV_LINUX_GUEST_ID
;
265 // See if the hypercall page is already set
266 hypercallMsr
.AsUINT64
= ReadMsr(HV_X64_MSR_HYPERCALL
);
268 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
270 // Allocate the hypercall page memory
271 //virtAddr = PageAlloc(1);
272 virtAddr
= VirtualAllocExec(PAGE_SIZE
);
276 DPRINT_ERR(VMBUS
, "unable to allocate hypercall page!!");
280 hypercallMsr
.Enable
= 1;
281 //hypercallMsr.GuestPhysicalAddress = Logical2PhysicalAddr(virtAddr) >> PAGE_SHIFT;
282 hypercallMsr
.GuestPhysicalAddress
= Virtual2Physical(virtAddr
) >> PAGE_SHIFT
;
283 WriteMsr(HV_X64_MSR_HYPERCALL
, hypercallMsr
.AsUINT64
);
285 // Confirm that hypercall page did get setup.
286 hypercallMsr
.AsUINT64
= 0;
287 hypercallMsr
.AsUINT64
= ReadMsr(HV_X64_MSR_HYPERCALL
);
289 if (!hypercallMsr
.Enable
)
291 DPRINT_ERR(VMBUS
, "unable to set hypercall page!!");
295 gHvContext
.HypercallPage
= virtAddr
;
299 DPRINT_ERR(VMBUS
, "Unknown guest id (0x%llx)!!", gHvContext
.GuestId
);
303 DPRINT_INFO(VMBUS
, "Hypercall page VA=0x%08x, PA=0x%08x",
304 (unsigned long)gHvContext
.HypercallPage
,
305 (unsigned long)hypercallMsr
.GuestPhysicalAddress
<< PAGE_SHIFT
);
307 // Setup the global signal event param for the signal event hypercall
308 gHvContext
.SignalEventBuffer
= MemAlloc(sizeof(HV_INPUT_SIGNAL_EVENT_BUFFER
));
309 if (!gHvContext
.SignalEventBuffer
)
314 gHvContext
.SignalEventParam
= (PHV_INPUT_SIGNAL_EVENT
)(ALIGN_UP((ULONG_PTR
)gHvContext
.SignalEventBuffer
, HV_HYPERCALL_PARAM_ALIGN
));
315 gHvContext
.SignalEventParam
->ConnectionId
.AsUINT32
= 0;
316 gHvContext
.SignalEventParam
->ConnectionId
.u
.Id
= VMBUS_EVENT_CONNECTION_ID
;
317 gHvContext
.SignalEventParam
->FlagNumber
= 0;
318 gHvContext
.SignalEventParam
->RsvdZ
= 0;
320 //DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId());
329 if (hypercallMsr
.Enable
)
331 hypercallMsr
.AsUINT64
= 0;
332 WriteMsr(HV_X64_MSR_HYPERCALL
, hypercallMsr
.AsUINT64
);
335 VirtualFree(virtAddr
);
350 Cleanup routine. This routine is called normally during driver unloading or exiting.
358 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr
;
362 if (gHvContext
.SignalEventBuffer
)
364 MemFree(gHvContext
.SignalEventBuffer
);
365 gHvContext
.SignalEventBuffer
= NULL
;
366 gHvContext
.SignalEventParam
= NULL
;
369 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
371 if (gHvContext
.HypercallPage
)
373 hypercallMsr
.AsUINT64
= 0;
374 WriteMsr(HV_X64_MSR_HYPERCALL
, hypercallMsr
.AsUINT64
);
375 VirtualFree(gHvContext
.HypercallPage
);
376 gHvContext
.HypercallPage
= NULL
;
391 Post a message using the hypervisor message IPC. This
392 involves a hypercall.
397 HV_CONNECTION_ID connectionId
,
398 HV_MESSAGE_TYPE messageType
,
403 struct alignedInput
{
405 HV_INPUT_POST_MESSAGE msg
;
408 PHV_INPUT_POST_MESSAGE alignedMsg
;
412 if (payloadSize
> HV_MESSAGE_PAYLOAD_BYTE_COUNT
)
417 addr
= (ULONG_PTR
)MemAllocAtomic(sizeof(struct alignedInput
));
424 alignedMsg
= (PHV_INPUT_POST_MESSAGE
)(ALIGN_UP(addr
, HV_HYPERCALL_PARAM_ALIGN
));
426 alignedMsg
->ConnectionId
= connectionId
;
427 alignedMsg
->MessageType
= messageType
;
428 alignedMsg
->PayloadSize
= payloadSize
;
429 memcpy((void*)alignedMsg
->Payload
, payload
, payloadSize
);
431 status
= HvDoHypercall(HvCallPostMessage
, alignedMsg
, 0) & 0xFFFF;
433 MemFree((void*)addr
);
445 Signal an event on the specified connection using the hypervisor event IPC. This
446 involves a hypercall.
455 status
= HvDoHypercall(HvCallSignalEvent
, gHvContext
.SignalEventParam
, 0) & 0xFFFF;
467 Initialize the Synthethic Interrupt Controller. If it is already initialized by
468 another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
469 Otherwise, we create and initialize the message and event pages.
479 HV_SYNIC_SIEFP siefp
;
480 HV_SYNIC_SINT sharedSint
;
481 HV_SYNIC_SCONTROL sctrl
;
487 if (!gHvContext
.HypercallPage
)
494 version
= ReadMsr(HV_X64_MSR_SVERSION
);
496 DPRINT_INFO(VMBUS
, "SynIC version: %llx", version
);
499 if (gHvContext
.GuestId
== HV_XENLINUX_GUEST_ID
)
501 DPRINT_INFO(VMBUS
, "Skipping SIMP and SIEFP setup since it is already set.");
503 simp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIMP
);
504 siefp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIEFP
);
506 DPRINT_DBG(VMBUS
, "Simp: %llx, Sifep: %llx", simp
.AsUINT64
, siefp
.AsUINT64
);
508 // Determine if we are running on xenlinux (ie x2v shim) or native linux
509 guestID
= ReadMsr(HV_X64_MSR_GUEST_OS_ID
);
511 if (guestID
== HV_LINUX_GUEST_ID
)
513 gHvContext
.synICMessagePage
[0] = GetVirtualAddress(simp
.BaseSimpGpa
<< PAGE_SHIFT
);
514 gHvContext
.synICEventPage
[0] = GetVirtualAddress(siefp
.BaseSiefpGpa
<< PAGE_SHIFT
);
518 DPRINT_ERR(VMBUS
, "unknown guest id!!");
521 DPRINT_DBG(VMBUS
, "MAPPED: Simp: %p, Sifep: %p", gHvContext
.synICMessagePage
[0], gHvContext
.synICEventPage
[0]);
525 gHvContext
.synICMessagePage
[0] = PageAlloc(1);
526 if (gHvContext
.synICMessagePage
[0] == NULL
)
528 DPRINT_ERR(VMBUS
, "unable to allocate SYNIC message page!!");
532 gHvContext
.synICEventPage
[0] = PageAlloc(1);
533 if (gHvContext
.synICEventPage
[0] == NULL
)
535 DPRINT_ERR(VMBUS
, "unable to allocate SYNIC event page!!");
540 // Setup the Synic's message page
542 simp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIMP
);
543 simp
.SimpEnabled
= 1;
544 simp
.BaseSimpGpa
= GetPhysicalAddress(gHvContext
.synICMessagePage
[0]) >> PAGE_SHIFT
;
546 DPRINT_DBG(VMBUS
, "HV_X64_MSR_SIMP msr set to: %llx", simp
.AsUINT64
);
548 WriteMsr(HV_X64_MSR_SIMP
, simp
.AsUINT64
);
551 // Setup the Synic's event page
553 siefp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIEFP
);
554 siefp
.SiefpEnabled
= 1;
555 siefp
.BaseSiefpGpa
= GetPhysicalAddress(gHvContext
.synICEventPage
[0]) >> PAGE_SHIFT
;
557 DPRINT_DBG(VMBUS
, "HV_X64_MSR_SIEFP msr set to: %llx", siefp
.AsUINT64
);
559 WriteMsr(HV_X64_MSR_SIEFP
, siefp
.AsUINT64
);
562 // Setup the interception SINT.
564 //WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX),
565 // interceptionSint.AsUINT64);
568 // Setup the shared SINT.
570 sharedSint
.AsUINT64
= ReadMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
);
572 sharedSint
.AsUINT64
= 0;
573 sharedSint
.Vector
= irqVector
; //HV_SHARED_SINT_IDT_VECTOR + 0x20;
574 sharedSint
.Masked
= FALSE
;
575 sharedSint
.AutoEoi
= TRUE
;
577 DPRINT_DBG(VMBUS
, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint
.AsUINT64
);
579 WriteMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
, sharedSint
.AsUINT64
);
581 // Enable the global synic bit
582 sctrl
.AsUINT64
= ReadMsr(HV_X64_MSR_SCONTROL
);
585 WriteMsr(HV_X64_MSR_SCONTROL
, sctrl
.AsUINT64
);
587 gHvContext
.SynICInitialized
= TRUE
;
596 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
598 if (gHvContext
.synICEventPage
[0])
600 PageFree(gHvContext
.synICEventPage
[0],1);
603 if (gHvContext
.synICMessagePage
[0])
605 PageFree(gHvContext
.synICMessagePage
[0], 1);
621 Cleanup routine for HvSynicInit().
629 HV_SYNIC_SINT sharedSint
;
631 HV_SYNIC_SIEFP siefp
;
635 if (!gHvContext
.SynICInitialized
)
641 sharedSint
.AsUINT64
= ReadMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
);
643 sharedSint
.Masked
= 1;
645 // Disable the interrupt
646 WriteMsr(HV_X64_MSR_SINT0
+ VMBUS_MESSAGE_SINT
, sharedSint
.AsUINT64
);
648 // Disable and free the resources only if we are running as native linux
649 // since in xenlinux, we are sharing the resources with the x2v shim
650 if (gHvContext
.GuestId
== HV_LINUX_GUEST_ID
)
652 simp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIMP
);
653 simp
.SimpEnabled
= 0;
654 simp
.BaseSimpGpa
= 0;
656 WriteMsr(HV_X64_MSR_SIMP
, simp
.AsUINT64
);
658 siefp
.AsUINT64
= ReadMsr(HV_X64_MSR_SIEFP
);
659 siefp
.SiefpEnabled
= 0;
660 siefp
.BaseSiefpGpa
= 0;
662 WriteMsr(HV_X64_MSR_SIEFP
, siefp
.AsUINT64
);
664 PageFree(gHvContext
.synICMessagePage
[0], 1);
665 PageFree(gHvContext
.synICEventPage
[0], 1);