]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - drivers/staging/hv/Hv.c
Staging: hv: Remove C99 comments
[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
25 #include "include/logging.h"
26 #include "VmbusPrivate.h"
27
28 /* Globals */
29
30 /* The one and only */
31 HV_CONTEXT gHvContext={
32 .SynICInitialized = false,
33 .HypercallPage = NULL,
34 .SignalEventParam = NULL,
35 .SignalEventBuffer = NULL,
36 };
37
38
39 /*++
40
41 Name:
42 HvQueryHypervisorPresence()
43
44 Description:
45 Query the cpuid for presense of windows hypervisor
46
47 --*/
48 static int
49 HvQueryHypervisorPresence (
50 void
51 )
52 {
53 unsigned int eax;
54 unsigned int ebx;
55 unsigned int ecx;
56 unsigned int edx;
57 unsigned int op;
58
59 eax = 0;
60 ebx = 0;
61 ecx = 0;
62 edx = 0;
63 op = HvCpuIdFunctionVersionAndFeatures;
64 do_cpuid(op, &eax, &ebx, &ecx, &edx);
65
66 return (ecx & HV_PRESENT_BIT);
67 }
68
69
70 /*++
71
72 Name:
73 HvQueryHypervisorInfo()
74
75 Description:
76 Get version info of the windows hypervisor
77
78 --*/
79 static int
80 HvQueryHypervisorInfo (
81 void
82 )
83 {
84 unsigned int eax;
85 unsigned int ebx;
86 unsigned int ecx;
87 unsigned int edx;
88 unsigned int maxLeaf;
89 unsigned int op;
90
91 /*
92 * Its assumed that this is called after confirming that Viridian
93 * is present. Query id and revision.
94 */
95
96
97 eax = 0;
98 ebx = 0;
99 ecx = 0;
100 edx = 0;
101 op = HvCpuIdFunctionHvVendorAndMaxFunction;
102 do_cpuid(op, &eax, &ebx, &ecx, &edx);
103
104 DPRINT_INFO(VMBUS, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c",
105 (ebx & 0xFF),
106 ((ebx >> 8) & 0xFF),
107 ((ebx >> 16) & 0xFF),
108 ((ebx >> 24) & 0xFF),
109 (ecx & 0xFF),
110 ((ecx >> 8) & 0xFF),
111 ((ecx >> 16) & 0xFF),
112 ((ecx >> 24) & 0xFF),
113 (edx & 0xFF),
114 ((edx >> 8) & 0xFF),
115 ((edx >> 16) & 0xFF),
116 ((edx >> 24) & 0xFF));
117
118 maxLeaf = eax;
119 eax = 0;
120 ebx = 0;
121 ecx = 0;
122 edx = 0;
123 op = HvCpuIdFunctionHvInterface;
124 do_cpuid(op, &eax, &ebx, &ecx, &edx);
125
126 DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c",
127 (eax & 0xFF),
128 ((eax >> 8) & 0xFF),
129 ((eax >> 16) & 0xFF),
130 ((eax >> 24) & 0xFF));
131
132 if (maxLeaf >= HvCpuIdFunctionMsHvVersion) {
133 eax = 0;
134 ebx = 0;
135 ecx = 0;
136 edx = 0;
137 op = HvCpuIdFunctionMsHvVersion;
138 do_cpuid(op, &eax, &ebx, &ecx, &edx);
139 DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",
140 eax,
141 ebx >> 16,
142 ebx & 0xFFFF,
143 ecx,
144 edx >> 24,
145 edx & 0xFFFFFF);
146 }
147 return maxLeaf;
148 }
149
150
151 /*++
152
153 Name:
154 HvDoHypercall()
155
156 Description:
157 Invoke the specified hypercall
158
159 --*/
160 static u64
161 HvDoHypercall (
162 u64 Control,
163 void* Input,
164 void* Output
165 )
166 {
167 #ifdef CONFIG_X86_64
168 u64 hvStatus=0;
169 u64 inputAddress = (Input)? GetPhysicalAddress(Input) : 0;
170 u64 outputAddress = (Output)? GetPhysicalAddress(Output) : 0;
171 volatile void* hypercallPage = gHvContext.HypercallPage;
172
173 DPRINT_DBG(VMBUS, "Hypercall <control %llx input phys %llx virt %p output phys %llx virt %p hypercall %p>",
174 Control,
175 inputAddress,
176 Input,
177 outputAddress,
178 Output,
179 hypercallPage);
180
181 __asm__ __volatile__ ("mov %0, %%r8" : : "r" (outputAddress): "r8");
182 __asm__ __volatile__ ("call *%3" : "=a"(hvStatus): "c" (Control), "d" (inputAddress), "m" (hypercallPage));
183
184 DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatus);
185
186 return hvStatus;
187
188 #else
189
190 u32 controlHi = Control >> 32;
191 u32 controlLo = Control & 0xFFFFFFFF;
192 u32 hvStatusHi = 1;
193 u32 hvStatusLo = 1;
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;
201
202 DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>",
203 Control,
204 Input,
205 Output);
206
207 __asm__ __volatile__ ("call *%8" : "=d"(hvStatusHi), "=a"(hvStatusLo) : "d" (controlHi), "a" (controlLo), "b" (inputAddressHi), "c" (inputAddressLo), "D"(outputAddressHi), "S"(outputAddressLo), "m" (hypercallPage));
208
209
210 DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatusLo | ((u64)hvStatusHi << 32));
211
212 return (hvStatusLo | ((u64)hvStatusHi << 32));
213 #endif /* x86_64 */
214 }
215
216 /*++
217
218 Name:
219 HvInit()
220
221 Description:
222 Main initialization routine. This routine must be called
223 before any other routines in here are called
224
225 --*/
226 static int
227 HvInit (
228 void
229 )
230 {
231 int ret=0;
232 int maxLeaf;
233 HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
234 void* virtAddr=0;
235 unsigned long physAddr=0;
236
237 DPRINT_ENTER(VMBUS);
238
239 memset(gHvContext.synICEventPage, 0, sizeof(HANDLE)*MAX_NUM_CPUS);
240 memset(gHvContext.synICMessagePage, 0, sizeof(HANDLE)*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 = Logical2PhysicalAddr(virtAddr) >> PAGE_SHIFT; */
281 hypercallMsr.GuestPhysicalAddress = Virtual2Physical(virtAddr) >> PAGE_SHIFT;
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 VirtualFree(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 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 VirtualFree(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 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, 0) & 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 HV_STATUS
449 HvSignalEvent(
450 )
451 {
452 HV_STATUS status;
453
454 status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, 0) & 0xFFFF;
455
456 return status;
457 }
458
459
460 /*++
461
462 Name:
463 HvSynicInit()
464
465 Description:
466 Initialize the Synthethic Interrupt Controller. If it is already initialized by
467 another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
468 Otherwise, we create and initialize the message and event pages.
469
470 --*/
471 int
472 HvSynicInit (
473 u32 irqVector
474 )
475 {
476 u64 version;
477 HV_SYNIC_SIMP simp;
478 HV_SYNIC_SIEFP siefp;
479 HV_SYNIC_SINT sharedSint;
480 HV_SYNIC_SCONTROL sctrl;
481 u64 guestID;
482 int ret=0;
483
484 DPRINT_ENTER(VMBUS);
485
486 if (!gHvContext.HypercallPage)
487 {
488 DPRINT_EXIT(VMBUS);
489 return ret;
490 }
491
492 /* Check the version */
493 version = ReadMsr(HV_X64_MSR_SVERSION);
494
495 DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
496
497 /* TODO: Handle SMP */
498 if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
499 {
500 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
501
502 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
503 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
504
505 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
506
507 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
508 guestID = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
509
510 if (guestID == HV_LINUX_GUEST_ID)
511 {
512 gHvContext.synICMessagePage[0] = GetVirtualAddress(simp.BaseSimpGpa << PAGE_SHIFT);
513 gHvContext.synICEventPage[0] = GetVirtualAddress(siefp.BaseSiefpGpa << PAGE_SHIFT);
514 }
515 else
516 {
517 DPRINT_ERR(VMBUS, "unknown guest id!!");
518 goto Cleanup;
519 }
520 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
521 }
522 else
523 {
524 gHvContext.synICMessagePage[0] = PageAlloc(1);
525 if (gHvContext.synICMessagePage[0] == NULL)
526 {
527 DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
528 goto Cleanup;
529 }
530
531 gHvContext.synICEventPage[0] = PageAlloc(1);
532 if (gHvContext.synICEventPage[0] == NULL)
533 {
534 DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
535 goto Cleanup;
536 }
537
538 /* Setup the Synic's message page */
539 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
540 simp.SimpEnabled = 1;
541 simp.BaseSimpGpa = GetPhysicalAddress(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
542
543 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
544
545 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
546
547 /* Setup the Synic's event page */
548 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
549 siefp.SiefpEnabled = 1;
550 siefp.BaseSiefpGpa = GetPhysicalAddress(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
551
552 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
553
554 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
555 }
556 /* Setup the interception SINT. */
557 /* WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
558 /* interceptionSint.AsUINT64); */
559
560 /* Setup the shared SINT. */
561 sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
562
563 sharedSint.AsUINT64 = 0;
564 sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
565 sharedSint.Masked = false;
566 sharedSint.AutoEoi = true;
567
568 DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
569
570 WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
571
572 /* Enable the global synic bit */
573 sctrl.AsUINT64 = ReadMsr(HV_X64_MSR_SCONTROL);
574 sctrl.Enable = 1;
575
576 WriteMsr(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
577
578 gHvContext.SynICInitialized = true;
579
580 DPRINT_EXIT(VMBUS);
581
582 return ret;
583
584 Cleanup:
585 ret = -1;
586
587 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
588 {
589 if (gHvContext.synICEventPage[0])
590 {
591 PageFree(gHvContext.synICEventPage[0],1);
592 }
593
594 if (gHvContext.synICMessagePage[0])
595 {
596 PageFree(gHvContext.synICMessagePage[0], 1);
597 }
598 }
599
600 DPRINT_EXIT(VMBUS);
601
602 return ret;
603
604 }
605
606 /*++
607
608 Name:
609 HvSynicCleanup()
610
611 Description:
612 Cleanup routine for HvSynicInit().
613
614 --*/
615 void
616 HvSynicCleanup(
617 void
618 )
619 {
620 HV_SYNIC_SINT sharedSint;
621 HV_SYNIC_SIMP simp;
622 HV_SYNIC_SIEFP siefp;
623
624 DPRINT_ENTER(VMBUS);
625
626 if (!gHvContext.SynICInitialized)
627 {
628 DPRINT_EXIT(VMBUS);
629 return;
630 }
631
632 sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
633
634 sharedSint.Masked = 1;
635
636 /* Disable the interrupt */
637 WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
638
639 /*
640 * Disable and free the resources only if we are running as
641 * native linux since in xenlinux, we are sharing the
642 * resources with the x2v shim
643 */
644 if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
645 {
646 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
647 simp.SimpEnabled = 0;
648 simp.BaseSimpGpa = 0;
649
650 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
651
652 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
653 siefp.SiefpEnabled = 0;
654 siefp.BaseSiefpGpa = 0;
655
656 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
657
658 PageFree(gHvContext.synICMessagePage[0], 1);
659 PageFree(gHvContext.synICEventPage[0], 1);
660 }
661
662 DPRINT_EXIT(VMBUS);
663 }
664
665
666 /* eof */