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;
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 gHvContext.GuestId = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
255 if (gHvContext.GuestId == 0)
257 /* Write our OS info */
258 WriteMsr(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
260 gHvContext.GuestId = HV_LINUX_GUEST_ID;
263 /* See if the hypercall page is already set */
264 hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
266 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
268 /* Allocate the hypercall page memory */
269 /* virtAddr = PageAlloc(1); */
270 virtAddr = VirtualAllocExec(PAGE_SIZE);
274 DPRINT_ERR(VMBUS, "unable to allocate hypercall page!!");
278 hypercallMsr.Enable = 1;
279 /* hypercallMsr.GuestPhysicalAddress = Logical2PhysicalAddr(virtAddr) >> PAGE_SHIFT; */
280 hypercallMsr.GuestPhysicalAddress = Virtual2Physical(virtAddr) >> PAGE_SHIFT;
281 WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
283 /* Confirm that hypercall page did get setup. */
284 hypercallMsr.AsUINT64 = 0;
285 hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
287 if (!hypercallMsr.Enable)
289 DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
293 gHvContext.HypercallPage = virtAddr;
297 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", gHvContext.GuestId);
301 DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
302 gHvContext.HypercallPage,
303 (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
305 /* Setup the global signal event param for the signal event hypercall */
306 gHvContext.SignalEventBuffer = kmalloc(sizeof(HV_INPUT_SIGNAL_EVENT_BUFFER), GFP_KERNEL);
307 if (!gHvContext.SignalEventBuffer)
312 gHvContext.SignalEventParam = (PHV_INPUT_SIGNAL_EVENT)(ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer, HV_HYPERCALL_PARAM_ALIGN));
313 gHvContext.SignalEventParam->ConnectionId.Asu32 = 0;
314 gHvContext.SignalEventParam->ConnectionId.u.Id = VMBUS_EVENT_CONNECTION_ID;
315 gHvContext.SignalEventParam->FlagNumber = 0;
316 gHvContext.SignalEventParam->RsvdZ = 0;
318 /* DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId()); */
327 if (hypercallMsr.Enable)
329 hypercallMsr.AsUINT64 = 0;
330 WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
333 VirtualFree(virtAddr);
348 Cleanup routine. This routine is called normally during driver unloading or exiting.
356 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
360 if (gHvContext.SignalEventBuffer)
362 kfree(gHvContext.SignalEventBuffer);
363 gHvContext.SignalEventBuffer = NULL;
364 gHvContext.SignalEventParam = NULL;
367 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
369 if (gHvContext.HypercallPage)
371 hypercallMsr.AsUINT64 = 0;
372 WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
373 VirtualFree(gHvContext.HypercallPage);
374 gHvContext.HypercallPage = NULL;
389 Post a message using the hypervisor message IPC. This
390 involves a hypercall.
395 HV_CONNECTION_ID connectionId,
396 HV_MESSAGE_TYPE messageType,
401 struct alignedInput {
403 HV_INPUT_POST_MESSAGE msg;
406 PHV_INPUT_POST_MESSAGE alignedMsg;
410 if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
415 addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
422 alignedMsg = (PHV_INPUT_POST_MESSAGE)(ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
424 alignedMsg->ConnectionId = connectionId;
425 alignedMsg->MessageType = messageType;
426 alignedMsg->PayloadSize = payloadSize;
427 memcpy((void*)alignedMsg->Payload, payload, payloadSize);
429 status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
443 Signal an event on the specified connection using the hypervisor event IPC. This
444 involves a hypercall.
452 status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, NULL) & 0xFFFF;
464 Initialize the Synthethic Interrupt Controller. If it is already initialized by
465 another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
466 Otherwise, we create and initialize the message and event pages.
476 HV_SYNIC_SIEFP siefp;
477 HV_SYNIC_SINT sharedSint;
478 HV_SYNIC_SCONTROL sctrl;
484 if (!gHvContext.HypercallPage)
490 /* Check the version */
491 version = ReadMsr(HV_X64_MSR_SVERSION);
493 DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
495 /* TODO: Handle SMP */
496 if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
498 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
500 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
501 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
503 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
505 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
506 guestID = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
508 if (guestID == HV_LINUX_GUEST_ID)
510 gHvContext.synICMessagePage[0] = GetVirtualAddress(simp.BaseSimpGpa << PAGE_SHIFT);
511 gHvContext.synICEventPage[0] = GetVirtualAddress(siefp.BaseSiefpGpa << PAGE_SHIFT);
515 DPRINT_ERR(VMBUS, "unknown guest id!!");
518 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
522 gHvContext.synICMessagePage[0] = PageAlloc(1);
523 if (gHvContext.synICMessagePage[0] == NULL)
525 DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
529 gHvContext.synICEventPage[0] = PageAlloc(1);
530 if (gHvContext.synICEventPage[0] == NULL)
532 DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
536 /* Setup the Synic's message page */
537 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
538 simp.SimpEnabled = 1;
539 simp.BaseSimpGpa = GetPhysicalAddress(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
541 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
543 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
545 /* Setup the Synic's event page */
546 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
547 siefp.SiefpEnabled = 1;
548 siefp.BaseSiefpGpa = GetPhysicalAddress(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
550 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
552 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
554 /* Setup the interception SINT. */
555 /* WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
556 /* interceptionSint.AsUINT64); */
558 /* Setup the shared SINT. */
559 sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
561 sharedSint.AsUINT64 = 0;
562 sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
563 sharedSint.Masked = false;
564 sharedSint.AutoEoi = true;
566 DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
568 WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
570 /* Enable the global synic bit */
571 sctrl.AsUINT64 = ReadMsr(HV_X64_MSR_SCONTROL);
574 WriteMsr(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
576 gHvContext.SynICInitialized = true;
585 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
587 if (gHvContext.synICEventPage[0])
589 PageFree(gHvContext.synICEventPage[0],1);
592 if (gHvContext.synICMessagePage[0])
594 PageFree(gHvContext.synICMessagePage[0], 1);
610 Cleanup routine for HvSynicInit().
618 HV_SYNIC_SINT sharedSint;
620 HV_SYNIC_SIEFP siefp;
624 if (!gHvContext.SynICInitialized)
630 sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
632 sharedSint.Masked = 1;
634 /* Disable the interrupt */
635 WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
638 * Disable and free the resources only if we are running as
639 * native linux since in xenlinux, we are sharing the
640 * resources with the x2v shim
642 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
644 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
645 simp.SimpEnabled = 0;
646 simp.BaseSimpGpa = 0;
648 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
650 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
651 siefp.SiefpEnabled = 0;
652 siefp.BaseSiefpGpa = 0;
654 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
656 PageFree(gHvContext.synICMessagePage[0], 1);
657 PageFree(gHvContext.synICEventPage[0], 1);