Drivers: hv: Setup a mapping for Hyper-V's notion cpu ID
[profile/ivi/kernel-x86-ivi.git] / drivers / 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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
23
24 #include <linux/kernel.h>
25 #include <linux/mm.h>
26 #include <linux/slab.h>
27 #include <linux/vmalloc.h>
28 #include <linux/hyperv.h>
29 #include <linux/version.h>
30 #include <asm/hyperv.h>
31 #include "hyperv_vmbus.h"
32
33 /* The one and only */
34 struct hv_context hv_context = {
35         .synic_initialized      = false,
36         .hypercall_page         = NULL,
37         .signal_event_param     = NULL,
38         .signal_event_buffer    = NULL,
39 };
40
41 /*
42  * query_hypervisor_info - Get version info of the windows hypervisor
43  */
44 static int query_hypervisor_info(void)
45 {
46         unsigned int eax;
47         unsigned int ebx;
48         unsigned int ecx;
49         unsigned int edx;
50         unsigned int max_leaf;
51         unsigned int op;
52
53         /*
54         * Its assumed that this is called after confirming that Viridian
55         * is present. Query id and revision.
56         */
57         eax = 0;
58         ebx = 0;
59         ecx = 0;
60         edx = 0;
61         op = HVCPUID_VENDOR_MAXFUNCTION;
62         cpuid(op, &eax, &ebx, &ecx, &edx);
63
64         max_leaf = eax;
65
66         if (max_leaf >= HVCPUID_VERSION) {
67                 eax = 0;
68                 ebx = 0;
69                 ecx = 0;
70                 edx = 0;
71                 op = HVCPUID_VERSION;
72                 cpuid(op, &eax, &ebx, &ecx, &edx);
73                 pr_info("Hyper-V Host OS Build:%d-%d.%d-%d-%d.%d\n",
74                             eax,
75                             ebx >> 16,
76                             ebx & 0xFFFF,
77                             ecx,
78                             edx >> 24,
79                             edx & 0xFFFFFF);
80         }
81         return max_leaf;
82 }
83
84 /*
85  * do_hypercall- Invoke the specified hypercall
86  */
87 static u64 do_hypercall(u64 control, void *input, void *output)
88 {
89 #ifdef CONFIG_X86_64
90         u64 hv_status = 0;
91         u64 input_address = (input) ? virt_to_phys(input) : 0;
92         u64 output_address = (output) ? virt_to_phys(output) : 0;
93         void *hypercall_page = hv_context.hypercall_page;
94
95         __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
96         __asm__ __volatile__("call *%3" : "=a" (hv_status) :
97                              "c" (control), "d" (input_address),
98                              "m" (hypercall_page));
99
100         return hv_status;
101
102 #else
103
104         u32 control_hi = control >> 32;
105         u32 control_lo = control & 0xFFFFFFFF;
106         u32 hv_status_hi = 1;
107         u32 hv_status_lo = 1;
108         u64 input_address = (input) ? virt_to_phys(input) : 0;
109         u32 input_address_hi = input_address >> 32;
110         u32 input_address_lo = input_address & 0xFFFFFFFF;
111         u64 output_address = (output) ? virt_to_phys(output) : 0;
112         u32 output_address_hi = output_address >> 32;
113         u32 output_address_lo = output_address & 0xFFFFFFFF;
114         void *hypercall_page = hv_context.hypercall_page;
115
116         __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
117                               "=a"(hv_status_lo) : "d" (control_hi),
118                               "a" (control_lo), "b" (input_address_hi),
119                               "c" (input_address_lo), "D"(output_address_hi),
120                               "S"(output_address_lo), "m" (hypercall_page));
121
122         return hv_status_lo | ((u64)hv_status_hi << 32);
123 #endif /* !x86_64 */
124 }
125
126 /*
127  * hv_init - Main initialization routine.
128  *
129  * This routine must be called before any other routines in here are called
130  */
131 int hv_init(void)
132 {
133         int max_leaf;
134         union hv_x64_msr_hypercall_contents hypercall_msr;
135         void *virtaddr = NULL;
136
137         memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS);
138         memset(hv_context.synic_message_page, 0,
139                sizeof(void *) * NR_CPUS);
140         memset(hv_context.vp_index, 0,
141                sizeof(int) * NR_CPUS);
142
143         max_leaf = query_hypervisor_info();
144
145         /*
146          * Write our OS ID.
147          */
148         hv_context.guestid = generate_guest_id(0, LINUX_VERSION_CODE, 0);
149         wrmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid);
150
151         /* See if the hypercall page is already set */
152         rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
153
154         virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC);
155
156         if (!virtaddr)
157                 goto cleanup;
158
159         hypercall_msr.enable = 1;
160
161         hypercall_msr.guest_physical_address = vmalloc_to_pfn(virtaddr);
162         wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
163
164         /* Confirm that hypercall page did get setup. */
165         hypercall_msr.as_uint64 = 0;
166         rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
167
168         if (!hypercall_msr.enable)
169                 goto cleanup;
170
171         hv_context.hypercall_page = virtaddr;
172
173         /* Setup the global signal event param for the signal event hypercall */
174         hv_context.signal_event_buffer =
175                         kmalloc(sizeof(struct hv_input_signal_event_buffer),
176                                 GFP_KERNEL);
177         if (!hv_context.signal_event_buffer)
178                 goto cleanup;
179
180         hv_context.signal_event_param =
181                 (struct hv_input_signal_event *)
182                         (ALIGN((unsigned long)
183                                   hv_context.signal_event_buffer,
184                                   HV_HYPERCALL_PARAM_ALIGN));
185         hv_context.signal_event_param->connectionid.asu32 = 0;
186         hv_context.signal_event_param->connectionid.u.id =
187                                                 VMBUS_EVENT_CONNECTION_ID;
188         hv_context.signal_event_param->flag_number = 0;
189         hv_context.signal_event_param->rsvdz = 0;
190
191         return 0;
192
193 cleanup:
194         if (virtaddr) {
195                 if (hypercall_msr.enable) {
196                         hypercall_msr.as_uint64 = 0;
197                         wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
198                 }
199
200                 vfree(virtaddr);
201         }
202
203         return -ENOTSUPP;
204 }
205
206 /*
207  * hv_cleanup - Cleanup routine.
208  *
209  * This routine is called normally during driver unloading or exiting.
210  */
211 void hv_cleanup(void)
212 {
213         union hv_x64_msr_hypercall_contents hypercall_msr;
214
215         /* Reset our OS id */
216         wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
217
218         kfree(hv_context.signal_event_buffer);
219         hv_context.signal_event_buffer = NULL;
220         hv_context.signal_event_param = NULL;
221
222         if (hv_context.hypercall_page) {
223                 hypercall_msr.as_uint64 = 0;
224                 wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
225                 vfree(hv_context.hypercall_page);
226                 hv_context.hypercall_page = NULL;
227         }
228 }
229
230 /*
231  * hv_post_message - Post a message using the hypervisor message IPC.
232  *
233  * This involves a hypercall.
234  */
235 int hv_post_message(union hv_connection_id connection_id,
236                   enum hv_message_type message_type,
237                   void *payload, size_t payload_size)
238 {
239         struct aligned_input {
240                 u64 alignment8;
241                 struct hv_input_post_message msg;
242         };
243
244         struct hv_input_post_message *aligned_msg;
245         u16 status;
246         unsigned long addr;
247
248         if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
249                 return -EMSGSIZE;
250
251         addr = (unsigned long)kmalloc(sizeof(struct aligned_input), GFP_ATOMIC);
252         if (!addr)
253                 return -ENOMEM;
254
255         aligned_msg = (struct hv_input_post_message *)
256                         (ALIGN(addr, HV_HYPERCALL_PARAM_ALIGN));
257
258         aligned_msg->connectionid = connection_id;
259         aligned_msg->message_type = message_type;
260         aligned_msg->payload_size = payload_size;
261         memcpy((void *)aligned_msg->payload, payload, payload_size);
262
263         status = do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL)
264                 & 0xFFFF;
265
266         kfree((void *)addr);
267
268         return status;
269 }
270
271
272 /*
273  * hv_signal_event -
274  * Signal an event on the specified connection using the hypervisor event IPC.
275  *
276  * This involves a hypercall.
277  */
278 u16 hv_signal_event(void *con_id)
279 {
280         u16 status;
281
282         status = (do_hypercall(HVCALL_SIGNAL_EVENT, con_id, NULL) & 0xFFFF);
283
284         return status;
285 }
286
287 /*
288  * hv_synic_init - Initialize the Synthethic Interrupt Controller.
289  *
290  * If it is already initialized by another entity (ie x2v shim), we need to
291  * retrieve the initialized message and event pages.  Otherwise, we create and
292  * initialize the message and event pages.
293  */
294 void hv_synic_init(void *irqarg)
295 {
296         u64 version;
297         union hv_synic_simp simp;
298         union hv_synic_siefp siefp;
299         union hv_synic_sint shared_sint;
300         union hv_synic_scontrol sctrl;
301         u64 vp_index;
302
303         u32 irq_vector = *((u32 *)(irqarg));
304         int cpu = smp_processor_id();
305
306         if (!hv_context.hypercall_page)
307                 return;
308
309         /* Check the version */
310         rdmsrl(HV_X64_MSR_SVERSION, version);
311
312         hv_context.synic_message_page[cpu] =
313                 (void *)get_zeroed_page(GFP_ATOMIC);
314
315         if (hv_context.synic_message_page[cpu] == NULL) {
316                 pr_err("Unable to allocate SYNIC message page\n");
317                 goto cleanup;
318         }
319
320         hv_context.synic_event_page[cpu] =
321                 (void *)get_zeroed_page(GFP_ATOMIC);
322
323         if (hv_context.synic_event_page[cpu] == NULL) {
324                 pr_err("Unable to allocate SYNIC event page\n");
325                 goto cleanup;
326         }
327
328         /* Setup the Synic's message page */
329         rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
330         simp.simp_enabled = 1;
331         simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu])
332                 >> PAGE_SHIFT;
333
334         wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
335
336         /* Setup the Synic's event page */
337         rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
338         siefp.siefp_enabled = 1;
339         siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu])
340                 >> PAGE_SHIFT;
341
342         wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
343
344         /* Setup the shared SINT. */
345         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
346
347         shared_sint.as_uint64 = 0;
348         shared_sint.vector = irq_vector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
349         shared_sint.masked = false;
350         shared_sint.auto_eoi = false;
351
352         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
353
354         /* Enable the global synic bit */
355         rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
356         sctrl.enable = 1;
357
358         wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
359
360         hv_context.synic_initialized = true;
361
362         /*
363          * Setup the mapping between Hyper-V's notion
364          * of cpuid and Linux' notion of cpuid.
365          * This array will be indexed using Linux cpuid.
366          */
367         rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
368         hv_context.vp_index[cpu] = (u32)vp_index;
369         return;
370
371 cleanup:
372         if (hv_context.synic_event_page[cpu])
373                 free_page((unsigned long)hv_context.synic_event_page[cpu]);
374
375         if (hv_context.synic_message_page[cpu])
376                 free_page((unsigned long)hv_context.synic_message_page[cpu]);
377         return;
378 }
379
380 /*
381  * hv_synic_cleanup - Cleanup routine for hv_synic_init().
382  */
383 void hv_synic_cleanup(void *arg)
384 {
385         union hv_synic_sint shared_sint;
386         union hv_synic_simp simp;
387         union hv_synic_siefp siefp;
388         int cpu = smp_processor_id();
389
390         if (!hv_context.synic_initialized)
391                 return;
392
393         rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
394
395         shared_sint.masked = 1;
396
397         /* Need to correctly cleanup in the case of SMP!!! */
398         /* Disable the interrupt */
399         wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
400
401         rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
402         simp.simp_enabled = 0;
403         simp.base_simp_gpa = 0;
404
405         wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
406
407         rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
408         siefp.siefp_enabled = 0;
409         siefp.base_siefp_gpa = 0;
410
411         wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
412
413         free_page((unsigned long)hv_context.synic_message_page[cpu]);
414         free_page((unsigned long)hv_context.synic_event_page[cpu]);
415 }