Staging: hv: remove ReadMsr and WriteMsr functions from Hv.h
[linux-2.6/mini2440.git] / drivers / staging / hv / Hv.c
blob77003bb191ecfe33369f8bbad8a4b29c8c279b23
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/vmalloc.h>
25 #include <asm/io.h>
26 #include "include/logging.h"
27 #include "VmbusPrivate.h"
29 /* Globals */
31 /* The one and only */
32 HV_CONTEXT gHvContext={
33 .SynICInitialized = false,
34 .HypercallPage = NULL,
35 .SignalEventParam = NULL,
36 .SignalEventBuffer = NULL,
40 /*++
42 Name:
43 HvQueryHypervisorPresence()
45 Description:
46 Query the cpuid for presense of windows hypervisor
48 --*/
49 static int
50 HvQueryHypervisorPresence (
51 void
54 unsigned int eax;
55 unsigned int ebx;
56 unsigned int ecx;
57 unsigned int edx;
58 unsigned int op;
60 eax = 0;
61 ebx = 0;
62 ecx = 0;
63 edx = 0;
64 op = HvCpuIdFunctionVersionAndFeatures;
65 cpuid(op, &eax, &ebx, &ecx, &edx);
67 return (ecx & HV_PRESENT_BIT);
71 /*++
73 Name:
74 HvQueryHypervisorInfo()
76 Description:
77 Get version info of the windows hypervisor
79 --*/
80 static int
81 HvQueryHypervisorInfo (
82 void
85 unsigned int eax;
86 unsigned int ebx;
87 unsigned int ecx;
88 unsigned int edx;
89 unsigned int maxLeaf;
90 unsigned int op;
93 * Its assumed that this is called after confirming that Viridian
94 * is present. Query id and revision.
98 eax = 0;
99 ebx = 0;
100 ecx = 0;
101 edx = 0;
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",
106 (ebx & 0xFF),
107 ((ebx >> 8) & 0xFF),
108 ((ebx >> 16) & 0xFF),
109 ((ebx >> 24) & 0xFF),
110 (ecx & 0xFF),
111 ((ecx >> 8) & 0xFF),
112 ((ecx >> 16) & 0xFF),
113 ((ecx >> 24) & 0xFF),
114 (edx & 0xFF),
115 ((edx >> 8) & 0xFF),
116 ((edx >> 16) & 0xFF),
117 ((edx >> 24) & 0xFF));
119 maxLeaf = eax;
120 eax = 0;
121 ebx = 0;
122 ecx = 0;
123 edx = 0;
124 op = HvCpuIdFunctionHvInterface;
125 cpuid(op, &eax, &ebx, &ecx, &edx);
127 DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c",
128 (eax & 0xFF),
129 ((eax >> 8) & 0xFF),
130 ((eax >> 16) & 0xFF),
131 ((eax >> 24) & 0xFF));
133 if (maxLeaf >= HvCpuIdFunctionMsHvVersion) {
134 eax = 0;
135 ebx = 0;
136 ecx = 0;
137 edx = 0;
138 op = HvCpuIdFunctionMsHvVersion;
139 cpuid(op, &eax, &ebx, &ecx, &edx);
140 DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",
141 eax,
142 ebx >> 16,
143 ebx & 0xFFFF,
144 ecx,
145 edx >> 24,
146 edx & 0xFFFFFF);
148 return maxLeaf;
152 /*++
154 Name:
155 HvDoHypercall()
157 Description:
158 Invoke the specified hypercall
160 --*/
161 static u64
162 HvDoHypercall (
163 u64 Control,
164 void* Input,
165 void* Output
168 #ifdef CONFIG_X86_64
169 u64 hvStatus=0;
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>",
175 Control,
176 inputAddress,
177 Input,
178 outputAddress,
179 Output,
180 hypercallPage);
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);
187 return hvStatus;
189 #else
191 u32 controlHi = Control >> 32;
192 u32 controlLo = Control & 0xFFFFFFFF;
193 u32 hvStatusHi = 1;
194 u32 hvStatusLo = 1;
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>",
204 Control,
205 Input,
206 Output);
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));
214 #endif /* x86_64 */
217 /*++
219 Name:
220 HvInit()
222 Description:
223 Main initialization routine. This routine must be called
224 before any other routines in here are called
226 --*/
227 static int
228 HvInit (
229 void
232 int ret=0;
233 int maxLeaf;
234 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
235 void *virtAddr = NULL;
237 DPRINT_ENTER(VMBUS);
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!!");
245 goto Cleanup;
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 rdmsrl(HV_X64_MSR_GUEST_OS_ID, gHvContext.GuestId);
255 if (gHvContext.GuestId == 0)
257 /* Write our OS info */
258 wrmsrl(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 rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
265 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
267 /* Allocate the hypercall page memory */
268 /* virtAddr = osd_PageAlloc(1); */
269 virtAddr = osd_VirtualAllocExec(PAGE_SIZE);
271 if (!virtAddr)
273 DPRINT_ERR(VMBUS, "unable to allocate hypercall page!!");
274 goto Cleanup;
277 hypercallMsr.Enable = 1;
278 /* hypercallMsr.GuestPhysicalAddress = virt_to_phys(virtAddr) >> PAGE_SHIFT; */
279 hypercallMsr.GuestPhysicalAddress = vmalloc_to_pfn(virtAddr);
280 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
282 /* Confirm that hypercall page did get setup. */
283 hypercallMsr.AsUINT64 = 0;
284 rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
285 if (!hypercallMsr.Enable)
287 DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
288 goto Cleanup;
291 gHvContext.HypercallPage = virtAddr;
293 else
295 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", gHvContext.GuestId);
296 goto Cleanup;
299 DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
300 gHvContext.HypercallPage,
301 (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
303 /* Setup the global signal event param for the signal event hypercall */
304 gHvContext.SignalEventBuffer = kmalloc(sizeof(HV_INPUT_SIGNAL_EVENT_BUFFER), GFP_KERNEL);
305 if (!gHvContext.SignalEventBuffer)
307 goto Cleanup;
310 gHvContext.SignalEventParam = (PHV_INPUT_SIGNAL_EVENT)(ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer, HV_HYPERCALL_PARAM_ALIGN));
311 gHvContext.SignalEventParam->ConnectionId.Asu32 = 0;
312 gHvContext.SignalEventParam->ConnectionId.u.Id = VMBUS_EVENT_CONNECTION_ID;
313 gHvContext.SignalEventParam->FlagNumber = 0;
314 gHvContext.SignalEventParam->RsvdZ = 0;
316 /* DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId()); */
318 DPRINT_EXIT(VMBUS);
320 return ret;
322 Cleanup:
323 if (virtAddr)
325 if (hypercallMsr.Enable)
327 hypercallMsr.AsUINT64 = 0;
328 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
331 vfree(virtAddr);
333 ret = -1;
334 DPRINT_EXIT(VMBUS);
336 return ret;
340 /*++
342 Name:
343 HvCleanup()
345 Description:
346 Cleanup routine. This routine is called normally during driver unloading or exiting.
348 --*/
349 static void
350 HvCleanup (
351 void
354 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
356 DPRINT_ENTER(VMBUS);
358 if (gHvContext.SignalEventBuffer)
360 kfree(gHvContext.SignalEventBuffer);
361 gHvContext.SignalEventBuffer = NULL;
362 gHvContext.SignalEventParam = NULL;
365 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
367 if (gHvContext.HypercallPage)
369 hypercallMsr.AsUINT64 = 0;
370 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
371 vfree(gHvContext.HypercallPage);
372 gHvContext.HypercallPage = NULL;
376 DPRINT_EXIT(VMBUS);
381 /*++
383 Name:
384 HvPostMessage()
386 Description:
387 Post a message using the hypervisor message IPC. This
388 involves a hypercall.
390 --*/
391 static HV_STATUS
392 HvPostMessage(
393 HV_CONNECTION_ID connectionId,
394 HV_MESSAGE_TYPE messageType,
395 void * payload,
396 size_t payloadSize
399 struct alignedInput {
400 u64 alignment8;
401 HV_INPUT_POST_MESSAGE msg;
404 PHV_INPUT_POST_MESSAGE alignedMsg;
405 HV_STATUS status;
406 unsigned long addr;
408 if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
410 return -1;
413 addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
415 if (!addr)
417 return -1;
420 alignedMsg = (PHV_INPUT_POST_MESSAGE)(ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
422 alignedMsg->ConnectionId = connectionId;
423 alignedMsg->MessageType = messageType;
424 alignedMsg->PayloadSize = payloadSize;
425 memcpy((void*)alignedMsg->Payload, payload, payloadSize);
427 status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
429 kfree((void*)addr);
431 return status;
435 /*++
437 Name:
438 HvSignalEvent()
440 Description:
441 Signal an event on the specified connection using the hypervisor event IPC. This
442 involves a hypercall.
444 --*/
445 static HV_STATUS
446 HvSignalEvent(void)
448 HV_STATUS status;
450 status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, NULL) & 0xFFFF;
452 return status;
456 /*++
458 Name:
459 HvSynicInit()
461 Description:
462 Initialize the Synthethic Interrupt Controller. If it is already initialized by
463 another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
464 Otherwise, we create and initialize the message and event pages.
466 --*/
467 static int
468 HvSynicInit (
469 u32 irqVector
472 u64 version;
473 HV_SYNIC_SIMP simp;
474 HV_SYNIC_SIEFP siefp;
475 HV_SYNIC_SINT sharedSint;
476 HV_SYNIC_SCONTROL sctrl;
477 u64 guestID;
478 int ret=0;
480 DPRINT_ENTER(VMBUS);
482 if (!gHvContext.HypercallPage)
484 DPRINT_EXIT(VMBUS);
485 return ret;
488 /* Check the version */
489 rdmsrl(HV_X64_MSR_SVERSION, version);
491 DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
493 /* TODO: Handle SMP */
494 if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
496 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
498 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
499 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
501 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
503 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
504 rdmsrl(HV_X64_MSR_GUEST_OS_ID, guestID);
505 if (guestID == HV_LINUX_GUEST_ID)
507 gHvContext.synICMessagePage[0] = phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT);
508 gHvContext.synICEventPage[0] = phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT);
510 else
512 DPRINT_ERR(VMBUS, "unknown guest id!!");
513 goto Cleanup;
515 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
517 else
519 gHvContext.synICMessagePage[0] = osd_PageAlloc(1);
520 if (gHvContext.synICMessagePage[0] == NULL)
522 DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
523 goto Cleanup;
526 gHvContext.synICEventPage[0] = osd_PageAlloc(1);
527 if (gHvContext.synICEventPage[0] == NULL)
529 DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
530 goto Cleanup;
533 /* Setup the Synic's message page */
534 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
535 simp.SimpEnabled = 1;
536 simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
538 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
540 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
542 /* Setup the Synic's event page */
543 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
544 siefp.SiefpEnabled = 1;
545 siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
547 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
549 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
551 /* Setup the interception SINT. */
552 /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
553 /* interceptionSint.AsUINT64); */
555 /* Setup the shared SINT. */
556 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
558 sharedSint.AsUINT64 = 0;
559 sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
560 sharedSint.Masked = false;
561 sharedSint.AutoEoi = true;
563 DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
565 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
567 /* Enable the global synic bit */
568 rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
569 sctrl.Enable = 1;
571 wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
573 gHvContext.SynICInitialized = true;
575 DPRINT_EXIT(VMBUS);
577 return ret;
579 Cleanup:
580 ret = -1;
582 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
584 if (gHvContext.synICEventPage[0])
586 osd_PageFree(gHvContext.synICEventPage[0],1);
589 if (gHvContext.synICMessagePage[0])
591 osd_PageFree(gHvContext.synICMessagePage[0], 1);
595 DPRINT_EXIT(VMBUS);
597 return ret;
601 /*++
603 Name:
604 HvSynicCleanup()
606 Description:
607 Cleanup routine for HvSynicInit().
609 --*/
610 static void
611 HvSynicCleanup(
612 void
615 HV_SYNIC_SINT sharedSint;
616 HV_SYNIC_SIMP simp;
617 HV_SYNIC_SIEFP siefp;
619 DPRINT_ENTER(VMBUS);
621 if (!gHvContext.SynICInitialized)
623 DPRINT_EXIT(VMBUS);
624 return;
627 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
629 sharedSint.Masked = 1;
631 /* Disable the interrupt */
632 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
635 * Disable and free the resources only if we are running as
636 * native linux since in xenlinux, we are sharing the
637 * resources with the x2v shim
639 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
641 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
642 simp.SimpEnabled = 0;
643 simp.BaseSimpGpa = 0;
645 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
647 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
648 siefp.SiefpEnabled = 0;
649 siefp.BaseSiefpGpa = 0;
651 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
653 osd_PageFree(gHvContext.synICMessagePage[0], 1);
654 osd_PageFree(gHvContext.synICEventPage[0], 1);
657 DPRINT_EXIT(VMBUS);
661 /* eof */