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