VirtualBox

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

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

VBoxGuest-win.cpp: State cleanups.

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

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