VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp@ 70214

Last change on this file since 70214 was 70208, checked in by vboxsync, 7 years ago

VBoxGuest-win.cpp: removed now unused function

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 93.4 KB
Line 
1/* $Id: VBoxGuest-win.cpp 70208 2017-12-18 20:40:57Z vboxsync $ */
2/** @file
3 * VBoxGuest - Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_SUP_DRV
32#include <iprt/nt/ntddk.h>
33
34#include "VBoxGuestInternal.h"
35#include <VBox/VBoxGuestLib.h>
36#include <VBox/log.h>
37
38#include <iprt/asm.h>
39#include <iprt/asm-amd64-x86.h>
40#include <iprt/dbg.h>
41#include <iprt/initterm.h>
42#include <iprt/memobj.h>
43#include <iprt/spinlock.h>
44#include <iprt/string.h>
45
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51#undef ExFreePool
52
53#ifndef PCI_MAX_BUSES
54# define PCI_MAX_BUSES 256
55#endif
56
57/** CM_RESOURCE_MEMORY_* flags which were used on XP or earlier. */
58#define VBOX_CM_PRE_VISTA_MASK (0x3f)
59
60
61#define VBOXGUEST_UPDATE_DEVSTATE(a_pDevExt, a_enmNewDevState) \
62 do { \
63 (a_pDevExt)->enmPrevDevState = (a_pDevExt)->enmDevState; \
64 (a_pDevExt)->enmDevState = (a_enmNewDevState); \
65 } while (0)
66
67
68/*********************************************************************************************************************************
69* Structures and Typedefs *
70*********************************************************************************************************************************/
71/**
72 * Possible device states for our state machine.
73 */
74typedef enum VGDRVNTDEVSTATE
75{
76 VGDRVNTDEVSTATE_STOPPED,
77 VGDRVNTDEVSTATE_WORKING,
78 VGDRVNTDEVSTATE_PENDINGSTOP,
79 VGDRVNTDEVSTATE_PENDINGREMOVE,
80 VGDRVNTDEVSTATE_SURPRISEREMOVED,
81 VGDRVNTDEVSTATE_REMOVED
82} VGDRVNTDEVSTATE;
83
84
85typedef struct VBOXGUESTWINBASEADDRESS
86{
87 /** Original device physical address. */
88 PHYSICAL_ADDRESS RangeStart;
89 /** Length of I/O or memory range. */
90 ULONG RangeLength;
91 /** Flag: Unmapped range is I/O or memory range. */
92 BOOLEAN RangeInMemory;
93 /** Mapped I/O or memory range. */
94 PVOID MappedRangeStart;
95 /** Flag: mapped range is I/O or memory range. */
96 BOOLEAN MappedRangeInMemory;
97 /** Flag: resource is mapped (i.e. MmMapIoSpace called). */
98 BOOLEAN ResourceMapped;
99} VBOXGUESTWINBASEADDRESS;
100typedef VBOXGUESTWINBASEADDRESS *PVBOXGUESTWINBASEADDRESS;
101
102
103/**
104 * Subclassing the device extension for adding windows-specific bits.
105 */
106typedef struct VBOXGUESTDEVEXTWIN
107{
108 /** The common device extension core. */
109 VBOXGUESTDEVEXT Core;
110
111 /** Our functional driver object. */
112 PDEVICE_OBJECT pDeviceObject;
113 /** Top of the stack. */
114 PDEVICE_OBJECT pNextLowerDriver;
115 /** Currently active Irp. */
116 IRP *pCurrentIrp;
117 /** Interrupt object pointer. */
118 PKINTERRUPT pInterruptObject;
119
120 /** Bus number where the device is located. */
121 ULONG uBus;
122 /** Slot number where the device is located. */
123 ULONG uSlot;
124 /** Device interrupt level. */
125 ULONG uInterruptLevel;
126 /** Device interrupt vector. */
127 ULONG uInterruptVector;
128 /** Affinity mask. */
129 KAFFINITY fInterruptAffinity;
130 /** LevelSensitive or Latched. */
131 KINTERRUPT_MODE enmInterruptMode;
132
133 /** PCI base address information. */
134 ULONG cPciAddresses;
135 VBOXGUESTWINBASEADDRESS aPciBaseAddresses[PCI_TYPE0_ADDRESSES];
136
137 /** Physical address and length of VMMDev memory. */
138 PHYSICAL_ADDRESS uVmmDevMemoryPhysAddr;
139 /** Length of VMMDev memory. */
140 ULONG cbVmmDevMemory;
141
142 /** Device state. */
143 VGDRVNTDEVSTATE enmDevState;
144 /** The previous device state. */
145 VGDRVNTDEVSTATE enmPrevDevState;
146
147 /** Last system power action set (see VBoxGuestPower). */
148 POWER_ACTION enmLastSystemPowerAction;
149 /** Preallocated generic request for shutdown. */
150 VMMDevPowerStateRequest *pPowerStateRequest;
151 /** Preallocated VMMDevEvents for IRQ handler. */
152 VMMDevEvents *pIrqAckEvents;
153
154 /** Spinlock protecting MouseNotifyCallback. Required since the consumer is
155 * in a DPC callback and not the ISR. */
156 KSPIN_LOCK MouseEventAccessSpinLock;
157} VBOXGUESTDEVEXTWIN;
158typedef VBOXGUESTDEVEXTWIN *PVBOXGUESTDEVEXTWIN;
159
160
161/** NT (windows) version identifier. */
162typedef enum VGDRVNTVER
163{
164 VGDRVNTVER_INVALID = 0,
165 VGDRVNTVER_WINNT31,
166 VGDRVNTVER_WINNT350,
167 VGDRVNTVER_WINNT351,
168 VGDRVNTVER_WINNT4,
169 VGDRVNTVER_WIN2K,
170 VGDRVNTVER_WINXP,
171 VGDRVNTVER_WIN2K3,
172 VGDRVNTVER_WINVISTA,
173 VGDRVNTVER_WIN7,
174 VGDRVNTVER_WIN8,
175 VGDRVNTVER_WIN81,
176 VGDRVNTVER_WIN10
177} VGDRVNTVER;
178
179
180/*********************************************************************************************************************************
181* Internal Functions *
182*********************************************************************************************************************************/
183RT_C_DECLS_BEGIN
184#ifdef TARGET_NT4
185static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
186static NTSTATUS vgdrvNt4FindPciDevice(PULONG puluBusNumber, PPCI_SLOT_NUMBER puSlotNumber);
187#endif
188static NTSTATUS vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
189static NTSTATUS vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp);
190static NTSTATUS vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp);
191static NTSTATUS vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
192static void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt);
193static NTSTATUS vgdrvNtCleanup(PDEVICE_OBJECT pDevObj);
194static void vgdrvNtUnload(PDRIVER_OBJECT pDrvObj);
195static NTSTATUS vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
196static NTSTATUS vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
197static NTSTATUS vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
198static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, PIRP pIrp, PIO_STACK_LOCATION pStack);
199static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
200static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt);
201static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
202static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
203#ifdef VBOX_STRICT
204static void vgdrvNtDoTests(void);
205#endif
206static VOID vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext);
207static BOOLEAN vgdrvNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext);
208static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt);
209static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
210 void **ppvMMIOBase, uint32_t *pcbMMIO);
211RT_C_DECLS_END
212
213
214/*********************************************************************************************************************************
215* Exported Functions *
216*********************************************************************************************************************************/
217RT_C_DECLS_BEGIN
218NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
219RT_C_DECLS_END
220
221#ifdef ALLOC_PRAGMA
222/* We only do INIT allocations. PAGE is too much work and risk for little gain. */
223# pragma alloc_text(INIT, DriverEntry)
224# ifdef TARGET_NT4
225# pragma alloc_text(INIT, vgdrvNt4CreateDevice)
226# pragma alloc_text(INIT, vgdrvNt4FindPciDevice)
227# endif
228#endif
229
230
231/*********************************************************************************************************************************
232* Global Variables *
233*********************************************************************************************************************************/
234/** The detected NT (windows) version. */
235static VGDRVNTVER g_enmVGDrvNtVer = VGDRVNTVER_INVALID;
236/** Pointer to the PoStartNextPowerIrp routine (in the NT kernel).
237 * Introduced in Windows 2000. */
238static decltype(PoStartNextPowerIrp) *g_pfnPoStartNextPowerIrp = NULL;
239/** Pointer to the PoCallDriver routine (in the NT kernel).
240 * Introduced in Windows 2000. */
241static decltype(PoCallDriver) *g_pfnPoCallDriver = NULL;
242
243
244
245/**
246 * Driver entry point.
247 *
248 * @returns appropriate status code.
249 * @param pDrvObj Pointer to driver object.
250 * @param pRegPath Registry base path.
251 */
252NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
253{
254 RT_NOREF1(pRegPath);
255
256 /*
257 * Start by initializing IPRT.
258 */
259 int rc = RTR0Init(0);
260 if (RT_FAILURE(rc))
261 {
262 RTLogBackdoorPrintf("VBoxGuest: RTR0Init failed: %Rrc!\n", rc);
263 return STATUS_UNSUCCESSFUL;
264 }
265
266 LogFunc(("Driver built: %s %s\n", __DATE__, __TIME__));
267
268 /*
269 * Check if the NT version is supported and initialize g_enmVGDrvNtVer.
270 */
271 ULONG ulMajorVer;
272 ULONG ulMinorVer;
273 ULONG ulBuildNo;
274 BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL);
275
276 /* Use RTLogBackdoorPrintf to make sure that this goes to VBox.log on the host. */
277 RTLogBackdoorPrintf("VBoxGuest: Windows version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo);
278 if (fCheckedBuild)
279 RTLogBackdoorPrintf("VBoxGuest: Windows checked build\n");
280
281#ifdef VBOX_STRICT
282 vgdrvNtDoTests();
283#endif
284 NTSTATUS rcNt = STATUS_SUCCESS;
285 switch (ulMajorVer)
286 {
287 case 10:
288 switch (ulMinorVer)
289 {
290 case 0:
291 /* Windows 10 Preview builds starting with 9926. */
292 default:
293 /* Also everything newer. */
294 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
295 break;
296 }
297 break;
298 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
299 switch (ulMinorVer)
300 {
301 case 0: /* Note: Also could be Windows 2008 Server! */
302 g_enmVGDrvNtVer = VGDRVNTVER_WINVISTA;
303 break;
304 case 1: /* Note: Also could be Windows 2008 Server R2! */
305 g_enmVGDrvNtVer = VGDRVNTVER_WIN7;
306 break;
307 case 2:
308 g_enmVGDrvNtVer = VGDRVNTVER_WIN8;
309 break;
310 case 3:
311 g_enmVGDrvNtVer = VGDRVNTVER_WIN81;
312 break;
313 case 4:
314 /* Windows 10 Preview builds. */
315 default:
316 /* Also everything newer. */
317 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
318 break;
319 }
320 break;
321 case 5:
322 switch (ulMinorVer)
323 {
324 default:
325 case 2:
326 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K3;
327 break;
328 case 1:
329 g_enmVGDrvNtVer = VGDRVNTVER_WINXP;
330 break;
331 case 0:
332 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K;
333 break;
334 }
335 break;
336 case 4:
337 g_enmVGDrvNtVer = VGDRVNTVER_WINNT4;
338 break;
339 case 3:
340 if (ulMinorVer > 50)
341 g_enmVGDrvNtVer = VGDRVNTVER_WINNT351;
342 else if (ulMinorVer >= 50)
343 g_enmVGDrvNtVer = VGDRVNTVER_WINNT350;
344 else
345 g_enmVGDrvNtVer = VGDRVNTVER_WINNT31;
346 break;
347 default:
348 if (ulMajorVer > 6)
349 {
350 /* "Windows 10 mode" for Windows 8.1+. */
351 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
352 }
353 else
354 {
355 if (ulMajorVer < 4)
356 LogRelFunc(("At least Windows NT4 required! (%u.%u)\n", ulMajorVer, ulMinorVer));
357 else
358 LogRelFunc(("Unknown version %u.%u!\n", ulMajorVer, ulMinorVer));
359 rcNt = STATUS_DRIVER_UNABLE_TO_LOAD;
360 }
361 break;
362 }
363 if (NT_SUCCESS(rcNt))
364 {
365 /*
366 * Dynamically resolve symbols not present in NT4.
367 */
368 int rc;
369#ifdef TARGET_NT4
370 if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4)
371 rc = VINF_SUCCESS;
372 else
373#endif
374 {
375 RTDBGKRNLINFO hKrnlInfo;
376 rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0 /*fFlags*/);
377 if (RT_SUCCESS(rc))
378 {
379 int rc1 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "PoCallDriver", (void **)&g_pfnPoCallDriver);
380 int rc2 = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "PoStartNextPowerIrp", (void **)&g_pfnPoStartNextPowerIrp);
381 if (g_enmVGDrvNtVer > VGDRVNTVER_WINNT4 && RT_FAILURE(rc1))
382 rc = rc1;
383 if (g_enmVGDrvNtVer > VGDRVNTVER_WINNT4 && RT_FAILURE(rc2))
384 rc = rc2;
385 RTR0DbgKrnlInfoRelease(hKrnlInfo);
386 }
387 }
388 if (RT_SUCCESS(rc))
389 {
390 /*
391 * Setup the driver entry points in pDrvObj.
392 */
393 pDrvObj->DriverUnload = vgdrvNtUnload;
394 pDrvObj->MajorFunction[IRP_MJ_CREATE] = vgdrvNtCreate;
395 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vgdrvNtClose;
396 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vgdrvNtDeviceControl;
397 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vgdrvNtInternalIOCtl;
398 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vgdrvNtShutdown;
399 pDrvObj->MajorFunction[IRP_MJ_READ] = vgdrvNtNotSupportedStub;
400 pDrvObj->MajorFunction[IRP_MJ_WRITE] = vgdrvNtNotSupportedStub;
401#ifdef TARGET_NT4
402 if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4)
403 rcNt = vgdrvNt4CreateDevice(pDrvObj, pRegPath);
404 else
405#endif
406 {
407 pDrvObj->MajorFunction[IRP_MJ_PNP] = vgdrvNtNt5PlusPnP;
408 pDrvObj->MajorFunction[IRP_MJ_POWER] = vgdrvNtNt5PlusPower;
409 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vgdrvNtNt5PlusSystemControl;
410 pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)vgdrvNtNt5PlusAddDevice;
411 }
412 if (NT_SUCCESS(rcNt))
413 {
414 LogFlowFunc(("Returning %#x\n", rcNt));
415 return rcNt;
416 }
417
418 }
419 }
420
421 /*
422 * Failed.
423 */
424 LogRelFunc(("Failed! rcNt=%#x\n", rcNt));
425 RTR0Term();
426 return rcNt;
427}
428
429
430/**
431 * Translates NT version to VBox OS.
432 *
433 * @returns VBox OS type.
434 * @param enmNtVer The NT version.
435 */
436static VBOXOSTYPE vgdrvNtVersionToOSType(VGDRVNTVER enmNtVer)
437{
438 VBOXOSTYPE enmOsType;
439 switch (enmNtVer)
440 {
441 case VGDRVNTVER_WINNT31:
442 case VGDRVNTVER_WINNT350:
443 case VGDRVNTVER_WINNT351:
444 case VGDRVNTVER_WINNT4:
445 enmOsType = VBOXOSTYPE_WinNT4;
446 break;
447
448 case VGDRVNTVER_WIN2K:
449 enmOsType = VBOXOSTYPE_Win2k;
450 break;
451
452 case VGDRVNTVER_WINXP:
453#if ARCH_BITS == 64
454 enmOsType = VBOXOSTYPE_WinXP_x64;
455#else
456 enmOsType = VBOXOSTYPE_WinXP;
457#endif
458 break;
459
460 case VGDRVNTVER_WIN2K3:
461#if ARCH_BITS == 64
462 enmOsType = VBOXOSTYPE_Win2k3_x64;
463#else
464 enmOsType = VBOXOSTYPE_Win2k3;
465#endif
466 break;
467
468 case VGDRVNTVER_WINVISTA:
469#if ARCH_BITS == 64
470 enmOsType = VBOXOSTYPE_WinVista_x64;
471#else
472 enmOsType = VBOXOSTYPE_WinVista;
473#endif
474 break;
475
476 case VGDRVNTVER_WIN7:
477#if ARCH_BITS == 64
478 enmOsType = VBOXOSTYPE_Win7_x64;
479#else
480 enmOsType = VBOXOSTYPE_Win7;
481#endif
482 break;
483
484 case VGDRVNTVER_WIN8:
485#if ARCH_BITS == 64
486 enmOsType = VBOXOSTYPE_Win8_x64;
487#else
488 enmOsType = VBOXOSTYPE_Win8;
489#endif
490 break;
491
492 case VGDRVNTVER_WIN81:
493#if ARCH_BITS == 64
494 enmOsType = VBOXOSTYPE_Win81_x64;
495#else
496 enmOsType = VBOXOSTYPE_Win81;
497#endif
498 break;
499
500 case VGDRVNTVER_WIN10:
501#if ARCH_BITS == 64
502 enmOsType = VBOXOSTYPE_Win10_x64;
503#else
504 enmOsType = VBOXOSTYPE_Win10;
505#endif
506 break;
507
508 default:
509 /* We don't know, therefore NT family. */
510 enmOsType = VBOXOSTYPE_WinNT;
511 break;
512 }
513 return enmOsType;
514}
515
516
517#ifdef LOG_ENABLED
518/**
519 * Debug helper to dump a device resource list.
520 *
521 * @param pResourceList list of device resources.
522 */
523static void vgdrvNtShowDeviceResources(PCM_RESOURCE_LIST pRsrcList)
524{
525 for (uint32_t iList = 0; iList < pRsrcList->Count; iList++)
526 {
527 PCM_FULL_RESOURCE_DESCRIPTOR pList = &pRsrcList->List[iList];
528 LogFunc(("List #%u: InterfaceType=%#x BusNumber=%#x ListCount=%u ListRev=%#x ListVer=%#x\n",
529 iList, pList->InterfaceType, pList->BusNumber, pList->PartialResourceList.Count,
530 pList->PartialResourceList.Revision, pList->PartialResourceList.Version ));
531
532 PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pList->PartialResourceList.PartialDescriptors;
533 for (ULONG i = 0; i < pList->PartialResourceList.Count; ++i, ++pResource)
534 {
535 ULONG uType = pResource->Type;
536 static char const * const s_apszName[] =
537 {
538 "CmResourceTypeNull",
539 "CmResourceTypePort",
540 "CmResourceTypeInterrupt",
541 "CmResourceTypeMemory",
542 "CmResourceTypeDma",
543 "CmResourceTypeDeviceSpecific",
544 "CmResourceTypeuBusNumber",
545 "CmResourceTypeDevicePrivate",
546 "CmResourceTypeAssignedResource",
547 "CmResourceTypeSubAllocateFrom",
548 };
549
550 if (uType < RT_ELEMENTS(s_apszName))
551 LogFunc((" %.30s Flags=%#x Share=%#x", s_apszName[uType], pResource->Flags, pResource->ShareDisposition));
552 else
553 LogFunc((" Type=%#x Flags=%#x Share=%#x", uType, pResource->Flags, pResource->ShareDisposition));
554 switch (uType)
555 {
556 case CmResourceTypePort:
557 case CmResourceTypeMemory:
558 Log((" Start %#RX64, length=%#x\n", pResource->u.Port.Start.QuadPart, pResource->u.Port.Length));
559 break;
560
561 case CmResourceTypeInterrupt:
562 Log((" Level=%X, vector=%#x, affinity=%#x\n",
563 pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector, pResource->u.Interrupt.Affinity));
564 break;
565
566 case CmResourceTypeDma:
567 Log((" Channel %d, Port %#x\n", pResource->u.Dma.Channel, pResource->u.Dma.Port));
568 break;
569
570 default:
571 Log(("\n"));
572 break;
573 }
574 }
575 }
576}
577#endif /* LOG_ENABLED */
578
579
580/**
581 * Global initialization stuff.
582 *
583 * @param pDevExt Our device extension data.
584 * @param pDevObj The device object.
585 * @param pIrp The request packet if NT5+, NULL for NT4 and earlier.
586 * @param pDrvObj The driver object for NT4, NULL for NT5+.
587 * @param pRegPath The registry path for NT4, NULL for NT5+.
588 */
589static NTSTATUS vgdrvNtInit(PVBOXGUESTDEVEXTWIN pDevExt, PDEVICE_OBJECT pDevObj,
590 PIRP pIrp, PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
591{
592 LogFlowFunc(("ENTER: pDevExt=%p pDevObj=%p pIrq=%p pDrvObj=%p pRegPath=%p\n", pDevExt, pDevObj, pIrp, pDrvObj, pRegPath));
593
594 NTSTATUS rcNt;
595 if (!pIrp)
596 {
597#ifdef TARGET_NT4
598 /*
599 * NT4: Let's have a look at what our PCI adapter offers.
600 */
601 LogFlowFunc(("Starting to scan PCI resources of VBoxGuest ...\n"));
602
603 /* Assign the PCI resources. */
604 UNICODE_STRING ClassName;
605 RtlInitUnicodeString(&ClassName, L"VBoxGuestAdapter");
606 PCM_RESOURCE_LIST pResourceList = NULL;
607 rcNt = HalAssignSlotResources(pRegPath, &ClassName, pDrvObj, pDevObj, PCIBus, pDevExt->uBus, pDevExt->uSlot,
608 &pResourceList);
609# ifdef LOG_ENABLED
610 if (pResourceList)
611 vgdrvNtShowDeviceResources(pResourceList);
612# endif
613 if (NT_SUCCESS(rcNt))
614 {
615 rcNt = vgdrvNtScanPCIResourceList(pResourceList, pDevExt);
616 ExFreePool(pResourceList);
617 }
618
619# else /* !TARGET_NT4 */
620 AssertFailed();
621 RT_NOREF(pDevObj, pDrvObj, pRegPath);
622 rcNt = STATUS_INTERNAL_ERROR;
623# endif /* !TARGET_NT4 */
624 }
625 else
626 {
627 /*
628 * NT5+: Scan the PCI resource list from the IRP.
629 */
630 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
631# ifdef LOG_ENABLED
632 vgdrvNtShowDeviceResources(pStack->Parameters.StartDevice.AllocatedResources);
633# endif
634 rcNt = vgdrvNtScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated, pDevExt);
635 }
636 if (NT_SUCCESS(rcNt))
637 {
638 /*
639 * Map physical address of VMMDev memory into MMIO region
640 * and init the common device extension bits.
641 */
642 void *pvMMIOBase = NULL;
643 uint32_t cbMMIO = 0;
644 rcNt = vgdrvNtMapVMMDevMemory(pDevExt,
645 pDevExt->uVmmDevMemoryPhysAddr,
646 pDevExt->cbVmmDevMemory,
647 &pvMMIOBase,
648 &cbMMIO);
649 if (NT_SUCCESS(rcNt))
650 {
651 pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
652
653 LogFunc(("pvMMIOBase=0x%p, pDevExt=0x%p, pDevExt->Core.pVMMDevMemory=0x%p\n",
654 pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL));
655
656 int vrc = VGDrvCommonInitDevExt(&pDevExt->Core,
657 pDevExt->Core.IOPortBase,
658 pvMMIOBase, cbMMIO,
659 vgdrvNtVersionToOSType(g_enmVGDrvNtVer),
660 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
661 if (RT_FAILURE(vrc))
662 {
663 LogFunc(("Could not init device extension, vrc=%Rrc\n", vrc));
664 rcNt = STATUS_DEVICE_CONFIGURATION_ERROR;
665 }
666 }
667 else
668 LogFunc(("Could not map physical address of VMMDev, rcNt=%#x\n", rcNt));
669 }
670
671 if (NT_SUCCESS(rcNt))
672 {
673 int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
674 sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
675 if (RT_FAILURE(vrc))
676 {
677 LogFunc(("Alloc for pPowerStateRequest failed, vrc=%Rrc\n", vrc));
678 rcNt = STATUS_UNSUCCESSFUL;
679 }
680 }
681
682 if (NT_SUCCESS(rcNt))
683 {
684 /*
685 * Register DPC and ISR.
686 */
687 LogFlowFunc(("Initializing DPC/ISR (pDevObj=%p)...\n", pDevExt->pDeviceObject));
688 IoInitializeDpcRequest(pDevExt->pDeviceObject, vgdrvNtDpcHandler);
689
690 ULONG uInterruptVector = pDevExt->uInterruptVector;
691 KIRQL uHandlerIrql = (KIRQL)pDevExt->uInterruptLevel;
692#ifdef TARGET_NT4
693 if (!pIrp)
694 {
695 /* NT4: Get an interrupt vector. Only proceed if the device provides an interrupt. */
696 if ( uInterruptVector
697 || pDevExt->uInterruptLevel)
698 {
699 LogFlowFunc(("Getting interrupt vector (HAL): Bus=%u, IRQL=%u, Vector=%u\n",
700 pDevExt->uBus, pDevExt->uInterruptLevel, pDevExt->uInterruptVector));
701 uInterruptVector = HalGetInterruptVector(PCIBus,
702 pDevExt->uBus,
703 pDevExt->uInterruptLevel,
704 pDevExt->uInterruptVector,
705 &uHandlerIrql,
706 &pDevExt->fInterruptAffinity);
707 LogFlowFunc(("HalGetInterruptVector returns vector=%u\n", uInterruptVector));
708 }
709 else
710 LogFunc(("Device does not provide an interrupt!\n"));
711 }
712#endif
713 if (uInterruptVector)
714 {
715 LogFlowFunc(("Connecting interrupt (IntVector=%#u), uHandlerIrql=%u) ...\n", uInterruptVector, uHandlerIrql));
716
717 rcNt = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
718 vgdrvNtIsrHandler, /* Our ISR handler. */
719 pDevExt, /* Device context. */
720 NULL, /* Optional spinlock. */
721 uInterruptVector, /* Interrupt vector. */
722 uHandlerIrql, /* Irql. */
723 uHandlerIrql, /* SynchronizeIrql. */
724 pDevExt->enmInterruptMode, /* LevelSensitive or Latched. */
725 TRUE, /* Shareable interrupt. */
726 pDevExt->fInterruptAffinity, /* CPU affinity. */
727 FALSE); /* Don't save FPU stack. */
728 if (NT_ERROR(rcNt))
729 LogFunc(("Could not connect interrupt: rcNt=%#x!\n", rcNt));
730 }
731 else
732 LogFunc(("No interrupt vector found!\n"));
733 }
734
735 if (NT_SUCCESS(rcNt))
736 {
737 /*
738 * Once we've read configuration from register and host, we're finally read.
739 */
740 pDevExt->Core.fLoggingEnabled = true; /** @todo clean up guest ring-3 logging, keeping it separate from the kernel to avoid sharing limits with it. */
741 vgdrvNtReadConfiguration(pDevExt);
742
743 /* Ready to rumble! */
744 LogRelFunc(("Device is ready!\n"));
745 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_WORKING);
746 }
747 else
748 pDevExt->pInterruptObject = NULL;
749
750 /** @todo r=bird: The error cleanup here is completely missing. We'll leak a
751 * whole bunch of things... */
752
753 LogFunc(("Returned with rcNt=%#x\n", rcNt));
754 return rcNt;
755}
756
757
758
759
760#ifdef TARGET_NT4
761
762/**
763 * Legacy helper function to create the device object.
764 *
765 * @returns NT status code.
766 *
767 * @param pDrvObj The driver object.
768 * @param pRegPath The driver registry path.
769 */
770static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
771{
772 Log(("vgdrvNt4CreateDevice: pDrvObj=%p, pRegPath=%p\n", pDrvObj, pRegPath));
773
774 /*
775 * Find our virtual PCI device
776 */
777 ULONG uBus;
778 PCI_SLOT_NUMBER uSlot;
779 NTSTATUS rc = vgdrvNt4FindPciDevice(&uBus, &uSlot);
780 if (NT_ERROR(rc))
781 {
782 Log(("vgdrvNt4CreateDevice: Device not found!\n"));
783 return rc;
784 }
785
786 /*
787 * Create device.
788 */
789 UNICODE_STRING DevName;
790 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
791 PDEVICE_OBJECT pDeviceObject = NULL;
792 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
793 if (NT_SUCCESS(rc))
794 {
795 Log(("vgdrvNt4CreateDevice: Device created\n"));
796
797 UNICODE_STRING DosName;
798 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
799 rc = IoCreateSymbolicLink(&DosName, &DevName);
800 if (NT_SUCCESS(rc))
801 {
802 Log(("vgdrvNt4CreateDevice: Symlink created\n"));
803
804 /*
805 * Setup the device extension.
806 */
807 Log(("vgdrvNt4CreateDevice: Setting up device extension ...\n"));
808
809 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
810 RT_ZERO(*pDevExt);
811
812 Log(("vgdrvNt4CreateDevice: Device extension created\n"));
813
814 /* Store a reference to ourself. */
815 pDevExt->pDeviceObject = pDeviceObject;
816
817 /* Store bus and slot number we've queried before. */
818 pDevExt->uBus = uBus;
819 pDevExt->uSlot = uSlot.u.AsULONG;
820
821# ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
822 rc = hlpRegisterBugCheckCallback(pDevExt);
823# endif
824 if (NT_SUCCESS(rc))
825 {
826 /* Do the actual VBox init ... */
827 rc = vgdrvNtInit(pDevExt, pDeviceObject, NULL /*pIrp*/, pDrvObj, pRegPath);
828 if (NT_SUCCESS(rc))
829 {
830 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x (succcess)\n", rc));
831 return rc;
832 }
833
834 /* bail out */
835 }
836 IoDeleteSymbolicLink(&DosName);
837 }
838 else
839 Log(("vgdrvNt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc));
840 IoDeleteDevice(pDeviceObject);
841 }
842 else
843 Log(("vgdrvNt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc));
844 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x\n", rc));
845 return rc;
846}
847
848
849/**
850 * Helper function to handle the PCI device lookup.
851 *
852 * @returns NT status code.
853 *
854 * @param puBus Where to return the bus number on success.
855 * @param pSlot Where to return the slot number on success.
856 */
857static NTSTATUS vgdrvNt4FindPciDevice(PULONG puBus, PPCI_SLOT_NUMBER pSlot)
858{
859 Log(("vgdrvNt4FindPciDevice\n"));
860
861 PCI_SLOT_NUMBER Slot;
862 Slot.u.AsULONG = 0;
863
864 /* Scan each bus. */
865 for (ULONG uBus = 0; uBus < PCI_MAX_BUSES; uBus++)
866 {
867 /* Scan each device. */
868 for (ULONG deviceNumber = 0; deviceNumber < PCI_MAX_DEVICES; deviceNumber++)
869 {
870 Slot.u.bits.DeviceNumber = deviceNumber;
871
872 /* Scan each function (not really required...). */
873 for (ULONG functionNumber = 0; functionNumber < PCI_MAX_FUNCTION; functionNumber++)
874 {
875 Slot.u.bits.FunctionNumber = functionNumber;
876
877 /* Have a look at what's in this slot. */
878 PCI_COMMON_CONFIG PciData;
879 if (!HalGetBusData(PCIConfiguration, uBus, Slot.u.AsULONG, &PciData, sizeof(ULONG)))
880 {
881 /* No such bus, we're done with it. */
882 deviceNumber = PCI_MAX_DEVICES;
883 break;
884 }
885
886 if (PciData.VendorID == PCI_INVALID_VENDORID)
887 /* We have to proceed to the next function. */
888 continue;
889
890 /* Check if it's another device. */
891 if ( PciData.VendorID != VMMDEV_VENDORID
892 || PciData.DeviceID != VMMDEV_DEVICEID)
893 continue;
894
895 /* Hooray, we've found it! */
896 Log(("vgdrvNt4FindPciDevice: Device found! Bus=%#x Slot=%#u (dev %#x, fun %#x, rvd %#x)\n",
897 uBus, Slot.u.AsULONG, Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber, Slot.u.bits.Reserved));
898
899 *puBus = uBus;
900 *pSlot = Slot;
901 return STATUS_SUCCESS;
902 }
903 }
904 }
905
906 return STATUS_DEVICE_DOES_NOT_EXIST;
907}
908
909#endif /* TARGET_NT4 */
910
911/**
912 * Handle request from the Plug & Play subsystem.
913 *
914 * @returns NT status code
915 * @param pDrvObj Driver object
916 * @param pDevObj Device object
917 *
918 * @remarks Parts of this is duplicated in VBoxGuest-win-legacy.cpp.
919 */
920static NTSTATUS vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
921{
922 NTSTATUS rc;
923 LogFlowFuncEnter();
924
925 /*
926 * Create device.
927 */
928 UNICODE_STRING DevName;
929 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
930 PDEVICE_OBJECT pDeviceObject = NULL;
931 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
932 if (NT_SUCCESS(rc))
933 {
934 /*
935 * Create symbolic link (DOS devices).
936 */
937 UNICODE_STRING DosName;
938 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
939 rc = IoCreateSymbolicLink(&DosName, &DevName);
940 if (NT_SUCCESS(rc))
941 {
942 /*
943 * Setup the device extension.
944 */
945 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
946 RT_ZERO(*pDevExt);
947
948 KeInitializeSpinLock(&pDevExt->MouseEventAccessSpinLock);
949
950 pDevExt->pDeviceObject = pDeviceObject;
951 pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_STOPPED;
952 pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
953
954 pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
955 if (pDevExt->pNextLowerDriver != NULL)
956 {
957 /*
958 * If we reached this point we're fine with the basic driver setup,
959 * so continue to init our own things.
960 */
961#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
962 vgdrvNtBugCheckCallback(pDevExt); /* Ignore failure! */
963#endif
964 if (NT_SUCCESS(rc))
965 {
966 /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */
967 pDeviceObject->Flags |= DO_POWER_PAGABLE;
968
969 /* Driver is ready now. */
970 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
971 LogFlowFunc(("Returning with rc=%#x (success)\n", rc));
972 return rc;
973 }
974
975 IoDetachDevice(pDevExt->pNextLowerDriver);
976 }
977 else
978 {
979 LogFunc(("IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
980 rc = STATUS_DEVICE_NOT_CONNECTED;
981 }
982
983 /* bail out */
984 IoDeleteSymbolicLink(&DosName);
985 }
986 else
987 LogFunc(("IoCreateSymbolicLink failed with rc=%#x!\n", rc));
988 IoDeleteDevice(pDeviceObject);
989 }
990 else
991 LogFunc(("IoCreateDevice failed with rc=%#x!\n", rc));
992
993 LogFunc(("Returning with rc=%#x\n", rc));
994 return rc;
995}
996
997/**
998 * Irp completion routine for PnP Irps we send.
999 *
1000 * @returns NT status code.
1001 * @param pDevObj Device object.
1002 * @param pIrp Request packet.
1003 * @param pEvent Semaphore.
1004 */
1005static NTSTATUS vgdrvNt5PlusPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent)
1006{
1007 RT_NOREF2(pDevObj, pIrp);
1008 KeSetEvent(pEvent, 0, FALSE);
1009 return STATUS_MORE_PROCESSING_REQUIRED;
1010}
1011
1012
1013/**
1014 * Helper to send a PnP IRP and wait until it's done.
1015 *
1016 * @returns NT status code.
1017 * @param pDevObj Device object.
1018 * @param pIrp Request packet.
1019 * @param fStrict When set, returns an error if the IRP gives an error.
1020 */
1021static NTSTATUS vgdrvNt5PlusPnPSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict)
1022{
1023 KEVENT Event;
1024
1025 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
1026
1027 IoCopyCurrentIrpStackLocationToNext(pIrp);
1028 IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)vgdrvNt5PlusPnpIrpComplete, &Event, TRUE, TRUE, TRUE);
1029
1030 NTSTATUS rc = IoCallDriver(pDevObj, pIrp);
1031
1032 if (rc == STATUS_PENDING)
1033 {
1034 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1035 rc = pIrp->IoStatus.Status;
1036 }
1037
1038 if ( !fStrict
1039 && (rc == STATUS_NOT_SUPPORTED || rc == STATUS_INVALID_DEVICE_REQUEST))
1040 {
1041 rc = STATUS_SUCCESS;
1042 }
1043
1044 Log(("vgdrvNt5PlusPnPSendIrpSynchronously: Returning 0x%x\n", rc));
1045 return rc;
1046}
1047
1048
1049/**
1050 * PnP Request handler.
1051 *
1052 * @param pDevObj Device object.
1053 * @param pIrp Request packet.
1054 */
1055static NTSTATUS vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1056{
1057 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1058 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1059
1060#ifdef LOG_ENABLED
1061 static char const * const s_apszFnctName[] =
1062 {
1063 "IRP_MN_START_DEVICE",
1064 "IRP_MN_QUERY_REMOVE_DEVICE",
1065 "IRP_MN_REMOVE_DEVICE",
1066 "IRP_MN_CANCEL_REMOVE_DEVICE",
1067 "IRP_MN_STOP_DEVICE",
1068 "IRP_MN_QUERY_STOP_DEVICE",
1069 "IRP_MN_CANCEL_STOP_DEVICE",
1070 "IRP_MN_QUERY_DEVICE_RELATIONS",
1071 "IRP_MN_QUERY_INTERFACE",
1072 "IRP_MN_QUERY_CAPABILITIES",
1073 "IRP_MN_QUERY_RESOURCES",
1074 "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
1075 "IRP_MN_QUERY_DEVICE_TEXT",
1076 "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
1077 "IRP_MN_0xE",
1078 "IRP_MN_READ_CONFIG",
1079 "IRP_MN_WRITE_CONFIG",
1080 "IRP_MN_EJECT",
1081 "IRP_MN_SET_LOCK",
1082 "IRP_MN_QUERY_ID",
1083 "IRP_MN_QUERY_PNP_DEVICE_STATE",
1084 "IRP_MN_QUERY_BUS_INFORMATION",
1085 "IRP_MN_DEVICE_USAGE_NOTIFICATION",
1086 "IRP_MN_SURPRISE_REMOVAL",
1087 };
1088 Log(("vgdrvNtNt5PlusPnP: MinorFunction: %s\n",
1089 pStack->MinorFunction < RT_ELEMENTS(s_apszFnctName) ? s_apszFnctName[pStack->MinorFunction] : "Unknown"));
1090#endif
1091
1092 NTSTATUS rc = STATUS_SUCCESS;
1093 switch (pStack->MinorFunction)
1094 {
1095 case IRP_MN_START_DEVICE:
1096 {
1097 Log(("vgdrvNtNt5PlusPnP: START_DEVICE\n"));
1098
1099 /* This must be handled first by the lower driver. */
1100 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1101 if ( NT_SUCCESS(rc)
1102 && NT_SUCCESS(pIrp->IoStatus.Status))
1103 {
1104 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n",
1105 pStack->Parameters.StartDevice.AllocatedResources));
1106
1107 if (pStack->Parameters.StartDevice.AllocatedResources)
1108 rc = vgdrvNtInit(pDevExt, pDevObj, pIrp, NULL, NULL);
1109 else
1110 {
1111 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: No resources, pDevExt = %p, nextLowerDriver = %p!\n",
1112 pDevExt, pDevExt ? pDevExt->pNextLowerDriver : NULL));
1113 rc = STATUS_UNSUCCESSFUL;
1114 }
1115 }
1116
1117 if (NT_ERROR(rc))
1118 {
1119 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: Error: rc = 0x%x\n", rc));
1120
1121 /* Need to unmap memory in case of errors ... */
1122/** @todo r=bird: vgdrvNtInit maps it and is responsible for cleaning up its own friggin mess...
1123 * Fix it instead of kind of working around things there!! */
1124 vgdrvNtUnmapVMMDevMemory(pDevExt);
1125 }
1126 break;
1127 }
1128
1129 case IRP_MN_CANCEL_REMOVE_DEVICE:
1130 {
1131 Log(("vgdrvNtNt5PlusPnP: CANCEL_REMOVE_DEVICE\n"));
1132
1133 /* This must be handled first by the lower driver. */
1134 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1135 if ( NT_SUCCESS(rc)
1136 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGREMOVE)
1137 {
1138 /* Return to the state prior to receiving the IRP_MN_QUERY_REMOVE_DEVICE request. */
1139 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1140 }
1141
1142 /* Complete the IRP. */
1143 break;
1144 }
1145
1146 case IRP_MN_SURPRISE_REMOVAL:
1147 {
1148 Log(("vgdrvNtNt5PlusPnP: IRP_MN_SURPRISE_REMOVAL\n"));
1149
1150 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_SURPRISEREMOVED);
1151
1152 /* Do nothing here actually. Cleanup is done in IRP_MN_REMOVE_DEVICE.
1153 * This request is not expected for VBoxGuest.
1154 */
1155 LogRel(("VBoxGuest: unexpected device removal\n"));
1156
1157 /* Pass to the lower driver. */
1158 pIrp->IoStatus.Status = STATUS_SUCCESS;
1159
1160 IoSkipCurrentIrpStackLocation(pIrp);
1161
1162 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1163
1164 /* Do not complete the IRP. */
1165 return rc;
1166 }
1167
1168 case IRP_MN_QUERY_REMOVE_DEVICE:
1169 {
1170 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE\n"));
1171
1172#ifdef VBOX_REBOOT_ON_UNINSTALL
1173 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Device cannot be removed without a reboot.\n"));
1174 rc = STATUS_UNSUCCESSFUL;
1175#endif
1176
1177 if (NT_SUCCESS(rc))
1178 {
1179 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_PENDINGREMOVE);
1180
1181 /* This IRP passed down to lower driver. */
1182 pIrp->IoStatus.Status = STATUS_SUCCESS;
1183
1184 IoSkipCurrentIrpStackLocation(pIrp);
1185
1186 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1187 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1188
1189 /* we must not do anything the IRP after doing IoSkip & CallDriver
1190 * since the driver below us will complete (or already have completed) the IRP.
1191 * I.e. just return the status we got from IoCallDriver */
1192 return rc;
1193 }
1194
1195 /* Complete the IRP on failure. */
1196 break;
1197 }
1198
1199 case IRP_MN_REMOVE_DEVICE:
1200 {
1201 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE\n"));
1202
1203 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_REMOVED);
1204
1205 /* Free hardware resources. */
1206 /** @todo this should actually free I/O ports, interrupts, etc.
1207 * Update/bird: vgdrvNtCleanup actually does that... So, what's there to do? */
1208 rc = vgdrvNtCleanup(pDevObj);
1209 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: vgdrvNtCleanup rc = 0x%08X\n", rc));
1210
1211 /*
1212 * We need to send the remove down the stack before we detach,
1213 * but we don't need to wait for the completion of this operation
1214 * (and to register a completion routine).
1215 */
1216 pIrp->IoStatus.Status = STATUS_SUCCESS;
1217
1218 IoSkipCurrentIrpStackLocation(pIrp);
1219
1220 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1221 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1222
1223 IoDetachDevice(pDevExt->pNextLowerDriver);
1224
1225 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Removing device ...\n"));
1226
1227 /* Destroy device extension and clean up everything else. */
1228 VGDrvCommonDeleteDevExt(&pDevExt->Core);
1229
1230 /* Remove DOS device + symbolic link. */
1231 UNICODE_STRING win32Name;
1232 RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS);
1233 IoDeleteSymbolicLink(&win32Name);
1234
1235 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Deleting device ...\n"));
1236
1237 /* Last action: Delete our device! pDevObj is *not* failed
1238 * anymore after this call! */
1239 IoDeleteDevice(pDevObj);
1240
1241 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Device removed!\n"));
1242
1243 /* Propagating rc from IoCallDriver. */
1244 return rc; /* Make sure that we don't do anything below here anymore! */
1245 }
1246
1247 case IRP_MN_CANCEL_STOP_DEVICE:
1248 {
1249 Log(("vgdrvNtNt5PlusPnP: CANCEL_STOP_DEVICE\n"));
1250
1251 /* This must be handled first by the lower driver. */
1252 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1253 if ( NT_SUCCESS(rc)
1254 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGSTOP)
1255 {
1256 /* Return to the state prior to receiving the IRP_MN_QUERY_STOP_DEVICE request. */
1257 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1258 }
1259
1260 /* Complete the IRP. */
1261 break;
1262 }
1263
1264 case IRP_MN_QUERY_STOP_DEVICE:
1265 {
1266 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE\n"));
1267
1268#ifdef VBOX_REBOOT_ON_UNINSTALL /** @todo r=bird: this code and log msg is pointless as rc = success and status will be overwritten below. */
1269 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Device cannot be stopped without a reboot!\n"));
1270 pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
1271#endif
1272
1273 if (NT_SUCCESS(rc))
1274 {
1275 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_PENDINGSTOP);
1276
1277 /* This IRP passed down to lower driver. */
1278 pIrp->IoStatus.Status = STATUS_SUCCESS;
1279
1280 IoSkipCurrentIrpStackLocation(pIrp);
1281
1282 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1283 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1284
1285 /* we must not do anything with the IRP after doing IoSkip & CallDriver
1286 * since the driver below us will complete (or already have completed) the IRP.
1287 * I.e. just return the status we got from IoCallDriver */
1288 return rc;
1289 }
1290
1291 /* Complete the IRP on failure. */
1292 break;
1293 }
1294
1295 case IRP_MN_STOP_DEVICE:
1296 {
1297 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE\n"));
1298
1299 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, VGDRVNTDEVSTATE_STOPPED);
1300
1301 /* Free hardware resources. */
1302 /** @todo this should actually free I/O ports, interrupts, etc.
1303 * Update/bird: vgdrvNtCleanup actually does that... So, what's there to do? */
1304 rc = vgdrvNtCleanup(pDevObj);
1305 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE: cleaning up, rc = 0x%x\n", rc));
1306
1307 /* Pass to the lower driver. */
1308 pIrp->IoStatus.Status = STATUS_SUCCESS;
1309
1310 IoSkipCurrentIrpStackLocation(pIrp);
1311
1312 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1313 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1314
1315 return rc;
1316 }
1317
1318 default:
1319 {
1320 IoSkipCurrentIrpStackLocation(pIrp);
1321 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1322 return rc;
1323 }
1324 }
1325
1326 pIrp->IoStatus.Status = rc;
1327 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1328
1329 Log(("vgdrvNtNt5PlusPnP: Returning with rc = 0x%x\n", rc));
1330 return rc;
1331}
1332
1333
1334/**
1335 * Handle the power completion event.
1336 *
1337 * @returns NT status code.
1338 * @param pDevObj Targetted device object.
1339 * @param pIrp IO request packet.
1340 * @param pContext Context value passed to IoSetCompletionRoutine in VBoxGuestPower.
1341 */
1342static NTSTATUS vgdrvNtNt5PlusPowerComplete(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext)
1343{
1344#ifdef VBOX_STRICT
1345 RT_NOREF1(pDevObj);
1346 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pContext;
1347 PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
1348
1349 Assert(pDevExt);
1350
1351 if (pIrpSp)
1352 {
1353 Assert(pIrpSp->MajorFunction == IRP_MJ_POWER);
1354 if (NT_SUCCESS(pIrp->IoStatus.Status))
1355 {
1356 switch (pIrpSp->MinorFunction)
1357 {
1358 case IRP_MN_SET_POWER:
1359 switch (pIrpSp->Parameters.Power.Type)
1360 {
1361 case DevicePowerState:
1362 switch (pIrpSp->Parameters.Power.State.DeviceState)
1363 {
1364 case PowerDeviceD0:
1365 break;
1366 default: /* Shut up MSC */
1367 break;
1368 }
1369 break;
1370 default: /* Shut up MSC */
1371 break;
1372 }
1373 break;
1374 }
1375 }
1376 }
1377#else
1378 RT_NOREF3(pDevObj, pIrp, pContext);
1379#endif
1380
1381 return STATUS_SUCCESS;
1382}
1383
1384
1385/**
1386 * Handle the Power requests.
1387 *
1388 * @returns NT status code
1389 * @param pDevObj device object
1390 * @param pIrp IRP
1391 */
1392static NTSTATUS vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1393{
1394 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1395 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1396 POWER_STATE_TYPE enmPowerType = pStack->Parameters.Power.Type;
1397 POWER_STATE PowerState = pStack->Parameters.Power.State;
1398 POWER_ACTION enmPowerAction = pStack->Parameters.Power.ShutdownType;
1399
1400 Log(("vgdrvNtNt5PlusPower:\n"));
1401
1402 switch (pStack->MinorFunction)
1403 {
1404 case IRP_MN_SET_POWER:
1405 {
1406 Log(("vgdrvNtNt5PlusPower: IRP_MN_SET_POWER, type= %d\n", enmPowerType));
1407 switch (enmPowerType)
1408 {
1409 case SystemPowerState:
1410 {
1411 Log(("vgdrvNtNt5PlusPower: SystemPowerState, action = %d, state = %d/%d\n",
1412 enmPowerAction, PowerState.SystemState, PowerState.DeviceState));
1413
1414 switch (enmPowerAction)
1415 {
1416 case PowerActionSleep:
1417
1418 /* System now is in a working state. */
1419 if (PowerState.SystemState == PowerSystemWorking)
1420 {
1421 if ( pDevExt
1422 && pDevExt->enmLastSystemPowerAction == PowerActionHibernate)
1423 {
1424 Log(("vgdrvNtNt5PlusPower: Returning from hibernation!\n"));
1425 int rc = VGDrvCommonReinitDevExtAfterHibernation(&pDevExt->Core,
1426 vgdrvNtVersionToOSType(g_enmVGDrvNtVer));
1427 if (RT_FAILURE(rc))
1428 Log(("vgdrvNtNt5PlusPower: Cannot re-init VMMDev chain, rc = %d!\n", rc));
1429 }
1430 }
1431 break;
1432
1433 case PowerActionShutdownReset:
1434 {
1435 Log(("vgdrvNtNt5PlusPower: Power action reset!\n"));
1436
1437 /* Tell the VMM that we no longer support mouse pointer integration. */
1438 VMMDevReqMouseStatus *pReq = NULL;
1439 int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof (VMMDevReqMouseStatus),
1440 VMMDevReq_SetMouseStatus);
1441 if (RT_SUCCESS(vrc))
1442 {
1443 pReq->mouseFeatures = 0;
1444 pReq->pointerXPos = 0;
1445 pReq->pointerYPos = 0;
1446
1447 vrc = VbglR0GRPerform(&pReq->header);
1448 if (RT_FAILURE(vrc))
1449 {
1450 Log(("vgdrvNtNt5PlusPower: error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
1451 }
1452
1453 VbglR0GRFree(&pReq->header);
1454 }
1455
1456 /* Don't do any cleanup here; there might be still coming in some IOCtls after we got this
1457 * power action and would assert/crash when we already cleaned up all the stuff! */
1458 break;
1459 }
1460
1461 case PowerActionShutdown:
1462 case PowerActionShutdownOff:
1463 {
1464 Log(("vgdrvNtNt5PlusPower: Power action shutdown!\n"));
1465 if (PowerState.SystemState >= PowerSystemShutdown)
1466 {
1467 Log(("vgdrvNtNt5PlusPower: Telling the VMMDev to close the VM ...\n"));
1468
1469 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
1470 int vrc = VERR_NOT_IMPLEMENTED;
1471 if (pReq)
1472 {
1473 pReq->header.requestType = VMMDevReq_SetPowerStatus;
1474 pReq->powerState = VMMDevPowerState_PowerOff;
1475
1476 vrc = VbglR0GRPerform(&pReq->header);
1477 }
1478 if (RT_FAILURE(vrc))
1479 Log(("vgdrvNtNt5PlusPower: Error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
1480
1481 /* No need to do cleanup here; at this point we should've been
1482 * turned off by VMMDev already! */
1483 }
1484 break;
1485 }
1486
1487 case PowerActionHibernate:
1488 Log(("vgdrvNtNt5PlusPower: Power action hibernate!\n"));
1489 break;
1490
1491 case PowerActionWarmEject:
1492 Log(("vgdrvNtNt5PlusPower: PowerActionWarmEject!\n"));
1493 break;
1494
1495 default:
1496 Log(("vgdrvNtNt5PlusPower: %d\n", enmPowerAction));
1497 break;
1498 }
1499
1500 /*
1501 * Save the current system power action for later use.
1502 * This becomes handy when we return from hibernation for example.
1503 */
1504 if (pDevExt)
1505 pDevExt->enmLastSystemPowerAction = enmPowerAction;
1506
1507 break;
1508 }
1509 default:
1510 break;
1511 }
1512 break;
1513 }
1514 default:
1515 break;
1516 }
1517
1518 /*
1519 * Whether we are completing or relaying this power IRP,
1520 * we must call PoStartNextPowerIrp.
1521 */
1522 g_pfnPoStartNextPowerIrp(pIrp);
1523
1524 /*
1525 * Send the IRP down the driver stack, using PoCallDriver
1526 * (not IoCallDriver, as for non-power irps).
1527 */
1528 IoCopyCurrentIrpStackLocationToNext(pIrp);
1529 IoSetCompletionRoutine(pIrp,
1530 vgdrvNtNt5PlusPowerComplete,
1531 (PVOID)pDevExt,
1532 TRUE,
1533 TRUE,
1534 TRUE);
1535 return g_pfnPoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1536}
1537
1538
1539/**
1540 * IRP_MJ_SYSTEM_CONTROL handler.
1541 *
1542 * @returns NT status code
1543 * @param pDevObj Device object.
1544 * @param pIrp IRP.
1545 */
1546static NTSTATUS vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1547{
1548 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1549
1550 LogFlowFuncEnter();
1551
1552 /* Always pass it on to the next driver. */
1553 IoSkipCurrentIrpStackLocation(pIrp);
1554
1555 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1556}
1557
1558
1559
1560/**
1561 * Unmaps the VMMDev I/O range from kernel space.
1562 *
1563 * @param pDevExt The device extension.
1564 */
1565static void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
1566{
1567 LogFlowFunc(("pVMMDevMemory = %#x\n", pDevExt->Core.pVMMDevMemory));
1568 if (pDevExt->Core.pVMMDevMemory)
1569 {
1570 MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->cbVmmDevMemory);
1571 pDevExt->Core.pVMMDevMemory = NULL;
1572 }
1573
1574 pDevExt->uVmmDevMemoryPhysAddr.QuadPart = 0;
1575 pDevExt->cbVmmDevMemory = 0;
1576}
1577
1578
1579/**
1580 * Cleans up hardware resources.
1581 * Do not delete DevExt here.
1582 *
1583 * @todo r=bird: HC SVNT DRACONES!
1584 *
1585 * This code leaves clients hung when vgdrvNtInit is called afterwards.
1586 * This happens when for instance hotplugging a CPU. Problem is
1587 * vgdrvNtInit doing a full VGDrvCommonInitDevExt, orphaning all pDevExt
1588 * members, like session lists and stuff.
1589 *
1590 * @param pDevObj Device object.
1591 */
1592static NTSTATUS vgdrvNtCleanup(PDEVICE_OBJECT pDevObj)
1593{
1594 LogFlowFuncEnter();
1595
1596 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1597 if (pDevExt)
1598 {
1599 if (pDevExt->pInterruptObject)
1600 {
1601 IoDisconnectInterrupt(pDevExt->pInterruptObject);
1602 pDevExt->pInterruptObject = NULL;
1603 }
1604
1605 /** @todo cleanup the rest stuff */
1606
1607
1608#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
1609 hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */
1610#endif
1611 /* According to MSDN we have to unmap previously mapped memory. */
1612 vgdrvNtUnmapVMMDevMemory(pDevExt);
1613 }
1614
1615 return STATUS_SUCCESS;
1616}
1617
1618
1619/**
1620 * Unload the driver.
1621 *
1622 * @param pDrvObj Driver object.
1623 */
1624static void vgdrvNtUnload(PDRIVER_OBJECT pDrvObj)
1625{
1626 LogFlowFuncEnter();
1627
1628#ifdef TARGET_NT4
1629 vgdrvNtCleanup(pDrvObj->DeviceObject);
1630
1631 /* Destroy device extension and clean up everything else. */
1632 if (pDrvObj->DeviceObject && pDrvObj->DeviceObject->DeviceExtension)
1633 VGDrvCommonDeleteDevExt((PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension);
1634
1635 /*
1636 * I don't think it's possible to unload a driver which processes have
1637 * opened, at least we'll blindly assume that here.
1638 */
1639 UNICODE_STRING DosName;
1640 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1641 IoDeleteSymbolicLink(&DosName);
1642
1643 IoDeleteDevice(pDrvObj->DeviceObject);
1644#else /* !TARGET_NT4 */
1645 /*
1646 * On a PnP driver this routine will be called after IRP_MN_REMOVE_DEVICE
1647 * where we already did the cleanup, so don't do anything here (yet).
1648 */
1649 RT_NOREF1(pDrvObj);
1650#endif /* !TARGET_NT4 */
1651
1652 RTR0Term();
1653 LogFlowFunc(("Returning\n"));
1654}
1655
1656
1657/**
1658 * For simplifying request completion into a simple return statement, extended
1659 * version.
1660 *
1661 * @returns rcNt
1662 * @param rcNt The status code.
1663 * @param uInfo Extra info value.
1664 * @param pIrp The IRP.
1665 */
1666DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
1667{
1668 pIrp->IoStatus.Status = rcNt;
1669 pIrp->IoStatus.Information = uInfo;
1670 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1671 return rcNt;
1672}
1673
1674
1675/**
1676 * For simplifying request completion into a simple return statement.
1677 *
1678 * @returns rcNt
1679 * @param rcNt The status code.
1680 * @param pIrp The IRP.
1681 */
1682DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
1683{
1684 return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
1685}
1686
1687
1688/**
1689 * Create (i.e. Open) file entry point.
1690 *
1691 * @param pDevObj Device object.
1692 * @param pIrp Request packet.
1693 */
1694static NTSTATUS vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1695{
1696 Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
1697 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1698 PFILE_OBJECT pFileObj = pStack->FileObject;
1699 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1700
1701 Assert(pFileObj->FsContext == NULL);
1702
1703 /*
1704 * We are not remotely similar to a directory...
1705 * (But this is possible.)
1706 */
1707 if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
1708 {
1709 LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
1710 return vgdrvNtCompleteRequest(STATUS_NOT_A_DIRECTORY, pIrp);
1711 }
1712
1713 /*
1714 * Check the device state.
1715 */
1716 if (pDevExt->enmDevState != VGDRVNTDEVSTATE_WORKING)
1717 {
1718 LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", pDevExt->enmDevState));
1719 return vgdrvNtCompleteRequest(STATUS_DEVICE_NOT_READY, pIrp);
1720 }
1721
1722 /*
1723 * Create a client session.
1724 */
1725 int rc;
1726 PVBOXGUESTSESSION pSession;
1727 if (pIrp->RequestorMode == KernelMode)
1728 rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
1729 else
1730 rc = VGDrvCommonCreateUserSession(&pDevExt->Core, &pSession);
1731 if (RT_SUCCESS(rc))
1732 {
1733 pFileObj->FsContext = pSession;
1734 Log(("vgdrvNtCreate: Successfully created %s session %p\n",
1735 pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession));
1736 return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
1737 }
1738
1739 Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
1740 /* Note. the IoStatus is completely ignored on error. */
1741 if (rc == VERR_NO_MEMORY)
1742 return vgdrvNtCompleteRequest(STATUS_NO_MEMORY, pIrp);
1743 return vgdrvNtCompleteRequest(STATUS_UNSUCCESSFUL, pIrp);
1744}
1745
1746
1747/**
1748 * Close file entry point.
1749 *
1750 * @param pDevObj Device object.
1751 * @param pIrp Request packet.
1752 */
1753static NTSTATUS vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1754{
1755 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1756 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1757 PFILE_OBJECT pFileObj = pStack->FileObject;
1758
1759 LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
1760
1761#ifdef VBOX_WITH_HGCM
1762 /* Close both, R0 and R3 sessions. */
1763 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
1764 if (pSession)
1765 VGDrvCommonCloseSession(&pDevExt->Core, pSession);
1766#endif
1767
1768 pFileObj->FsContext = NULL;
1769 pIrp->IoStatus.Information = 0;
1770 pIrp->IoStatus.Status = STATUS_SUCCESS;
1771 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1772
1773 return STATUS_SUCCESS;
1774}
1775
1776
1777/**
1778 * Device I/O Control entry point.
1779 *
1780 * @param pDevObj Device object.
1781 * @param pIrp Request packet.
1782 */
1783NTSTATUS _stdcall vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1784{
1785 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1786 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1787 PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
1788
1789 if (!RT_VALID_PTR(pSession))
1790 return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
1791
1792#if 0 /* No fast I/O controls defined yet. */
1793 /*
1794 * Deal with the 2-3 high-speed IOCtl that takes their arguments from
1795 * the session and iCmd, and does not return anything.
1796 */
1797 if (pSession->fUnrestricted)
1798 {
1799 ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
1800 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
1801 || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
1802 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
1803 {
1804 int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
1805
1806 /* Complete the I/O request. */
1807 supdrvSessionRelease(pSession);
1808 return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
1809 }
1810 }
1811#endif
1812
1813 return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
1814}
1815
1816
1817/**
1818 * Device I/O Control entry point.
1819 *
1820 * @param pDevExt The device extension.
1821 * @param pSession The session.
1822 * @param pIrp Request packet.
1823 * @param pStack The request stack pointer.
1824 */
1825static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
1826 PIRP pIrp, PIO_STACK_LOCATION pStack)
1827{
1828 NTSTATUS rcNt;
1829 uint32_t cbOut = 0;
1830 int rc = 0;
1831 Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
1832 pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
1833 pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
1834 pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
1835
1836#if 0 /*def RT_ARCH_AMD64*/
1837 /* Don't allow 32-bit processes to do any I/O controls. */
1838 if (!IoIs32bitProcess(pIrp))
1839#endif
1840 {
1841 /* Verify that it's a buffered CTL. */
1842 if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
1843 {
1844 /* Verify that the sizes in the request header are correct. */
1845 PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
1846 if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
1847 && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
1848 && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
1849 {
1850 /* Zero extra output bytes to make sure we don't leak anything. */
1851 if (pHdr->cbIn < pHdr->cbOut)
1852 RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
1853
1854 /*
1855 * Do the job.
1856 */
1857 rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
1858 RT_MAX(pHdr->cbIn, pHdr->cbOut));
1859 if (RT_SUCCESS(rc))
1860 {
1861 rcNt = STATUS_SUCCESS;
1862 cbOut = pHdr->cbOut;
1863 if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
1864 {
1865 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
1866 LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
1867 pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
1868 }
1869
1870 /* If IDC successful disconnect request, we must set the context pointer to NULL. */
1871 if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
1872 && RT_SUCCESS(pHdr->rc))
1873 pStack->FileObject->FsContext = NULL;
1874 }
1875 else if (rc == VERR_NOT_SUPPORTED)
1876 rcNt = STATUS_NOT_SUPPORTED;
1877 else
1878 rcNt = STATUS_INVALID_PARAMETER;
1879 Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
1880 }
1881 else
1882 {
1883 Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
1884 pStack->Parameters.DeviceIoControl.IoControlCode,
1885 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
1886 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
1887 pStack->Parameters.DeviceIoControl.InputBufferLength,
1888 pStack->Parameters.DeviceIoControl.OutputBufferLength));
1889 rcNt = STATUS_INVALID_PARAMETER;
1890 }
1891 }
1892 else
1893 {
1894 Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
1895 pStack->Parameters.DeviceIoControl.IoControlCode));
1896 rcNt = STATUS_NOT_SUPPORTED;
1897 }
1898 }
1899#if 0 /*def RT_ARCH_AMD64*/
1900 else
1901 {
1902 Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
1903 rcNt = STATUS_NOT_SUPPORTED;
1904 }
1905#endif
1906
1907 return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
1908}
1909
1910
1911/**
1912 * Internal Device I/O Control entry point (for IDC).
1913 *
1914 * @param pDevObj Device object.
1915 * @param pIrp Request packet.
1916 */
1917static NTSTATUS vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1918{
1919 /* Currently no special code here. */
1920 return vgdrvNtDeviceControl(pDevObj, pIrp);
1921}
1922
1923
1924/**
1925 * IRP_MJ_SHUTDOWN handler.
1926 *
1927 * @returns NT status code
1928 * @param pDevObj Device object.
1929 * @param pIrp IRP.
1930 */
1931static NTSTATUS vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1932{
1933 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1934 LogFlowFuncEnter();
1935
1936 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
1937 if (pReq)
1938 {
1939 pReq->header.requestType = VMMDevReq_SetPowerStatus;
1940 pReq->powerState = VMMDevPowerState_PowerOff;
1941
1942 int rc = VbglR0GRPerform(&pReq->header);
1943 if (RT_FAILURE(rc))
1944 LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
1945 }
1946
1947 /* just in case, since we shouldn't normally get here. */
1948 pIrp->IoStatus.Information = 0;
1949 pIrp->IoStatus.Status = STATUS_SUCCESS;
1950 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1951 return STATUS_SUCCESS;
1952}
1953
1954
1955/**
1956 * Stub function for functions we don't implemented.
1957 *
1958 * @returns STATUS_NOT_SUPPORTED
1959 * @param pDevObj Device object.
1960 * @param pIrp IRP.
1961 */
1962static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1963{
1964 RT_NOREF1(pDevObj);
1965 LogFlowFuncEnter();
1966
1967 pIrp->IoStatus.Information = 0;
1968 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1969 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1970
1971 return STATUS_NOT_SUPPORTED;
1972}
1973
1974
1975/**
1976 * Sets the mouse notification callback.
1977 *
1978 * @returns VBox status code.
1979 * @param pDevExt Pointer to the device extension.
1980 * @param pNotify Pointer to the mouse notify struct.
1981 */
1982int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
1983{
1984 PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
1985 /* we need a lock here to avoid concurrency with the set event functionality */
1986 KIRQL OldIrql;
1987 KeAcquireSpinLock(&pDevExtWin->MouseEventAccessSpinLock, &OldIrql);
1988 pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
1989 pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
1990 KeReleaseSpinLock(&pDevExtWin->MouseEventAccessSpinLock, OldIrql);
1991 return VINF_SUCCESS;
1992}
1993
1994
1995/**
1996 * DPC handler.
1997 *
1998 * @param pDPC DPC descriptor.
1999 * @param pDevObj Device object.
2000 * @param pIrp Interrupt request packet.
2001 * @param pContext Context specific pointer.
2002 */
2003static void NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
2004{
2005 RT_NOREF3(pDPC, pIrp, pContext);
2006 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2007 Log3Func(("pDevExt=0x%p\n", pDevExt));
2008
2009 /* Test & reset the counter. */
2010 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
2011 {
2012 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
2013 * i.e. to prevent the event from destroyed while we're using it */
2014 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
2015 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessSpinLock);
2016
2017 if (pDevExt->Core.pfnMouseNotifyCallback)
2018 pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
2019
2020 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessSpinLock);
2021 }
2022
2023 /* Process the wake-up list we were asked by the scheduling a DPC
2024 * in vgdrvNtIsrHandler(). */
2025 VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
2026}
2027
2028
2029/**
2030 * ISR handler.
2031 *
2032 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
2033 * @param pInterrupt Interrupt that was triggered.
2034 * @param pServiceContext Context specific pointer.
2035 */
2036static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
2037{
2038 RT_NOREF1(pInterrupt);
2039 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
2040 if (pDevExt == NULL)
2041 return FALSE;
2042
2043 /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
2044
2045 /* Enter the common ISR routine and do the actual work. */
2046 BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
2047
2048 /* If we need to wake up some events we do that in a DPC to make
2049 * sure we're called at the right IRQL. */
2050 if (fIRQTaken)
2051 {
2052 Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
2053 if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
2054 || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
2055 {
2056 Log3Func(("Requesting DPC ...\n"));
2057 IoRequestDpc(pDevExt->pDeviceObject, pDevExt->pCurrentIrp, NULL);
2058 }
2059 }
2060 return fIRQTaken;
2061}
2062
2063
2064/**
2065 * Overridden routine for mouse polling events.
2066 *
2067 * @param pDevExt Device extension structure.
2068 */
2069void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
2070{
2071 NOREF(pDevExt);
2072 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
2073 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
2074 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
2075}
2076
2077
2078/**
2079 * Hook for handling OS specfic options from the host.
2080 *
2081 * @returns true if handled, false if not.
2082 * @param pDevExt The device extension.
2083 * @param pszName The option name.
2084 * @param pszValue The option value.
2085 */
2086bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
2087{
2088 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
2089 return false;
2090}
2091
2092
2093/**
2094 * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key.
2095 */
2096static NTSTATUS NTAPI vbdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType,
2097 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
2098{
2099 /*
2100 * Filter out general service config values.
2101 */
2102 if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0
2103 || RTUtf16ICmpAscii(pwszValueName, "Start") == 0
2104 || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0
2105 || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0
2106 || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0
2107 || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0
2108 || RTUtf16ICmpAscii(pwszValueName, "Group") == 0
2109 || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0
2110 || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0
2111 )
2112 {
2113 return STATUS_SUCCESS;
2114 }
2115
2116 /*
2117 * Convert the value name.
2118 */
2119 size_t cch = RTUtf16CalcUtf8Len(pwszValueName);
2120 if (cch < 64 && cch > 0)
2121 {
2122 char szValueName[72];
2123 char *pszTmp = szValueName;
2124 int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL);
2125 if (RT_SUCCESS(rc))
2126 {
2127 /*
2128 * Convert the value.
2129 */
2130 char szValue[72];
2131 char *pszFree = NULL;
2132 char *pszValue = NULL;
2133 szValue[0] = '\0';
2134 switch (uValueType)
2135 {
2136 case REG_SZ:
2137 case REG_EXPAND_SZ:
2138 rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch);
2139 if (RT_SUCCESS(rc) && cch < _1K)
2140 {
2141 if (cch < sizeof(szValue))
2142 {
2143 pszValue = szValue;
2144 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
2145 }
2146 else
2147 {
2148 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
2149 if (RT_SUCCESS(rc))
2150 pszFree = pszValue;
2151 }
2152 if (RT_FAILURE(rc))
2153 {
2154 LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n",
2155 pwszValueName, rc));
2156 pszValue = NULL;
2157 }
2158 }
2159 else if (RT_SUCCESS(rc))
2160 LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n",
2161 pwszValueName, cbValue, uValueType));
2162 else
2163 LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n",
2164 pwszValueName, cbValue, uValueType));
2165 break;
2166
2167 case REG_DWORD:
2168 if (cbValue == sizeof(uint32_t))
2169 {
2170 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
2171 pszValue = szValue;
2172 }
2173 else
2174 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
2175 break;
2176
2177 case REG_QWORD:
2178 if (cbValue == sizeof(uint64_t))
2179 {
2180 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
2181 pszValue = szValue;
2182 }
2183 else
2184 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
2185 break;
2186
2187 default:
2188 LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType));
2189 break;
2190 }
2191 if (pszValue)
2192 {
2193 /*
2194 * Process it.
2195 */
2196 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
2197 VGDrvCommonProcessOption(pDevExt, szValueName, pszValue);
2198 if (pszFree)
2199 RTStrFree(pszFree);
2200 }
2201 }
2202 }
2203 else if (cch > 0)
2204 LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName));
2205 else
2206 LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName));
2207 NOREF(pvEntryCtx);
2208 return STATUS_SUCCESS;
2209}
2210
2211
2212/**
2213 * Reads configuration from the registry and guest properties.
2214 *
2215 * We ignore failures and instead preserve existing configuration values.
2216 *
2217 * Thie routine will block.
2218 *
2219 * @param pDevExt The device extension.
2220 */
2221static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt)
2222{
2223 /*
2224 * First the registry.
2225 */
2226 RTL_QUERY_REGISTRY_TABLE aQuery[2];
2227 RT_ZERO(aQuery);
2228 aQuery[0].QueryRoutine = vbdrvNtRegistryEnumCallback;
2229 aQuery[0].Flags = 0;
2230 aQuery[0].Name = NULL;
2231 aQuery[0].EntryContext = NULL;
2232 aQuery[0].DefaultType = REG_NONE;
2233 NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /* Environment */);
2234 if (!NT_SUCCESS(rcNt))
2235 LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt));
2236
2237 /*
2238 * Read configuration from the host.
2239 */
2240 VGDrvCommonProcessOptionsFromHost(&pDevExt->Core);
2241}
2242
2243
2244/**
2245 * Helper to scan the PCI resource list and remember stuff.
2246 *
2247 * @param pResList Resource list
2248 * @param pDevExt Device extension
2249 */
2250static NTSTATUS vgdrvNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt)
2251{
2252 /* Enumerate the resource list. */
2253 LogFlowFunc(("Found %d resources\n",
2254 pResList->List->PartialResourceList.Count));
2255
2256 NTSTATUS rc = STATUS_SUCCESS;
2257 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
2258 ULONG rangeCount = 0;
2259 ULONG cMMIORange = 0;
2260 PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->aPciBaseAddresses;
2261 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
2262 {
2263 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
2264 switch (pPartialData->Type)
2265 {
2266 case CmResourceTypePort:
2267 {
2268 /* Overflow protection. */
2269 if (rangeCount < PCI_TYPE0_ADDRESSES)
2270 {
2271 LogFlowFunc(("I/O range: Base=%08x:%08x, length=%08x\n",
2272 pPartialData->u.Port.Start.HighPart,
2273 pPartialData->u.Port.Start.LowPart,
2274 pPartialData->u.Port.Length));
2275
2276 /* Save the IO port base. */
2277 /** @todo Not so good.
2278 * Update/bird: What is not so good? That we just consider the last range? */
2279 pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
2280
2281 /* Save resource information. */
2282 pBaseAddress->RangeStart = pPartialData->u.Port.Start;
2283 pBaseAddress->RangeLength = pPartialData->u.Port.Length;
2284 pBaseAddress->RangeInMemory = FALSE;
2285 pBaseAddress->ResourceMapped = FALSE;
2286
2287 LogFunc(("I/O range for VMMDev found! Base=%08x:%08x, length=%08x\n",
2288 pPartialData->u.Port.Start.HighPart,
2289 pPartialData->u.Port.Start.LowPart,
2290 pPartialData->u.Port.Length));
2291
2292 /* Next item ... */
2293 rangeCount++; pBaseAddress++;
2294 }
2295 break;
2296 }
2297
2298 case CmResourceTypeInterrupt:
2299 {
2300 LogFunc(("Interrupt: Level=%x, vector=%x, mode=%x\n",
2301 pPartialData->u.Interrupt.Level,
2302 pPartialData->u.Interrupt.Vector,
2303 pPartialData->Flags));
2304
2305 /* Save information. */
2306 pDevExt->uInterruptLevel = pPartialData->u.Interrupt.Level;
2307 pDevExt->uInterruptVector = pPartialData->u.Interrupt.Vector;
2308 pDevExt->fInterruptAffinity = pPartialData->u.Interrupt.Affinity;
2309
2310 /* Check interrupt mode. */
2311 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
2312 pDevExt->enmInterruptMode = Latched;
2313 else
2314 pDevExt->enmInterruptMode = LevelSensitive;
2315 break;
2316 }
2317
2318 case CmResourceTypeMemory:
2319 {
2320 /* Overflow protection. */
2321 if (rangeCount < PCI_TYPE0_ADDRESSES)
2322 {
2323 LogFlowFunc(("Memory range: Base=%08x:%08x, length=%08x\n",
2324 pPartialData->u.Memory.Start.HighPart,
2325 pPartialData->u.Memory.Start.LowPart,
2326 pPartialData->u.Memory.Length));
2327
2328 /* We only care about read/write memory. */
2329 /** @todo Reconsider memory type. */
2330 if ( cMMIORange == 0 /* Only care about the first MMIO range (!!!). */
2331 && (pPartialData->Flags & VBOX_CM_PRE_VISTA_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
2332 {
2333 /* Save physical MMIO base + length for VMMDev. */
2334 pDevExt->uVmmDevMemoryPhysAddr = pPartialData->u.Memory.Start;
2335 pDevExt->cbVmmDevMemory = (ULONG)pPartialData->u.Memory.Length;
2336
2337 /* Technically we need to make the HAL translate the address. since we
2338 didn't used to do this and it probably just returns the input address,
2339 we allow ourselves to ignore failures. */
2340 ULONG uAddressSpace = 0;
2341 PHYSICAL_ADDRESS PhysAddr = pPartialData->u.Memory.Start;
2342 if (HalTranslateBusAddress(pResList->List->InterfaceType, pResList->List->BusNumber, PhysAddr,
2343 &uAddressSpace, &PhysAddr))
2344 {
2345 Log(("HalTranslateBusAddress(%#RX64) -> %RX64, type %#x\n",
2346 pPartialData->u.Memory.Start.QuadPart, PhysAddr.QuadPart, uAddressSpace));
2347 if (pPartialData->u.Memory.Start.QuadPart != PhysAddr.QuadPart)
2348 pDevExt->uVmmDevMemoryPhysAddr = PhysAddr;
2349 }
2350 else
2351 Log(("HalTranslateBusAddress(%#RX64) -> failed!\n", pPartialData->u.Memory.Start.QuadPart));
2352
2353 /* Save resource information. */
2354 pBaseAddress->RangeStart = pPartialData->u.Memory.Start;
2355 pBaseAddress->RangeLength = pPartialData->u.Memory.Length;
2356 pBaseAddress->RangeInMemory = TRUE;
2357 pBaseAddress->ResourceMapped = FALSE;
2358
2359 LogFunc(("Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
2360 pPartialData->u.Memory.Start.HighPart,
2361 pPartialData->u.Memory.Start.LowPart,
2362 pPartialData->u.Memory.Length));
2363
2364 /* Next item ... */
2365 rangeCount++; pBaseAddress++; cMMIORange++;
2366 }
2367 else
2368 LogFunc(("Ignoring memory: Flags=%08x\n", pPartialData->Flags));
2369 }
2370 break;
2371 }
2372
2373 default:
2374 {
2375 LogFunc(("Unhandled resource found, type=%d\n", pPartialData->Type));
2376 break;
2377 }
2378 }
2379 }
2380
2381 /* Memorize the number of resources found. */
2382 pDevExt->cPciAddresses = rangeCount;
2383 return rc;
2384}
2385
2386
2387/**
2388 * Maps the I/O space from VMMDev to virtual kernel address space.
2389 *
2390 * @return NTSTATUS
2391 *
2392 * @param pDevExt The device extension.
2393 * @param PhysAddr Physical address to map.
2394 * @param cbToMap Number of bytes to map.
2395 * @param ppvMMIOBase Pointer of mapped I/O base.
2396 * @param pcbMMIO Length of mapped I/O base.
2397 */
2398static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
2399 void **ppvMMIOBase, uint32_t *pcbMMIO)
2400{
2401 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
2402 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
2403 /* pcbMMIO is optional. */
2404
2405 NTSTATUS rc = STATUS_SUCCESS;
2406 if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
2407 {
2408 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
2409 LogFlowFunc(("pVMMDevMemory = %#x\n", pVMMDevMemory));
2410 if (pVMMDevMemory)
2411 {
2412 LogFunc(("VMMDevMemory: Version = %#x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
2413
2414 /* Check version of the structure; do we have the right memory version? */
2415 if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
2416 {
2417 /* Save results. */
2418 *ppvMMIOBase = pVMMDevMemory;
2419 if (pcbMMIO) /* Optional. */
2420 *pcbMMIO = pVMMDevMemory->u32Size;
2421
2422 LogFlowFunc(("VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase));
2423 }
2424 else
2425 {
2426 /* Not our version, refuse operation and unmap the memory. */
2427 LogFunc(("Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
2428
2429 vgdrvNtUnmapVMMDevMemory(pDevExt);
2430 rc = STATUS_UNSUCCESSFUL;
2431 }
2432 }
2433 else
2434 rc = STATUS_UNSUCCESSFUL;
2435 }
2436 return rc;
2437}
2438
2439#ifdef VBOX_STRICT
2440
2441/**
2442 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
2443 */
2444static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
2445{
2446 AssertPtrReturn(pu32Bits, 0);
2447 LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
2448 uint32_t u32Result = 0;
2449 uint32_t u32WorkingMask = u32Mask;
2450 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
2451
2452 while (iBitOffset > 0)
2453 {
2454 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
2455 if (fSet)
2456 u32Result |= 1 << (iBitOffset - 1);
2457 u32WorkingMask &= ~(1 << (iBitOffset - 1));
2458 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
2459 }
2460 LogFlowFunc(("Returning %#x\n", u32Result));
2461 return u32Result;
2462}
2463
2464
2465static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
2466{
2467 ULONG u32Bits2 = u32Bits;
2468 uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
2469 if ( u32Result != u32Exp
2470 || (u32Bits2 & u32Mask)
2471 || (u32Bits2 & u32Result)
2472 || ((u32Bits2 | u32Result) != u32Bits)
2473 )
2474 AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
2475 u32Mask, u32Bits, u32Bits2, u32Result));
2476}
2477
2478
2479static void vgdrvNtDoTests(void)
2480{
2481 vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
2482 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
2483 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
2484 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
2485 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
2486 vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
2487}
2488
2489#endif /* VBOX_STRICT */
2490
2491#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
2492
2493/*
2494 * DPC latency checker.
2495 */
2496
2497/**
2498 * One DPC latency sample.
2499 */
2500typedef struct DPCSAMPLE
2501{
2502 LARGE_INTEGER PerfDelta;
2503 LARGE_INTEGER PerfCounter;
2504 LARGE_INTEGER PerfFrequency;
2505 uint64_t u64TSC;
2506} DPCSAMPLE;
2507AssertCompileSize(DPCSAMPLE, 4*8);
2508
2509/**
2510 * The DPC latency measurement workset.
2511 */
2512typedef struct DPCDATA
2513{
2514 KDPC Dpc;
2515 KTIMER Timer;
2516 KSPIN_LOCK SpinLock;
2517
2518 ULONG ulTimerRes;
2519
2520 bool volatile fFinished;
2521
2522 /** The timer interval (relative). */
2523 LARGE_INTEGER DueTime;
2524
2525 LARGE_INTEGER PerfCounterPrev;
2526
2527 /** Align the sample array on a 64 byte boundrary just for the off chance
2528 * that we'll get cache line aligned memory backing this structure. */
2529 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
2530
2531 int cSamples;
2532 DPCSAMPLE aSamples[8192];
2533} DPCDATA;
2534
2535AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
2536
2537# define VBOXGUEST_DPC_TAG 'DPCS'
2538
2539
2540/**
2541 * DPC callback routine for the DPC latency measurement code.
2542 *
2543 * @param pDpc The DPC, not used.
2544 * @param pvDeferredContext Pointer to the DPCDATA.
2545 * @param SystemArgument1 System use, ignored.
2546 * @param SystemArgument2 System use, ignored.
2547 */
2548static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
2549{
2550 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
2551 RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
2552
2553 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
2554
2555 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
2556 pData->fFinished = true;
2557 else
2558 {
2559 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
2560
2561 pSample->u64TSC = ASMReadTSC();
2562 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
2563 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
2564
2565 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
2566
2567 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
2568 }
2569
2570 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
2571}
2572
2573
2574/**
2575 * Handles the DPC latency checker request.
2576 *
2577 * @returns VBox status code.
2578 */
2579int VGDrvNtIOCtl_DpcLatencyChecker(void)
2580{
2581 /*
2582 * Allocate a block of non paged memory for samples and related data.
2583 */
2584 DPCDATA *pData = (DPCDATA *)RTMemAlloc(sizeof(DPCDATA));
2585 if (!pData)
2586 {
2587 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
2588 return VERR_NO_MEMORY;
2589 }
2590
2591 /*
2592 * Initialize the data.
2593 */
2594 KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
2595 KeInitializeTimer(&pData->Timer);
2596 KeInitializeSpinLock(&pData->SpinLock);
2597
2598 pData->fFinished = false;
2599 pData->cSamples = 0;
2600 pData->PerfCounterPrev.QuadPart = 0;
2601
2602 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
2603 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
2604
2605 /*
2606 * Start the DPC measurements and wait for a full set.
2607 */
2608 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
2609
2610 while (!pData->fFinished)
2611 {
2612 LARGE_INTEGER Interval;
2613 Interval.QuadPart = -100 * 1000 * 10;
2614 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
2615 }
2616
2617 ExSetTimerResolution(0, 0);
2618
2619 /*
2620 * Log everything to the host.
2621 */
2622 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
2623 for (int i = 0; i < pData->cSamples; i++)
2624 {
2625 DPCSAMPLE *pSample = &pData->aSamples[i];
2626
2627 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
2628 i,
2629 pSample->PerfDelta.QuadPart,
2630 pSample->PerfCounter.QuadPart,
2631 pSample->PerfFrequency.QuadPart,
2632 pSample->u64TSC);
2633 }
2634
2635 RTMemFree(pData);
2636 return VINF_SUCCESS;
2637}
2638
2639#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
2640
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette