Staging: hv: move osd.h
[linux-2.6/linux-2.6-openrd.git] / drivers / staging / hv / Hv.c
blob31459f76f16a6f9078285f38448227fe8d04ae92
1 /*
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
12 * more details.
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.
18 * Authors:
19 * Haiyang Zhang <haiyangz@microsoft.com>
20 * Hank Janssen <hjanssen@microsoft.com>
24 #include <linux/kernel.h>
25 #include <linux/mm.h>
26 #include <linux/vmalloc.h>
27 #include "osd.h"
28 #include "include/logging.h"
29 #include "VmbusPrivate.h"
31 /* Globals */
33 /* The one and only */
34 struct hv_context gHvContext = {
35 .SynICInitialized = false,
36 .HypercallPage = NULL,
37 .SignalEventParam = NULL,
38 .SignalEventBuffer = NULL,
42 /*++
44 Name:
45 HvQueryHypervisorPresence()
47 Description:
48 Query the cpuid for presense of windows hypervisor
50 --*/
51 static int
52 HvQueryHypervisorPresence (
53 void
56 unsigned int eax;
57 unsigned int ebx;
58 unsigned int ecx;
59 unsigned int edx;
60 unsigned int op;
62 eax = 0;
63 ebx = 0;
64 ecx = 0;
65 edx = 0;
66 op = HvCpuIdFunctionVersionAndFeatures;
67 cpuid(op, &eax, &ebx, &ecx, &edx);
69 return (ecx & HV_PRESENT_BIT);
73 /*++
75 Name:
76 HvQueryHypervisorInfo()
78 Description:
79 Get version info of the windows hypervisor
81 --*/
82 static int
83 HvQueryHypervisorInfo (
84 void
87 unsigned int eax;
88 unsigned int ebx;
89 unsigned int ecx;
90 unsigned int edx;
91 unsigned int maxLeaf;
92 unsigned int op;
95 * Its assumed that this is called after confirming that Viridian
96 * is present. Query id and revision.
100 eax = 0;
101 ebx = 0;
102 ecx = 0;
103 edx = 0;
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",
108 (ebx & 0xFF),
109 ((ebx >> 8) & 0xFF),
110 ((ebx >> 16) & 0xFF),
111 ((ebx >> 24) & 0xFF),
112 (ecx & 0xFF),
113 ((ecx >> 8) & 0xFF),
114 ((ecx >> 16) & 0xFF),
115 ((ecx >> 24) & 0xFF),
116 (edx & 0xFF),
117 ((edx >> 8) & 0xFF),
118 ((edx >> 16) & 0xFF),
119 ((edx >> 24) & 0xFF));
121 maxLeaf = eax;
122 eax = 0;
123 ebx = 0;
124 ecx = 0;
125 edx = 0;
126 op = HvCpuIdFunctionHvInterface;
127 cpuid(op, &eax, &ebx, &ecx, &edx);
129 DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c",
130 (eax & 0xFF),
131 ((eax >> 8) & 0xFF),
132 ((eax >> 16) & 0xFF),
133 ((eax >> 24) & 0xFF));
135 if (maxLeaf >= HvCpuIdFunctionMsHvVersion) {
136 eax = 0;
137 ebx = 0;
138 ecx = 0;
139 edx = 0;
140 op = HvCpuIdFunctionMsHvVersion;
141 cpuid(op, &eax, &ebx, &ecx, &edx);
142 DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",
143 eax,
144 ebx >> 16,
145 ebx & 0xFFFF,
146 ecx,
147 edx >> 24,
148 edx & 0xFFFFFF);
150 return maxLeaf;
154 /*++
156 Name:
157 HvDoHypercall()
159 Description:
160 Invoke the specified hypercall
162 --*/
163 static u64
164 HvDoHypercall (
165 u64 Control,
166 void* Input,
167 void* Output
170 #ifdef CONFIG_X86_64
171 u64 hvStatus=0;
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>",
177 Control,
178 inputAddress,
179 Input,
180 outputAddress,
181 Output,
182 hypercallPage);
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);
189 return hvStatus;
191 #else
193 u32 controlHi = Control >> 32;
194 u32 controlLo = Control & 0xFFFFFFFF;
195 u32 hvStatusHi = 1;
196 u32 hvStatusLo = 1;
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>",
206 Control,
207 Input,
208 Output);
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));
216 #endif /* x86_64 */
219 /*++
221 Name:
222 HvInit()
224 Description:
225 Main initialization routine. This routine must be called
226 before any other routines in here are called
228 --*/
229 int HvInit (void)
231 int ret=0;
232 int maxLeaf;
233 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
234 void *virtAddr = NULL;
236 DPRINT_ENTER(VMBUS);
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!!");
244 goto Cleanup;
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);
270 if (!virtAddr)
272 DPRINT_ERR(VMBUS, "unable to allocate hypercall page!!");
273 goto Cleanup;
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!!");
287 goto Cleanup;
290 gHvContext.HypercallPage = virtAddr;
292 else
294 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", gHvContext.GuestId);
295 goto Cleanup;
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)
306 goto Cleanup;
309 gHvContext.SignalEventParam = (PHV_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()); */
317 DPRINT_EXIT(VMBUS);
319 return ret;
321 Cleanup:
322 if (virtAddr)
324 if (hypercallMsr.Enable)
326 hypercallMsr.AsUINT64 = 0;
327 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
330 vfree(virtAddr);
332 ret = -1;
333 DPRINT_EXIT(VMBUS);
335 return ret;
339 /*++
341 Name:
342 HvCleanup()
344 Description:
345 Cleanup routine. This routine is called normally during driver unloading or exiting.
347 --*/
348 void HvCleanup (void)
350 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
352 DPRINT_ENTER(VMBUS);
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;
372 DPRINT_EXIT(VMBUS);
377 /*++
379 Name:
380 HvPostMessage()
382 Description:
383 Post a message using the hypervisor message IPC. This
384 involves a hypercall.
386 --*/
387 HV_STATUS HvPostMessage(
388 HV_CONNECTION_ID connectionId,
389 HV_MESSAGE_TYPE messageType,
390 void * payload,
391 size_t payloadSize
394 struct alignedInput {
395 u64 alignment8;
396 HV_INPUT_POST_MESSAGE msg;
399 PHV_INPUT_POST_MESSAGE alignedMsg;
400 HV_STATUS status;
401 unsigned long addr;
403 if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
405 return -1;
408 addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
410 if (!addr)
412 return -1;
415 alignedMsg = (PHV_INPUT_POST_MESSAGE)(ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
417 alignedMsg->ConnectionId = connectionId;
418 alignedMsg->MessageType = messageType;
419 alignedMsg->PayloadSize = payloadSize;
420 memcpy((void*)alignedMsg->Payload, payload, payloadSize);
422 status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
424 kfree((void*)addr);
426 return status;
430 /*++
432 Name:
433 HvSignalEvent()
435 Description:
436 Signal an event on the specified connection using the hypervisor event IPC. This
437 involves a hypercall.
439 --*/
440 HV_STATUS HvSignalEvent(void)
442 HV_STATUS status;
444 status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, NULL) & 0xFFFF;
446 return status;
450 /*++
452 Name:
453 HvSynicInit()
455 Description:
456 Initialize the Synthethic Interrupt Controller. If it is already initialized by
457 another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
458 Otherwise, we create and initialize the message and event pages.
460 --*/
461 int HvSynicInit (u32 irqVector)
463 u64 version;
464 HV_SYNIC_SIMP simp;
465 HV_SYNIC_SIEFP siefp;
466 HV_SYNIC_SINT sharedSint;
467 HV_SYNIC_SCONTROL sctrl;
468 u64 guestID;
469 int ret=0;
471 DPRINT_ENTER(VMBUS);
473 if (!gHvContext.HypercallPage)
475 DPRINT_EXIT(VMBUS);
476 return ret;
479 /* Check the version */
480 rdmsrl(HV_X64_MSR_SVERSION, version);
482 DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
484 /* TODO: Handle SMP */
485 if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
487 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
489 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
490 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
492 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
494 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
495 rdmsrl(HV_X64_MSR_GUEST_OS_ID, guestID);
496 if (guestID == HV_LINUX_GUEST_ID)
498 gHvContext.synICMessagePage[0] = phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT);
499 gHvContext.synICEventPage[0] = phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT);
501 else
503 DPRINT_ERR(VMBUS, "unknown guest id!!");
504 goto Cleanup;
506 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
508 else
510 gHvContext.synICMessagePage[0] = osd_PageAlloc(1);
511 if (gHvContext.synICMessagePage[0] == NULL)
513 DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
514 goto Cleanup;
517 gHvContext.synICEventPage[0] = osd_PageAlloc(1);
518 if (gHvContext.synICEventPage[0] == NULL)
520 DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
521 goto Cleanup;
524 /* Setup the Synic's message page */
525 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
526 simp.SimpEnabled = 1;
527 simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
529 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
531 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
533 /* Setup the Synic's event page */
534 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
535 siefp.SiefpEnabled = 1;
536 siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
538 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
540 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
542 /* Setup the interception SINT. */
543 /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
544 /* interceptionSint.AsUINT64); */
546 /* Setup the shared SINT. */
547 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
549 sharedSint.AsUINT64 = 0;
550 sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
551 sharedSint.Masked = false;
552 sharedSint.AutoEoi = true;
554 DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
556 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
558 /* Enable the global synic bit */
559 rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
560 sctrl.Enable = 1;
562 wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
564 gHvContext.SynICInitialized = true;
566 DPRINT_EXIT(VMBUS);
568 return ret;
570 Cleanup:
571 ret = -1;
573 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
575 if (gHvContext.synICEventPage[0])
577 osd_PageFree(gHvContext.synICEventPage[0],1);
580 if (gHvContext.synICMessagePage[0])
582 osd_PageFree(gHvContext.synICMessagePage[0], 1);
586 DPRINT_EXIT(VMBUS);
588 return ret;
592 /*++
594 Name:
595 HvSynicCleanup()
597 Description:
598 Cleanup routine for HvSynicInit().
600 --*/
601 void HvSynicCleanup(void)
603 HV_SYNIC_SINT sharedSint;
604 HV_SYNIC_SIMP simp;
605 HV_SYNIC_SIEFP siefp;
607 DPRINT_ENTER(VMBUS);
609 if (!gHvContext.SynICInitialized)
611 DPRINT_EXIT(VMBUS);
612 return;
615 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
617 sharedSint.Masked = 1;
619 /* Disable the interrupt */
620 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
623 * Disable and free the resources only if we are running as
624 * native linux since in xenlinux, we are sharing the
625 * resources with the x2v shim
627 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
629 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
630 simp.SimpEnabled = 0;
631 simp.BaseSimpGpa = 0;
633 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
635 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
636 siefp.SiefpEnabled = 0;
637 siefp.BaseSiefpGpa = 0;
639 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
641 osd_PageFree(gHvContext.synICMessagePage[0], 1);
642 osd_PageFree(gHvContext.synICEventPage[0], 1);
645 DPRINT_EXIT(VMBUS);
649 /* eof */