Staging: hv: fix typedefs in nvspprotocol.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 u16 HvPostMessage(union hv_connection_id connectionId,
388                   enum hv_message_type messageType,
389                   void *payload, size_t payloadSize)
390 {
391         struct alignedInput {
392                 u64                                     alignment8;
393                 struct hv_input_post_message msg;
394         };
395
396         struct hv_input_post_message *alignedMsg;
397         u16 status;
398         unsigned long addr;
399
400         if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
401         {
402                 return -1;
403         }
404
405         addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
406
407         if (!addr)
408         {
409                 return -1;
410         }
411
412         alignedMsg = (struct hv_input_post_message *)(ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
413
414         alignedMsg->ConnectionId = connectionId;
415         alignedMsg->MessageType = messageType;
416         alignedMsg->PayloadSize = payloadSize;
417         memcpy((void*)alignedMsg->Payload, payload, payloadSize);
418
419         status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
420
421         kfree((void*)addr);
422
423         return status;
424 }
425
426
427 /*++
428
429 Name:
430         HvSignalEvent()
431
432 Description:
433         Signal an event on the specified connection using the hypervisor event IPC. This
434         involves a hypercall.
435
436 --*/
437 u16 HvSignalEvent(void)
438 {
439         u16 status;
440
441         status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, NULL) & 0xFFFF;
442
443         return status;
444 }
445
446
447 /*++
448
449 Name:
450         HvSynicInit()
451
452 Description:
453         Initialize the Synthethic Interrupt Controller. If it is already initialized by
454         another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
455         Otherwise, we create and initialize the message and event pages.
456
457 --*/
458 int HvSynicInit (u32 irqVector)
459 {
460         u64                     version;
461         union hv_synic_simp simp;
462         union hv_synic_siefp siefp;
463         union hv_synic_sint sharedSint;
464         union hv_synic_scontrol sctrl;
465         u64                     guestID;
466         int ret=0;
467
468         DPRINT_ENTER(VMBUS);
469
470         if (!gHvContext.HypercallPage)
471         {
472                 DPRINT_EXIT(VMBUS);
473                 return ret;
474         }
475
476         /* Check the version */
477         rdmsrl(HV_X64_MSR_SVERSION, version);
478
479         DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
480
481         /* TODO: Handle SMP */
482         if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
483         {
484                 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
485
486                 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
487                 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
488
489                 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
490
491                 /* Determine if we are running on xenlinux (ie x2v shim) or native linux */
492                 rdmsrl(HV_X64_MSR_GUEST_OS_ID, guestID);
493                 if (guestID == HV_LINUX_GUEST_ID)
494                 {
495                         gHvContext.synICMessagePage[0] = phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT);
496                         gHvContext.synICEventPage[0] = phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT);
497                 }
498                 else
499                 {
500                         DPRINT_ERR(VMBUS, "unknown guest id!!");
501                         goto Cleanup;
502                 }
503                 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
504         }
505         else
506         {
507                 gHvContext.synICMessagePage[0] = osd_PageAlloc(1);
508                 if (gHvContext.synICMessagePage[0] == NULL)
509                 {
510                         DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
511                         goto Cleanup;
512                 }
513
514                 gHvContext.synICEventPage[0] = osd_PageAlloc(1);
515                 if (gHvContext.synICEventPage[0] == NULL)
516                 {
517                         DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
518                         goto Cleanup;
519                 }
520
521                 /* Setup the Synic's message page */
522                 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
523                 simp.SimpEnabled = 1;
524                 simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
525
526                 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
527
528                 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
529
530                 /* Setup the Synic's event page */
531                 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
532                 siefp.SiefpEnabled = 1;
533                 siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
534
535                 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
536
537                 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
538         }
539     /* Setup the interception SINT. */
540         /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
541     /*             interceptionSint.AsUINT64); */
542
543     /* Setup the shared SINT. */
544         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
545
546         sharedSint.AsUINT64 = 0;
547         sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
548         sharedSint.Masked = false;
549         sharedSint.AutoEoi = true;
550
551         DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
552
553         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
554
555         /* Enable the global synic bit */
556         rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
557         sctrl.Enable = 1;
558
559         wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
560
561         gHvContext.SynICInitialized = true;
562
563         DPRINT_EXIT(VMBUS);
564
565         return ret;
566
567 Cleanup:
568         ret = -1;
569
570         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
571         {
572                 if (gHvContext.synICEventPage[0])
573                 {
574                         osd_PageFree(gHvContext.synICEventPage[0],1);
575                 }
576
577                 if (gHvContext.synICMessagePage[0])
578                 {
579                         osd_PageFree(gHvContext.synICMessagePage[0], 1);
580                 }
581         }
582
583         DPRINT_EXIT(VMBUS);
584
585         return ret;
586
587 }
588
589 /*++
590
591 Name:
592         HvSynicCleanup()
593
594 Description:
595         Cleanup routine for HvSynicInit().
596
597 --*/
598 void HvSynicCleanup(void)
599 {
600         union hv_synic_sint sharedSint;
601         union hv_synic_simp simp;
602         union hv_synic_siefp siefp;
603
604         DPRINT_ENTER(VMBUS);
605
606         if (!gHvContext.SynICInitialized)
607         {
608                 DPRINT_EXIT(VMBUS);
609                 return;
610         }
611
612         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
613
614         sharedSint.Masked = 1;
615
616         /* Disable the interrupt */
617         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
618
619         /*
620          * Disable and free the resources only if we are running as
621          * native linux since in xenlinux, we are sharing the
622          * resources with the x2v shim
623          */
624         if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
625         {
626                 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
627                 simp.SimpEnabled = 0;
628                 simp.BaseSimpGpa = 0;
629
630                 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
631
632                 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
633                 siefp.SiefpEnabled = 0;
634                 siefp.BaseSiefpGpa = 0;
635
636                 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
637
638                 osd_PageFree(gHvContext.synICMessagePage[0], 1);
639                 osd_PageFree(gHvContext.synICEventPage[0], 1);
640         }
641
642         DPRINT_EXIT(VMBUS);
643 }
644
645
646 /* eof */