blob: c5b6613f2f2f7963b07aecd8e9c3104cfedd4fca [file] [log] [blame]
Hank Janssen3e7ee492009-07-13 16:02:34 -07001/*
Hank Janssen3e7ee492009-07-13 16:02:34 -07002 * 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 */
Greg Kroah-Hartmana0086dc2009-08-17 17:22:08 -070022#include <linux/kernel.h>
23#include <linux/mm.h>
Bill Pembertonb7c947f2009-07-29 17:00:13 -040024#include <linux/vmalloc.h>
Greg Kroah-Hartman4983b392009-08-19 16:14:47 -070025#include "osd.h"
Greg Kroah-Hartman645954c2009-08-28 16:22:59 -070026#include "logging.h"
Hank Janssen3e7ee492009-07-13 16:02:34 -070027#include "VmbusPrivate.h"
28
Bill Pemberton454f18a2009-07-27 16:47:24 -040029/* The one and only */
Greg Kroah-Hartmanaf248e12009-08-18 15:20:34 -070030struct hv_context gHvContext = {
31 .SynICInitialized = false,
32 .HypercallPage = NULL,
33 .SignalEventParam = NULL,
34 .SignalEventBuffer = NULL,
Hank Janssen3e7ee492009-07-13 16:02:34 -070035};
36
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070037/**
38 * HvQueryHypervisorPresence - Query the cpuid for presense of windows hypervisor
39 */
40static int HvQueryHypervisorPresence(void)
Hank Janssen3e7ee492009-07-13 16:02:34 -070041{
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070042 unsigned int eax;
43 unsigned int ebx;
44 unsigned int ecx;
45 unsigned int edx;
46 unsigned int op;
Hank Janssen3e7ee492009-07-13 16:02:34 -070047
Bill Pemberton454f18a2009-07-27 16:47:24 -040048 eax = 0;
49 ebx = 0;
50 ecx = 0;
51 edx = 0;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070052 op = HvCpuIdFunctionVersionAndFeatures;
Greg Kroah-Hartmanf931a702009-07-29 15:37:12 -070053 cpuid(op, &eax, &ebx, &ecx, &edx);
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070054
55 return ecx & HV_PRESENT_BIT;
Hank Janssen3e7ee492009-07-13 16:02:34 -070056}
57
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070058/**
59 * HvQueryHypervisorInfo - Get version info of the windows hypervisor
60 */
61static 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;
Hank Janssen3e7ee492009-07-13 16:02:34 -070069
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070070 /*
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);
Hank Janssen3e7ee492009-07-13 16:02:34 -070080
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070081 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));
Hank Janssen3e7ee492009-07-13 16:02:34 -070094
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -070095 maxLeaf = eax;
96 eax = 0;
97 ebx = 0;
98 ecx = 0;
99 edx = 0;
100 op = HvCpuIdFunctionHvInterface;
101 cpuid(op, &eax, &ebx, &ecx, &edx);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700102
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700103 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 */
130static u64 HvDoHypercall(u64 Control, void *Input, void *Output)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700131{
Greg Kroah-Hartman530cf202009-07-16 22:31:15 -0700132#ifdef CONFIG_X86_64
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700133 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;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700137
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700138 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);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700142
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700143 __asm__ __volatile__("mov %0, %%r8" : : "r" (outputAddress) : "r8");
144 __asm__ __volatile__("call *%3" : "=a" (hvStatus) :
145 "c" (Control), "d" (inputAddress),
146 "m" (hypercallPage));
Hank Janssen3e7ee492009-07-13 16:02:34 -0700147
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700148 DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatus);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700149
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700150 return hvStatus;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700151
152#else
153
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700154 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;
Greg Kroah-Hartmanfa56d362009-07-29 15:39:27 -0700161 u64 outputAddress = (Output) ? virt_to_phys(Output) : 0;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700162 u32 outputAddressHi = outputAddress >> 32;
163 u32 outputAddressLo = outputAddress & 0xFFFFFFFF;
164 volatile void *hypercallPage = gHvContext.HypercallPage;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700165
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700166 DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>",
167 Control, Input, Output);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700168
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700169 __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));
Hank Janssen3e7ee492009-07-13 16:02:34 -0700174
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700175 DPRINT_DBG(VMBUS, "Hypercall <return %llx>",
176 hvStatusLo | ((u64)hvStatusHi << 32));
Hank Janssen3e7ee492009-07-13 16:02:34 -0700177
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700178 return hvStatusLo | ((u64)hvStatusHi << 32);
179#endif /* !x86_64 */
Hank Janssen3e7ee492009-07-13 16:02:34 -0700180}
181
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700182/**
183 * HvInit - Main initialization routine.
184 *
185 * This routine must be called before any other routines in here are called
186 */
187int HvInit(void)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700188{
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700189 int ret = 0;
190 int maxLeaf;
Greg Kroah-Hartmanf80b3d52009-08-20 12:09:34 -0700191 union hv_x64_msr_hypercall_contents hypercallMsr;
Greg Kroah-Hartman949cada2009-07-29 09:06:51 -0700192 void *virtAddr = NULL;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700193
194 DPRINT_ENTER(VMBUS);
195
Bill Pemberton44f357f2009-07-28 13:46:26 -0400196 memset(gHvContext.synICEventPage, 0, sizeof(void *) * MAX_NUM_CPUS);
197 memset(gHvContext.synICMessagePage, 0, sizeof(void *) * MAX_NUM_CPUS);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700198
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700199 if (!HvQueryHypervisorPresence()) {
Hank Janssen3e7ee492009-07-13 16:02:34 -0700200 DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!");
201 goto Cleanup;
202 }
203
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700204 DPRINT_INFO(VMBUS,
205 "Windows hypervisor detected! Retrieving more info...");
Hank Janssen3e7ee492009-07-13 16:02:34 -0700206
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700207 maxLeaf = HvQueryHypervisorInfo();
208 /* HvQueryHypervisorFeatures(maxLeaf); */
Hank Janssen3e7ee492009-07-13 16:02:34 -0700209
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700210 /*
211 * Determine if we are running on xenlinux (ie x2v shim) or native
212 * linux
213 */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700214 rdmsrl(HV_X64_MSR_GUEST_OS_ID, gHvContext.GuestId);
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700215 if (gHvContext.GuestId == 0) {
Bill Pemberton454f18a2009-07-27 16:47:24 -0400216 /* Write our OS info */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700217 wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700218 gHvContext.GuestId = HV_LINUX_GUEST_ID;
219 }
220
Bill Pemberton454f18a2009-07-27 16:47:24 -0400221 /* See if the hypercall page is already set */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700222 rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700223 if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
Bill Pemberton454f18a2009-07-27 16:47:24 -0400224 /* Allocate the hypercall page memory */
Greg Kroah-Hartmanbfc30aae2009-07-29 15:40:18 -0700225 /* virtAddr = osd_PageAlloc(1); */
226 virtAddr = osd_VirtualAllocExec(PAGE_SIZE);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700227
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700228 if (!virtAddr) {
229 DPRINT_ERR(VMBUS,
230 "unable to allocate hypercall page!!");
Hank Janssen3e7ee492009-07-13 16:02:34 -0700231 goto Cleanup;
232 }
233
234 hypercallMsr.Enable = 1;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700235 /* hypercallMsr.GuestPhysicalAddress =
236 * virt_to_phys(virtAddr) >> PAGE_SHIFT; */
Greg Kroah-Hartmanfa56d362009-07-29 15:39:27 -0700237 hypercallMsr.GuestPhysicalAddress = vmalloc_to_pfn(virtAddr);
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700238 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700239
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700240 /* Confirm that hypercall page did get setup. */
Hank Janssen3e7ee492009-07-13 16:02:34 -0700241 hypercallMsr.AsUINT64 = 0;
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700242 rdmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700243 if (!hypercallMsr.Enable) {
Hank Janssen3e7ee492009-07-13 16:02:34 -0700244 DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
245 goto Cleanup;
246 }
247
248 gHvContext.HypercallPage = virtAddr;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700249 } else {
250 DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!",
251 gHvContext.GuestId);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700252 goto Cleanup;
253 }
254
Greg Kroah-Hartman2701f682009-07-16 12:36:37 -0700255 DPRINT_INFO(VMBUS, "Hypercall page VA=%p, PA=0x%0llx",
256 gHvContext.HypercallPage,
257 (u64)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700258
Bill Pemberton454f18a2009-07-27 16:47:24 -0400259 /* Setup the global signal event param for the signal event hypercall */
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700260 gHvContext.SignalEventBuffer =
261 kmalloc(sizeof(struct hv_input_signal_event_buffer),
262 GFP_KERNEL);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700263 if (!gHvContext.SignalEventBuffer)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700264 goto Cleanup;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700265
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700266 gHvContext.SignalEventParam =
267 (struct hv_input_signal_event *)
268 (ALIGN_UP((unsigned long)gHvContext.SignalEventBuffer,
269 HV_HYPERCALL_PARAM_ALIGN));
Greg Kroah-Hartman4d643112009-07-14 15:09:36 -0700270 gHvContext.SignalEventParam->ConnectionId.Asu32 = 0;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700271 gHvContext.SignalEventParam->ConnectionId.u.Id =
272 VMBUS_EVENT_CONNECTION_ID;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700273 gHvContext.SignalEventParam->FlagNumber = 0;
274 gHvContext.SignalEventParam->RsvdZ = 0;
275
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700276 /* DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId()); */
Hank Janssen3e7ee492009-07-13 16:02:34 -0700277
278 DPRINT_EXIT(VMBUS);
279
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700280 return ret;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700281
282Cleanup:
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700283 if (virtAddr) {
284 if (hypercallMsr.Enable) {
Hank Janssen3e7ee492009-07-13 16:02:34 -0700285 hypercallMsr.AsUINT64 = 0;
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700286 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700287 }
288
Bill Pembertonb7c947f2009-07-29 17:00:13 -0400289 vfree(virtAddr);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700290 }
291 ret = -1;
292 DPRINT_EXIT(VMBUS);
293
294 return ret;
295}
296
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700297/**
298 * HvCleanup - Cleanup routine.
299 *
300 * This routine is called normally during driver unloading or exiting.
301 */
302void HvCleanup(void)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700303{
Greg Kroah-Hartmanf80b3d52009-08-20 12:09:34 -0700304 union hv_x64_msr_hypercall_contents hypercallMsr;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700305
306 DPRINT_ENTER(VMBUS);
307
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700308 if (gHvContext.SignalEventBuffer) {
Hank Janssen3e7ee492009-07-13 16:02:34 -0700309 gHvContext.SignalEventBuffer = NULL;
310 gHvContext.SignalEventParam = NULL;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700311 kfree(gHvContext.SignalEventBuffer);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700312 }
313
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700314 if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
315 if (gHvContext.HypercallPage) {
Hank Janssen3e7ee492009-07-13 16:02:34 -0700316 hypercallMsr.AsUINT64 = 0;
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700317 wrmsrl(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
Bill Pembertonb7c947f2009-07-29 17:00:13 -0400318 vfree(gHvContext.HypercallPage);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700319 gHvContext.HypercallPage = NULL;
320 }
321 }
322
323 DPRINT_EXIT(VMBUS);
324
325}
326
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700327/**
328 * HvPostMessage - Post a message using the hypervisor message IPC.
329 *
330 * This involves a hypercall.
331 */
Greg Kroah-Hartman034469e2009-08-20 12:14:11 -0700332u16 HvPostMessage(union hv_connection_id connectionId,
333 enum hv_message_type messageType,
334 void *payload, size_t payloadSize)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700335{
336 struct alignedInput {
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700337 u64 alignment8;
Greg Kroah-Hartmancba4dec2009-08-19 16:21:28 -0700338 struct hv_input_post_message msg;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700339 };
340
Greg Kroah-Hartmancba4dec2009-08-19 16:21:28 -0700341 struct hv_input_post_message *alignedMsg;
Greg Kroah-Hartman034469e2009-08-20 12:14:11 -0700342 u16 status;
Greg Kroah-Hartmanc4b0bc92009-07-14 15:12:46 -0700343 unsigned long addr;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700344
345 if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700346 return -1;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700347
Greg Kroah-Hartman0a72f3c2009-07-15 12:48:01 -0700348 addr = (unsigned long)kmalloc(sizeof(struct alignedInput), GFP_ATOMIC);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700349 if (!addr)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700350 return -1;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700351
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700352 alignedMsg = (struct hv_input_post_message *)
353 (ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
Hank Janssen3e7ee492009-07-13 16:02:34 -0700354
355 alignedMsg->ConnectionId = connectionId;
356 alignedMsg->MessageType = messageType;
357 alignedMsg->PayloadSize = payloadSize;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700358 memcpy((void *)alignedMsg->Payload, payload, payloadSize);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700359
Greg Kroah-Hartman949cada2009-07-29 09:06:51 -0700360 status = HvDoHypercall(HvCallPostMessage, alignedMsg, NULL) & 0xFFFF;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700361
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700362 kfree((void *)addr);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700363
364 return status;
365}
366
367
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700368/**
369 * HvSignalEvent - Signal an event on the specified connection using the hypervisor event IPC.
370 *
371 * This involves a hypercall.
372 */
Greg Kroah-Hartman034469e2009-08-20 12:14:11 -0700373u16 HvSignalEvent(void)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700374{
Greg Kroah-Hartman034469e2009-08-20 12:14:11 -0700375 u16 status;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700376
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700377 status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam,
378 NULL) & 0xFFFF;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700379 return status;
380}
381
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700382/**
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 */
389int HvSynicInit(u32 irqVector)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700390{
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700391 u64 version;
Greg Kroah-Hartmaneacb1b42009-08-20 12:11:26 -0700392 union hv_synic_simp simp;
393 union hv_synic_siefp siefp;
394 union hv_synic_sint sharedSint;
395 union hv_synic_scontrol sctrl;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700396 u64 guestID;
397 int ret = 0;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700398
399 DPRINT_ENTER(VMBUS);
400
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700401 if (!gHvContext.HypercallPage) {
Hank Janssen3e7ee492009-07-13 16:02:34 -0700402 DPRINT_EXIT(VMBUS);
403 return ret;
404 }
405
Bill Pemberton454f18a2009-07-27 16:47:24 -0400406 /* Check the version */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700407 rdmsrl(HV_X64_MSR_SVERSION, version);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700408
409 DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
410
Bill Pemberton454f18a2009-07-27 16:47:24 -0400411 /* TODO: Handle SMP */
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700412 if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID) {
413 DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since "
414 "it is already set.");
Hank Janssen3e7ee492009-07-13 16:02:34 -0700415
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700416 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
417 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700418
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700419 DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx",
420 simp.AsUINT64, siefp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700421
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700422 /*
423 * Determine if we are running on xenlinux (ie x2v shim) or
424 * native linux
425 */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700426 rdmsrl(HV_X64_MSR_GUEST_OS_ID, guestID);
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700427 if (guestID == HV_LINUX_GUEST_ID) {
428 gHvContext.synICMessagePage[0] =
429 phys_to_virt(simp.BaseSimpGpa << PAGE_SHIFT);
430 gHvContext.synICEventPage[0] =
431 phys_to_virt(siefp.BaseSiefpGpa << PAGE_SHIFT);
432 } else {
Hank Janssen3e7ee492009-07-13 16:02:34 -0700433 DPRINT_ERR(VMBUS, "unknown guest id!!");
434 goto Cleanup;
435 }
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700436 DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p",
437 gHvContext.synICMessagePage[0],
438 gHvContext.synICEventPage[0]);
439 } else {
Greg Kroah-Hartmanbfc30aae2009-07-29 15:40:18 -0700440 gHvContext.synICMessagePage[0] = osd_PageAlloc(1);
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700441 if (gHvContext.synICMessagePage[0] == NULL) {
442 DPRINT_ERR(VMBUS,
443 "unable to allocate SYNIC message page!!");
Hank Janssen3e7ee492009-07-13 16:02:34 -0700444 goto Cleanup;
445 }
446
Greg Kroah-Hartmanbfc30aae2009-07-29 15:40:18 -0700447 gHvContext.synICEventPage[0] = osd_PageAlloc(1);
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700448 if (gHvContext.synICEventPage[0] == NULL) {
449 DPRINT_ERR(VMBUS,
450 "unable to allocate SYNIC event page!!");
Hank Janssen3e7ee492009-07-13 16:02:34 -0700451 goto Cleanup;
452 }
453
Bill Pemberton454f18a2009-07-27 16:47:24 -0400454 /* Setup the Synic's message page */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700455 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700456 simp.SimpEnabled = 1;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700457 simp.BaseSimpGpa = virt_to_phys(gHvContext.synICMessagePage[0])
458 >> PAGE_SHIFT;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700459
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700460 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx",
461 simp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700462
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700463 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700464
Bill Pemberton454f18a2009-07-27 16:47:24 -0400465 /* Setup the Synic's event page */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700466 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700467 siefp.SiefpEnabled = 1;
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700468 siefp.BaseSiefpGpa = virt_to_phys(gHvContext.synICEventPage[0])
469 >> PAGE_SHIFT;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700470
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700471 DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx",
472 siefp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700473
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700474 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700475 }
Hank Janssen3e7ee492009-07-13 16:02:34 -0700476
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700477 /* Setup the interception SINT. */
478 /* wrmsrl((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX), */
479 /* interceptionSint.AsUINT64); */
480
481 /* Setup the shared SINT. */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700482 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700483
484 sharedSint.AsUINT64 = 0;
Bill Pemberton454f18a2009-07-27 16:47:24 -0400485 sharedSint.Vector = irqVector; /* HV_SHARED_SINT_IDT_VECTOR + 0x20; */
486 sharedSint.Masked = false;
487 sharedSint.AutoEoi = true;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700488
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700489 DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx",
490 sharedSint.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700491
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700492 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700493
Bill Pemberton454f18a2009-07-27 16:47:24 -0400494 /* Enable the global synic bit */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700495 rdmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700496 sctrl.Enable = 1;
497
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700498 wrmsrl(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700499
Greg Kroah-Hartman0e727612009-07-15 12:46:44 -0700500 gHvContext.SynICInitialized = true;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700501
502 DPRINT_EXIT(VMBUS);
503
504 return ret;
505
506Cleanup:
507 ret = -1;
508
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700509 if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
Hank Janssen3e7ee492009-07-13 16:02:34 -0700510 if (gHvContext.synICEventPage[0])
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700511 osd_PageFree(gHvContext.synICEventPage[0], 1);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700512
513 if (gHvContext.synICMessagePage[0])
Greg Kroah-Hartmanbfc30aae2009-07-29 15:40:18 -0700514 osd_PageFree(gHvContext.synICMessagePage[0], 1);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700515 }
516
517 DPRINT_EXIT(VMBUS);
518
519 return ret;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700520}
521
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700522/**
523 * HvSynicCleanup - Cleanup routine for HvSynicInit().
524 */
Greg Kroah-Hartman98d9fac2009-08-17 17:20:55 -0700525void HvSynicCleanup(void)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700526{
Greg Kroah-Hartmaneacb1b42009-08-20 12:11:26 -0700527 union hv_synic_sint sharedSint;
528 union hv_synic_simp simp;
529 union hv_synic_siefp siefp;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700530
531 DPRINT_ENTER(VMBUS);
532
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700533 if (!gHvContext.SynICInitialized) {
Hank Janssen3e7ee492009-07-13 16:02:34 -0700534 DPRINT_EXIT(VMBUS);
535 return;
536 }
537
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700538 rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700539
540 sharedSint.Masked = 1;
541
Bill Pemberton454f18a2009-07-27 16:47:24 -0400542 /* Disable the interrupt */
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700543 wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700544
Bill Pemberton454f18a2009-07-27 16:47:24 -0400545 /*
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 */
Greg Kroah-Hartman0831ad02009-08-31 20:23:33 -0700550 if (gHvContext.GuestId == HV_LINUX_GUEST_ID) {
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700551 rdmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700552 simp.SimpEnabled = 0;
553 simp.BaseSimpGpa = 0;
554
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700555 wrmsrl(HV_X64_MSR_SIMP, simp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700556
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700557 rdmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700558 siefp.SiefpEnabled = 0;
559 siefp.BaseSiefpGpa = 0;
560
Greg Kroah-Hartmana51ed7d2009-08-17 17:20:02 -0700561 wrmsrl(HV_X64_MSR_SIEFP, siefp.AsUINT64);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700562
Greg Kroah-Hartmanbfc30aae2009-07-29 15:40:18 -0700563 osd_PageFree(gHvContext.synICMessagePage[0], 1);
564 osd_PageFree(gHvContext.synICEventPage[0], 1);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700565 }
566
567 DPRINT_EXIT(VMBUS);
568}