drm/radeon/kms: enable use of unmappable VRAM V2
[pandora-kernel.git] / 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          * We only support running on top of Hyper-V
212          */
213         rdmsrl(HV_X64_MSR_GUEST_OS_ID, gHvContext.GuestId);
214
215         if (gHvContext.GuestId != 0) {
216                 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!",
217                                 gHvContext.GuestId);
218                 goto Cleanup;
219         }
220
221         /* Write our OS info */
222         wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
223         gHvContext.GuestId = HV_LINUX_GUEST_ID;
224
225         /* See if the hypercall page is already set */
226         rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
227
228         /*
229         * Allocate the hypercall page memory
230         * virtAddr = osd_PageAlloc(1);
231         */
232         virtAddr = osd_VirtualAllocExec(PAGE_SIZE);
233
234         if (!virtAddr) {
235                 DPRINT_ERR(VMBUS,
236                            "unable to allocate hypercall page!!");
237                 goto Cleanup;
238         }
239
240         hypercallMsr.Enable = 1;
241
242         hypercallMsr.GuestPhysicalAddress = vmalloc_to_pfn(virtAddr);
243         wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
244
245         /* Confirm that hypercall page did get setup. */
246         hypercallMsr.AsUINT64 = 0;
247         rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
248
249         if (!hypercallMsr.Enable) {
250                 DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
251                 goto Cleanup;
252         }
253
254         gHvContext.HypercallPage = virtAddr;
255
256         DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
257                     gHvContext.HypercallPage,
258                     (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
259
260         /* Setup the global signal event param for the signal event hypercall */
261         gHvContext.SignalEventBuffer =
262                         kmalloc(sizeof(struct hv_input_signal_event_buffer),
263                                 GFP_KERNEL);
264         if (!gHvContext.SignalEventBuffer)
265                 goto Cleanup;
266
267         gHvContext.SignalEventParam =
268                 (struct hv_input_signal_event *)
269                         (ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer,
270                                   HV_HYPERCALL_PARAM_ALIGN));
271         gHvContext.SignalEventParam->ConnectionId.Asu32 = 0;
272         gHvContext.SignalEventParam->ConnectionId.u.Id =
273                                                 VMBUS_EVENT_CONNECTION_ID;
274         gHvContext.SignalEventParam->FlagNumber = 0;
275         gHvContext.SignalEventParam->RsvdZ = 0;
276
277         DPRINT_EXIT(VMBUS);
278
279         return ret;
280
281 Cleanup:
282         if (virtAddr) {
283                 if (hypercallMsr.Enable) {
284                         hypercallMsr.AsUINT64 = 0;
285                         wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
286                 }
287
288                 vfree(virtAddr);
289         }
290         ret = -1;
291         DPRINT_EXIT(VMBUS);
292
293         return ret;
294 }
295
296 /**
297  * HvCleanup - Cleanup routine.
298  *
299  * This routine is called normally during driver unloading or exiting.
300  */
301 void HvCleanup(void)
302 {
303         union hv_x64_msr_hypercall_contents hypercallMsr;
304
305         DPRINT_ENTER(VMBUS);
306
307         if (gHvContext.SignalEventBuffer) {
308                 gHvContext.SignalEventBuffer = NULL;
309                 gHvContext.SignalEventParam = NULL;
310                 kfree(gHvContext.SignalEventBuffer);
311         }
312
313         if (gHvContext.HypercallPage) {
314                 hypercallMsr.AsUINT64 = 0;
315                 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
316                 vfree(gHvContext.HypercallPage);
317                 gHvContext.HypercallPage = NULL;
318         }
319
320         DPRINT_EXIT(VMBUS);
321 }
322
323 /**
324  * HvPostMessage - Post a message using the hypervisor message IPC.
325  *
326  * This involves a hypercall.
327  */
328 u16 HvPostMessage(union hv_connection_id connectionId,
329                   enum hv_message_type messageType,
330                   void *payload, size_t payloadSize)
331 {
332         struct alignedInput {
333                 u64 alignment8;
334                 struct hv_input_post_message msg;
335         };
336
337         struct hv_input_post_message *alignedMsg;
338         u16 status;
339         unsigned long addr;
340
341         if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
342                 return -1;
343
344         addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
345         if (!addr)
346                 return -1;
347
348         alignedMsg = (struct hv_input_post_message *)
349                         (ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
350
351         alignedMsg->ConnectionId = connectionId;
352         alignedMsg->MessageType = messageType;
353         alignedMsg->PayloadSize = payloadSize;
354         memcpy((void *)alignedMsg->Payload, payload, payloadSize);
355
356         status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
357
358         kfree((void *)addr);
359
360         return status;
361 }
362
363
364 /**
365  * HvSignalEvent - Signal an event on the specified connection using the hypervisor event IPC.
366  *
367  * This involves a hypercall.
368  */
369 u16 HvSignalEvent(void)
370 {
371         u16 status;
372
373         status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam,
374                                NULL) & 0xFFFF;
375         return status;
376 }
377
378 /**
379  * HvSynicInit - Initialize the Synthethic Interrupt Controller.
380  *
381  * If it is already initialized by another entity (ie x2v shim), we need to
382  * retrieve the initialized message and event pages.  Otherwise, we create and
383  * initialize the message and event pages.
384  */
385 void HvSynicInit(void *irqarg)
386 {
387         u64 version;
388         union hv_synic_simp simp;
389         union hv_synic_siefp siefp;
390         union hv_synic_sint sharedSint;
391         union hv_synic_scontrol sctrl;
392
393         u32 irqVector = *((u32 *)(irqarg));
394         int cpu = smp_processor_id();
395
396         DPRINT_ENTER(VMBUS);
397
398         if (!gHvContext.HypercallPage) {
399                 DPRINT_EXIT(VMBUS);
400                 return;
401         }
402
403         /* Check the version */
404         rdmsrl(HV_X64_MSR_SVERSION, version);
405
406         DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
407
408         gHvContext.synICMessagePage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC);
409
410         if (gHvContext.synICMessagePage[cpu] == NULL) {
411                 DPRINT_ERR(VMBUS,
412                            "unable to allocate SYNIC message page!!");
413                 goto Cleanup;
414         }
415
416         gHvContext.synICEventPage[cpu] = (void *)get_zeroed_page(GFP_ATOMIC);
417
418         if (gHvContext.synICEventPage[cpu] == NULL) {
419                 DPRINT_ERR(VMBUS,
420                            "unable to allocate SYNIC event page!!");
421                 goto Cleanup;
422         }
423
424         /* Setup the Synic's message page */
425         rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
426         simp.SimpEnabled = 1;
427         simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[cpu])
428                 >> PAGE_SHIFT;
429
430         DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
431
432         wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
433
434         /* Setup the Synic's event page */
435         rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
436         siefp.SiefpEnabled = 1;
437         siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[cpu])
438                 >> PAGE_SHIFT;
439
440         DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
441
442         wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
443
444         /* Setup the interception SINT. */
445         /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
446         /*        interceptionSint.AsUINT64); */
447
448         /* Setup the shared SINT. */
449         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
450
451         sharedSint.AsUINT64 = 0;
452         sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
453         sharedSint.Masked = false;
454         sharedSint.AutoEoi = true;
455
456         DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx",
457                    sharedSint.AsUINT64);
458
459         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
460
461         /* Enable the global synic bit */
462         rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
463         sctrl.Enable = 1;
464
465         wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
466
467         gHvContext.SynICInitialized = true;
468
469         DPRINT_EXIT(VMBUS);
470
471         return;
472
473 Cleanup:
474         if (gHvContext.synICEventPage[cpu])
475                 osd_PageFree(gHvContext.synICEventPage[cpu], 1);
476
477         if (gHvContext.synICMessagePage[cpu])
478                 osd_PageFree(gHvContext.synICMessagePage[cpu], 1);
479
480         DPRINT_EXIT(VMBUS);
481         return;
482 }
483
484 /**
485  * HvSynicCleanup - Cleanup routine for HvSynicInit().
486  */
487 void HvSynicCleanup(void *arg)
488 {
489         union hv_synic_sint sharedSint;
490         union hv_synic_simp simp;
491         union hv_synic_siefp siefp;
492         int cpu = smp_processor_id();
493
494         DPRINT_ENTER(VMBUS);
495
496         if (!gHvContext.SynICInitialized) {
497                 DPRINT_EXIT(VMBUS);
498                 return;
499         }
500
501         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
502
503         sharedSint.Masked = 1;
504
505         /* Need to correctly cleanup in the case of SMP!!! */
506         /* Disable the interrupt */
507         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
508
509         rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
510         simp.SimpEnabled = 0;
511         simp.BaseSimpGpa = 0;
512
513         wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
514
515         rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
516         siefp.SiefpEnabled = 0;
517         siefp.BaseSiefpGpa = 0;
518
519         wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
520
521         osd_PageFree(gHvContext.synICMessagePage[cpu], 1);
522         osd_PageFree(gHvContext.synICEventPage[cpu], 1);
523
524         DPRINT_EXIT(VMBUS);
525 }