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