]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - drivers/staging/hv/Hv.c
Staging: hv: osd: remove physical address wrapper functions
[mirror_ubuntu-bionic-kernel.git] / drivers / staging / hv / Hv.c
1 /*
2 *
3 * Copyright (c) 2009, Microsoft Corporation.
4 *
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.
8 *
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.
13 *
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.
17 *
18 * Authors:
19 * Haiyang Zhang <haiyangz@microsoft.com>
20 * Hank Janssen <hjanssen@microsoft.com>
21 *
22 */
23
24 #include <linux/vmalloc.h>
25 #include <asm/io.h>
26 #include "include/logging.h"
27 #include "VmbusPrivate.h"
28
29 /* Globals */
30
31 /* The one and only */
32 HV_CONTEXT gHvContext={
33 .SynICInitialized = false,
34 .HypercallPage = NULL,
35 .SignalEventParam = NULL,
36 .SignalEventBuffer = NULL,
37 };
38
39
40 /*++
41
42 Name:
43 HvQueryHypervisorPresence()
44
45 Description:
46 Query the cpuid for presense of windows hypervisor
47
48 --*/
49 static int
50 HvQueryHypervisorPresence (
51 void
52 )
53 {
54 unsigned int eax;
55 unsigned int ebx;
56 unsigned int ecx;
57 unsigned int edx;
58 unsigned int op;
59
60 eax = 0;
61 ebx = 0;
62 ecx = 0;
63 edx = 0;
64 op = HvCpuIdFunctionVersionAndFeatures;
65 cpuid(op, &eax, &ebx, &ecx, &edx);
66
67 return (ecx & HV_PRESENT_BIT);
68 }
69
70
71 /*++
72
73 Name:
74 HvQueryHypervisorInfo()
75
76 Description:
77 Get version info of the windows hypervisor
78
79 --*/
80 static int
81 HvQueryHypervisorInfo (
82 void
83 )
84 {
85 unsigned int eax;
86 unsigned int ebx;
87 unsigned int ecx;
88 unsigned int edx;
89 unsigned int maxLeaf;
90 unsigned int op;
91
92 /*
93 * Its assumed that this is called after confirming that Viridian
94 * is present. Query id and revision.
95 */
96
97
98 eax = 0;
99 ebx = 0;
100 ecx = 0;
101 edx = 0;
102 op = HvCpuIdFunctionHvVendorAndMaxFunction;
103 cpuid(op, &eax, &ebx, &ecx, &edx);
104
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));
118
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);
126
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));
132
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);
147 }
148 return maxLeaf;
149 }
150
151
152 /*++
153
154 Name:
155 HvDoHypercall()
156
157 Description:
158 Invoke the specified hypercall
159
160 --*/
161 static u64
162 HvDoHypercall (
163 u64 Control,
164 void* Input,
165 void* Output
166 )
167 {
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;
173
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);
181
182 __asm__ __volatile__ ("mov %0, %%r8" : : "r" (outputAddress): "r8");
183 __asm__ __volatile__ ("call *%3" : "=a"(hvStatus): "c" (Control), "d" (inputAddress), "m" (hypercallPage));
184
185 DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatus);
186
187 return hvStatus;
188
189 #else
190
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;
202
203 DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>",
204 Control,
205 Input,
206 Output);
207
208 __asm__ __volatile__ ("call *%8" : "=d"(hvStatusHi), "=a"(hvStatusLo) : "d" (controlHi), "a" (controlLo), "b" (inputAddressHi), "c" (inputAddressLo), "D"(outputAddressHi), "S"(outputAddressLo), "m" (hypercallPage));
209
210
211 DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatusLo | ((u64)hvStatusHi << 32));
212
213 return (hvStatusLo | ((u64)hvStatusHi << 32));
214 #endif /* x86_64 */
215 }
216
217 /*++
218
219 Name:
220 HvInit()
221
222 Description:
223 Main initialization routine. This routine must be called
224 before any other routines in here are called
225
226 --*/
227 static int
228 HvInit (
229 void
230 )
231 {
232 int ret=0;
233 int maxLeaf;
234 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
235 void *virtAddr = NULL;
236
237 DPRINT_ENTER(VMBUS);
238
239 memset(gHvContext.synICEventPage, 0, sizeof(void *) * MAX_NUM_CPUS);
240 memset(gHvContext.synICMessagePage, 0, sizeof(void *) * MAX_NUM_CPUS);
241
242 if (!HvQueryHypervisorPresence())
243 {
244 DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!");
245 goto Cleanup;
246 }
247
248 DPRINT_INFO(VMBUS, "Windows hypervisor detected! Retrieving more info...");
249
250 maxLeaf = HvQueryHypervisorInfo();
251 /* HvQueryHypervisorFeatures(maxLeaf); */
252
253 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
254 gHvContext.GuestId = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
255
256 if (gHvContext.GuestId == 0)
257 {
258 /* Write our OS info */
259 WriteMsr(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
260
261 gHvContext.GuestId = HV_LINUX_GUEST_ID;
262 }
263
264 /* See if the hypercall page is already set */
265 hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
266
267 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
268 {
269 /* Allocate the hypercall page memory */
270 /* virtAddr = PageAlloc(1); */
271 virtAddr = VirtualAllocExec(PAGE_SIZE);
272
273 if (!virtAddr)
274 {
275 DPRINT_ERR(VMBUS, "unable to allocate hypercall page!!");
276 goto Cleanup;
277 }
278
279 hypercallMsr.Enable = 1;
280 /* hypercallMsr.GuestPhysicalAddress = virt_to_phys(virtAddr) >> PAGE_SHIFT; */
281 hypercallMsr.GuestPhysicalAddress = vmalloc_to_pfn(virtAddr);
282 WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
283
284 /* Confirm that hypercall page did get setup. */
285 hypercallMsr.AsUINT64 = 0;
286 hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
287
288 if (!hypercallMsr.Enable)
289 {
290 DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
291 goto Cleanup;
292 }
293
294 gHvContext.HypercallPage = virtAddr;
295 }
296 else
297 {
298 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", gHvContext.GuestId);
299 goto Cleanup;
300 }
301
302 DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
303 gHvContext.HypercallPage,
304 (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
305
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)
309 {
310 goto Cleanup;
311 }
312
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;
318
319 /* DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId()); */
320
321 DPRINT_EXIT(VMBUS);
322
323 return ret;
324
325 Cleanup:
326 if (virtAddr)
327 {
328 if (hypercallMsr.Enable)
329 {
330 hypercallMsr.AsUINT64 = 0;
331 WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
332 }
333
334 vfree(virtAddr);
335 }
336 ret = -1;
337 DPRINT_EXIT(VMBUS);
338
339 return ret;
340 }
341
342
343 /*++
344
345 Name:
346 HvCleanup()
347
348 Description:
349 Cleanup routine. This routine is called normally during driver unloading or exiting.
350
351 --*/
352 static void
353 HvCleanup (
354 void
355 )
356 {
357 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
358
359 DPRINT_ENTER(VMBUS);
360
361 if (gHvContext.SignalEventBuffer)
362 {
363 kfree(gHvContext.SignalEventBuffer);
364 gHvContext.SignalEventBuffer = NULL;
365 gHvContext.SignalEventParam = NULL;
366 }
367
368 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
369 {
370 if (gHvContext.HypercallPage)
371 {
372 hypercallMsr.AsUINT64 = 0;
373 WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
374 vfree(gHvContext.HypercallPage);
375 gHvContext.HypercallPage = NULL;
376 }
377 }
378
379 DPRINT_EXIT(VMBUS);
380
381 }
382
383
384 /*++
385
386 Name:
387 HvPostMessage()
388
389 Description:
390 Post a message using the hypervisor message IPC. This
391 involves a hypercall.
392
393 --*/
394 static HV_STATUS
395 HvPostMessage(
396 HV_CONNECTION_ID connectionId,
397 HV_MESSAGE_TYPE messageType,
398 void * payload,
399 size_t payloadSize
400 )
401 {
402 struct alignedInput {
403 u64 alignment8;
404 HV_INPUT_POST_MESSAGE msg;
405 };
406
407 PHV_INPUT_POST_MESSAGE alignedMsg;
408 HV_STATUS status;
409 unsigned long addr;
410
411 if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
412 {
413 return -1;
414 }
415
416 addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
417
418 if (!addr)
419 {
420 return -1;
421 }
422
423 alignedMsg = (PHV_INPUT_POST_MESSAGE)(ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
424
425 alignedMsg->ConnectionId = connectionId;
426 alignedMsg->MessageType = messageType;
427 alignedMsg->PayloadSize = payloadSize;
428 memcpy((void*)alignedMsg->Payload, payload, payloadSize);
429
430 status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
431
432 kfree((void*)addr);
433
434 return status;
435 }
436
437
438 /*++
439
440 Name:
441 HvSignalEvent()
442
443 Description:
444 Signal an event on the specified connection using the hypervisor event IPC. This
445 involves a hypercall.
446
447 --*/
448 static HV_STATUS
449 HvSignalEvent(void)
450 {
451 HV_STATUS status;
452
453 status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, NULL) & 0xFFFF;
454
455 return status;
456 }
457
458
459 /*++
460
461 Name:
462 HvSynicInit()
463
464 Description:
465 Initialize the Synthethic Interrupt Controller. If it is already initialized by
466 another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
467 Otherwise, we create and initialize the message and event pages.
468
469 --*/
470 static int
471 HvSynicInit (
472 u32 irqVector
473 )
474 {
475 u64 version;
476 HV_SYNIC_SIMP simp;
477 HV_SYNIC_SIEFP siefp;
478 HV_SYNIC_SINT sharedSint;
479 HV_SYNIC_SCONTROL sctrl;
480 u64 guestID;
481 int ret=0;
482
483 DPRINT_ENTER(VMBUS);
484
485 if (!gHvContext.HypercallPage)
486 {
487 DPRINT_EXIT(VMBUS);
488 return ret;
489 }
490
491 /* Check the version */
492 version = ReadMsr(HV_X64_MSR_SVERSION);
493
494 DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
495
496 /* TODO: Handle SMP */
497 if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
498 {
499 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
500
501 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
502 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
503
504 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
505
506 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
507 guestID = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
508
509 if (guestID == HV_LINUX_GUEST_ID)
510 {
511 gHvContext.synICMessagePage[0] = phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT);
512 gHvContext.synICEventPage[0] = phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT);
513 }
514 else
515 {
516 DPRINT_ERR(VMBUS, "unknown guest id!!");
517 goto Cleanup;
518 }
519 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
520 }
521 else
522 {
523 gHvContext.synICMessagePage[0] = PageAlloc(1);
524 if (gHvContext.synICMessagePage[0] == NULL)
525 {
526 DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
527 goto Cleanup;
528 }
529
530 gHvContext.synICEventPage[0] = PageAlloc(1);
531 if (gHvContext.synICEventPage[0] == NULL)
532 {
533 DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
534 goto Cleanup;
535 }
536
537 /* Setup the Synic's message page */
538 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
539 simp.SimpEnabled = 1;
540 simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
541
542 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
543
544 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
545
546 /* Setup the Synic's event page */
547 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
548 siefp.SiefpEnabled = 1;
549 siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
550
551 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
552
553 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
554 }
555 /* Setup the interception SINT. */
556 /* WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
557 /* interceptionSint.AsUINT64); */
558
559 /* Setup the shared SINT. */
560 sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
561
562 sharedSint.AsUINT64 = 0;
563 sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
564 sharedSint.Masked = false;
565 sharedSint.AutoEoi = true;
566
567 DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
568
569 WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
570
571 /* Enable the global synic bit */
572 sctrl.AsUINT64 = ReadMsr(HV_X64_MSR_SCONTROL);
573 sctrl.Enable = 1;
574
575 WriteMsr(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
576
577 gHvContext.SynICInitialized = true;
578
579 DPRINT_EXIT(VMBUS);
580
581 return ret;
582
583 Cleanup:
584 ret = -1;
585
586 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
587 {
588 if (gHvContext.synICEventPage[0])
589 {
590 PageFree(gHvContext.synICEventPage[0],1);
591 }
592
593 if (gHvContext.synICMessagePage[0])
594 {
595 PageFree(gHvContext.synICMessagePage[0], 1);
596 }
597 }
598
599 DPRINT_EXIT(VMBUS);
600
601 return ret;
602
603 }
604
605 /*++
606
607 Name:
608 HvSynicCleanup()
609
610 Description:
611 Cleanup routine for HvSynicInit().
612
613 --*/
614 static void
615 HvSynicCleanup(
616 void
617 )
618 {
619 HV_SYNIC_SINT sharedSint;
620 HV_SYNIC_SIMP simp;
621 HV_SYNIC_SIEFP siefp;
622
623 DPRINT_ENTER(VMBUS);
624
625 if (!gHvContext.SynICInitialized)
626 {
627 DPRINT_EXIT(VMBUS);
628 return;
629 }
630
631 sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
632
633 sharedSint.Masked = 1;
634
635 /* Disable the interrupt */
636 WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
637
638 /*
639 * Disable and free the resources only if we are running as
640 * native linux since in xenlinux, we are sharing the
641 * resources with the x2v shim
642 */
643 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
644 {
645 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
646 simp.SimpEnabled = 0;
647 simp.BaseSimpGpa = 0;
648
649 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
650
651 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
652 siefp.SiefpEnabled = 0;
653 siefp.BaseSiefpGpa = 0;
654
655 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
656
657 PageFree(gHvContext.synICMessagePage[0], 1);
658 PageFree(gHvContext.synICEventPage[0], 1);
659 }
660
661 DPRINT_EXIT(VMBUS);
662 }
663
664
665 /* eof */