Staging: comedi: poc: fix coding style issues
[safe/jmp/linux-2.6] / drivers / staging / hv / Hv.c
1 /*
2  * Copyright (c) 2009, Microsoft Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Authors:
18  *   Haiyang Zhang <haiyangz@microsoft.com>
19  *   Hank Janssen  <hjanssen@microsoft.com>
20  *
21  */
22 #include <linux/kernel.h>
23 #include <linux/mm.h>
24 #include <linux/vmalloc.h>
25 #include "osd.h"
26 #include "logging.h"
27 #include "VmbusPrivate.h"
28
29 /* The one and only */
30 struct hv_context gHvContext = {
31         .SynICInitialized       = false,
32         .HypercallPage          = NULL,
33         .SignalEventParam       = NULL,
34         .SignalEventBuffer      = NULL,
35 };
36
37 /**
38  * HvQueryHypervisorPresence - Query the cpuid for presense of windows hypervisor
39  */
40 static int HvQueryHypervisorPresence(void)
41 {
42         unsigned int eax;
43         unsigned int ebx;
44         unsigned int ecx;
45         unsigned int edx;
46         unsigned int op;
47
48         eax = 0;
49         ebx = 0;
50         ecx = 0;
51         edx = 0;
52         op = HvCpuIdFunctionVersionAndFeatures;
53         cpuid(op, &eax, &ebx, &ecx, &edx);
54
55         return ecx & HV_PRESENT_BIT;
56 }
57
58 /**
59  * HvQueryHypervisorInfo - Get version info of the windows hypervisor
60  */
61 static int HvQueryHypervisorInfo(void)
62 {
63         unsigned int eax;
64         unsigned int ebx;
65         unsigned int ecx;
66         unsigned int edx;
67         unsigned int maxLeaf;
68         unsigned int op;
69
70         /*
71         * Its assumed that this is called after confirming that Viridian
72         * is present. Query id and revision.
73         */
74         eax = 0;
75         ebx = 0;
76         ecx = 0;
77         edx = 0;
78         op = HvCpuIdFunctionHvVendorAndMaxFunction;
79         cpuid(op, &eax, &ebx, &ecx, &edx);
80
81         DPRINT_INFO(VMBUS, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c",
82                     (ebx & 0xFF),
83                     ((ebx >> 8) & 0xFF),
84                     ((ebx >> 16) & 0xFF),
85                     ((ebx >> 24) & 0xFF),
86                     (ecx & 0xFF),
87                     ((ecx >> 8) & 0xFF),
88                     ((ecx >> 16) & 0xFF),
89                     ((ecx >> 24) & 0xFF),
90                     (edx & 0xFF),
91                     ((edx >> 8) & 0xFF),
92                     ((edx >> 16) & 0xFF),
93                     ((edx >> 24) & 0xFF));
94
95         maxLeaf = eax;
96         eax = 0;
97         ebx = 0;
98         ecx = 0;
99         edx = 0;
100         op = HvCpuIdFunctionHvInterface;
101         cpuid(op, &eax, &ebx, &ecx, &edx);
102
103         DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c",
104                     (eax & 0xFF),
105                     ((eax >> 8) & 0xFF),
106                     ((eax >> 16) & 0xFF),
107                     ((eax >> 24) & 0xFF));
108
109         if (maxLeaf >= HvCpuIdFunctionMsHvVersion) {
110                 eax = 0;
111                 ebx = 0;
112                 ecx = 0;
113                 edx = 0;
114                 op = HvCpuIdFunctionMsHvVersion;
115                 cpuid(op, &eax, &ebx, &ecx, &edx);
116                 DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",\
117                             eax,
118                             ebx >> 16,
119                             ebx & 0xFFFF,
120                             ecx,
121                             edx >> 24,
122                             edx & 0xFFFFFF);
123         }
124         return maxLeaf;
125 }
126
127 /**
128  * HvDoHypercall - Invoke the specified hypercall
129  */
130 static u64 HvDoHypercall(u64 Control, void *Input, void *Output)
131 {
132 #ifdef CONFIG_X86_64
133         u64 hvStatus = 0;
134         u64 inputAddress = (Input) ? virt_to_phys(Input) : 0;
135         u64 outputAddress = (Output) ? virt_to_phys(Output) : 0;
136         volatile void *hypercallPage = gHvContext.HypercallPage;
137
138         DPRINT_DBG(VMBUS, "Hypercall <control %llx input phys %llx virt %p "
139                    "output phys %llx virt %p hypercall %p>",
140                    Control, inputAddress, Input,
141                    outputAddress, Output, hypercallPage);
142
143         __asm__ __volatile__("mov %0, %%r8" : : "r" (outputAddress) : "r8");
144         __asm__ __volatile__("call *%3" : "=a" (hvStatus) :
145                              "c" (Control), "d" (inputAddress),
146                              "m" (hypercallPage));
147
148         DPRINT_DBG(VMBUS, "Hypercall <return %llx>",  hvStatus);
149
150         return hvStatus;
151
152 #else
153
154         u32 controlHi = Control >> 32;
155         u32 controlLo = Control & 0xFFFFFFFF;
156         u32 hvStatusHi = 1;
157         u32 hvStatusLo = 1;
158         u64 inputAddress = (Input) ? virt_to_phys(Input) : 0;
159         u32 inputAddressHi = inputAddress >> 32;
160         u32 inputAddressLo = inputAddress & 0xFFFFFFFF;
161         u64 outputAddress = (Output) ? virt_to_phys(Output) : 0;
162         u32 outputAddressHi = outputAddress >> 32;
163         u32 outputAddressLo = outputAddress & 0xFFFFFFFF;
164         volatile void *hypercallPage = gHvContext.HypercallPage;
165
166         DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>",
167                    Control, Input, Output);
168
169         __asm__ __volatile__ ("call *%8" : "=d"(hvStatusHi),
170                               "=a"(hvStatusLo) : "d" (controlHi),
171                               "a" (controlLo), "b" (inputAddressHi),
172                               "c" (inputAddressLo), "D"(outputAddressHi),
173                               "S"(outputAddressLo), "m" (hypercallPage));
174
175         DPRINT_DBG(VMBUS, "Hypercall <return %llx>",
176                    hvStatusLo | ((u64)hvStatusHi << 32));
177
178         return hvStatusLo | ((u64)hvStatusHi << 32);
179 #endif /* !x86_64 */
180 }
181
182 /**
183  * HvInit - Main initialization routine.
184  *
185  * This routine must be called before any other routines in here are called
186  */
187 int HvInit(void)
188 {
189         int ret = 0;
190         int maxLeaf;
191         union hv_x64_msr_hypercall_contents hypercallMsr;
192         void *virtAddr = NULL;
193
194         DPRINT_ENTER(VMBUS);
195
196         memset(gHvContext.synICEventPage, 0, sizeof(void *) * MAX_NUM_CPUS);
197         memset(gHvContext.synICMessagePage, 0, sizeof(void *) * MAX_NUM_CPUS);
198
199         if (!HvQueryHypervisorPresence()) {
200                 DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!");
201                 goto Cleanup;
202         }
203
204         DPRINT_INFO(VMBUS,
205                     "Windows hypervisor detected! Retrieving more info...");
206
207         maxLeaf = HvQueryHypervisorInfo();
208         /* HvQueryHypervisorFeatures(maxLeaf); */
209
210         /*
211          * Determine if we are running on xenlinux (ie x2v shim) or native
212          * linux
213          */
214         rdmsrl(HV_X64_MSR_GUEST_OS_ID, gHvContext.GuestId);
215         if (gHvContext.GuestId == 0) {
216                 /* Write our OS info */
217                 wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
218                 gHvContext.GuestId = HV_LINUX_GUEST_ID;
219         }
220
221         /* See if the hypercall page is already set */
222         rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
223         if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
224                 /* Allocate the hypercall page memory */
225                 /* virtAddr = osd_PageAlloc(1); */
226                 virtAddr = osd_VirtualAllocExec(PAGE_SIZE);
227
228                 if (!virtAddr) {
229                         DPRINT_ERR(VMBUS,
230                                    "unable to allocate hypercall page!!");
231                         goto Cleanup;
232                 }
233
234                 hypercallMsr.Enable = 1;
235                 /* hypercallMsr.GuestPhysicalAddress =
236                  *              virt_to_phys(virtAddr) >> PAGE_SHIFT; */
237                 hypercallMsr.GuestPhysicalAddress = vmalloc_to_pfn(virtAddr);
238                 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
239
240                 /* Confirm that hypercall page did get setup. */
241                 hypercallMsr.AsUINT64 = 0;
242                 rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
243                 if (!hypercallMsr.Enable) {
244                         DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
245                         goto Cleanup;
246                 }
247
248                 gHvContext.HypercallPage = virtAddr;
249         } else {
250                 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!",
251                                 gHvContext.GuestId);
252                 goto Cleanup;
253         }
254
255         DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
256                     gHvContext.HypercallPage,
257                     (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
258
259         /* Setup the global signal event param for the signal event hypercall */
260         gHvContext.SignalEventBuffer =
261                         kmalloc(sizeof(struct hv_input_signal_event_buffer),
262                                 GFP_KERNEL);
263         if (!gHvContext.SignalEventBuffer)
264                 goto Cleanup;
265
266         gHvContext.SignalEventParam =
267                 (struct hv_input_signal_event *)
268                         (ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer,
269                                   HV_HYPERCALL_PARAM_ALIGN));
270         gHvContext.SignalEventParam->ConnectionId.Asu32 = 0;
271         gHvContext.SignalEventParam->ConnectionId.u.Id =
272                                                 VMBUS_EVENT_CONNECTION_ID;
273         gHvContext.SignalEventParam->FlagNumber = 0;
274         gHvContext.SignalEventParam->RsvdZ = 0;
275
276         /* DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId()); */
277
278         DPRINT_EXIT(VMBUS);
279
280         return ret;
281
282 Cleanup:
283         if (virtAddr) {
284                 if (hypercallMsr.Enable) {
285                         hypercallMsr.AsUINT64 = 0;
286                         wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
287                 }
288
289                 vfree(virtAddr);
290         }
291         ret = -1;
292         DPRINT_EXIT(VMBUS);
293
294         return ret;
295 }
296
297 /**
298  * HvCleanup - Cleanup routine.
299  *
300  * This routine is called normally during driver unloading or exiting.
301  */
302 void HvCleanup(void)
303 {
304         union hv_x64_msr_hypercall_contents hypercallMsr;
305
306         DPRINT_ENTER(VMBUS);
307
308         if (gHvContext.SignalEventBuffer) {
309                 gHvContext.SignalEventBuffer = NULL;
310                 gHvContext.SignalEventParam = NULL;
311                 kfree(gHvContext.SignalEventBuffer);
312         }
313
314         if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
315                 if (gHvContext.HypercallPage) {
316                         hypercallMsr.AsUINT64 = 0;
317                         wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
318                         vfree(gHvContext.HypercallPage);
319                         gHvContext.HypercallPage = NULL;
320                 }
321         }
322
323         DPRINT_EXIT(VMBUS);
324
325 }
326
327 /**
328  * HvPostMessage - Post a message using the hypervisor message IPC.
329  *
330  * This involves a hypercall.
331  */
332 u16 HvPostMessage(union hv_connection_id connectionId,
333                   enum hv_message_type messageType,
334                   void *payload, size_t payloadSize)
335 {
336         struct alignedInput {
337                 u64 alignment8;
338                 struct hv_input_post_message msg;
339         };
340
341         struct hv_input_post_message *alignedMsg;
342         u16 status;
343         unsigned long addr;
344
345         if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
346                 return -1;
347
348         addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
349         if (!addr)
350                 return -1;
351
352         alignedMsg = (struct hv_input_post_message *)
353                         (ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
354
355         alignedMsg->ConnectionId = connectionId;
356         alignedMsg->MessageType = messageType;
357         alignedMsg->PayloadSize = payloadSize;
358         memcpy((void *)alignedMsg->Payload, payload, payloadSize);
359
360         status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
361
362         kfree((void *)addr);
363
364         return status;
365 }
366
367
368 /**
369  * HvSignalEvent - Signal an event on the specified connection using the hypervisor event IPC.
370  *
371  * This involves a hypercall.
372  */
373 u16 HvSignalEvent(void)
374 {
375         u16 status;
376
377         status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam,
378                                NULL) & 0xFFFF;
379         return status;
380 }
381
382 /**
383  * HvSynicInit - Initialize the Synthethic Interrupt Controller.
384  *
385  * If it is already initialized by another entity (ie x2v shim), we need to
386  * retrieve the initialized message and event pages.  Otherwise, we create and
387  * initialize the message and event pages.
388  */
389 void HvSynicInit(void *irqarg)
390 {
391         u64 version;
392         union hv_synic_simp simp;
393         union hv_synic_siefp siefp;
394         union hv_synic_sint sharedSint;
395         union hv_synic_scontrol sctrl;
396         u64 guestID;
397         u32 irqVector = *((u32 *)(irqarg));
398         int cpu = smp_processor_id();
399
400         DPRINT_ENTER(VMBUS);
401
402         if (!gHvContext.HypercallPage) {
403                 DPRINT_EXIT(VMBUS);
404                 return;
405         }
406
407         /* Check the version */
408         rdmsrl(HV_X64_MSR_SVERSION, version);
409
410         DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
411
412         /* TODO: Handle SMP */
413         if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID) {
414                 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since "
415                                 "it is already set.");
416
417                 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
418                 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
419
420                 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx",
421                            simp.AsUINT64, siefp.AsUINT64);
422
423                 /*
424                  * Determine if we are running on xenlinux (ie x2v shim) or
425                  * native linux
426                  */
427                 rdmsrl(HV_X64_MSR_GUEST_OS_ID, guestID);
428                 if (guestID == HV_LINUX_GUEST_ID) {
429                         gHvContext.synICMessagePage[cpu] =
430                                 phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT);
431                         gHvContext.synICEventPage[cpu] =
432                                 phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT);
433                 } else {
434                         DPRINT_ERR(VMBUS, "unknown guest id!!");
435                         goto Cleanup;
436                 }
437                 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p",
438                            gHvContext.synICMessagePage[cpu],
439                            gHvContext.synICEventPage[cpu]);
440         } else {
441                 gHvContext.synICMessagePage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC);
442                 if (gHvContext.synICMessagePage[cpu] == NULL) {
443                         DPRINT_ERR(VMBUS,
444                                    "unable to allocate SYNIC message page!!");
445                         goto Cleanup;
446                 }
447
448                 gHvContext.synICEventPage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC);
449                 if (gHvContext.synICEventPage[cpu] == NULL) {
450                         DPRINT_ERR(VMBUS,
451                                    "unable to allocate SYNIC event page!!");
452                         goto Cleanup;
453                 }
454
455                 /* Setup the Synic's message page */
456                 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
457                 simp.SimpEnabled = 1;
458                 simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[cpu])
459                                         >> PAGE_SHIFT;
460
461                 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx",
462                                 simp.AsUINT64);
463
464                 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
465
466                 /* Setup the Synic's event page */
467                 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
468                 siefp.SiefpEnabled = 1;
469                 siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[cpu])
470                                         >> PAGE_SHIFT;
471
472                 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx",
473                                 siefp.AsUINT64);
474
475                 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
476         }
477
478         /* Setup the interception SINT. */
479         /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
480         /*        interceptionSint.AsUINT64); */
481
482         /* Setup the shared SINT. */
483         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
484
485         sharedSint.AsUINT64 = 0;
486         sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
487         sharedSint.Masked = false;
488         sharedSint.AutoEoi = true;
489
490         DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx",
491                    sharedSint.AsUINT64);
492
493         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
494
495         /* Enable the global synic bit */
496         rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
497         sctrl.Enable = 1;
498
499         wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
500
501         gHvContext.SynICInitialized = true;
502
503         DPRINT_EXIT(VMBUS);
504
505         return;
506
507 Cleanup:
508         if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
509                 if (gHvContext.synICEventPage[cpu])
510                         osd_PageFree(gHvContext.synICEventPage[cpu], 1);
511
512                 if (gHvContext.synICMessagePage[cpu])
513                         osd_PageFree(gHvContext.synICMessagePage[cpu], 1);
514         }
515
516         DPRINT_EXIT(VMBUS);
517         return;
518 }
519
520 /**
521  * HvSynicCleanup - Cleanup routine for HvSynicInit().
522  */
523 void HvSynicCleanup(void *arg)
524 {
525         union hv_synic_sint sharedSint;
526         union hv_synic_simp simp;
527         union hv_synic_siefp siefp;
528         int cpu = smp_processor_id();
529
530         DPRINT_ENTER(VMBUS);
531
532         if (!gHvContext.SynICInitialized) {
533                 DPRINT_EXIT(VMBUS);
534                 return;
535         }
536
537         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
538
539         sharedSint.Masked = 1;
540
541         /* Need to correctly cleanup in the case of SMP!!! */
542         /* Disable the interrupt */
543         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
544
545         /*
546          * Disable and free the resources only if we are running as
547          * native linux since in xenlinux, we are sharing the
548          * resources with the x2v shim
549          */
550         if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
551                 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
552                 simp.SimpEnabled = 0;
553                 simp.BaseSimpGpa = 0;
554
555                 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
556
557                 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
558                 siefp.SiefpEnabled = 0;
559                 siefp.BaseSiefpGpa = 0;
560
561                 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
562
563                 osd_PageFree(gHvContext.synICMessagePage[cpu], 1);
564                 osd_PageFree(gHvContext.synICEventPage[cpu], 1);
565         }
566
567         DPRINT_EXIT(VMBUS);
568 }