]> Pileus Git - ~andy/linux/blob - drivers/staging/hv/Hv.c
Staging: hv: remove UINT32 and INT32 typedefs
[~andy/linux] / 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 //
29 // Globals
30 //
31
32 // The one and only
33 HV_CONTEXT gHvContext={
34         .SynICInitialized = FALSE,
35         .HypercallPage = NULL,
36         .SignalEventParam = NULL,
37         .SignalEventBuffer = NULL,
38 };
39
40
41 /*++
42
43 Name:
44         HvQueryHypervisorPresence()
45
46 Description:
47         Query the cpuid for presense of windows hypervisor
48
49 --*/
50 static int
51 HvQueryHypervisorPresence (
52     void
53     )
54 {
55     unsigned int eax;
56     unsigned int ebx;
57     unsigned int ecx;
58     unsigned int edx;
59     unsigned int op;
60
61     eax = 0;
62     ebx = 0;
63     ecx = 0;
64     edx = 0;
65     op = HvCpuIdFunctionVersionAndFeatures;
66     do_cpuid(op, &eax, &ebx, &ecx, &edx);
67
68         return (ecx & HV_PRESENT_BIT);
69 }
70
71
72 /*++
73
74 Name:
75         HvQueryHypervisorInfo()
76
77 Description:
78         Get version info of the windows hypervisor
79
80 --*/
81 static int
82 HvQueryHypervisorInfo (
83     void
84     )
85 {
86     unsigned int eax;
87     unsigned int ebx;
88     unsigned int ecx;
89     unsigned int edx;
90     unsigned int maxLeaf;
91     unsigned int op;
92
93     //
94     // Its assumed that this is called after confirming that Viridian is present.
95     // Query id and revision.
96     //
97
98     eax = 0;
99     ebx = 0;
100     ecx = 0;
101     edx = 0;
102     op = HvCpuIdFunctionHvVendorAndMaxFunction;
103     do_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     do_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         do_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 UINT64
162 HvDoHypercall (
163     UINT64  Control,
164     void*   Input,
165     void*   Output
166     )
167 {
168 #ifdef CONFIG_X86_64
169     UINT64 hvStatus=0;
170     UINT64 inputAddress = (Input)? GetPhysicalAddress(Input) : 0;
171         UINT64 outputAddress = (Output)? GetPhysicalAddress(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     UINT64 inputAddress = (Input) ? GetPhysicalAddress(Input) : 0;
196     u32 inputAddressHi = inputAddress >> 32;
197     u32 inputAddressLo = inputAddress & 0xFFFFFFFF;
198         UINT64 outputAddress = (Output) ?GetPhysicalAddress(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 | ((UINT64)hvStatusHi << 32));
212
213     return (hvStatusLo | ((UINT64)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=0;
236         ULONG_PTR physAddr=0;
237
238         DPRINT_ENTER(VMBUS);
239
240         memset(gHvContext.synICEventPage, 0, sizeof(HANDLE)*MAX_NUM_CPUS);
241         memset(gHvContext.synICMessagePage, 0, sizeof(HANDLE)*MAX_NUM_CPUS);
242
243         if (!HvQueryHypervisorPresence())
244         {
245                 DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!");
246                 goto Cleanup;
247         }
248
249         DPRINT_INFO(VMBUS, "Windows hypervisor detected! Retrieving more info...");
250
251     maxLeaf = HvQueryHypervisorInfo();
252     //HvQueryHypervisorFeatures(maxLeaf);
253
254         // Determine if we are running on xenlinux (ie x2v shim) or native linux
255         gHvContext.GuestId = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
256
257         if (gHvContext.GuestId == 0)
258         {
259                 // Write our OS info
260                 WriteMsr(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
261
262                 gHvContext.GuestId = HV_LINUX_GUEST_ID;
263         }
264
265         // See if the hypercall page is already set
266         hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
267
268         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
269         {
270                 // Allocate the hypercall page memory
271                 //virtAddr = PageAlloc(1);
272                 virtAddr = VirtualAllocExec(PAGE_SIZE);
273
274                 if (!virtAddr)
275                 {
276                         DPRINT_ERR(VMBUS, "unable to allocate hypercall page!!");
277                         goto Cleanup;
278                 }
279
280                 hypercallMsr.Enable = 1;
281                 //hypercallMsr.GuestPhysicalAddress = Logical2PhysicalAddr(virtAddr) >> PAGE_SHIFT;
282                 hypercallMsr.GuestPhysicalAddress = Virtual2Physical(virtAddr) >> PAGE_SHIFT;
283                 WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
284
285         // Confirm that hypercall page did get setup.
286                 hypercallMsr.AsUINT64 = 0;
287                 hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
288
289                 if (!hypercallMsr.Enable)
290                 {
291                         DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
292                         goto Cleanup;
293                 }
294
295                 gHvContext.HypercallPage = virtAddr;
296         }
297         else
298         {
299                 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", gHvContext.GuestId);
300                 goto Cleanup;
301         }
302
303     DPRINT_INFO(VMBUS, "Hypercall page VA=0x%08x, PA=0x%08x",
304                (unsigned long)gHvContext.HypercallPage,
305                (unsigned long)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
306
307         // Setup the global signal event param for the signal event hypercall
308         gHvContext.SignalEventBuffer = MemAlloc(sizeof(HV_INPUT_SIGNAL_EVENT_BUFFER));
309         if (!gHvContext.SignalEventBuffer)
310         {
311                 goto Cleanup;
312         }
313
314         gHvContext.SignalEventParam = (PHV_INPUT_SIGNAL_EVENT)(ALIGN_UP((ULONG_PTR)gHvContext.SignalEventBuffer, HV_HYPERCALL_PARAM_ALIGN));
315         gHvContext.SignalEventParam->ConnectionId.Asu32 = 0;
316         gHvContext.SignalEventParam->ConnectionId.u.Id = VMBUS_EVENT_CONNECTION_ID;
317         gHvContext.SignalEventParam->FlagNumber = 0;
318         gHvContext.SignalEventParam->RsvdZ = 0;
319
320     //DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId());
321
322         DPRINT_EXIT(VMBUS);
323
324     return ret;
325
326 Cleanup:
327         if (virtAddr)
328         {
329                 if (hypercallMsr.Enable)
330                 {
331                         hypercallMsr.AsUINT64 = 0;
332                         WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
333                 }
334
335                 VirtualFree(virtAddr);
336         }
337         ret = -1;
338         DPRINT_EXIT(VMBUS);
339
340         return ret;
341 }
342
343
344 /*++
345
346 Name:
347         HvCleanup()
348
349 Description:
350         Cleanup routine. This routine is called normally during driver unloading or exiting.
351
352 --*/
353 void
354 HvCleanup (
355     void
356     )
357 {
358         HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
359
360         DPRINT_ENTER(VMBUS);
361
362         if (gHvContext.SignalEventBuffer)
363         {
364                 MemFree(gHvContext.SignalEventBuffer);
365                 gHvContext.SignalEventBuffer = NULL;
366                 gHvContext.SignalEventParam = NULL;
367         }
368
369         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
370         {
371                 if (gHvContext.HypercallPage)
372                 {
373                         hypercallMsr.AsUINT64 = 0;
374                         WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
375                         VirtualFree(gHvContext.HypercallPage);
376                         gHvContext.HypercallPage = NULL;
377                 }
378         }
379
380         DPRINT_EXIT(VMBUS);
381
382 }
383
384
385 /*++
386
387 Name:
388         HvPostMessage()
389
390 Description:
391         Post a message using the hypervisor message IPC. This
392         involves a hypercall.
393
394 --*/
395 HV_STATUS
396 HvPostMessage(
397         HV_CONNECTION_ID connectionId,
398         HV_MESSAGE_TYPE  messageType,
399         void *            payload,
400         SIZE_T           payloadSize
401         )
402 {
403         struct alignedInput {
404                 UINT64                                  alignment8;
405                 HV_INPUT_POST_MESSAGE   msg;
406         };
407
408         PHV_INPUT_POST_MESSAGE alignedMsg;
409         HV_STATUS status;
410         ULONG_PTR addr;
411
412         if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
413         {
414                 return -1;
415         }
416
417         addr = (ULONG_PTR)MemAllocAtomic(sizeof(struct alignedInput));
418
419         if (!addr)
420         {
421                 return -1;
422         }
423
424         alignedMsg = (PHV_INPUT_POST_MESSAGE)(ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
425
426         alignedMsg->ConnectionId = connectionId;
427         alignedMsg->MessageType = messageType;
428         alignedMsg->PayloadSize = payloadSize;
429         memcpy((void*)alignedMsg->Payload, payload, payloadSize);
430
431         status = HvDoHypercall(HvCallPostMessage, alignedMsg, 0) & 0xFFFF;
432
433         MemFree((void*)addr);
434
435         return status;
436 }
437
438
439 /*++
440
441 Name:
442         HvSignalEvent()
443
444 Description:
445         Signal an event on the specified connection using the hypervisor event IPC. This
446         involves a hypercall.
447
448 --*/
449 HV_STATUS
450 HvSignalEvent(
451         )
452 {
453         HV_STATUS status;
454
455         status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, 0) & 0xFFFF;
456
457         return status;
458 }
459
460
461 /*++
462
463 Name:
464         HvSynicInit()
465
466 Description:
467         Initialize the Synthethic Interrupt Controller. If it is already initialized by
468         another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
469         Otherwise, we create and initialize the message and event pages.
470
471 --*/
472 int
473 HvSynicInit (
474         u32 irqVector
475         )
476 {
477         UINT64                  version;
478         HV_SYNIC_SIMP   simp;
479         HV_SYNIC_SIEFP  siefp;
480     HV_SYNIC_SINT       sharedSint;
481         HV_SYNIC_SCONTROL sctrl;
482         UINT64                  guestID;
483         int ret=0;
484
485         DPRINT_ENTER(VMBUS);
486
487         if (!gHvContext.HypercallPage)
488         {
489                 DPRINT_EXIT(VMBUS);
490                 return ret;
491         }
492
493         // Check the version
494         version = ReadMsr(HV_X64_MSR_SVERSION);
495
496         DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
497
498         // TODO: Handle SMP
499         if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
500         {
501                 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
502
503                 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
504                 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
505
506                 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
507
508                 // Determine if we are running on xenlinux (ie x2v shim) or native linux
509                 guestID = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
510
511                 if (guestID == HV_LINUX_GUEST_ID)
512                 {
513                         gHvContext.synICMessagePage[0] = GetVirtualAddress(simp.BaseSimpGpa << PAGE_SHIFT);
514                         gHvContext.synICEventPage[0] = GetVirtualAddress(siefp.BaseSiefpGpa << PAGE_SHIFT);
515                 }
516                 else
517                 {
518                         DPRINT_ERR(VMBUS, "unknown guest id!!");
519                         goto Cleanup;
520                 }
521                 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
522         }
523         else
524         {
525                 gHvContext.synICMessagePage[0] = PageAlloc(1);
526                 if (gHvContext.synICMessagePage[0] == NULL)
527                 {
528                         DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
529                         goto Cleanup;
530                 }
531
532                 gHvContext.synICEventPage[0] = PageAlloc(1);
533                 if (gHvContext.synICEventPage[0] == NULL)
534                 {
535                         DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
536                         goto Cleanup;
537                 }
538
539                 //
540                 // Setup the Synic's message page
541                 //
542                 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
543                 simp.SimpEnabled = 1;
544                 simp.BaseSimpGpa = GetPhysicalAddress(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
545
546                 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
547
548                 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
549
550                 //
551                 // Setup the Synic's event page
552                 //
553                 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
554                 siefp.SiefpEnabled = 1;
555                 siefp.BaseSiefpGpa = GetPhysicalAddress(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
556
557                 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
558
559                 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
560         }
561         //
562     // Setup the interception SINT.
563     //
564         //WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX),
565     //             interceptionSint.AsUINT64);
566
567     //
568     // Setup the shared SINT.
569     //
570         sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
571
572         sharedSint.AsUINT64 = 0;
573     sharedSint.Vector = irqVector; //HV_SHARED_SINT_IDT_VECTOR + 0x20;
574     sharedSint.Masked = FALSE;
575     sharedSint.AutoEoi = TRUE;
576
577         DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
578
579         WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
580
581         // Enable the global synic bit
582         sctrl.AsUINT64 = ReadMsr(HV_X64_MSR_SCONTROL);
583         sctrl.Enable = 1;
584
585     WriteMsr(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
586
587         gHvContext.SynICInitialized = TRUE;
588
589         DPRINT_EXIT(VMBUS);
590
591         return ret;
592
593 Cleanup:
594         ret = -1;
595
596         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
597         {
598                 if (gHvContext.synICEventPage[0])
599                 {
600                         PageFree(gHvContext.synICEventPage[0],1);
601                 }
602
603                 if (gHvContext.synICMessagePage[0])
604                 {
605                         PageFree(gHvContext.synICMessagePage[0], 1);
606                 }
607         }
608
609         DPRINT_EXIT(VMBUS);
610
611         return ret;
612
613 }
614
615 /*++
616
617 Name:
618         HvSynicCleanup()
619
620 Description:
621         Cleanup routine for HvSynicInit().
622
623 --*/
624 void
625 HvSynicCleanup(
626         void
627         )
628 {
629     HV_SYNIC_SINT       sharedSint;
630         HV_SYNIC_SIMP   simp;
631         HV_SYNIC_SIEFP  siefp;
632
633         DPRINT_ENTER(VMBUS);
634
635         if (!gHvContext.SynICInitialized)
636         {
637                 DPRINT_EXIT(VMBUS);
638                 return;
639         }
640
641         sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
642
643         sharedSint.Masked = 1;
644
645         // Disable the interrupt
646     WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
647
648         // Disable and free the resources only if we are running as native linux
649         // since in xenlinux, we are sharing the resources with the x2v shim
650         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
651         {
652                 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
653                 simp.SimpEnabled = 0;
654                 simp.BaseSimpGpa = 0;
655
656                 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
657
658                 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
659                 siefp.SiefpEnabled = 0;
660                 siefp.BaseSiefpGpa = 0;
661
662                 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
663
664                 PageFree(gHvContext.synICMessagePage[0], 1);
665                 PageFree(gHvContext.synICEventPage[0], 1);
666         }
667
668         DPRINT_EXIT(VMBUS);
669 }
670
671
672 // eof