a34d713d9c5766f40c86b08fc27973bcefd18796
[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/slab.h>
25 #include <linux/vmalloc.h>
26 #include "osd.h"
27 #include "logging.h"
28 #include "vmbus_private.h"
29
30 /* The one and only */
31 struct hv_context hv_context = {
32         .synic_initialized      = false,
33         .hypercall_page         = NULL,
34         .signal_event_param     = NULL,
35         .signal_event_buffer    = NULL,
36 };
37
38 /*
39  * query_hypervisor_presence
40  * - Query the cpuid for presense of windows hypervisor
41  */
42 static int query_hypervisor_presence(void)
43 {
44         unsigned int eax;
45         unsigned int ebx;
46         unsigned int ecx;
47         unsigned int edx;
48         unsigned int op;
49
50         eax = 0;
51         ebx = 0;
52         ecx = 0;
53         edx = 0;
54         op = HVCPUID_VERSION_FEATURES;
55         cpuid(op, &eax, &ebx, &ecx, &edx);
56
57         return ecx & HV_PRESENT_BIT;
58 }
59
60 /*
61  * query_hypervisor_info - Get version info of the windows hypervisor
62  */
63 static int query_hypervisor_info(void)
64 {
65         unsigned int eax;
66         unsigned int ebx;
67         unsigned int ecx;
68         unsigned int edx;
69         unsigned int max_leaf;
70         unsigned int op;
71
72         /*
73         * Its assumed that this is called after confirming that Viridian
74         * is present. Query id and revision.
75         */
76         eax = 0;
77         ebx = 0;
78         ecx = 0;
79         edx = 0;
80         op = HVCPUID_VENDOR_MAXFUNCTION;
81         cpuid(op, &eax, &ebx, &ecx, &edx);
82
83         DPRINT_INFO(VMBUS, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c",
84                     (ebx & 0xFF),
85                     ((ebx >> 8) & 0xFF),
86                     ((ebx >> 16) & 0xFF),
87                     ((ebx >> 24) & 0xFF),
88                     (ecx & 0xFF),
89                     ((ecx >> 8) & 0xFF),
90                     ((ecx >> 16) & 0xFF),
91                     ((ecx >> 24) & 0xFF),
92                     (edx & 0xFF),
93                     ((edx >> 8) & 0xFF),
94                     ((edx >> 16) & 0xFF),
95                     ((edx >> 24) & 0xFF));
96
97         max_leaf = eax;
98         eax = 0;
99         ebx = 0;
100         ecx = 0;
101         edx = 0;
102         op = HVCPUID_INTERFACE;
103         cpuid(op, &eax, &ebx, &ecx, &edx);
104
105         DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c",
106                     (eax & 0xFF),
107                     ((eax >> 8) & 0xFF),
108                     ((eax >> 16) & 0xFF),
109                     ((eax >> 24) & 0xFF));
110
111         if (max_leaf >= HVCPUID_VERSION) {
112                 eax = 0;
113                 ebx = 0;
114                 ecx = 0;
115                 edx = 0;
116                 op = HVCPUID_VERSION;
117                 cpuid(op, &eax, &ebx, &ecx, &edx);
118                 DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",\
119                             eax,
120                             ebx >> 16,
121                             ebx & 0xFFFF,
122                             ecx,
123                             edx >> 24,
124                             edx & 0xFFFFFF);
125         }
126         return max_leaf;
127 }
128
129 /*
130  * do_hypercall- Invoke the specified hypercall
131  */
132 static u64 do_hypercall(u64 control, void *input, void *output)
133 {
134 #ifdef CONFIG_X86_64
135         u64 hv_status = 0;
136         u64 input_address = (input) ? virt_to_phys(input) : 0;
137         u64 output_address = (output) ? virt_to_phys(output) : 0;
138         volatile void *hypercall_page = hv_context.hypercall_page;
139
140         DPRINT_DBG(VMBUS, "Hypercall <control %llx input phys %llx virt %p "
141                    "output phys %llx virt %p hypercall %p>",
142                    control, input_address, input,
143                    output_address, output, hypercall_page);
144
145         __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
146         __asm__ __volatile__("call *%3" : "=a" (hv_status) :
147                              "c" (control), "d" (input_address),
148                              "m" (hypercall_page));
149
150         DPRINT_DBG(VMBUS, "Hypercall <return %llx>",  hv_status);
151
152         return hv_status;
153
154 #else
155
156         u32 control_hi = control >> 32;
157         u32 control_lo = control & 0xFFFFFFFF;
158         u32 hv_status_hi = 1;
159         u32 hv_status_lo = 1;
160         u64 input_address = (input) ? virt_to_phys(input) : 0;
161         u32 input_address_hi = input_address >> 32;
162         u32 input_address_lo = input_address & 0xFFFFFFFF;
163         u64 output_address = (output) ? virt_to_phys(output) : 0;
164         u32 output_address_hi = output_address >> 32;
165         u32 output_address_lo = output_address & 0xFFFFFFFF;
166         volatile void *hypercall_page = hv_context.hypercall_page;
167
168         DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>",
169                    control, input, output);
170
171         __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
172                               "=a"(hv_status_lo) : "d" (control_hi),
173                               "a" (control_lo), "b" (input_address_hi),
174                               "c" (input_address_lo), "D"(output_address_hi),
175                               "S"(output_address_lo), "m" (hypercall_page));
176
177         DPRINT_DBG(VMBUS, "Hypercall <return %llx>",
178                    hv_status_lo | ((u64)hv_status_hi << 32));
179
180         return hv_status_lo | ((u64)hv_status_hi << 32);
181 #endif /* !x86_64 */
182 }
183
184 /*
185  * hv_init - Main initialization routine.
186  *
187  * This routine must be called before any other routines in here are called
188  */
189 int hv_init(void)
190 {
191         int ret = 0;
192         int max_leaf;
193         union hv_x64_msr_hypercall_contents hypercall_msr;
194         void *virtaddr = NULL;
195
196         memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS);
197         memset(hv_context.synic_message_page, 0,
198                sizeof(void *) * MAX_NUM_CPUS);
199
200         if (!query_hypervisor_presence()) {
201                 DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!");
202                 goto Cleanup;
203         }
204
205         DPRINT_INFO(VMBUS,
206                     "Windows hypervisor detected! Retrieving more info...");
207
208         max_leaf = query_hypervisor_info();
209         /* HvQueryHypervisorFeatures(maxLeaf); */
210
211         /*
212          * We only support running on top of Hyper-V
213          */
214         rdmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid);
215
216         if (hv_context.guestid != 0) {
217                 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!",
218                                 hv_context.guestid);
219                 goto Cleanup;
220         }
221
222         /* Write our OS info */
223         wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
224         hv_context.guestid = HV_LINUX_GUEST_ID;
225
226         /* See if the hypercall page is already set */
227         rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
228
229         /*
230         * Allocate the hypercall page memory
231         * virtaddr = osd_page_alloc(1);
232         */
233         virtaddr = osd_virtual_alloc_exec(PAGE_SIZE);
234
235         if (!virtaddr) {
236                 DPRINT_ERR(VMBUS,
237                            "unable to allocate hypercall page!!");
238                 goto Cleanup;
239         }
240
241         hypercall_msr.enable = 1;
242
243         hypercall_msr.guest_physical_address = vmalloc_to_pfn(virtaddr);
244         wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
245
246         /* Confirm that hypercall page did get setup. */
247         hypercall_msr.as_uint64 = 0;
248         rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
249
250         if (!hypercall_msr.enable) {
251                 DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
252                 goto Cleanup;
253         }
254
255         hv_context.hypercall_page = virtaddr;
256
257         DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
258                     hv_context.hypercall_page,
259                     (u64)hypercall_msr.guest_physical_address << PAGE_SHIFT);
260
261         /* Setup the global signal event param for the signal event hypercall */
262         hv_context.signal_event_buffer =
263                         kmalloc(sizeof(struct hv_input_signal_event_buffer),
264                                 GFP_KERNEL);
265         if (!hv_context.signal_event_buffer)
266                 goto Cleanup;
267
268         hv_context.signal_event_param =
269                 (struct hv_input_signal_event *)
270                         (ALIGN_UP((unsigned long)
271                                   hv_context.signal_event_buffer,
272                                   HV_HYPERCALL_PARAM_ALIGN));
273         hv_context.signal_event_param->connectionid.asu32 = 0;
274         hv_context.signal_event_param->connectionid.u.id =
275                                                 VMBUS_EVENT_CONNECTION_ID;
276         hv_context.signal_event_param->flag_number = 0;
277         hv_context.signal_event_param->rsvdz = 0;
278
279         return ret;
280
281 Cleanup:
282         if (virtaddr) {
283                 if (hypercall_msr.enable) {
284                         hypercall_msr.as_uint64 = 0;
285                         wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
286                 }
287
288                 vfree(virtaddr);
289         }
290         ret = -1;
291         return ret;
292 }
293
294 /*
295  * hv_cleanup - Cleanup routine.
296  *
297  * This routine is called normally during driver unloading or exiting.
298  */
299 void hv_cleanup(void)
300 {
301         union hv_x64_msr_hypercall_contents hypercall_msr;
302
303         kfree(hv_context.signal_event_buffer);
304         hv_context.signal_event_buffer = NULL;
305         hv_context.signal_event_param = NULL;
306
307         if (hv_context.hypercall_page) {
308                 hypercall_msr.as_uint64 = 0;
309                 wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
310                 vfree(hv_context.hypercall_page);
311                 hv_context.hypercall_page = NULL;
312         }
313 }
314
315 /*
316  * hv_post_message - Post a message using the hypervisor message IPC.
317  *
318  * This involves a hypercall.
319  */
320 u16 hv_post_message(union hv_connection_id connection_id,
321                   enum hv_message_type message_type,
322                   void *payload, size_t payload_size)
323 {
324         struct aligned_input {
325                 u64 alignment8;
326                 struct hv_input_post_message msg;
327         };
328
329         struct hv_input_post_message *aligned_msg;
330         u16 status;
331         unsigned long addr;
332
333         if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
334                 return -1;
335
336         addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC);
337         if (!addr)
338                 return -1;
339
340         aligned_msg = (struct hv_input_post_message *)
341                         (ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
342
343         aligned_msg->connectionid = connection_id;
344         aligned_msg->message_type = message_type;
345         aligned_msg->payload_size = payload_size;
346         memcpy((void *)aligned_msg->payload, payload, payload_size);
347
348         status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL)
349                 & 0xFFFF;
350
351         kfree((void *)addr);
352
353         return status;
354 }
355
356
357 /*
358  * hv_signal_event -
359  * Signal an event on the specified connection using the hypervisor event IPC.
360  *
361  * This involves a hypercall.
362  */
363 u16 hv_signal_event(void)
364 {
365         u16 status;
366
367         status = do_hypercall(HVCALL_SIGNAL_EVENT,
368                                hv_context.signal_event_param,
369                                NULL) & 0xFFFF;
370         return status;
371 }
372
373 /*
374  * hv_synic_init - Initialize the Synthethic Interrupt Controller.
375  *
376  * If it is already initialized by another entity (ie x2v shim), we need to
377  * retrieve the initialized message and event pages.  Otherwise, we create and
378  * initialize the message and event pages.
379  */
380 void hv_synic_init(void *irqarg)
381 {
382         u64 version;
383         union hv_synic_simp simp;
384         union hv_synic_siefp siefp;
385         union hv_synic_sint shared_sint;
386         union hv_synic_scontrol sctrl;
387
388         u32 irq_vector = *((u32 *)(irqarg));
389         int cpu = smp_processor_id();
390
391         if (!hv_context.hypercall_page)
392                 return;
393
394         /* Check the version */
395         rdmsrl(HV_X64_MSR_SVERSION, version);
396
397         DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
398
399         hv_context.synic_message_page[cpu] =
400                 (void *)get_zeroed_page(GFP_ATOMIC);
401
402         if (hv_context.synic_message_page[cpu] == NULL) {
403                 DPRINT_ERR(VMBUS,
404                            "unable to allocate SYNIC message page!!");
405                 goto Cleanup;
406         }
407
408         hv_context.synic_event_page[cpu] =
409                 (void *)get_zeroed_page(GFP_ATOMIC);
410
411         if (hv_context.synic_event_page[cpu] == NULL) {
412                 DPRINT_ERR(VMBUS,
413                            "unable to allocate SYNIC event page!!");
414                 goto Cleanup;
415         }
416
417         /* Setup the Synic's message page */
418         rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
419         simp.simp_enabled = 1;
420         simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu])
421                 >> PAGE_SHIFT;
422
423         DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.as_uint64);
424
425         wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
426
427         /* Setup the Synic's event page */
428         rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
429         siefp.siefp_enabled = 1;
430         siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu])
431                 >> PAGE_SHIFT;
432
433         DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.as_uint64);
434
435         wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
436
437         /* Setup the interception SINT. */
438         /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
439         /*        interceptionSint.as_uint64); */
440
441         /* Setup the shared SINT. */
442         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
443
444         shared_sint.as_uint64 = 0;
445         shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
446         shared_sint.masked = false;
447         shared_sint.auto_eoi = true;
448
449         DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx",
450                    shared_sint.as_uint64);
451
452         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
453
454         /* Enable the global synic bit */
455         rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
456         sctrl.enable = 1;
457
458         wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
459
460         hv_context.synic_initialized = true;
461         return;
462
463 Cleanup:
464         if (hv_context.synic_event_page[cpu])
465                 osd_page_free(hv_context.synic_event_page[cpu], 1);
466
467         if (hv_context.synic_message_page[cpu])
468                 osd_page_free(hv_context.synic_message_page[cpu], 1);
469         return;
470 }
471
472 /*
473  * hv_synic_cleanup - Cleanup routine for hv_synic_init().
474  */
475 void hv_synic_cleanup(void *arg)
476 {
477         union hv_synic_sint shared_sint;
478         union hv_synic_simp simp;
479         union hv_synic_siefp siefp;
480         int cpu = smp_processor_id();
481
482         if (!hv_context.synic_initialized)
483                 return;
484
485         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
486
487         shared_sint.masked = 1;
488
489         /* Need to correctly cleanup in the case of SMP!!! */
490         /* Disable the interrupt */
491         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
492
493         rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
494         simp.simp_enabled = 0;
495         simp.base_simp_gpa = 0;
496
497         wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
498
499         rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
500         siefp.siefp_enabled = 0;
501         siefp.base_siefp_gpa = 0;
502
503         wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
504
505         osd_page_free(hv_context.synic_message_page[cpu], 1);
506         osd_page_free(hv_context.synic_event_page[cpu], 1);
507 }