Staging: hv: remove custom cpuid function
[safe/jmp/linux-2.6] / 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 "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     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     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     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         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 = NULL;
235
236         DPRINT_ENTER(VMBUS);
237
238         memset(gHvContext.synICEventPage, 0, sizeof(void *) * MAX_NUM_CPUS);
239         memset(gHvContext.synICMessagePage, 0, sizeof(void *) * MAX_NUM_CPUS);
240
241         if (!HvQueryHypervisorPresence())
242         {
243                 DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!");
244                 goto Cleanup;
245         }
246
247         DPRINT_INFO(VMBUS, "Windows hypervisor detected! Retrieving more info...");
248
249     maxLeaf = HvQueryHypervisorInfo();
250     /* HvQueryHypervisorFeatures(maxLeaf); */
251
252         /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
253         gHvContext.GuestId = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
254
255         if (gHvContext.GuestId == 0)
256         {
257                 /* Write our OS info */
258                 WriteMsr(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
259
260                 gHvContext.GuestId = HV_LINUX_GUEST_ID;
261         }
262
263         /* See if the hypercall page is already set */
264         hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
265
266         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
267         {
268                 /* Allocate the hypercall page memory */
269                 /* virtAddr = PageAlloc(1); */
270                 virtAddr = VirtualAllocExec(PAGE_SIZE);
271
272                 if (!virtAddr)
273                 {
274                         DPRINT_ERR(VMBUS, "unable to allocate hypercall page!!");
275                         goto Cleanup;
276                 }
277
278                 hypercallMsr.Enable = 1;
279                 /* hypercallMsr.GuestPhysicalAddress = Logical2PhysicalAddr(virtAddr) >> PAGE_SHIFT; */
280                 hypercallMsr.GuestPhysicalAddress = Virtual2Physical(virtAddr) >> PAGE_SHIFT;
281                 WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
282
283         /* Confirm that hypercall page did get setup. */
284                 hypercallMsr.AsUINT64 = 0;
285                 hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
286
287                 if (!hypercallMsr.Enable)
288                 {
289                         DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
290                         goto Cleanup;
291                 }
292
293                 gHvContext.HypercallPage = virtAddr;
294         }
295         else
296         {
297                 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", gHvContext.GuestId);
298                 goto Cleanup;
299         }
300
301         DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
302                     gHvContext.HypercallPage,
303                     (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
304
305         /* Setup the global signal event param for the signal event hypercall */
306         gHvContext.SignalEventBuffer = kmalloc(sizeof(HV_INPUT_SIGNAL_EVENT_BUFFER), GFP_KERNEL);
307         if (!gHvContext.SignalEventBuffer)
308         {
309                 goto Cleanup;
310         }
311
312         gHvContext.SignalEventParam = (PHV_INPUT_SIGNAL_EVENT)(ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer, HV_HYPERCALL_PARAM_ALIGN));
313         gHvContext.SignalEventParam->ConnectionId.Asu32 = 0;
314         gHvContext.SignalEventParam->ConnectionId.u.Id = VMBUS_EVENT_CONNECTION_ID;
315         gHvContext.SignalEventParam->FlagNumber = 0;
316         gHvContext.SignalEventParam->RsvdZ = 0;
317
318     /* DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId()); */
319
320         DPRINT_EXIT(VMBUS);
321
322     return ret;
323
324 Cleanup:
325         if (virtAddr)
326         {
327                 if (hypercallMsr.Enable)
328                 {
329                         hypercallMsr.AsUINT64 = 0;
330                         WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
331                 }
332
333                 vfree(virtAddr);
334         }
335         ret = -1;
336         DPRINT_EXIT(VMBUS);
337
338         return ret;
339 }
340
341
342 /*++
343
344 Name:
345         HvCleanup()
346
347 Description:
348         Cleanup routine. This routine is called normally during driver unloading or exiting.
349
350 --*/
351 static void
352 HvCleanup (
353     void
354     )
355 {
356         HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
357
358         DPRINT_ENTER(VMBUS);
359
360         if (gHvContext.SignalEventBuffer)
361         {
362                 kfree(gHvContext.SignalEventBuffer);
363                 gHvContext.SignalEventBuffer = NULL;
364                 gHvContext.SignalEventParam = NULL;
365         }
366
367         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
368         {
369                 if (gHvContext.HypercallPage)
370                 {
371                         hypercallMsr.AsUINT64 = 0;
372                         WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
373                         vfree(gHvContext.HypercallPage);
374                         gHvContext.HypercallPage = NULL;
375                 }
376         }
377
378         DPRINT_EXIT(VMBUS);
379
380 }
381
382
383 /*++
384
385 Name:
386         HvPostMessage()
387
388 Description:
389         Post a message using the hypervisor message IPC. This
390         involves a hypercall.
391
392 --*/
393 static HV_STATUS
394 HvPostMessage(
395         HV_CONNECTION_ID connectionId,
396         HV_MESSAGE_TYPE  messageType,
397         void *            payload,
398         size_t           payloadSize
399         )
400 {
401         struct alignedInput {
402                 u64                                     alignment8;
403                 HV_INPUT_POST_MESSAGE   msg;
404         };
405
406         PHV_INPUT_POST_MESSAGE alignedMsg;
407         HV_STATUS status;
408         unsigned long addr;
409
410         if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
411         {
412                 return -1;
413         }
414
415         addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
416
417         if (!addr)
418         {
419                 return -1;
420         }
421
422         alignedMsg = (PHV_INPUT_POST_MESSAGE)(ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
423
424         alignedMsg->ConnectionId = connectionId;
425         alignedMsg->MessageType = messageType;
426         alignedMsg->PayloadSize = payloadSize;
427         memcpy((void*)alignedMsg->Payload, payload, payloadSize);
428
429         status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
430
431         kfree((void*)addr);
432
433         return status;
434 }
435
436
437 /*++
438
439 Name:
440         HvSignalEvent()
441
442 Description:
443         Signal an event on the specified connection using the hypervisor event IPC. This
444         involves a hypercall.
445
446 --*/
447 static HV_STATUS
448 HvSignalEvent(void)
449 {
450         HV_STATUS status;
451
452         status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, NULL) & 0xFFFF;
453
454         return status;
455 }
456
457
458 /*++
459
460 Name:
461         HvSynicInit()
462
463 Description:
464         Initialize the Synthethic Interrupt Controller. If it is already initialized by
465         another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
466         Otherwise, we create and initialize the message and event pages.
467
468 --*/
469 static int
470 HvSynicInit (
471         u32 irqVector
472         )
473 {
474         u64                     version;
475         HV_SYNIC_SIMP   simp;
476         HV_SYNIC_SIEFP  siefp;
477     HV_SYNIC_SINT       sharedSint;
478         HV_SYNIC_SCONTROL sctrl;
479         u64                     guestID;
480         int ret=0;
481
482         DPRINT_ENTER(VMBUS);
483
484         if (!gHvContext.HypercallPage)
485         {
486                 DPRINT_EXIT(VMBUS);
487                 return ret;
488         }
489
490         /* Check the version */
491         version = ReadMsr(HV_X64_MSR_SVERSION);
492
493         DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
494
495         /* TODO: Handle SMP */
496         if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
497         {
498                 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
499
500                 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
501                 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
502
503                 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
504
505                 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
506                 guestID = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
507
508                 if (guestID == HV_LINUX_GUEST_ID)
509                 {
510                         gHvContext.synICMessagePage[0] = GetVirtualAddress(simp.BaseSimpGpa << PAGE_SHIFT);
511                         gHvContext.synICEventPage[0] = GetVirtualAddress(siefp.BaseSiefpGpa << PAGE_SHIFT);
512                 }
513                 else
514                 {
515                         DPRINT_ERR(VMBUS, "unknown guest id!!");
516                         goto Cleanup;
517                 }
518                 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
519         }
520         else
521         {
522                 gHvContext.synICMessagePage[0] = PageAlloc(1);
523                 if (gHvContext.synICMessagePage[0] == NULL)
524                 {
525                         DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
526                         goto Cleanup;
527                 }
528
529                 gHvContext.synICEventPage[0] = PageAlloc(1);
530                 if (gHvContext.synICEventPage[0] == NULL)
531                 {
532                         DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
533                         goto Cleanup;
534                 }
535
536                 /* Setup the Synic's message page */
537                 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
538                 simp.SimpEnabled = 1;
539                 simp.BaseSimpGpa = GetPhysicalAddress(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
540
541                 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
542
543                 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
544
545                 /* Setup the Synic's event page */
546                 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
547                 siefp.SiefpEnabled = 1;
548                 siefp.BaseSiefpGpa = GetPhysicalAddress(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
549
550                 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
551
552                 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
553         }
554     /* Setup the interception SINT. */
555         /* WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
556     /*             interceptionSint.AsUINT64); */
557
558     /* Setup the shared SINT. */
559         sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
560
561         sharedSint.AsUINT64 = 0;
562         sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
563         sharedSint.Masked = false;
564         sharedSint.AutoEoi = true;
565
566         DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
567
568         WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
569
570         /* Enable the global synic bit */
571         sctrl.AsUINT64 = ReadMsr(HV_X64_MSR_SCONTROL);
572         sctrl.Enable = 1;
573
574         WriteMsr(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
575
576         gHvContext.SynICInitialized = true;
577
578         DPRINT_EXIT(VMBUS);
579
580         return ret;
581
582 Cleanup:
583         ret = -1;
584
585         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
586         {
587                 if (gHvContext.synICEventPage[0])
588                 {
589                         PageFree(gHvContext.synICEventPage[0],1);
590                 }
591
592                 if (gHvContext.synICMessagePage[0])
593                 {
594                         PageFree(gHvContext.synICMessagePage[0], 1);
595                 }
596         }
597
598         DPRINT_EXIT(VMBUS);
599
600         return ret;
601
602 }
603
604 /*++
605
606 Name:
607         HvSynicCleanup()
608
609 Description:
610         Cleanup routine for HvSynicInit().
611
612 --*/
613 static void
614 HvSynicCleanup(
615         void
616         )
617 {
618     HV_SYNIC_SINT       sharedSint;
619         HV_SYNIC_SIMP   simp;
620         HV_SYNIC_SIEFP  siefp;
621
622         DPRINT_ENTER(VMBUS);
623
624         if (!gHvContext.SynICInitialized)
625         {
626                 DPRINT_EXIT(VMBUS);
627                 return;
628         }
629
630         sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
631
632         sharedSint.Masked = 1;
633
634         /* Disable the interrupt */
635     WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
636
637         /*
638          * Disable and free the resources only if we are running as
639          * native linux since in xenlinux, we are sharing the
640          * resources with the x2v shim
641          */
642         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
643         {
644                 simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
645                 simp.SimpEnabled = 0;
646                 simp.BaseSimpGpa = 0;
647
648                 WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
649
650                 siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
651                 siefp.SiefpEnabled = 0;
652                 siefp.BaseSiefpGpa = 0;
653
654                 WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
655
656                 PageFree(gHvContext.synICMessagePage[0], 1);
657                 PageFree(gHvContext.synICEventPage[0], 1);
658         }
659
660         DPRINT_EXIT(VMBUS);
661 }
662
663
664 /* eof */