VirtualBox

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

Last change on this file since 87579 was 87579, checked in by vboxsync, 4 years ago

VBoxGuest: doxygen fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 129.8 KB
Line 
1/* $Id: VBoxGuest-win.cpp 87579 2021-02-03 15:48:28Z vboxsync $ */
2/** @file
3 * VBoxGuest - Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2010-2020 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/nt.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/critsect.h>
41#include <iprt/dbg.h>
42#include <iprt/err.h>
43#include <iprt/initterm.h>
44#include <iprt/memobj.h>
45#include <iprt/mem.h>
46#include <iprt/mp.h>
47#include <iprt/spinlock.h>
48#include <iprt/string.h>
49#include <iprt/utf16.h>
50
51#ifdef TARGET_NT4
52# include <VBox/pci.h>
53# define PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS32_IPRT
54# include <iprt/formats/mz.h>
55# include <iprt/formats/pecoff.h>
56extern "C" IMAGE_DOS_HEADER __ImageBase;
57#endif
58
59
60/*********************************************************************************************************************************
61* Defined Constants And Macros *
62*********************************************************************************************************************************/
63#undef ExFreePool
64
65#ifndef PCI_MAX_BUSES
66# define PCI_MAX_BUSES 256
67#endif
68
69/** CM_RESOURCE_MEMORY_* flags which were used on XP or earlier. */
70#define VBOX_CM_PRE_VISTA_MASK (0x3f)
71
72
73/*********************************************************************************************************************************
74* Structures and Typedefs *
75*********************************************************************************************************************************/
76/**
77 * Possible device states for our state machine.
78 */
79typedef enum VGDRVNTDEVSTATE
80{
81 /** @name Stable states
82 * @{ */
83 VGDRVNTDEVSTATE_REMOVED = 0,
84 VGDRVNTDEVSTATE_STOPPED,
85 VGDRVNTDEVSTATE_OPERATIONAL,
86 /** @} */
87
88 /** @name Transitional states
89 * @{ */
90 VGDRVNTDEVSTATE_PENDINGSTOP,
91 VGDRVNTDEVSTATE_PENDINGREMOVE,
92 VGDRVNTDEVSTATE_SURPRISEREMOVED
93 /** @} */
94} VGDRVNTDEVSTATE;
95
96
97/**
98 * Subclassing the device extension for adding windows-specific bits.
99 */
100typedef struct VBOXGUESTDEVEXTWIN
101{
102 /** The common device extension core. */
103 VBOXGUESTDEVEXT Core;
104
105 /** Our functional driver object. */
106 PDEVICE_OBJECT pDeviceObject;
107 /** Top of the stack. */
108 PDEVICE_OBJECT pNextLowerDriver;
109
110 /** @name PCI bus and slot (device+function) set by for legacy NT only.
111 * @{ */
112 /** Bus number where the device is located. */
113 ULONG uBus;
114 /** Slot number where the device is located (PCI_SLOT_NUMBER). */
115 ULONG uSlot;
116 /** @} */
117
118 /** @name Interrupt stuff.
119 * @{ */
120 /** Interrupt object pointer. */
121 PKINTERRUPT pInterruptObject;
122 /** Device interrupt level. */
123 ULONG uInterruptLevel;
124 /** Device interrupt vector. */
125 ULONG uInterruptVector;
126 /** Affinity mask. */
127 KAFFINITY fInterruptAffinity;
128 /** LevelSensitive or Latched. */
129 KINTERRUPT_MODE enmInterruptMode;
130 /** @} */
131
132 /** Physical address and length of VMMDev memory. */
133 PHYSICAL_ADDRESS uVmmDevMemoryPhysAddr;
134 /** Length of VMMDev memory. */
135 ULONG cbVmmDevMemory;
136
137 /** Device state. */
138 VGDRVNTDEVSTATE volatile enmDevState;
139 /** The previous stable device state. */
140 VGDRVNTDEVSTATE enmPrevDevState;
141
142 /** Last system power action set (see VBoxGuestPower). */
143 POWER_ACTION enmLastSystemPowerAction;
144 /** Preallocated generic request for shutdown. */
145 VMMDevPowerStateRequest *pPowerStateRequest;
146
147 /** Spinlock protecting MouseNotifyCallback. Required since the consumer is
148 * in a DPC callback and not the ISR. */
149 KSPIN_LOCK MouseEventAccessSpinLock;
150
151 /** Read/write critical section for handling race between checking for idle
152 * driver (in IRP_MN_QUERY_REMOVE_DEVICE & IRP_MN_QUERY_STOP_DEVICE) and
153 * creating new sessions. The session creation code enteres the critical
154 * section in read (shared) access mode, whereas the idle checking code
155 * enteres is in write (exclusive) access mode. */
156 RTCRITSECTRW SessionCreateCritSect;
157} VBOXGUESTDEVEXTWIN;
158typedef VBOXGUESTDEVEXTWIN *PVBOXGUESTDEVEXTWIN;
159
160
161/** NT (windows) version identifier. */
162typedef enum VGDRVNTVER
163{
164 VGDRVNTVER_INVALID = 0,
165 VGDRVNTVER_WINNT310,
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
184static NTSTATUS NTAPI vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
185static NTSTATUS NTAPI vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp);
186static NTSTATUS NTAPI vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp);
187static NTSTATUS NTAPI vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
188static void NTAPI vgdrvNtUnload(PDRIVER_OBJECT pDrvObj);
189static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
190static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
191static NTSTATUS NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
192static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
193 PIRP pIrp, PIO_STACK_LOCATION pStack);
194static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
195static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt);
196static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
197static NTSTATUS NTAPI vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
198static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer);
199static VOID NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext);
200static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext);
201#ifdef VBOX_STRICT
202static void vgdrvNtDoTests(void);
203#endif
204#ifdef TARGET_NT4
205static ULONG NTAPI vgdrvNt31GetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
206 void *pvData, ULONG offData, ULONG cbData);
207static ULONG NTAPI vgdrvNt31SetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
208 void *pvData, ULONG offData, ULONG cbData);
209#endif
210
211/*
212 * We only do INIT allocations. PAGE is too much work and risk for little gain.
213 */
214#ifdef ALLOC_PRAGMA
215NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
216# pragma alloc_text(INIT, DriverEntry)
217# ifdef TARGET_NT4
218static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
219# pragma alloc_text(INIT, vgdrvNt4CreateDevice)
220static NTSTATUS vgdrvNt4FindPciDevice(PULONG puluBusNumber, PPCI_SLOT_NUMBER puSlotNumber);
221# pragma alloc_text(INIT, vgdrvNt4FindPciDevice)
222# endif
223#endif
224RT_C_DECLS_END
225
226
227/*********************************************************************************************************************************
228* Global Variables *
229*********************************************************************************************************************************/
230/** The detected NT (windows) version. */
231static VGDRVNTVER g_enmVGDrvNtVer = VGDRVNTVER_INVALID;
232/** Pointer to the PoStartNextPowerIrp routine (in the NT kernel).
233 * Introduced in Windows 2000. */
234static decltype(PoStartNextPowerIrp) *g_pfnPoStartNextPowerIrp = NULL;
235/** Pointer to the PoCallDriver routine (in the NT kernel).
236 * Introduced in Windows 2000. */
237static decltype(PoCallDriver) *g_pfnPoCallDriver = NULL;
238#ifdef TARGET_NT4
239/** Pointer to the HalAssignSlotResources routine (in the HAL).
240 * Introduced in NT 3.50. */
241static decltype(HalAssignSlotResources) *g_pfnHalAssignSlotResources= NULL;
242/** Pointer to the HalGetBusDataByOffset routine (in the HAL).
243 * Introduced in NT 3.50. */
244static decltype(HalGetBusDataByOffset) *g_pfnHalGetBusDataByOffset = NULL;
245/** Pointer to the HalSetBusDataByOffset routine (in the HAL).
246 * Introduced in NT 3.50 (we provide fallback and use it only for NT 3.1). */
247static decltype(HalSetBusDataByOffset) *g_pfnHalSetBusDataByOffset = NULL;
248#endif
249/** Pointer to the KeRegisterBugCheckCallback routine (in the NT kernel).
250 * Introduced in Windows 3.50. */
251static decltype(KeRegisterBugCheckCallback) *g_pfnKeRegisterBugCheckCallback = NULL;
252/** Pointer to the KeRegisterBugCheckCallback routine (in the NT kernel).
253 * Introduced in Windows 3.50. */
254static decltype(KeDeregisterBugCheckCallback) *g_pfnKeDeregisterBugCheckCallback = NULL;
255/** Pointer to the KiBugCheckData array (in the NT kernel).
256 * Introduced in Windows 4. */
257static uintptr_t const *g_pauKiBugCheckData = NULL;
258/** Set if the callback was successfully registered and needs deregistering. */
259static bool g_fBugCheckCallbackRegistered = false;
260/** The bugcheck callback record. */
261static KBUGCHECK_CALLBACK_RECORD g_BugCheckCallbackRec;
262
263
264
265/**
266 * Driver entry point.
267 *
268 * @returns appropriate status code.
269 * @param pDrvObj Pointer to driver object.
270 * @param pRegPath Registry base path.
271 */
272NTSTATUS DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
273{
274 RT_NOREF1(pRegPath);
275#ifdef TARGET_NT4
276 /*
277 * Looks like NT 3.1 doesn't necessarily zero our uninitialized data segments
278 * (like ".bss"), at least not when loading at runtime, so do that.
279 */
280 PIMAGE_DOS_HEADER pMzHdr = &__ImageBase;
281 PIMAGE_NT_HEADERS32 pNtHdrs = (PIMAGE_NT_HEADERS32)((uint8_t *)pMzHdr + pMzHdr->e_lfanew);
282 if ( pNtHdrs->Signature == IMAGE_NT_SIGNATURE
283 && pNtHdrs->FileHeader.NumberOfSections > 2
284 && pNtHdrs->FileHeader.NumberOfSections < 64)
285 {
286 uint32_t iShdr = pNtHdrs->FileHeader.NumberOfSections;
287 uint32_t uRvaEnd = pNtHdrs->OptionalHeader.SizeOfImage; /* (may be changed to exclude tail sections) */
288 PIMAGE_SECTION_HEADER paShdrs;
289 paShdrs = (PIMAGE_SECTION_HEADER)&pNtHdrs->OptionalHeader.DataDirectory[pNtHdrs->OptionalHeader.NumberOfRvaAndSizes];
290 while (iShdr-- > 0)
291 {
292 if ( !(paShdrs[iShdr].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
293 && paShdrs[iShdr].VirtualAddress < uRvaEnd)
294 {
295 uint32_t const cbSection = uRvaEnd - paShdrs[iShdr].VirtualAddress;
296 uint32_t const offUninitialized = paShdrs[iShdr].SizeOfRawData;
297 //RTLogBackdoorPrintf("section #%u: rva=%#x size=%#x calcsize=%#x) rawsize=%#x\n", iShdr,
298 // paShdrs[iShdr].VirtualAddress, paShdrs[iShdr].Misc.VirtualSize, cbSection, offUninitialized);
299 if ( offUninitialized < cbSection
300 && (paShdrs[iShdr].Characteristics & IMAGE_SCN_MEM_WRITE))
301 memset((uint8_t *)pMzHdr + paShdrs[iShdr].VirtualAddress + offUninitialized, 0, cbSection - offUninitialized);
302 uRvaEnd = paShdrs[iShdr].VirtualAddress;
303 }
304 }
305 }
306 else
307 RTLogBackdoorPrintf("VBoxGuest: Bad pNtHdrs=%p: %#x\n", pNtHdrs, pNtHdrs->Signature);
308#endif
309
310 /*
311 * Start by initializing IPRT.
312 */
313 int rc = RTR0Init(0);
314 if (RT_FAILURE(rc))
315 {
316 RTLogBackdoorPrintf("VBoxGuest: RTR0Init failed: %Rrc!\n", rc);
317 return STATUS_UNSUCCESSFUL;
318 }
319 VGDrvCommonInitLoggers();
320
321 LogFunc(("Driver built: %s %s\n", __DATE__, __TIME__));
322
323 /*
324 * Check if the NT version is supported and initialize g_enmVGDrvNtVer.
325 */
326 ULONG ulMajorVer;
327 ULONG ulMinorVer;
328 ULONG ulBuildNo;
329 BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL);
330
331 /* Use RTLogBackdoorPrintf to make sure that this goes to VBox.log on the host. */
332 RTLogBackdoorPrintf("VBoxGuest: Windows version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo);
333 if (fCheckedBuild)
334 RTLogBackdoorPrintf("VBoxGuest: Windows checked build\n");
335
336#ifdef VBOX_STRICT
337 vgdrvNtDoTests();
338#endif
339 NTSTATUS rcNt = STATUS_SUCCESS;
340 switch (ulMajorVer)
341 {
342 case 10: /* Windows 10 Preview builds starting with 9926. */
343 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
344 break;
345 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
346 switch (ulMinorVer)
347 {
348 case 0: /* Note: Also could be Windows 2008 Server! */
349 g_enmVGDrvNtVer = VGDRVNTVER_WINVISTA;
350 break;
351 case 1: /* Note: Also could be Windows 2008 Server R2! */
352 g_enmVGDrvNtVer = VGDRVNTVER_WIN7;
353 break;
354 case 2:
355 g_enmVGDrvNtVer = VGDRVNTVER_WIN8;
356 break;
357 case 3:
358 g_enmVGDrvNtVer = VGDRVNTVER_WIN81;
359 break;
360 case 4: /* Windows 10 Preview builds. */
361 default:
362 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
363 break;
364 }
365 break;
366 case 5:
367 switch (ulMinorVer)
368 {
369 default:
370 case 2:
371 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K3;
372 break;
373 case 1:
374 g_enmVGDrvNtVer = VGDRVNTVER_WINXP;
375 break;
376 case 0:
377 g_enmVGDrvNtVer = VGDRVNTVER_WIN2K;
378 break;
379 }
380 break;
381 case 4:
382 g_enmVGDrvNtVer = VGDRVNTVER_WINNT4;
383 break;
384 case 3:
385 if (ulMinorVer > 50)
386 g_enmVGDrvNtVer = VGDRVNTVER_WINNT351;
387 else if (ulMinorVer >= 50)
388 g_enmVGDrvNtVer = VGDRVNTVER_WINNT350;
389 else
390 g_enmVGDrvNtVer = VGDRVNTVER_WINNT310;
391 break;
392 default:
393 /* Major versions above 6 gets classified as windows 10. */
394 if (ulMajorVer > 6)
395 g_enmVGDrvNtVer = VGDRVNTVER_WIN10;
396 else
397 {
398 RTLogBackdoorPrintf("At least Windows NT 3.10 required! Found %u.%u!\n", ulMajorVer, ulMinorVer);
399 rcNt = STATUS_DRIVER_UNABLE_TO_LOAD;
400 }
401 break;
402 }
403 if (NT_SUCCESS(rcNt))
404 {
405 /*
406 * Dynamically resolve symbols not present in NT4.
407 */
408 RTDBGKRNLINFO hKrnlInfo;
409 rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0 /*fFlags*/);
410 if (RT_SUCCESS(rc))
411 {
412 g_pfnKeRegisterBugCheckCallback = (decltype(KeRegisterBugCheckCallback) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeRegisterBugCheckCallback");
413 g_pfnKeDeregisterBugCheckCallback = (decltype(KeDeregisterBugCheckCallback) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeDeregisterBugCheckCallback");
414 g_pauKiBugCheckData = (uintptr_t const *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KiBugCheckData");
415 g_pfnPoCallDriver = (decltype(PoCallDriver) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "PoCallDriver");
416 g_pfnPoStartNextPowerIrp = (decltype(PoStartNextPowerIrp) *) RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "PoStartNextPowerIrp");
417#ifdef TARGET_NT4
418 if (g_enmVGDrvNtVer > VGDRVNTVER_WINNT4)
419#endif
420 {
421 if (!g_pfnPoCallDriver) { LogRelFunc(("Missing PoCallDriver!\n")); rc = VERR_SYMBOL_NOT_FOUND; }
422 if (!g_pfnPoStartNextPowerIrp) { LogRelFunc(("Missing PoStartNextPowerIrp!\n")); rc = VERR_SYMBOL_NOT_FOUND; }
423 }
424
425#ifdef TARGET_NT4
426 g_pfnHalAssignSlotResources = (decltype(HalAssignSlotResources) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalAssignSlotResources");
427 if (!g_pfnHalAssignSlotResources && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K)
428 {
429 RTLogBackdoorPrintf("VBoxGuest: Missing HalAssignSlotResources!\n");
430 rc = VERR_SYMBOL_NOT_FOUND;
431 }
432
433 g_pfnHalGetBusDataByOffset = (decltype(HalGetBusDataByOffset) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalGetBusDataByOffset");
434 if (!g_pfnHalGetBusDataByOffset && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K)
435 {
436 RTLogBackdoorPrintf("VBoxGuest: Missing HalGetBusDataByOffset!\n");
437 rc = VERR_SYMBOL_NOT_FOUND;
438 }
439 if (!g_pfnHalGetBusDataByOffset)
440 g_pfnHalGetBusDataByOffset = vgdrvNt31GetBusDataByOffset;
441
442 g_pfnHalSetBusDataByOffset = (decltype(HalSetBusDataByOffset) *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalSetBusDataByOffset");
443 if (!g_pfnHalSetBusDataByOffset && g_enmVGDrvNtVer >= VGDRVNTVER_WINNT350 && g_enmVGDrvNtVer < VGDRVNTVER_WIN2K)
444 {
445 RTLogBackdoorPrintf("VBoxGuest: Missing HalSetBusDataByOffset!\n");
446 rc = VERR_SYMBOL_NOT_FOUND;
447 }
448 if (!g_pfnHalSetBusDataByOffset)
449 g_pfnHalSetBusDataByOffset = vgdrvNt31SetBusDataByOffset;
450#endif
451 RTR0DbgKrnlInfoRelease(hKrnlInfo);
452 }
453 if (RT_SUCCESS(rc))
454 {
455 /*
456 * Setup the driver entry points in pDrvObj.
457 */
458 pDrvObj->DriverUnload = vgdrvNtUnload;
459 pDrvObj->MajorFunction[IRP_MJ_CREATE] = vgdrvNtCreate;
460 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vgdrvNtClose;
461 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vgdrvNtDeviceControl;
462 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vgdrvNtInternalIOCtl;
463 /** @todo Need to call IoRegisterShutdownNotification or
464 * IoRegisterLastChanceShutdownNotification, possibly hooking the
465 * HalReturnToFirmware import in NTOSKRNL on older systems (<= ~NT4) and
466 * check for power off requests. */
467 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vgdrvNtShutdown;
468 pDrvObj->MajorFunction[IRP_MJ_READ] = vgdrvNtNotSupportedStub;
469 pDrvObj->MajorFunction[IRP_MJ_WRITE] = vgdrvNtNotSupportedStub;
470#ifdef TARGET_NT4
471 if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4)
472 rcNt = vgdrvNt4CreateDevice(pDrvObj, pRegPath);
473 else
474#endif
475 {
476 pDrvObj->MajorFunction[IRP_MJ_PNP] = vgdrvNtNt5PlusPnP;
477 pDrvObj->MajorFunction[IRP_MJ_POWER] = vgdrvNtNt5PlusPower;
478 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vgdrvNtNt5PlusSystemControl;
479 pDrvObj->DriverExtension->AddDevice = vgdrvNtNt5PlusAddDevice;
480 }
481 if (NT_SUCCESS(rcNt))
482 {
483 /*
484 * Try register the bugcheck callback (non-fatal).
485 */
486 if ( g_pfnKeRegisterBugCheckCallback
487 && g_pfnKeDeregisterBugCheckCallback)
488 {
489 AssertCompile(BufferEmpty == 0);
490 KeInitializeCallbackRecord(&g_BugCheckCallbackRec);
491 if (g_pfnKeRegisterBugCheckCallback(&g_BugCheckCallbackRec, vgdrvNtBugCheckCallback,
492 NULL, 0, (PUCHAR)"VBoxGuest"))
493 g_fBugCheckCallbackRegistered = true;
494 else
495 g_fBugCheckCallbackRegistered = false;
496 }
497 else
498 Assert(g_pfnKeRegisterBugCheckCallback == NULL && g_pfnKeDeregisterBugCheckCallback == NULL);
499
500 LogFlowFunc(("Returning %#x\n", rcNt));
501 return rcNt;
502 }
503 }
504 else
505 rcNt = STATUS_PROCEDURE_NOT_FOUND;
506 }
507
508 /*
509 * Failed.
510 */
511 LogRelFunc(("Failed! rcNt=%#x\n", rcNt));
512 VGDrvCommonDestroyLoggers();
513 RTR0Term();
514 return rcNt;
515}
516
517
518/**
519 * Translates our internal NT version enum to VBox OS.
520 *
521 * @returns VBox OS type.
522 * @param enmNtVer The NT version.
523 */
524static VBOXOSTYPE vgdrvNtVersionToOSType(VGDRVNTVER enmNtVer)
525{
526 VBOXOSTYPE enmOsType;
527 switch (enmNtVer)
528 {
529 case VGDRVNTVER_WINNT310: enmOsType = VBOXOSTYPE_WinNT3x; break;
530 case VGDRVNTVER_WINNT350: enmOsType = VBOXOSTYPE_WinNT3x; break;
531 case VGDRVNTVER_WINNT351: enmOsType = VBOXOSTYPE_WinNT3x; break;
532 case VGDRVNTVER_WINNT4: enmOsType = VBOXOSTYPE_WinNT4; break;
533 case VGDRVNTVER_WIN2K: enmOsType = VBOXOSTYPE_Win2k; break;
534 case VGDRVNTVER_WINXP: enmOsType = VBOXOSTYPE_WinXP; break;
535 case VGDRVNTVER_WIN2K3: enmOsType = VBOXOSTYPE_Win2k3; break;
536 case VGDRVNTVER_WINVISTA: enmOsType = VBOXOSTYPE_WinVista; break;
537 case VGDRVNTVER_WIN7: enmOsType = VBOXOSTYPE_Win7; break;
538 case VGDRVNTVER_WIN8: enmOsType = VBOXOSTYPE_Win8; break;
539 case VGDRVNTVER_WIN81: enmOsType = VBOXOSTYPE_Win81; break;
540 case VGDRVNTVER_WIN10: enmOsType = VBOXOSTYPE_Win10; break;
541
542 default:
543 /* We don't know, therefore NT family. */
544 enmOsType = VBOXOSTYPE_WinNT;
545 break;
546 }
547#if ARCH_BITS == 64
548 enmOsType = (VBOXOSTYPE)((int)enmOsType | VBOXOSTYPE_x64);
549#endif
550 return enmOsType;
551}
552
553
554/**
555 * Does the fundamental device extension initialization.
556 *
557 * @returns NT status.
558 * @param pDevExt The device extension.
559 * @param pDevObj The device object.
560 */
561static NTSTATUS vgdrvNtInitDevExtFundament(PVBOXGUESTDEVEXTWIN pDevExt, PDEVICE_OBJECT pDevObj)
562{
563 RT_ZERO(*pDevExt);
564
565 KeInitializeSpinLock(&pDevExt->MouseEventAccessSpinLock);
566 pDevExt->pDeviceObject = pDevObj;
567 pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_STOPPED;
568 pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
569
570 int rc = RTCritSectRwInit(&pDevExt->SessionCreateCritSect);
571 if (RT_SUCCESS(rc))
572 {
573 rc = VGDrvCommonInitDevExtFundament(&pDevExt->Core);
574 if (RT_SUCCESS(rc))
575 {
576 LogFlow(("vgdrvNtInitDevExtFundament: returning success\n"));
577 return STATUS_SUCCESS;
578 }
579
580 RTCritSectRwDelete(&pDevExt->SessionCreateCritSect);
581 }
582 Log(("vgdrvNtInitDevExtFundament: failed: rc=%Rrc\n", rc));
583 return STATUS_UNSUCCESSFUL;
584}
585
586
587/**
588 * Counter part to vgdrvNtInitDevExtFundament.
589 *
590 * @param pDevExt The device extension.
591 */
592static void vgdrvNtDeleteDevExtFundament(PVBOXGUESTDEVEXTWIN pDevExt)
593{
594 LogFlow(("vgdrvNtDeleteDevExtFundament:\n"));
595 VGDrvCommonDeleteDevExtFundament(&pDevExt->Core);
596 RTCritSectRwDelete(&pDevExt->SessionCreateCritSect);
597}
598
599
600#ifdef LOG_ENABLED
601/**
602 * Debug helper to dump a device resource list.
603 *
604 * @param pResourceList list of device resources.
605 */
606static void vgdrvNtShowDeviceResources(PCM_RESOURCE_LIST pRsrcList)
607{
608 for (uint32_t iList = 0; iList < pRsrcList->Count; iList++)
609 {
610 PCM_FULL_RESOURCE_DESCRIPTOR pList = &pRsrcList->List[iList];
611 LogFunc(("List #%u: InterfaceType=%#x BusNumber=%#x ListCount=%u ListRev=%#x ListVer=%#x\n",
612 iList, pList->InterfaceType, pList->BusNumber, pList->PartialResourceList.Count,
613 pList->PartialResourceList.Revision, pList->PartialResourceList.Version ));
614
615 PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pList->PartialResourceList.PartialDescriptors;
616 for (ULONG i = 0; i < pList->PartialResourceList.Count; ++i, ++pResource)
617 {
618 ULONG uType = pResource->Type;
619 static char const * const s_apszName[] =
620 {
621 "CmResourceTypeNull",
622 "CmResourceTypePort",
623 "CmResourceTypeInterrupt",
624 "CmResourceTypeMemory",
625 "CmResourceTypeDma",
626 "CmResourceTypeDeviceSpecific",
627 "CmResourceTypeuBusNumber",
628 "CmResourceTypeDevicePrivate",
629 "CmResourceTypeAssignedResource",
630 "CmResourceTypeSubAllocateFrom",
631 };
632
633 if (uType < RT_ELEMENTS(s_apszName))
634 LogFunc((" %.30s Flags=%#x Share=%#x", s_apszName[uType], pResource->Flags, pResource->ShareDisposition));
635 else
636 LogFunc((" Type=%#x Flags=%#x Share=%#x", uType, pResource->Flags, pResource->ShareDisposition));
637 switch (uType)
638 {
639 case CmResourceTypePort:
640 case CmResourceTypeMemory:
641 Log((" Start %#RX64, length=%#x\n", pResource->u.Port.Start.QuadPart, pResource->u.Port.Length));
642 break;
643
644 case CmResourceTypeInterrupt:
645 Log((" Level=%X, vector=%#x, affinity=%#x\n",
646 pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector, pResource->u.Interrupt.Affinity));
647 break;
648
649 case CmResourceTypeDma:
650 Log((" Channel %d, Port %#x\n", pResource->u.Dma.Channel, pResource->u.Dma.Port));
651 break;
652
653 default:
654 Log(("\n"));
655 break;
656 }
657 }
658 }
659}
660#endif /* LOG_ENABLED */
661
662
663/**
664 * Helper to scan the PCI resource list and remember stuff.
665 *
666 * @param pDevExt The device extension.
667 * @param pResList Resource list
668 * @param fTranslated Whether the addresses are translated or not.
669 */
670static NTSTATUS vgdrvNtScanPCIResourceList(PVBOXGUESTDEVEXTWIN pDevExt, PCM_RESOURCE_LIST pResList, bool fTranslated)
671{
672 LogFlowFunc(("Found %d resources\n", pResList->List->PartialResourceList.Count));
673 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
674 bool fGotIrq = false;
675 bool fGotMmio = false;
676 bool fGotIoPorts = false;
677 NTSTATUS rc = STATUS_SUCCESS;
678 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
679 {
680 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
681 switch (pPartialData->Type)
682 {
683 case CmResourceTypePort:
684 LogFlowFunc(("I/O range: Base=%#RX64, length=%08x\n",
685 pPartialData->u.Port.Start.QuadPart, pPartialData->u.Port.Length));
686 /* Save the first I/O port base. */
687 if (!fGotIoPorts)
688 {
689 pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
690 fGotIoPorts = true;
691 LogFunc(("I/O range for VMMDev found! Base=%#RX64, length=%08x\n",
692 pPartialData->u.Port.Start.QuadPart, pPartialData->u.Port.Length));
693 }
694 else
695 LogRelFunc(("More than one I/O port range?!?\n"));
696 break;
697
698 case CmResourceTypeInterrupt:
699 LogFunc(("Interrupt: Level=%x, vector=%x, mode=%x\n",
700 pPartialData->u.Interrupt.Level, pPartialData->u.Interrupt.Vector, pPartialData->Flags));
701 if (!fGotIrq)
702 {
703 /* Save information. */
704 pDevExt->uInterruptLevel = pPartialData->u.Interrupt.Level;
705 pDevExt->uInterruptVector = pPartialData->u.Interrupt.Vector;
706 pDevExt->fInterruptAffinity = pPartialData->u.Interrupt.Affinity;
707
708 /* Check interrupt mode. */
709 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
710 pDevExt->enmInterruptMode = Latched;
711 else
712 pDevExt->enmInterruptMode = LevelSensitive;
713 fGotIrq = true;
714 LogFunc(("Interrupt for VMMDev found! Vector=%#x Level=%#x Affinity=%zx Mode=%d\n", pDevExt->uInterruptVector,
715 pDevExt->uInterruptLevel, pDevExt->fInterruptAffinity, pDevExt->enmInterruptMode));
716 }
717 else
718 LogFunc(("More than one IRQ resource!\n"));
719 break;
720
721 case CmResourceTypeMemory:
722 LogFlowFunc(("Memory range: Base=%#RX64, length=%08x\n",
723 pPartialData->u.Memory.Start.QuadPart, pPartialData->u.Memory.Length));
724 /* We only care about the first read/write memory range. */
725 if ( !fGotMmio
726 && (pPartialData->Flags & CM_RESOURCE_MEMORY_WRITEABILITY_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
727 {
728 /* Save physical MMIO base + length for VMMDev. */
729 pDevExt->uVmmDevMemoryPhysAddr = pPartialData->u.Memory.Start;
730 pDevExt->cbVmmDevMemory = (ULONG)pPartialData->u.Memory.Length;
731
732 if (!fTranslated)
733 {
734 /* Technically we need to make the HAL translate the address. since we
735 didn't used to do this and it probably just returns the input address,
736 we allow ourselves to ignore failures. */
737 ULONG uAddressSpace = 0;
738 PHYSICAL_ADDRESS PhysAddr = pPartialData->u.Memory.Start;
739 if (HalTranslateBusAddress(pResList->List->InterfaceType, pResList->List->BusNumber, PhysAddr,
740 &uAddressSpace, &PhysAddr))
741 {
742 Log(("HalTranslateBusAddress(%#RX64) -> %RX64, type %#x\n",
743 pPartialData->u.Memory.Start.QuadPart, PhysAddr.QuadPart, uAddressSpace));
744 if (pPartialData->u.Memory.Start.QuadPart != PhysAddr.QuadPart)
745 pDevExt->uVmmDevMemoryPhysAddr = PhysAddr;
746 }
747 else
748 Log(("HalTranslateBusAddress(%#RX64) -> failed!\n", pPartialData->u.Memory.Start.QuadPart));
749 }
750
751 fGotMmio = true;
752 LogFunc(("Found memory range for VMMDev! Base = %#RX64, Length = %08x\n",
753 pPartialData->u.Memory.Start.QuadPart, pPartialData->u.Memory.Length));
754 }
755 else
756 LogFunc(("Ignoring memory: Flags=%08x Base=%#RX64\n",
757 pPartialData->Flags, pPartialData->u.Memory.Start.QuadPart));
758 break;
759
760 default:
761 LogFunc(("Unhandled resource found, type=%d\n", pPartialData->Type));
762 break;
763 }
764 }
765 return rc;
766}
767
768
769#ifdef TARGET_NT4
770
771/**
772 * Scans the PCI resources on NT 3.1.
773 *
774 * @returns STATUS_SUCCESS or STATUS_DEVICE_CONFIGURATION_ERROR.
775 * @param pDevExt The device extension.
776 * @param uBus The bus number.
777 * @param uSlot The PCI slot to scan.
778 */
779static NTSTATUS vgdrvNt31ScanSlotResources(PVBOXGUESTDEVEXTWIN pDevExt, ULONG uBus, ULONG uSlot)
780{
781 /*
782 * Disable memory mappings so we can determin the BAR lengths
783 * without upsetting other mappings.
784 */
785 uint16_t fCmd = 0;
786 g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmd, VBOX_PCI_COMMAND, sizeof(fCmd));
787 if (fCmd & VBOX_PCI_COMMAND_MEMORY)
788 {
789 uint16_t fCmdTmp = fCmd & ~VBOX_PCI_COMMAND_MEMORY;
790 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmdTmp, VBOX_PCI_COMMAND, sizeof(fCmdTmp));
791 }
792
793 /*
794 * Scan the address resources first.
795 */
796 uint32_t aBars[6] = { UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX };
797 g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &aBars, VBOX_PCI_BASE_ADDRESS_0, sizeof(aBars));
798
799 bool fGotMmio = false;
800 bool fGotIoPorts = false;
801 for (uint32_t i = 0; i < RT_ELEMENTS(aBars); i++)
802 {
803 uint32_t uBar = aBars[i];
804 if (uBar == UINT32_MAX)
805 continue;
806 if ((uBar & 1) == PCI_ADDRESS_SPACE_IO)
807 {
808 uint32_t uAddr = uBar & UINT32_C(0xfffffffc);
809 if (!uAddr)
810 continue;
811 if (!fGotIoPorts)
812 {
813 pDevExt->Core.IOPortBase = (uint16_t)uAddr & UINT16_C(0xfffc);
814 fGotIoPorts = true;
815 LogFunc(("I/O range for VMMDev found in BAR%u! %#x\n", i, pDevExt->Core.IOPortBase));
816 }
817 else
818 LogRelFunc(("More than one I/O port range?!? BAR%u=%#x\n", i, uBar));
819 }
820 else
821 {
822 uint32_t uAddr = uBar & UINT32_C(0xfffffff0);
823 if (!uAddr)
824 continue;
825
826 if (!fGotMmio)
827 {
828 /* Figure the length by trying to set all address bits and seeing
829 how many we're allowed to set. */
830 uint32_t iBit = 4;
831 while (!(uAddr & RT_BIT_32(iBit)))
832 iBit++;
833
834 uint32_t const offPciBar = VBOX_PCI_BASE_ADDRESS_0 + i * 4;
835 uint32_t uTmpBar = uBar | ((RT_BIT_32(iBit) - 1) & UINT32_C(0xfffffff0));
836 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uTmpBar, offPciBar, sizeof(uTmpBar));
837 uTmpBar = uBar;
838 g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uTmpBar, offPciBar, sizeof(uTmpBar));
839 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &uBar, offPciBar, sizeof(uBar));
840
841 while (iBit > 4 && (uTmpBar & RT_BIT_32(iBit - 1)))
842 iBit--;
843
844 /* got it */
845 pDevExt->cbVmmDevMemory = RT_BIT_32(iBit);
846 pDevExt->uVmmDevMemoryPhysAddr.QuadPart = uAddr;
847 fGotMmio = true;
848 LogFunc(("Found memory range for VMMDev in BAR%u! %#RX64 LB %#x (raw %#x)\n",
849 i, pDevExt->uVmmDevMemoryPhysAddr.QuadPart, pDevExt->cbVmmDevMemory, uBar));
850 }
851 else
852 LogFunc(("Ignoring memory: BAR%u=%#x\n", i, uBar));
853 }
854 }
855
856 /*
857 * Get the IRQ
858 */
859 struct
860 {
861 uint8_t bInterruptLine;
862 uint8_t bInterruptPin;
863 } Buf = { 0, 0 };
864 g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, uSlot, &Buf, VBOX_PCI_INTERRUPT_LINE, sizeof(Buf));
865 if (Buf.bInterruptPin != 0)
866 {
867 pDevExt->uInterruptVector = Buf.bInterruptLine;
868 pDevExt->uInterruptLevel = Buf.bInterruptLine;
869 pDevExt->enmInterruptMode = LevelSensitive;
870 pDevExt->fInterruptAffinity = RT_BIT_32(RTMpGetCount()) - 1;
871 LogFunc(("Interrupt for VMMDev found! Vector=%#x Level=%#x Affinity=%zx Mode=%d\n",
872 pDevExt->uInterruptVector, pDevExt->uInterruptLevel, pDevExt->fInterruptAffinity, pDevExt->enmInterruptMode));
873 }
874
875 /*
876 * Got what we need?
877 */
878 if (fGotIoPorts && (!fGotMmio || Buf.bInterruptPin != 0))
879 {
880 /*
881 * Enable both MMIO, I/O space and busmastering so we can use the device.
882 */
883 uint16_t fCmdNew = fCmd | VBOX_PCI_COMMAND_IO | VBOX_PCI_COMMAND_MEMORY | VBOX_PCI_COMMAND_MASTER;
884 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmdNew, VBOX_PCI_COMMAND, sizeof(fCmdNew));
885
886 return STATUS_SUCCESS;
887 }
888
889 /* No. Complain, restore device command value and return failure. */
890 if (!fGotIoPorts)
891 LogRel(("VBoxGuest: Did not find I/O port range: %#x %#x %#x %#x %#x %#x\n",
892 aBars[0], aBars[1], aBars[2], aBars[3], aBars[4], aBars[5]));
893 if (!fGotMmio || Buf.bInterruptPin != 0)
894 LogRel(("VBoxGuest: Got MMIO but no interrupts!\n"));
895
896 g_pfnHalSetBusDataByOffset(PCIConfiguration, uBus, uSlot, &fCmd, VBOX_PCI_COMMAND, sizeof(fCmd));
897 return STATUS_DEVICE_CONFIGURATION_ERROR;
898}
899
900#endif /* TARGET_NT4 */
901
902/**
903 * Unmaps the VMMDev I/O range from kernel space.
904 *
905 * @param pDevExt The device extension.
906 */
907static void vgdrvNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
908{
909 LogFlowFunc(("pVMMDevMemory = %#x\n", pDevExt->Core.pVMMDevMemory));
910 if (pDevExt->Core.pVMMDevMemory)
911 {
912 MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->cbVmmDevMemory);
913 pDevExt->Core.pVMMDevMemory = NULL;
914 }
915
916 pDevExt->uVmmDevMemoryPhysAddr.QuadPart = 0;
917 pDevExt->cbVmmDevMemory = 0;
918}
919
920
921/**
922 * Maps the I/O space from VMMDev to virtual kernel address space.
923 *
924 * @return NTSTATUS
925 *
926 * @param pDevExt The device extension.
927 * @param PhysAddr Physical address to map.
928 * @param cbToMap Number of bytes to map.
929 * @param ppvMMIOBase Pointer of mapped I/O base.
930 * @param pcbMMIO Length of mapped I/O base.
931 */
932static NTSTATUS vgdrvNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
933 void **ppvMMIOBase, uint32_t *pcbMMIO)
934{
935 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
936 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
937 /* pcbMMIO is optional. */
938
939 NTSTATUS rc = STATUS_SUCCESS;
940 if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
941 {
942 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
943 LogFlowFunc(("pVMMDevMemory = %#x\n", pVMMDevMemory));
944 if (pVMMDevMemory)
945 {
946 LogFunc(("VMMDevMemory: Version = %#x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
947
948 /* Check version of the structure; do we have the right memory version? */
949 if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
950 {
951 /* Save results. */
952 *ppvMMIOBase = pVMMDevMemory;
953 if (pcbMMIO) /* Optional. */
954 *pcbMMIO = pVMMDevMemory->u32Size;
955
956 LogFlowFunc(("VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase));
957 }
958 else
959 {
960 /* Not our version, refuse operation and unmap the memory. */
961 LogFunc(("Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
962
963 vgdrvNtUnmapVMMDevMemory(pDevExt);
964 rc = STATUS_UNSUCCESSFUL;
965 }
966 }
967 else
968 rc = STATUS_UNSUCCESSFUL;
969 }
970 return rc;
971}
972
973
974/**
975 * Sets up the device and its resources.
976 *
977 * @param pDevExt Our device extension data.
978 * @param pDevObj The device object.
979 * @param pIrp The request packet if NT5+, NULL for NT4 and earlier.
980 * @param pDrvObj The driver object for NT4, NULL for NT5+.
981 * @param pRegPath The registry path for NT4, NULL for NT5+.
982 */
983static NTSTATUS vgdrvNtSetupDevice(PVBOXGUESTDEVEXTWIN pDevExt, PDEVICE_OBJECT pDevObj,
984 PIRP pIrp, PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
985{
986 LogFlowFunc(("ENTER: pDevExt=%p pDevObj=%p pIrq=%p pDrvObj=%p pRegPath=%p\n", pDevExt, pDevObj, pIrp, pDrvObj, pRegPath));
987
988 NTSTATUS rcNt;
989 if (!pIrp)
990 {
991#ifdef TARGET_NT4
992 /*
993 * NT4, NT3.x: Let's have a look at what our PCI adapter offers.
994 */
995 LogFlowFunc(("Starting to scan PCI resources of VBoxGuest ...\n"));
996
997 /* Assign the PCI resources. */
998 UNICODE_STRING ClassName;
999 RtlInitUnicodeString(&ClassName, L"VBoxGuestAdapter");
1000 PCM_RESOURCE_LIST pResourceList = NULL;
1001 if (g_pfnHalAssignSlotResources)
1002 {
1003 rcNt = g_pfnHalAssignSlotResources(pRegPath, &ClassName, pDrvObj, pDevObj, PCIBus, pDevExt->uBus, pDevExt->uSlot,
1004 &pResourceList);
1005# ifdef LOG_ENABLED
1006 if (pResourceList)
1007 vgdrvNtShowDeviceResources(pResourceList);
1008# endif
1009 if (NT_SUCCESS(rcNt))
1010 {
1011 rcNt = vgdrvNtScanPCIResourceList(pDevExt, pResourceList, false /*fTranslated*/);
1012 ExFreePool(pResourceList);
1013 }
1014 }
1015 else
1016 rcNt = vgdrvNt31ScanSlotResources(pDevExt, pDevExt->uBus, pDevExt->uSlot);
1017
1018# else /* !TARGET_NT4 */
1019 AssertFailed();
1020 RT_NOREF(pDevObj, pDrvObj, pRegPath);
1021 rcNt = STATUS_INTERNAL_ERROR;
1022# endif /* !TARGET_NT4 */
1023 }
1024 else
1025 {
1026 /*
1027 * NT5+: Scan the PCI resource list from the IRP.
1028 */
1029 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1030# ifdef LOG_ENABLED
1031 vgdrvNtShowDeviceResources(pStack->Parameters.StartDevice.AllocatedResourcesTranslated);
1032# endif
1033 rcNt = vgdrvNtScanPCIResourceList(pDevExt, pStack->Parameters.StartDevice.AllocatedResourcesTranslated,
1034 true /*fTranslated*/);
1035 }
1036 if (NT_SUCCESS(rcNt))
1037 {
1038 /*
1039 * Map physical address of VMMDev memory into MMIO region
1040 * and init the common device extension bits.
1041 */
1042 void *pvMMIOBase = NULL;
1043 uint32_t cbMMIO = 0;
1044 rcNt = vgdrvNtMapVMMDevMemory(pDevExt,
1045 pDevExt->uVmmDevMemoryPhysAddr,
1046 pDevExt->cbVmmDevMemory,
1047 &pvMMIOBase,
1048 &cbMMIO);
1049 if (NT_SUCCESS(rcNt))
1050 {
1051 pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
1052
1053 LogFunc(("pvMMIOBase=0x%p, pDevExt=0x%p, pDevExt->Core.pVMMDevMemory=0x%p\n",
1054 pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL));
1055
1056 int vrc = VGDrvCommonInitDevExtResources(&pDevExt->Core,
1057 pDevExt->Core.IOPortBase,
1058 pvMMIOBase, cbMMIO,
1059 vgdrvNtVersionToOSType(g_enmVGDrvNtVer),
1060 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
1061 if (RT_SUCCESS(vrc))
1062 {
1063
1064 vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
1065 sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
1066 if (RT_SUCCESS(vrc))
1067 {
1068 /*
1069 * Register DPC and ISR.
1070 */
1071 LogFlowFunc(("Initializing DPC/ISR (pDevObj=%p)...\n", pDevExt->pDeviceObject));
1072 IoInitializeDpcRequest(pDevExt->pDeviceObject, vgdrvNtDpcHandler);
1073
1074 ULONG uInterruptVector = pDevExt->uInterruptVector;
1075 KIRQL uHandlerIrql = (KIRQL)pDevExt->uInterruptLevel;
1076#ifdef TARGET_NT4
1077 if (!pIrp)
1078 {
1079 /* NT4: Get an interrupt vector. Only proceed if the device provides an interrupt. */
1080 if ( uInterruptVector
1081 || pDevExt->uInterruptLevel)
1082 {
1083 LogFlowFunc(("Getting interrupt vector (HAL): Bus=%u, IRQL=%u, Vector=%u\n",
1084 pDevExt->uBus, pDevExt->uInterruptLevel, pDevExt->uInterruptVector));
1085 uInterruptVector = HalGetInterruptVector(g_enmVGDrvNtVer == VGDRVNTVER_WINNT310 ? Isa : PCIBus,
1086 pDevExt->uBus,
1087 pDevExt->uInterruptLevel,
1088 pDevExt->uInterruptVector,
1089 &uHandlerIrql,
1090 &pDevExt->fInterruptAffinity);
1091 LogFlowFunc(("HalGetInterruptVector returns vector=%u\n", uInterruptVector));
1092 }
1093 else
1094 LogFunc(("Device does not provide an interrupt!\n"));
1095 }
1096#endif
1097 if (uInterruptVector)
1098 {
1099 LogFlowFunc(("Connecting interrupt (IntVector=%#u), uHandlerIrql=%u) ...\n",
1100 uInterruptVector, uHandlerIrql));
1101
1102 rcNt = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
1103 vgdrvNtIsrHandler, /* Our ISR handler. */
1104 pDevExt, /* Device context. */
1105 NULL, /* Optional spinlock. */
1106 uInterruptVector, /* Interrupt vector. */
1107 uHandlerIrql, /* Irql. */
1108 uHandlerIrql, /* SynchronizeIrql. */
1109 pDevExt->enmInterruptMode, /* LevelSensitive or Latched. */
1110 TRUE, /* Shareable interrupt. */
1111 pDevExt->fInterruptAffinity, /* CPU affinity. */
1112 FALSE); /* Don't save FPU stack. */
1113 if (NT_ERROR(rcNt))
1114 LogFunc(("Could not connect interrupt: rcNt=%#x!\n", rcNt));
1115 }
1116 else
1117 LogFunc(("No interrupt vector found!\n"));
1118 if (NT_SUCCESS(rcNt))
1119 {
1120 /*
1121 * Once we've read configuration from register and host, we're finally read.
1122 */
1123 /** @todo clean up guest ring-3 logging, keeping it separate from the kernel to avoid sharing limits with it. */
1124 pDevExt->Core.fLoggingEnabled = true;
1125 vgdrvNtReadConfiguration(pDevExt);
1126
1127 /* Ready to rumble! */
1128 LogRelFunc(("Device is ready!\n"));
1129 pDevExt->enmDevState = VGDRVNTDEVSTATE_OPERATIONAL;
1130 pDevExt->enmPrevDevState = VGDRVNTDEVSTATE_OPERATIONAL;
1131 return STATUS_SUCCESS;
1132 }
1133
1134 pDevExt->pInterruptObject = NULL;
1135
1136 VbglR0GRFree(&pDevExt->pPowerStateRequest->header);
1137 pDevExt->pPowerStateRequest = NULL;
1138 }
1139 else
1140 {
1141 LogFunc(("Alloc for pPowerStateRequest failed, vrc=%Rrc\n", vrc));
1142 rcNt = STATUS_UNSUCCESSFUL;
1143 }
1144
1145 VGDrvCommonDeleteDevExtResources(&pDevExt->Core);
1146 }
1147 else
1148 {
1149 LogFunc(("Could not init device extension resources: vrc=%Rrc\n", vrc));
1150 rcNt = STATUS_DEVICE_CONFIGURATION_ERROR;
1151 }
1152 vgdrvNtUnmapVMMDevMemory(pDevExt);
1153 }
1154 else
1155 LogFunc(("Could not map physical address of VMMDev, rcNt=%#x\n", rcNt));
1156 }
1157
1158 LogFunc(("Returned with rcNt=%#x\n", rcNt));
1159 return rcNt;
1160}
1161
1162
1163
1164
1165#ifdef TARGET_NT4
1166# define PCI_CFG_ADDR 0xcf8
1167# define PCI_CFG_DATA 0xcfc
1168
1169/**
1170 * NT 3.1 doesn't do PCI nor HalSetBusDataByOffset, this is our fallback.
1171 */
1172static ULONG NTAPI vgdrvNt31SetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
1173 void *pvData, ULONG offData, ULONG cbData)
1174{
1175 /*
1176 * Validate input a little bit.
1177 */
1178 RT_NOREF(enmBusDataType);
1179 Assert(idxBus <= 255);
1180 Assert(uSlot <= 255);
1181 Assert(offData <= 255);
1182 Assert(cbData > 0);
1183
1184 PCI_SLOT_NUMBER PciSlot;
1185 PciSlot.u.AsULONG = uSlot;
1186 uint32_t const idxAddrTop = UINT32_C(0x80000000)
1187 | (idxBus << 16)
1188 | (PciSlot.u.bits.DeviceNumber << 11)
1189 | (PciSlot.u.bits.FunctionNumber << 8);
1190
1191 /*
1192 * Write the given bytes.
1193 */
1194 uint8_t const *pbData = (uint8_t const *)pvData;
1195 uint32_t off = offData;
1196 uint32_t cbRet = 0;
1197
1198 /* Unaligned start. */
1199 if (off & 3)
1200 {
1201 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3));
1202 switch (off & 3)
1203 {
1204 case 1:
1205 ASMOutU8(PCI_CFG_DATA + 1, pbData[cbRet++]);
1206 if (cbRet >= cbData)
1207 break;
1208 RT_FALL_THRU();
1209 case 2:
1210 ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet++]);
1211 if (cbRet >= cbData)
1212 break;
1213 RT_FALL_THRU();
1214 case 3:
1215 ASMOutU8(PCI_CFG_DATA + 3, pbData[cbRet++]);
1216 break;
1217 }
1218 off = (off | 3) + 1;
1219 }
1220
1221 /* Bulk. */
1222 while (off < 256 && cbRet < cbData)
1223 {
1224 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off);
1225 switch (cbData - cbRet)
1226 {
1227 case 1:
1228 ASMOutU8(PCI_CFG_DATA, pbData[cbRet]);
1229 cbRet += 1;
1230 break;
1231 case 2:
1232 ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1]));
1233 cbRet += 2;
1234 break;
1235 case 3:
1236 ASMOutU16(PCI_CFG_DATA, RT_MAKE_U16(pbData[cbRet], pbData[cbRet + 1]));
1237 ASMOutU8(PCI_CFG_DATA + 2, pbData[cbRet + 2]);
1238 cbRet += 3;
1239 break;
1240 default:
1241 ASMOutU32(PCI_CFG_DATA, RT_MAKE_U32_FROM_U8(pbData[cbRet], pbData[cbRet + 1],
1242 pbData[cbRet + 2], pbData[cbRet + 3]));
1243 cbRet += 4;
1244 break;
1245 }
1246 off += 4;
1247 }
1248
1249 return cbRet;
1250}
1251
1252
1253/**
1254 * NT 3.1 doesn't do PCI nor HalGetBusDataByOffset, this is our fallback.
1255 */
1256static ULONG NTAPI vgdrvNt31GetBusDataByOffset(BUS_DATA_TYPE enmBusDataType, ULONG idxBus, ULONG uSlot,
1257 void *pvData, ULONG offData, ULONG cbData)
1258{
1259 /*
1260 * Validate input a little bit.
1261 */
1262 RT_NOREF(enmBusDataType);
1263 Assert(idxBus <= 255);
1264 Assert(uSlot <= 255);
1265 Assert(offData <= 255);
1266 Assert(cbData > 0);
1267
1268 PCI_SLOT_NUMBER PciSlot;
1269 PciSlot.u.AsULONG = uSlot;
1270 uint32_t const idxAddrTop = UINT32_C(0x80000000)
1271 | (idxBus << 16)
1272 | (PciSlot.u.bits.DeviceNumber << 11)
1273 | (PciSlot.u.bits.FunctionNumber << 8);
1274
1275 /*
1276 * Read the header type.
1277 */
1278 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (VBOX_PCI_HEADER_TYPE & ~3));
1279 uint8_t bHdrType = ASMInU8(PCI_CFG_DATA + (VBOX_PCI_HEADER_TYPE & 3));
1280 if (bHdrType == 0xff)
1281 return idxBus < 8 ? 2 : 0; /* No device here */
1282 if ( offData == VBOX_PCI_HEADER_TYPE
1283 && cbData == 1)
1284 {
1285 *(uint8_t *)pvData = bHdrType;
1286 /*Log("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %02x\n", idxAddrTop, offData, bHdrType);*/
1287 return 1;
1288 }
1289
1290 /*
1291 * Read the requested bytes.
1292 */
1293 uint8_t *pbData = (uint8_t *)pvData;
1294 uint32_t off = offData;
1295 uint32_t cbRet = 0;
1296
1297 /* Unaligned start. */
1298 if (off & 3)
1299 {
1300 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | (off & ~3));
1301 uint32_t uValue = ASMInU32(PCI_CFG_DATA);
1302 switch (off & 3)
1303 {
1304 case 1:
1305 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1306 if (cbRet >= cbData)
1307 break;
1308 RT_FALL_THRU();
1309 case 2:
1310 pbData[cbRet++] = (uint8_t)(uValue >> 16);
1311 if (cbRet >= cbData)
1312 break;
1313 RT_FALL_THRU();
1314 case 3:
1315 pbData[cbRet++] = (uint8_t)(uValue >> 24);
1316 break;
1317 }
1318 off = (off | 3) + 1;
1319 }
1320
1321 /* Bulk. */
1322 while (off < 256 && cbRet < cbData)
1323 {
1324 ASMOutU32(PCI_CFG_ADDR, idxAddrTop | off);
1325 uint32_t uValue = ASMInU32(PCI_CFG_DATA);
1326 switch (cbData - cbRet)
1327 {
1328 case 1:
1329 pbData[cbRet++] = (uint8_t)uValue;
1330 break;
1331 case 2:
1332 pbData[cbRet++] = (uint8_t)uValue;
1333 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1334 break;
1335 case 3:
1336 pbData[cbRet++] = (uint8_t)uValue;
1337 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1338 pbData[cbRet++] = (uint8_t)(uValue >> 16);
1339 break;
1340 default:
1341 pbData[cbRet++] = (uint8_t)uValue;
1342 pbData[cbRet++] = (uint8_t)(uValue >> 8);
1343 pbData[cbRet++] = (uint8_t)(uValue >> 16);
1344 pbData[cbRet++] = (uint8_t)(uValue >> 24);
1345 break;
1346 }
1347 off += 4;
1348 }
1349
1350 Log(("vgdrvNt31GetBusDataByOffset: PCI %#x/%#x -> %.*Rhxs\n", idxAddrTop, offData, cbRet, pvData));
1351 return cbRet;
1352}
1353
1354
1355/**
1356 * Helper function to handle the PCI device lookup.
1357 *
1358 * @returns NT status code.
1359 *
1360 * @param puBus Where to return the bus number on success.
1361 * @param pSlot Where to return the slot number on success.
1362 */
1363static NTSTATUS vgdrvNt4FindPciDevice(PULONG puBus, PPCI_SLOT_NUMBER pSlot)
1364{
1365 Log(("vgdrvNt4FindPciDevice\n"));
1366
1367 PCI_SLOT_NUMBER Slot;
1368 Slot.u.AsULONG = 0;
1369
1370 /* Scan each bus. */
1371 for (ULONG uBus = 0; uBus < PCI_MAX_BUSES; uBus++)
1372 {
1373 /* Scan each device. */
1374 for (ULONG idxDevice = 0; idxDevice < PCI_MAX_DEVICES; idxDevice++)
1375 {
1376 Slot.u.bits.DeviceNumber = idxDevice;
1377 Slot.u.bits.FunctionNumber = 0;
1378
1379 /* Check the device header. */
1380 uint8_t bHeaderType = 0xff;
1381 ULONG cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG,
1382 &bHeaderType, VBOX_PCI_HEADER_TYPE, sizeof(bHeaderType));
1383 if (cbRet == 0)
1384 break;
1385 if (cbRet == 2 || bHeaderType == 0xff)
1386 continue;
1387
1388 /* Scan functions. */
1389 uint32_t const cFunctionStep = bHeaderType & 0x80 ? 1 : 8;
1390 Log(("vgdrvNt4FindPciDevice: %#x:%#x cFunctionStep=%d bHeaderType=%#x\n", uBus, idxDevice, cFunctionStep, bHeaderType));
1391 for (ULONG idxFunction = 0; idxFunction < PCI_MAX_FUNCTION; idxFunction += cFunctionStep)
1392 {
1393 Slot.u.bits.FunctionNumber = idxFunction;
1394
1395 /* Read the vendor and device IDs of this device and compare with the VMMDev. */
1396 struct
1397 {
1398 uint16_t idVendor;
1399 uint16_t idDevice;
1400 } Buf = { PCI_INVALID_VENDORID, PCI_INVALID_VENDORID };
1401 cbRet = g_pfnHalGetBusDataByOffset(PCIConfiguration, uBus, Slot.u.AsULONG, &Buf, VBOX_PCI_VENDOR_ID, sizeof(Buf));
1402 if ( cbRet == sizeof(Buf)
1403 && Buf.idVendor == VMMDEV_VENDORID
1404 && Buf.idDevice == VMMDEV_DEVICEID)
1405 {
1406 /* Hooray, we've found it! */
1407 Log(("vgdrvNt4FindPciDevice: Device found! Bus=%#x Slot=%#u (dev %#x, fun %#x, rvd %#x)\n",
1408 uBus, Slot.u.AsULONG, Slot.u.bits.DeviceNumber, Slot.u.bits.FunctionNumber, Slot.u.bits.Reserved));
1409
1410 *puBus = uBus;
1411 *pSlot = Slot;
1412 return STATUS_SUCCESS;
1413 }
1414 }
1415 }
1416 }
1417
1418 return STATUS_DEVICE_DOES_NOT_EXIST;
1419}
1420
1421
1422/**
1423 * Legacy helper function to create the device object.
1424 *
1425 * @returns NT status code.
1426 *
1427 * @param pDrvObj The driver object.
1428 * @param pRegPath The driver registry path.
1429 */
1430static NTSTATUS vgdrvNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
1431{
1432 Log(("vgdrvNt4CreateDevice: pDrvObj=%p, pRegPath=%p\n", pDrvObj, pRegPath));
1433
1434 /*
1435 * Find our virtual PCI device
1436 */
1437 ULONG uBus;
1438 PCI_SLOT_NUMBER uSlot;
1439 NTSTATUS rc = vgdrvNt4FindPciDevice(&uBus, &uSlot);
1440 if (NT_ERROR(rc))
1441 {
1442 Log(("vgdrvNt4CreateDevice: Device not found!\n"));
1443 return rc;
1444 }
1445
1446 /*
1447 * Create device.
1448 */
1449 UNICODE_STRING DevName;
1450 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
1451 PDEVICE_OBJECT pDeviceObject = NULL;
1452 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
1453 if (NT_SUCCESS(rc))
1454 {
1455 Log(("vgdrvNt4CreateDevice: Device created\n"));
1456
1457 UNICODE_STRING DosName;
1458 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1459 rc = IoCreateSymbolicLink(&DosName, &DevName);
1460 if (NT_SUCCESS(rc))
1461 {
1462 Log(("vgdrvNt4CreateDevice: Symlink created\n"));
1463
1464 /*
1465 * Setup the device extension.
1466 */
1467 Log(("vgdrvNt4CreateDevice: Setting up device extension ...\n"));
1468 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
1469 int vrc = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject);
1470 if (RT_SUCCESS(vrc))
1471 {
1472 /* Store bus and slot number we've queried before. */
1473 pDevExt->uBus = uBus;
1474 pDevExt->uSlot = uSlot.u.AsULONG;
1475
1476 Log(("vgdrvNt4CreateDevice: Device extension created\n"));
1477
1478 /* Do the actual VBox init ... */
1479 rc = vgdrvNtSetupDevice(pDevExt, pDeviceObject, NULL /*pIrp*/, pDrvObj, pRegPath);
1480 if (NT_SUCCESS(rc))
1481 {
1482 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x (succcess)\n", rc));
1483 return rc;
1484 }
1485
1486 /* bail out */
1487 vgdrvNtDeleteDevExtFundament(pDevExt);
1488 }
1489 IoDeleteSymbolicLink(&DosName);
1490 }
1491 else
1492 Log(("vgdrvNt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc));
1493 IoDeleteDevice(pDeviceObject);
1494 }
1495 else
1496 Log(("vgdrvNt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc));
1497 Log(("vgdrvNt4CreateDevice: Returning rc = 0x%x\n", rc));
1498 return rc;
1499}
1500
1501#endif /* TARGET_NT4 */
1502
1503/**
1504 * Handle request from the Plug & Play subsystem.
1505 *
1506 * @returns NT status code
1507 * @param pDrvObj Driver object
1508 * @param pDevObj Device object
1509 *
1510 * @remarks Parts of this is duplicated in VBoxGuest-win-legacy.cpp.
1511 */
1512static NTSTATUS NTAPI vgdrvNtNt5PlusAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
1513{
1514 LogFlowFuncEnter();
1515
1516 /*
1517 * Create device.
1518 */
1519 UNICODE_STRING DevName;
1520 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
1521 PDEVICE_OBJECT pDeviceObject = NULL;
1522 NTSTATUS rcNt = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
1523 if (NT_SUCCESS(rcNt))
1524 {
1525 /*
1526 * Create symbolic link (DOS devices).
1527 */
1528 UNICODE_STRING DosName;
1529 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1530 rcNt = IoCreateSymbolicLink(&DosName, &DevName);
1531 if (NT_SUCCESS(rcNt))
1532 {
1533 /*
1534 * Setup the device extension.
1535 */
1536 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
1537 rcNt = vgdrvNtInitDevExtFundament(pDevExt, pDeviceObject);
1538 if (NT_SUCCESS(rcNt))
1539 {
1540 pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
1541 if (pDevExt->pNextLowerDriver != NULL)
1542 {
1543 /* Ensure we are not called at elevated IRQL, even if our code isn't pagable any more. */
1544 pDeviceObject->Flags |= DO_POWER_PAGABLE;
1545
1546 /* Driver is ready now. */
1547 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1548 LogFlowFunc(("Returning with rcNt=%#x (success)\n", rcNt));
1549 return rcNt;
1550 }
1551 LogFunc(("IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
1552 rcNt = STATUS_DEVICE_NOT_CONNECTED;
1553 vgdrvNtDeleteDevExtFundament(pDevExt);
1554 }
1555
1556 IoDeleteSymbolicLink(&DosName);
1557 }
1558 else
1559 LogFunc(("IoCreateSymbolicLink failed with rcNt=%#x!\n", rcNt));
1560 IoDeleteDevice(pDeviceObject);
1561 }
1562 else
1563 LogFunc(("IoCreateDevice failed with rcNt=%#x!\n", rcNt));
1564
1565 LogFunc(("Returning with rcNt=%#x\n", rcNt));
1566 return rcNt;
1567}
1568
1569
1570/**
1571 * Irp completion routine for PnP Irps we send.
1572 *
1573 * @returns NT status code.
1574 * @param pDevObj Device object.
1575 * @param pIrp Request packet.
1576 * @param pEvent Semaphore.
1577 */
1578static NTSTATUS vgdrvNt5PlusPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent)
1579{
1580 RT_NOREF2(pDevObj, pIrp);
1581 KeSetEvent(pEvent, 0, FALSE);
1582 return STATUS_MORE_PROCESSING_REQUIRED;
1583}
1584
1585
1586/**
1587 * Helper to send a PnP IRP and wait until it's done.
1588 *
1589 * @returns NT status code.
1590 * @param pDevObj Device object.
1591 * @param pIrp Request packet.
1592 * @param fStrict When set, returns an error if the IRP gives an error.
1593 */
1594static NTSTATUS vgdrvNt5PlusPnPSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict)
1595{
1596 KEVENT Event;
1597
1598 KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
1599
1600 IoCopyCurrentIrpStackLocationToNext(pIrp);
1601 IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)vgdrvNt5PlusPnpIrpComplete, &Event, TRUE, TRUE, TRUE);
1602
1603 NTSTATUS rcNt = IoCallDriver(pDevObj, pIrp);
1604 if (rcNt == STATUS_PENDING)
1605 {
1606 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
1607 rcNt = pIrp->IoStatus.Status;
1608 }
1609
1610 if ( !fStrict
1611 && (rcNt == STATUS_NOT_SUPPORTED || rcNt == STATUS_INVALID_DEVICE_REQUEST))
1612 {
1613 rcNt = STATUS_SUCCESS;
1614 }
1615
1616 Log(("vgdrvNt5PlusPnPSendIrpSynchronously: Returning %#x\n", rcNt));
1617 return rcNt;
1618}
1619
1620
1621/**
1622 * Deletes the device hardware resources.
1623 *
1624 * Used during removal, stopping and legacy module unloading.
1625 *
1626 * @param pDevExt The device extension.
1627 */
1628static void vgdrvNtDeleteDeviceResources(PVBOXGUESTDEVEXTWIN pDevExt)
1629{
1630 if (pDevExt->pInterruptObject)
1631 {
1632 IoDisconnectInterrupt(pDevExt->pInterruptObject);
1633 pDevExt->pInterruptObject = NULL;
1634 }
1635 pDevExt->pPowerStateRequest = NULL; /* Will be deleted by the following call. */
1636 if (pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES)
1637 VGDrvCommonDeleteDevExtResources(&pDevExt->Core);
1638 vgdrvNtUnmapVMMDevMemory(pDevExt);
1639}
1640
1641
1642/**
1643 * Deletes the device extension fundament and unlinks the device
1644 *
1645 * Used during removal and legacy module unloading. Must have called
1646 * vgdrvNtDeleteDeviceResources.
1647 *
1648 * @param pDevObj Device object.
1649 * @param pDevExt The device extension.
1650 */
1651static void vgdrvNtDeleteDeviceFundamentAndUnlink(PDEVICE_OBJECT pDevObj, PVBOXGUESTDEVEXTWIN pDevExt)
1652{
1653 /*
1654 * Delete the remainder of the device extension.
1655 */
1656 vgdrvNtDeleteDevExtFundament(pDevExt);
1657
1658 /*
1659 * Delete the DOS symlink to the device and finally the device itself.
1660 */
1661 UNICODE_STRING DosName;
1662 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
1663 IoDeleteSymbolicLink(&DosName);
1664
1665 Log(("vgdrvNtDeleteDeviceFundamentAndUnlink: Deleting device ...\n"));
1666 IoDeleteDevice(pDevObj);
1667}
1668
1669
1670/**
1671 * Checks if the device is idle.
1672 * @returns STATUS_SUCCESS if idle, STATUS_UNSUCCESSFUL if busy.
1673 * @param pDevExt The device extension.
1674 * @param pszQueryNm The query name.
1675 */
1676static NTSTATUS vgdrvNtCheckIdle(PVBOXGUESTDEVEXTWIN pDevExt, const char *pszQueryNm)
1677{
1678 uint32_t cSessions = pDevExt->Core.cSessions;
1679 if (cSessions == 0)
1680 return STATUS_SUCCESS;
1681 LogRel(("vgdrvNtCheckIdle/%s: cSessions=%d\n", pszQueryNm, cSessions));
1682 return STATUS_UNSUCCESSFUL;
1683}
1684
1685
1686/**
1687 * PnP Request handler.
1688 *
1689 * @param pDevObj Device object.
1690 * @param pIrp Request packet.
1691 */
1692static NTSTATUS NTAPI vgdrvNtNt5PlusPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp)
1693{
1694 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
1695 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
1696
1697#ifdef LOG_ENABLED
1698 static char const * const s_apszFnctName[] =
1699 {
1700 "IRP_MN_START_DEVICE",
1701 "IRP_MN_QUERY_REMOVE_DEVICE",
1702 "IRP_MN_REMOVE_DEVICE",
1703 "IRP_MN_CANCEL_REMOVE_DEVICE",
1704 "IRP_MN_STOP_DEVICE",
1705 "IRP_MN_QUERY_STOP_DEVICE",
1706 "IRP_MN_CANCEL_STOP_DEVICE",
1707 "IRP_MN_QUERY_DEVICE_RELATIONS",
1708 "IRP_MN_QUERY_INTERFACE",
1709 "IRP_MN_QUERY_CAPABILITIES",
1710 "IRP_MN_QUERY_RESOURCES",
1711 "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
1712 "IRP_MN_QUERY_DEVICE_TEXT",
1713 "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
1714 "IRP_MN_0xE",
1715 "IRP_MN_READ_CONFIG",
1716 "IRP_MN_WRITE_CONFIG",
1717 "IRP_MN_EJECT",
1718 "IRP_MN_SET_LOCK",
1719 "IRP_MN_QUERY_ID",
1720 "IRP_MN_QUERY_PNP_DEVICE_STATE",
1721 "IRP_MN_QUERY_BUS_INFORMATION",
1722 "IRP_MN_DEVICE_USAGE_NOTIFICATION",
1723 "IRP_MN_SURPRISE_REMOVAL",
1724 };
1725 Log(("vgdrvNtNt5PlusPnP: MinorFunction: %s\n",
1726 pStack->MinorFunction < RT_ELEMENTS(s_apszFnctName) ? s_apszFnctName[pStack->MinorFunction] : "Unknown"));
1727#endif
1728
1729 NTSTATUS rc = STATUS_SUCCESS;
1730 uint8_t bMinorFunction = pStack->MinorFunction;
1731 switch (bMinorFunction)
1732 {
1733 case IRP_MN_START_DEVICE:
1734 {
1735 Log(("vgdrvNtNt5PlusPnP: START_DEVICE\n"));
1736
1737 /* This must be handled first by the lower driver. */
1738 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1739 if ( NT_SUCCESS(rc)
1740 && NT_SUCCESS(pIrp->IoStatus.Status))
1741 {
1742 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n",
1743 pStack->Parameters.StartDevice.AllocatedResources));
1744 if (pStack->Parameters.StartDevice.AllocatedResources)
1745 {
1746 rc = vgdrvNtSetupDevice(pDevExt, pDevObj, pIrp, NULL, NULL);
1747 if (NT_SUCCESS(rc))
1748 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: success\n"));
1749 else
1750 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNtSetupDevice failed: %#x\n", rc));
1751 }
1752 else
1753 {
1754 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: No resources, pDevExt = %p, nextLowerDriver = %p!\n",
1755 pDevExt, pDevExt ? pDevExt->pNextLowerDriver : NULL));
1756 rc = STATUS_UNSUCCESSFUL;
1757 }
1758 }
1759 else
1760 Log(("vgdrvNtNt5PlusPnP: START_DEVICE: vgdrvNt5PlusPnPSendIrpSynchronously failed: %#x + %#x\n",
1761 rc, pIrp->IoStatus.Status));
1762
1763 pIrp->IoStatus.Status = rc;
1764 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1765 return rc;
1766 }
1767
1768
1769 /*
1770 * Sent before removing the device and/or driver.
1771 */
1772 case IRP_MN_QUERY_REMOVE_DEVICE:
1773 {
1774 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE\n"));
1775
1776 RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect);
1777#ifdef VBOX_REBOOT_ON_UNINSTALL
1778 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Device cannot be removed without a reboot.\n"));
1779 rc = STATUS_UNSUCCESSFUL;
1780#endif
1781 if (NT_SUCCESS(rc))
1782 rc = vgdrvNtCheckIdle(pDevExt, "QUERY_REMOVE_DEVICE");
1783 if (NT_SUCCESS(rc))
1784 {
1785 pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGREMOVE;
1786 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1787
1788 /* This IRP passed down to lower driver. */
1789 pIrp->IoStatus.Status = STATUS_SUCCESS;
1790
1791 IoSkipCurrentIrpStackLocation(pIrp);
1792 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1793 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1794
1795 /* We must not do anything the IRP after doing IoSkip & CallDriver
1796 since the driver below us will complete (or already have completed) the IRP.
1797 I.e. just return the status we got from IoCallDriver */
1798 }
1799 else
1800 {
1801 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1802 pIrp->IoStatus.Status = rc;
1803 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1804 }
1805
1806 Log(("vgdrvNtNt5PlusPnP: QUERY_REMOVE_DEVICE: Returning with rc = 0x%x\n", rc));
1807 return rc;
1808 }
1809
1810 /*
1811 * Cancels a pending remove, IRP_MN_QUERY_REMOVE_DEVICE.
1812 * We only have to revert the state.
1813 */
1814 case IRP_MN_CANCEL_REMOVE_DEVICE:
1815 {
1816 Log(("vgdrvNtNt5PlusPnP: CANCEL_REMOVE_DEVICE\n"));
1817
1818 /* This must be handled first by the lower driver. */
1819 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1820 if ( NT_SUCCESS(rc)
1821 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGREMOVE)
1822 {
1823 /* Return to the state prior to receiving the IRP_MN_QUERY_REMOVE_DEVICE request. */
1824 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1825 }
1826
1827 /* Complete the IRP. */
1828 pIrp->IoStatus.Status = rc;
1829 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1830 return rc;
1831 }
1832
1833 /*
1834 * We do nothing here actually, esp. since this request is not expected for VBoxGuest.
1835 * The cleanup will be done in IRP_MN_REMOVE_DEVICE, which follows this call.
1836 */
1837 case IRP_MN_SURPRISE_REMOVAL:
1838 {
1839 Log(("vgdrvNtNt5PlusPnP: IRP_MN_SURPRISE_REMOVAL\n"));
1840 pDevExt->enmDevState = VGDRVNTDEVSTATE_SURPRISEREMOVED;
1841 LogRel(("VBoxGuest: unexpected device removal\n"));
1842
1843 /* Pass to the lower driver. */
1844 pIrp->IoStatus.Status = STATUS_SUCCESS;
1845
1846 IoSkipCurrentIrpStackLocation(pIrp);
1847 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1848
1849 /* Do not complete the IRP. */
1850 return rc;
1851 }
1852
1853 /*
1854 * Device and/or driver removal. Destroy everything.
1855 */
1856 case IRP_MN_REMOVE_DEVICE:
1857 {
1858 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE\n"));
1859 pDevExt->enmDevState = VGDRVNTDEVSTATE_REMOVED;
1860
1861 /*
1862 * Disconnect interrupts and delete all hardware resources.
1863 * Note! This may already have been done if we're STOPPED already, if that's a possibility.
1864 */
1865 vgdrvNtDeleteDeviceResources(pDevExt);
1866
1867 /*
1868 * We need to send the remove down the stack before we detach, but we don't need
1869 * to wait for the completion of this operation (nor register a completion routine).
1870 */
1871 pIrp->IoStatus.Status = STATUS_SUCCESS;
1872
1873 IoSkipCurrentIrpStackLocation(pIrp);
1874 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1875 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1876
1877 IoDetachDevice(pDevExt->pNextLowerDriver);
1878 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Removing device ...\n"));
1879
1880 /*
1881 * Delete the remainder of the device extension data, unlink it from the namespace and delete it.
1882 */
1883 vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt);
1884
1885 pDevObj = NULL; /* invalid */
1886 pDevExt = NULL; /* invalid */
1887
1888 Log(("vgdrvNtNt5PlusPnP: REMOVE_DEVICE: Device removed!\n"));
1889 return rc; /* Propagating rc from IoCallDriver. */
1890 }
1891
1892
1893 /*
1894 * Sent before stopping the device/driver to check whether it is okay to do so.
1895 */
1896 case IRP_MN_QUERY_STOP_DEVICE:
1897 {
1898 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE\n"));
1899 RTCritSectRwEnterExcl(&pDevExt->SessionCreateCritSect);
1900 rc = vgdrvNtCheckIdle(pDevExt, "QUERY_STOP_DEVICE");
1901 if (NT_SUCCESS(rc))
1902 {
1903 pDevExt->enmPrevDevState = pDevExt->enmDevState;
1904 pDevExt->enmDevState = VGDRVNTDEVSTATE_PENDINGSTOP;
1905 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1906
1907 /* This IRP passed down to lower driver. */
1908 pIrp->IoStatus.Status = STATUS_SUCCESS;
1909
1910 IoSkipCurrentIrpStackLocation(pIrp);
1911
1912 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1913 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1914
1915 /* we must not do anything with the IRP after doing IoSkip & CallDriver since the
1916 driver below us will complete (or already have completed) the IRP. I.e. just
1917 return the status we got from IoCallDriver. */
1918 }
1919 else
1920 {
1921 RTCritSectRwLeaveExcl(&pDevExt->SessionCreateCritSect);
1922 pIrp->IoStatus.Status = rc;
1923 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1924 }
1925
1926 Log(("vgdrvNtNt5PlusPnP: QUERY_STOP_DEVICE: Returning with rc = 0x%x\n", rc));
1927 return rc;
1928 }
1929
1930 /*
1931 * Cancels a pending remove, IRP_MN_QUERY_STOP_DEVICE.
1932 * We only have to revert the state.
1933 */
1934 case IRP_MN_CANCEL_STOP_DEVICE:
1935 {
1936 Log(("vgdrvNtNt5PlusPnP: CANCEL_STOP_DEVICE\n"));
1937
1938 /* This must be handled first by the lower driver. */
1939 rc = vgdrvNt5PlusPnPSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE);
1940 if ( NT_SUCCESS(rc)
1941 && pDevExt->enmDevState == VGDRVNTDEVSTATE_PENDINGSTOP)
1942 {
1943 /* Return to the state prior to receiving the IRP_MN_QUERY_STOP_DEVICE request. */
1944 pDevExt->enmDevState = pDevExt->enmPrevDevState;
1945 }
1946
1947 /* Complete the IRP. */
1948 pIrp->IoStatus.Status = rc;
1949 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
1950 return rc;
1951 }
1952
1953 /*
1954 * Stop the device.
1955 */
1956 case IRP_MN_STOP_DEVICE:
1957 {
1958 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE\n"));
1959 pDevExt->enmDevState = VGDRVNTDEVSTATE_STOPPED;
1960
1961 /*
1962 * Release the hardware resources.
1963 */
1964 vgdrvNtDeleteDeviceResources(pDevExt);
1965
1966 /*
1967 * Pass the request to the lower driver.
1968 */
1969 pIrp->IoStatus.Status = STATUS_SUCCESS;
1970 IoSkipCurrentIrpStackLocation(pIrp);
1971 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1972 Log(("vgdrvNtNt5PlusPnP: STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc));
1973 return rc;
1974 }
1975
1976 default:
1977 {
1978 IoSkipCurrentIrpStackLocation(pIrp);
1979 rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
1980 Log(("vgdrvNtNt5PlusPnP: Unknown request %#x: Lower driver replied: %x\n", bMinorFunction, rc));
1981 return rc;
1982 }
1983 }
1984}
1985
1986
1987/**
1988 * Handle the power completion event.
1989 *
1990 * @returns NT status code.
1991 * @param pDevObj Targetted device object.
1992 * @param pIrp IO request packet.
1993 * @param pContext Context value passed to IoSetCompletionRoutine in VBoxGuestPower.
1994 */
1995static NTSTATUS vgdrvNtNt5PlusPowerComplete(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext)
1996{
1997#ifdef VBOX_STRICT
1998 RT_NOREF1(pDevObj);
1999 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pContext;
2000 PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
2001
2002 Assert(pDevExt);
2003
2004 if (pIrpSp)
2005 {
2006 Assert(pIrpSp->MajorFunction == IRP_MJ_POWER);
2007 if (NT_SUCCESS(pIrp->IoStatus.Status))
2008 {
2009 switch (pIrpSp->MinorFunction)
2010 {
2011 case IRP_MN_SET_POWER:
2012 switch (pIrpSp->Parameters.Power.Type)
2013 {
2014 case DevicePowerState:
2015 switch (pIrpSp->Parameters.Power.State.DeviceState)
2016 {
2017 case PowerDeviceD0:
2018 break;
2019 default: /* Shut up MSC */
2020 break;
2021 }
2022 break;
2023 default: /* Shut up MSC */
2024 break;
2025 }
2026 break;
2027 }
2028 }
2029 }
2030#else
2031 RT_NOREF3(pDevObj, pIrp, pContext);
2032#endif
2033
2034 return STATUS_SUCCESS;
2035}
2036
2037
2038/**
2039 * Handle the Power requests.
2040 *
2041 * @returns NT status code
2042 * @param pDevObj device object
2043 * @param pIrp IRP
2044 */
2045static NTSTATUS NTAPI vgdrvNtNt5PlusPower(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2046{
2047 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2048 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2049 POWER_STATE_TYPE enmPowerType = pStack->Parameters.Power.Type;
2050 POWER_STATE PowerState = pStack->Parameters.Power.State;
2051 POWER_ACTION enmPowerAction = pStack->Parameters.Power.ShutdownType;
2052
2053 Log(("vgdrvNtNt5PlusPower:\n"));
2054
2055 switch (pStack->MinorFunction)
2056 {
2057 case IRP_MN_SET_POWER:
2058 {
2059 Log(("vgdrvNtNt5PlusPower: IRP_MN_SET_POWER, type= %d\n", enmPowerType));
2060 switch (enmPowerType)
2061 {
2062 case SystemPowerState:
2063 {
2064 Log(("vgdrvNtNt5PlusPower: SystemPowerState, action = %d, state = %d/%d\n",
2065 enmPowerAction, PowerState.SystemState, PowerState.DeviceState));
2066
2067 switch (enmPowerAction)
2068 {
2069 case PowerActionSleep:
2070
2071 /* System now is in a working state. */
2072 if (PowerState.SystemState == PowerSystemWorking)
2073 {
2074 if ( pDevExt
2075 && pDevExt->enmLastSystemPowerAction == PowerActionHibernate)
2076 {
2077 Log(("vgdrvNtNt5PlusPower: Returning from hibernation!\n"));
2078 int rc = VGDrvCommonReinitDevExtAfterHibernation(&pDevExt->Core,
2079 vgdrvNtVersionToOSType(g_enmVGDrvNtVer));
2080 if (RT_FAILURE(rc))
2081 Log(("vgdrvNtNt5PlusPower: Cannot re-init VMMDev chain, rc = %d!\n", rc));
2082 }
2083 }
2084 break;
2085
2086 case PowerActionShutdownReset:
2087 {
2088 Log(("vgdrvNtNt5PlusPower: Power action reset!\n"));
2089
2090 /* Tell the VMM that we no longer support mouse pointer integration. */
2091 VMMDevReqMouseStatus *pReq = NULL;
2092 int vrc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof (VMMDevReqMouseStatus),
2093 VMMDevReq_SetMouseStatus);
2094 if (RT_SUCCESS(vrc))
2095 {
2096 pReq->mouseFeatures = 0;
2097 pReq->pointerXPos = 0;
2098 pReq->pointerYPos = 0;
2099
2100 vrc = VbglR0GRPerform(&pReq->header);
2101 if (RT_FAILURE(vrc))
2102 {
2103 Log(("vgdrvNtNt5PlusPower: error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
2104 }
2105
2106 VbglR0GRFree(&pReq->header);
2107 }
2108
2109 /* Don't do any cleanup here; there might be still coming in some IOCtls after we got this
2110 * power action and would assert/crash when we already cleaned up all the stuff! */
2111 break;
2112 }
2113
2114 case PowerActionShutdown:
2115 case PowerActionShutdownOff:
2116 {
2117 Log(("vgdrvNtNt5PlusPower: Power action shutdown!\n"));
2118 if (PowerState.SystemState >= PowerSystemShutdown)
2119 {
2120 Log(("vgdrvNtNt5PlusPower: Telling the VMMDev to close the VM ...\n"));
2121
2122 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
2123 int vrc = VERR_NOT_IMPLEMENTED;
2124 if (pReq)
2125 {
2126 pReq->header.requestType = VMMDevReq_SetPowerStatus;
2127 pReq->powerState = VMMDevPowerState_PowerOff;
2128
2129 vrc = VbglR0GRPerform(&pReq->header);
2130 }
2131 if (RT_FAILURE(vrc))
2132 Log(("vgdrvNtNt5PlusPower: Error communicating new power status to VMMDev. vrc = %Rrc\n", vrc));
2133
2134 /* No need to do cleanup here; at this point we should've been
2135 * turned off by VMMDev already! */
2136 }
2137 break;
2138 }
2139
2140 case PowerActionHibernate:
2141 Log(("vgdrvNtNt5PlusPower: Power action hibernate!\n"));
2142 break;
2143
2144 case PowerActionWarmEject:
2145 Log(("vgdrvNtNt5PlusPower: PowerActionWarmEject!\n"));
2146 break;
2147
2148 default:
2149 Log(("vgdrvNtNt5PlusPower: %d\n", enmPowerAction));
2150 break;
2151 }
2152
2153 /*
2154 * Save the current system power action for later use.
2155 * This becomes handy when we return from hibernation for example.
2156 */
2157 if (pDevExt)
2158 pDevExt->enmLastSystemPowerAction = enmPowerAction;
2159
2160 break;
2161 }
2162 default:
2163 break;
2164 }
2165 break;
2166 }
2167 default:
2168 break;
2169 }
2170
2171 /*
2172 * Whether we are completing or relaying this power IRP,
2173 * we must call PoStartNextPowerIrp.
2174 */
2175 g_pfnPoStartNextPowerIrp(pIrp);
2176
2177 /*
2178 * Send the IRP down the driver stack, using PoCallDriver
2179 * (not IoCallDriver, as for non-power irps).
2180 */
2181 IoCopyCurrentIrpStackLocationToNext(pIrp);
2182 IoSetCompletionRoutine(pIrp,
2183 vgdrvNtNt5PlusPowerComplete,
2184 (PVOID)pDevExt,
2185 TRUE,
2186 TRUE,
2187 TRUE);
2188 return g_pfnPoCallDriver(pDevExt->pNextLowerDriver, pIrp);
2189}
2190
2191
2192/**
2193 * IRP_MJ_SYSTEM_CONTROL handler.
2194 *
2195 * @returns NT status code
2196 * @param pDevObj Device object.
2197 * @param pIrp IRP.
2198 */
2199static NTSTATUS NTAPI vgdrvNtNt5PlusSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2200{
2201 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2202
2203 LogFlowFuncEnter();
2204
2205 /* Always pass it on to the next driver. */
2206 IoSkipCurrentIrpStackLocation(pIrp);
2207
2208 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
2209}
2210
2211
2212/**
2213 * Unload the driver.
2214 *
2215 * @param pDrvObj Driver object.
2216 */
2217static void NTAPI vgdrvNtUnload(PDRIVER_OBJECT pDrvObj)
2218{
2219 LogFlowFuncEnter();
2220
2221#ifdef TARGET_NT4
2222 /*
2223 * We need to destroy the device object here on NT4 and earlier.
2224 */
2225 PDEVICE_OBJECT pDevObj = pDrvObj->DeviceObject;
2226 if (pDevObj)
2227 {
2228 if (g_enmVGDrvNtVer <= VGDRVNTVER_WINNT4)
2229 {
2230 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2231 AssertPtr(pDevExt);
2232 AssertMsg(pDevExt->Core.uInitState == VBOXGUESTDEVEXT_INIT_STATE_RESOURCES,
2233 ("uInitState=%#x\n", pDevExt->Core.uInitState));
2234
2235 vgdrvNtDeleteDeviceResources(pDevExt);
2236 vgdrvNtDeleteDeviceFundamentAndUnlink(pDevObj, pDevExt);
2237 }
2238 }
2239#else /* !TARGET_NT4 */
2240 /*
2241 * On a PnP driver this routine will be called after IRP_MN_REMOVE_DEVICE
2242 * where we already did the cleanup, so don't do anything here (yet).
2243 */
2244 RT_NOREF1(pDrvObj);
2245#endif /* !TARGET_NT4 */
2246
2247 VGDrvCommonDestroyLoggers();
2248 RTR0Term();
2249
2250 /*
2251 * Finally deregister the bugcheck callback. Do it late to catch trouble in RTR0Term.
2252 */
2253 if (g_fBugCheckCallbackRegistered)
2254 {
2255 g_pfnKeDeregisterBugCheckCallback(&g_BugCheckCallbackRec);
2256 g_fBugCheckCallbackRegistered = false;
2257 }
2258}
2259
2260
2261/**
2262 * For simplifying request completion into a simple return statement, extended
2263 * version.
2264 *
2265 * @returns rcNt
2266 * @param rcNt The status code.
2267 * @param uInfo Extra info value.
2268 * @param pIrp The IRP.
2269 */
2270DECLINLINE(NTSTATUS) vgdrvNtCompleteRequestEx(NTSTATUS rcNt, ULONG_PTR uInfo, PIRP pIrp)
2271{
2272 pIrp->IoStatus.Status = rcNt;
2273 pIrp->IoStatus.Information = uInfo;
2274 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2275 return rcNt;
2276}
2277
2278
2279/**
2280 * For simplifying request completion into a simple return statement.
2281 *
2282 * @returns rcNt
2283 * @param rcNt The status code.
2284 * @param pIrp The IRP.
2285 */
2286DECLINLINE(NTSTATUS) vgdrvNtCompleteRequest(NTSTATUS rcNt, PIRP pIrp)
2287{
2288 return vgdrvNtCompleteRequestEx(rcNt, 0 /*uInfo*/, pIrp);
2289}
2290
2291
2292/**
2293 * Checks if NT authority rev 1 SID (SECURITY_NT_AUTHORITY).
2294 *
2295 * @returns true / false.
2296 * @param pSid The SID to check.
2297 */
2298DECLINLINE(bool) vgdrvNtIsSidNtAuth(struct _SID const *pSid)
2299{
2300 return pSid != NULL
2301 && pSid->Revision == 1
2302 && pSid->IdentifierAuthority.Value[5] == 5
2303 && pSid->IdentifierAuthority.Value[4] == 0
2304 && pSid->IdentifierAuthority.Value[3] == 0
2305 && pSid->IdentifierAuthority.Value[2] == 0
2306 && pSid->IdentifierAuthority.Value[1] == 0
2307 && pSid->IdentifierAuthority.Value[0] == 0;
2308}
2309
2310
2311/**
2312 * Matches SID with local system user (S-1-5-18 / SECURITY_LOCAL_SYSTEM_RID).
2313 */
2314DECLINLINE(bool) vgdrvNtIsSidLocalSystemUser(SID const *pSid)
2315{
2316 return vgdrvNtIsSidNtAuth(pSid)
2317 && pSid->SubAuthorityCount == 1
2318 && pSid->SubAuthority[0] == SECURITY_LOCAL_SYSTEM_RID;
2319}
2320
2321
2322/**
2323 * Matches SID with NT system admin user (S-1-5-*-500 / DOMAIN_USER_RID_ADMIN).
2324 */
2325DECLINLINE(bool) vgdrvNtIsSidAdminUser(SID const *pSid)
2326{
2327 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2328 return vgdrvNtIsSidNtAuth(pSid)
2329 && pSid->SubAuthorityCount >= 2
2330 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2331 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_ADMIN;
2332}
2333
2334
2335/**
2336 * Matches SID with NT system guest user (S-1-5-*-501 / DOMAIN_USER_RID_GUEST).
2337 */
2338DECLINLINE(bool) vgdrvNtIsSidGuestUser(SID const *pSid)
2339{
2340 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2341 return vgdrvNtIsSidNtAuth(pSid)
2342 && pSid->SubAuthorityCount >= 2
2343 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2344 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_USER_RID_GUEST;
2345}
2346
2347
2348/**
2349 * Matches SID with NT system admins group (S-1-5-32-544, S-1-5-*-512).
2350 */
2351DECLINLINE(bool) vgdrvNtIsSidAdminsGroup(SID const *pSid)
2352{
2353 return vgdrvNtIsSidNtAuth(pSid)
2354 && ( ( pSid->SubAuthorityCount == 2
2355 && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
2356 && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_ADMINS)
2357#if 0
2358 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2359 || ( pSid->SubAuthorityCount >= 2
2360 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2361 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_ADMINS)
2362#endif
2363 );
2364}
2365
2366
2367/**
2368 * Matches SID with NT system users group (S-1-5-32-545, S-1-5-32-547, S-1-5-*-512).
2369 */
2370DECLINLINE(bool) vgdrvNtIsSidUsersGroup(SID const *pSid)
2371{
2372 return vgdrvNtIsSidNtAuth(pSid)
2373 && ( ( pSid->SubAuthorityCount == 2
2374 && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
2375 && ( pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_USERS
2376 || pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_POWER_USERS) )
2377#if 0
2378 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2379 || ( pSid->SubAuthorityCount >= 2
2380 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2381 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_USERS)
2382#endif
2383 );
2384}
2385
2386
2387/**
2388 * Matches SID with NT system guests group (S-1-5-32-546, S-1-5-*-512).
2389 */
2390DECLINLINE(bool) vgdrvNtIsSidGuestsGroup(SID const *pSid)
2391{
2392 return vgdrvNtIsSidNtAuth(pSid)
2393 && ( ( pSid->SubAuthorityCount == 2
2394 && pSid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID
2395 && pSid->SubAuthority[1] == DOMAIN_ALIAS_RID_GUESTS)
2396#if 0
2397 /** @todo restrict to SECURITY_NT_NON_UNIQUE? */
2398 || ( pSid->SubAuthorityCount >= 2
2399 && pSid->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES
2400 && pSid->SubAuthority[pSid->SubAuthorityCount - 1] == DOMAIN_GROUP_RID_GUESTS)
2401#endif
2402 );
2403}
2404
2405
2406/**
2407 * Checks if local authority rev 1 SID (SECURITY_LOCAL_SID_AUTHORITY).
2408 *
2409 * @returns true / false.
2410 * @param pSid The SID to check.
2411 */
2412DECLINLINE(bool) vgdrvNtIsSidLocalAuth(struct _SID const *pSid)
2413{
2414 return pSid != NULL
2415 && pSid->Revision == 1
2416 && pSid->IdentifierAuthority.Value[5] == 2
2417 && pSid->IdentifierAuthority.Value[4] == 0
2418 && pSid->IdentifierAuthority.Value[3] == 0
2419 && pSid->IdentifierAuthority.Value[2] == 0
2420 && pSid->IdentifierAuthority.Value[1] == 0
2421 && pSid->IdentifierAuthority.Value[0] == 0;
2422}
2423
2424
2425/**
2426 * Matches SID with console logon group (S-1-2-1 / SECURITY_LOCAL_LOGON_RID).
2427 */
2428DECLINLINE(bool) vgdrvNtIsSidConsoleLogonGroup(SID const *pSid)
2429{
2430 return vgdrvNtIsSidLocalAuth(pSid)
2431 && pSid->SubAuthorityCount == 1
2432 && pSid->SubAuthority[0] == SECURITY_LOCAL_LOGON_RID;
2433}
2434
2435
2436/**
2437 * Checks if mandatory label authority rev 1 SID (SECURITY_MANDATORY_LABEL_AUTHORITY).
2438 *
2439 * @returns true / false.
2440 * @param pSid The SID to check.
2441 */
2442DECLINLINE(bool) vgdrvNtIsSidMandatoryLabelAuth(struct _SID const *pSid)
2443{
2444 return pSid != NULL
2445 && pSid->Revision == 1
2446 && pSid->IdentifierAuthority.Value[5] == 16
2447 && pSid->IdentifierAuthority.Value[4] == 0
2448 && pSid->IdentifierAuthority.Value[3] == 0
2449 && pSid->IdentifierAuthority.Value[2] == 0
2450 && pSid->IdentifierAuthority.Value[1] == 0
2451 && pSid->IdentifierAuthority.Value[0] == 0;
2452}
2453
2454
2455#ifdef LOG_ENABLED
2456/** Format an SID for logging. */
2457static const char *vgdrvNtFormatSid(char *pszBuf, size_t cbBuf, struct _SID const *pSid)
2458{
2459 uint64_t uAuth = RT_MAKE_U64_FROM_U8(pSid->IdentifierAuthority.Value[5], pSid->IdentifierAuthority.Value[4],
2460 pSid->IdentifierAuthority.Value[3], pSid->IdentifierAuthority.Value[2],
2461 pSid->IdentifierAuthority.Value[1], pSid->IdentifierAuthority.Value[0],
2462 0, 0);
2463 ssize_t offCur = RTStrPrintf2(pszBuf, cbBuf, "S-%u-%RU64", pSid->Revision, uAuth);
2464 ULONG const *puSubAuth = &pSid->SubAuthority[0];
2465 unsigned cSubAuths = pSid->SubAuthorityCount;
2466 while (cSubAuths > 0 && (size_t)offCur < cbBuf)
2467 {
2468 ssize_t cchThis = RTStrPrintf2(&pszBuf[offCur], cbBuf - (size_t)offCur, "-%u", *puSubAuth);
2469 if (cchThis > 0)
2470 {
2471 offCur += cchThis;
2472 puSubAuth++;
2473 cSubAuths--;
2474 }
2475 else
2476 {
2477 Assert(cbBuf >= 5);
2478 pszBuf[cbBuf - 4] = '.';
2479 pszBuf[cbBuf - 3] = '.';
2480 pszBuf[cbBuf - 2] = '.';
2481 pszBuf[cbBuf - 1] = '\0';
2482 break;
2483 }
2484 }
2485 return pszBuf;
2486}
2487#endif
2488
2489
2490/**
2491 * Calculate requestor flags for the current process.
2492 *
2493 * ASSUMES vgdrvNtCreate is executed in the context of the process and thread
2494 * doing the NtOpenFile call.
2495 *
2496 * @returns VMMDEV_REQUESTOR_XXX
2497 */
2498static uint32_t vgdrvNtCalcRequestorFlags(void)
2499{
2500 uint32_t fRequestor = VMMDEV_REQUESTOR_USERMODE
2501 | VMMDEV_REQUESTOR_USR_NOT_GIVEN
2502 | VMMDEV_REQUESTOR_CON_DONT_KNOW
2503 | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN
2504 | VMMDEV_REQUESTOR_NO_USER_DEVICE;
2505 HANDLE hToken = NULL;
2506 NTSTATUS rcNt = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken);
2507 if (NT_SUCCESS(rcNt))
2508 {
2509 union
2510 {
2511 TOKEN_USER CurUser;
2512 TOKEN_GROUPS CurGroups;
2513 uint8_t abPadding[256];
2514 } Buf;
2515#ifdef LOG_ENABLED
2516 char szSid[200];
2517#endif
2518
2519 /*
2520 * Get the user SID and see if it's a standard one.
2521 */
2522 RT_ZERO(Buf.CurUser);
2523 ULONG cbReturned = 0;
2524 rcNt = ZwQueryInformationToken(hToken, TokenUser, &Buf.CurUser, sizeof(Buf), &cbReturned);
2525 if (NT_SUCCESS(rcNt))
2526 {
2527 struct _SID const *pSid = (struct _SID const *)Buf.CurUser.User.Sid;
2528 Log5(("vgdrvNtCalcRequestorFlags: TokenUser: %#010x %s\n",
2529 Buf.CurUser.User.Attributes, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid)));
2530
2531 if (vgdrvNtIsSidLocalSystemUser(pSid))
2532 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_SYSTEM;
2533 else if (vgdrvNtIsSidAdminUser(pSid))
2534 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_ROOT;
2535 else if (vgdrvNtIsSidGuestUser(pSid))
2536 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST;
2537 }
2538 else
2539 LogRel(("vgdrvNtCalcRequestorFlags: TokenUser query failed: %#x\n", rcNt));
2540
2541 /*
2542 * Get the groups.
2543 */
2544 TOKEN_GROUPS *pCurGroupsFree = NULL;
2545 TOKEN_GROUPS *pCurGroups = &Buf.CurGroups;
2546 uint32_t cbCurGroups = sizeof(Buf);
2547 cbReturned = 0;
2548 RT_ZERO(Buf);
2549 rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned);
2550 if (rcNt == STATUS_BUFFER_TOO_SMALL)
2551 {
2552 uint32_t cTries = 8;
2553 do
2554 {
2555 RTMemTmpFree(pCurGroupsFree);
2556 if (cbCurGroups < cbReturned)
2557 cbCurGroups = RT_ALIGN_32(cbCurGroups + 32, 64);
2558 else
2559 cbCurGroups += 64;
2560 pCurGroupsFree = pCurGroups = (TOKEN_GROUPS *)RTMemTmpAllocZ(cbCurGroups);
2561 if (pCurGroupsFree)
2562 rcNt = ZwQueryInformationToken(hToken, TokenGroups, pCurGroups, cbCurGroups, &cbReturned);
2563 else
2564 rcNt = STATUS_NO_MEMORY;
2565 } while (rcNt == STATUS_BUFFER_TOO_SMALL && cTries-- > 0);
2566 }
2567 if (NT_SUCCESS(rcNt))
2568 {
2569 bool fGuestsMember = false;
2570 bool fUsersMember = false;
2571 if (g_enmVGDrvNtVer >= VGDRVNTVER_WIN7)
2572 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_NO;
2573
2574 for (uint32_t iGrp = 0; iGrp < pCurGroups->GroupCount; iGrp++)
2575 {
2576 uint32_t const fAttribs = pCurGroups->Groups[iGrp].Attributes;
2577 struct _SID const *pSid = (struct _SID const *)pCurGroups->Groups[iGrp].Sid;
2578 Log5(("vgdrvNtCalcRequestorFlags: TokenGroups[%u]: %#10x %s\n",
2579 iGrp, fAttribs, vgdrvNtFormatSid(szSid, sizeof(szSid), pSid)));
2580
2581 if ( (fAttribs & SE_GROUP_INTEGRITY_ENABLED)
2582 && vgdrvNtIsSidMandatoryLabelAuth(pSid)
2583 && pSid->SubAuthorityCount == 1
2584 && (fRequestor & VMMDEV_REQUESTOR_TRUST_MASK) == VMMDEV_REQUESTOR_TRUST_NOT_GIVEN)
2585 {
2586 fRequestor &= ~VMMDEV_REQUESTOR_TRUST_MASK;
2587 if (pSid->SubAuthority[0] < SECURITY_MANDATORY_LOW_RID)
2588 fRequestor |= VMMDEV_REQUESTOR_TRUST_UNTRUSTED;
2589 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_RID)
2590 fRequestor |= VMMDEV_REQUESTOR_TRUST_LOW;
2591 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_MEDIUM_PLUS_RID)
2592 fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM;
2593 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_HIGH_RID)
2594 fRequestor |= VMMDEV_REQUESTOR_TRUST_MEDIUM_PLUS;
2595 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_SYSTEM_RID)
2596 fRequestor |= VMMDEV_REQUESTOR_TRUST_HIGH;
2597 else if (pSid->SubAuthority[0] < SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
2598 fRequestor |= VMMDEV_REQUESTOR_TRUST_SYSTEM;
2599 else
2600 fRequestor |= VMMDEV_REQUESTOR_TRUST_PROTECTED;
2601 Log5(("vgdrvNtCalcRequestorFlags: mandatory label %u: => %#x\n", pSid->SubAuthority[0], fRequestor));
2602 }
2603 else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY))
2604 == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY)
2605 && vgdrvNtIsSidConsoleLogonGroup(pSid))
2606 {
2607 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_CON_MASK) | VMMDEV_REQUESTOR_CON_YES;
2608 Log5(("vgdrvNtCalcRequestorFlags: console: => %#x\n", fRequestor));
2609 }
2610 else if ( (fAttribs & (SE_GROUP_ENABLED | SE_GROUP_MANDATORY | SE_GROUP_USE_FOR_DENY_ONLY))
2611 == (SE_GROUP_ENABLED | SE_GROUP_MANDATORY)
2612 && vgdrvNtIsSidNtAuth(pSid))
2613 {
2614 if (vgdrvNtIsSidAdminsGroup(pSid))
2615 {
2616 fRequestor |= VMMDEV_REQUESTOR_GRP_WHEEL;
2617 Log5(("vgdrvNtCalcRequestorFlags: admins group: => %#x\n", fRequestor));
2618 }
2619 else if (vgdrvNtIsSidUsersGroup(pSid))
2620 {
2621 Log5(("vgdrvNtCalcRequestorFlags: users group\n"));
2622 fUsersMember = true;
2623 }
2624 else if (vgdrvNtIsSidGuestsGroup(pSid))
2625 {
2626 Log5(("vgdrvNtCalcRequestorFlags: guests group\n"));
2627 fGuestsMember = true;
2628 }
2629 }
2630 }
2631 if ((fRequestor & VMMDEV_REQUESTOR_USR_MASK) == VMMDEV_REQUESTOR_USR_NOT_GIVEN)
2632 {
2633 if (fUsersMember)
2634 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST;
2635 else if (fGuestsMember)
2636 fRequestor = (fRequestor & ~VMMDEV_REQUESTOR_USR_MASK) | VMMDEV_REQUESTOR_USR_GUEST;
2637 }
2638 }
2639 else
2640 LogRel(("vgdrvNtCalcRequestorFlags: TokenGroups query failed: %#x\n", rcNt));
2641
2642 RTMemTmpFree(pCurGroupsFree);
2643 ZwClose(hToken);
2644 }
2645 else
2646 LogRel(("vgdrvNtCalcRequestorFlags: NtOpenProcessToken query failed: %#x\n", rcNt));
2647
2648 Log5(("vgdrvNtCalcRequestorFlags: returns %#x\n", fRequestor));
2649 return fRequestor;
2650}
2651
2652
2653/**
2654 * Create (i.e. Open) file entry point.
2655 *
2656 * @param pDevObj Device object.
2657 * @param pIrp Request packet.
2658 */
2659static NTSTATUS NTAPI vgdrvNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2660{
2661 Log(("vgdrvNtCreate: RequestorMode=%d\n", pIrp->RequestorMode));
2662 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2663 PFILE_OBJECT pFileObj = pStack->FileObject;
2664 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2665
2666 Assert(pFileObj->FsContext == NULL);
2667
2668 /*
2669 * We are not remotely similar to a directory...
2670 */
2671 NTSTATUS rcNt;
2672 if (!(pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE))
2673 {
2674 /*
2675 * Check the device state. We enter the critsect in shared mode to
2676 * prevent race with PnP system requests checking whether we're idle.
2677 */
2678 RTCritSectRwEnterShared(&pDevExt->SessionCreateCritSect);
2679 VGDRVNTDEVSTATE const enmDevState = pDevExt->enmDevState;
2680 if (enmDevState == VGDRVNTDEVSTATE_OPERATIONAL)
2681 {
2682 /*
2683 * Create a client session.
2684 */
2685 int rc;
2686 PVBOXGUESTSESSION pSession;
2687 if (pIrp->RequestorMode == KernelMode)
2688 rc = VGDrvCommonCreateKernelSession(&pDevExt->Core, &pSession);
2689 else
2690 rc = VGDrvCommonCreateUserSession(&pDevExt->Core, vgdrvNtCalcRequestorFlags(), &pSession);
2691 RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
2692 if (RT_SUCCESS(rc))
2693 {
2694 pFileObj->FsContext = pSession;
2695 Log(("vgdrvNtCreate: Successfully created %s session %p (fRequestor=%#x)\n",
2696 pIrp->RequestorMode == KernelMode ? "kernel" : "user", pSession, pSession->fRequestor));
2697
2698 return vgdrvNtCompleteRequestEx(STATUS_SUCCESS, FILE_OPENED, pIrp);
2699 }
2700
2701 /* Note. the IoStatus is completely ignored on error. */
2702 Log(("vgdrvNtCreate: Failed to create session: rc=%Rrc\n", rc));
2703 if (rc == VERR_NO_MEMORY)
2704 rcNt = STATUS_NO_MEMORY;
2705 else
2706 rcNt = STATUS_UNSUCCESSFUL;
2707 }
2708 else
2709 {
2710 RTCritSectRwLeaveShared(&pDevExt->SessionCreateCritSect);
2711 LogFlow(("vgdrvNtCreate: Failed. Device is not in 'working' state: %d\n", enmDevState));
2712 rcNt = STATUS_DEVICE_NOT_READY;
2713 }
2714 }
2715 else
2716 {
2717 LogFlow(("vgdrvNtCreate: Failed. FILE_DIRECTORY_FILE set\n"));
2718 rcNt = STATUS_NOT_A_DIRECTORY;
2719 }
2720 return vgdrvNtCompleteRequest(rcNt, pIrp);
2721}
2722
2723
2724/**
2725 * Close file entry point.
2726 *
2727 * @param pDevObj Device object.
2728 * @param pIrp Request packet.
2729 */
2730static NTSTATUS NTAPI vgdrvNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2731{
2732 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2733 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2734 PFILE_OBJECT pFileObj = pStack->FileObject;
2735
2736 LogFlowFunc(("pDevExt=0x%p, pFileObj=0x%p, FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext));
2737
2738#ifdef VBOX_WITH_HGCM
2739 /* Close both, R0 and R3 sessions. */
2740 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
2741 if (pSession)
2742 VGDrvCommonCloseSession(&pDevExt->Core, pSession);
2743#endif
2744
2745 pFileObj->FsContext = NULL;
2746 pIrp->IoStatus.Information = 0;
2747 pIrp->IoStatus.Status = STATUS_SUCCESS;
2748 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2749
2750 return STATUS_SUCCESS;
2751}
2752
2753
2754/**
2755 * Device I/O Control entry point.
2756 *
2757 * @param pDevObj Device object.
2758 * @param pIrp Request packet.
2759 */
2760NTSTATUS NTAPI vgdrvNtDeviceControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2761{
2762 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2763 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
2764 PVBOXGUESTSESSION pSession = pStack->FileObject ? (PVBOXGUESTSESSION)pStack->FileObject->FsContext : NULL;
2765
2766 if (!RT_VALID_PTR(pSession))
2767 return vgdrvNtCompleteRequest(STATUS_TRUST_FAILURE, pIrp);
2768
2769#if 0 /* No fast I/O controls defined yet. */
2770 /*
2771 * Deal with the 2-3 high-speed IOCtl that takes their arguments from
2772 * the session and iCmd, and does not return anything.
2773 */
2774 if (pSession->fUnrestricted)
2775 {
2776 ULONG ulCmd = pStack->Parameters.DeviceIoControl.IoControlCode;
2777 if ( ulCmd == SUP_IOCTL_FAST_DO_RAW_RUN
2778 || ulCmd == SUP_IOCTL_FAST_DO_HM_RUN
2779 || ulCmd == SUP_IOCTL_FAST_DO_NOP)
2780 {
2781 int rc = supdrvIOCtlFast(ulCmd, (unsigned)(uintptr_t)pIrp->UserBuffer /* VMCPU id */, pDevExt, pSession);
2782
2783 /* Complete the I/O request. */
2784 supdrvSessionRelease(pSession);
2785 return vgdrvNtCompleteRequest(RT_SUCCESS(rc) ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER, pIrp);
2786 }
2787 }
2788#endif
2789
2790 return vgdrvNtDeviceControlSlow(&pDevExt->Core, pSession, pIrp, pStack);
2791}
2792
2793
2794/**
2795 * Device I/O Control entry point.
2796 *
2797 * @param pDevExt The device extension.
2798 * @param pSession The session.
2799 * @param pIrp Request packet.
2800 * @param pStack The request stack pointer.
2801 */
2802static NTSTATUS vgdrvNtDeviceControlSlow(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
2803 PIRP pIrp, PIO_STACK_LOCATION pStack)
2804{
2805 NTSTATUS rcNt;
2806 uint32_t cbOut = 0;
2807 int rc = 0;
2808 Log2(("vgdrvNtDeviceControlSlow(%p,%p): ioctl=%#x pBuf=%p cbIn=%#x cbOut=%#x pSession=%p\n",
2809 pDevExt, pIrp, pStack->Parameters.DeviceIoControl.IoControlCode,
2810 pIrp->AssociatedIrp.SystemBuffer, pStack->Parameters.DeviceIoControl.InputBufferLength,
2811 pStack->Parameters.DeviceIoControl.OutputBufferLength, pSession));
2812
2813#if 0 /*def RT_ARCH_AMD64*/
2814 /* Don't allow 32-bit processes to do any I/O controls. */
2815 if (!IoIs32bitProcess(pIrp))
2816#endif
2817 {
2818 /* Verify that it's a buffered CTL. */
2819 if ((pStack->Parameters.DeviceIoControl.IoControlCode & 0x3) == METHOD_BUFFERED)
2820 {
2821 /* Verify that the sizes in the request header are correct. */
2822 PVBGLREQHDR pHdr = (PVBGLREQHDR)pIrp->AssociatedIrp.SystemBuffer;
2823 if ( pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr)
2824 && pStack->Parameters.DeviceIoControl.InputBufferLength == pHdr->cbIn
2825 && pStack->Parameters.DeviceIoControl.OutputBufferLength == pHdr->cbOut)
2826 {
2827 /* Zero extra output bytes to make sure we don't leak anything. */
2828 if (pHdr->cbIn < pHdr->cbOut)
2829 RtlZeroMemory((uint8_t *)pHdr + pHdr->cbIn, pHdr->cbOut - pHdr->cbIn);
2830
2831 /*
2832 * Do the job.
2833 */
2834 rc = VGDrvCommonIoCtl(pStack->Parameters.DeviceIoControl.IoControlCode, pDevExt, pSession, pHdr,
2835 RT_MAX(pHdr->cbIn, pHdr->cbOut));
2836 if (RT_SUCCESS(rc))
2837 {
2838 rcNt = STATUS_SUCCESS;
2839 cbOut = pHdr->cbOut;
2840 if (cbOut > pStack->Parameters.DeviceIoControl.OutputBufferLength)
2841 {
2842 cbOut = pStack->Parameters.DeviceIoControl.OutputBufferLength;
2843 LogRel(("vgdrvNtDeviceControlSlow: too much output! %#x > %#x; uCmd=%#x!\n",
2844 pHdr->cbOut, cbOut, pStack->Parameters.DeviceIoControl.IoControlCode));
2845 }
2846
2847 /* If IDC successful disconnect request, we must set the context pointer to NULL. */
2848 if ( pStack->Parameters.DeviceIoControl.IoControlCode == VBGL_IOCTL_IDC_DISCONNECT
2849 && RT_SUCCESS(pHdr->rc))
2850 pStack->FileObject->FsContext = NULL;
2851 }
2852 else if (rc == VERR_NOT_SUPPORTED)
2853 rcNt = STATUS_NOT_SUPPORTED;
2854 else
2855 rcNt = STATUS_INVALID_PARAMETER;
2856 Log2(("vgdrvNtDeviceControlSlow: returns %#x cbOut=%d rc=%#x\n", rcNt, cbOut, rc));
2857 }
2858 else
2859 {
2860 Log(("vgdrvNtDeviceControlSlow: Mismatching sizes (%#x) - Hdr=%#lx/%#lx Irp=%#lx/%#lx!\n",
2861 pStack->Parameters.DeviceIoControl.IoControlCode,
2862 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbIn : 0,
2863 pStack->Parameters.DeviceIoControl.InputBufferLength >= sizeof(*pHdr) ? pHdr->cbOut : 0,
2864 pStack->Parameters.DeviceIoControl.InputBufferLength,
2865 pStack->Parameters.DeviceIoControl.OutputBufferLength));
2866 rcNt = STATUS_INVALID_PARAMETER;
2867 }
2868 }
2869 else
2870 {
2871 Log(("vgdrvNtDeviceControlSlow: not buffered request (%#x) - not supported\n",
2872 pStack->Parameters.DeviceIoControl.IoControlCode));
2873 rcNt = STATUS_NOT_SUPPORTED;
2874 }
2875 }
2876#if 0 /*def RT_ARCH_AMD64*/
2877 else
2878 {
2879 Log(("VBoxDrvNtDeviceControlSlow: WOW64 req - not supported\n"));
2880 rcNt = STATUS_NOT_SUPPORTED;
2881 }
2882#endif
2883
2884 return vgdrvNtCompleteRequestEx(rcNt, cbOut, pIrp);
2885}
2886
2887
2888/**
2889 * Internal Device I/O Control entry point (for IDC).
2890 *
2891 * @param pDevObj Device object.
2892 * @param pIrp Request packet.
2893 */
2894static NTSTATUS NTAPI vgdrvNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2895{
2896 /* Currently no special code here. */
2897 return vgdrvNtDeviceControl(pDevObj, pIrp);
2898}
2899
2900
2901/**
2902 * IRP_MJ_SHUTDOWN handler.
2903 *
2904 * @returns NT status code
2905 * @param pDevObj Device object.
2906 * @param pIrp IRP.
2907 */
2908static NTSTATUS NTAPI vgdrvNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2909{
2910 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
2911 LogFlowFuncEnter();
2912
2913 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
2914 if (pReq)
2915 {
2916 pReq->header.requestType = VMMDevReq_SetPowerStatus;
2917 pReq->powerState = VMMDevPowerState_PowerOff;
2918
2919 int rc = VbglR0GRPerform(&pReq->header);
2920 if (RT_FAILURE(rc))
2921 LogFunc(("Error performing request to VMMDev, rc=%Rrc\n", rc));
2922 }
2923
2924 /* just in case, since we shouldn't normally get here. */
2925 pIrp->IoStatus.Information = 0;
2926 pIrp->IoStatus.Status = STATUS_SUCCESS;
2927 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2928 return STATUS_SUCCESS;
2929}
2930
2931
2932/**
2933 * Stub function for functions we don't implemented.
2934 *
2935 * @returns STATUS_NOT_SUPPORTED
2936 * @param pDevObj Device object.
2937 * @param pIrp IRP.
2938 */
2939static NTSTATUS vgdrvNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
2940{
2941 RT_NOREF1(pDevObj);
2942 LogFlowFuncEnter();
2943
2944 pIrp->IoStatus.Information = 0;
2945 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
2946 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
2947
2948 return STATUS_NOT_SUPPORTED;
2949}
2950
2951
2952/**
2953 * Bug check callback (KBUGCHECK_CALLBACK_ROUTINE).
2954 *
2955 * This adds a log entry on the host, in case Hyper-V isn't active or the guest
2956 * is too old for reporting it itself via the crash MSRs.
2957 *
2958 * @param pvBuffer Not used.
2959 * @param cbBuffer Not used.
2960 */
2961static VOID NTAPI vgdrvNtBugCheckCallback(PVOID pvBuffer, ULONG cbBuffer)
2962{
2963 if (g_pauKiBugCheckData)
2964 {
2965 RTLogBackdoorPrintf("VBoxGuest: BugCheck! P0=%#zx P1=%#zx P2=%#zx P3=%#zx P4=%#zx\n", g_pauKiBugCheckData[0],
2966 g_pauKiBugCheckData[1], g_pauKiBugCheckData[2], g_pauKiBugCheckData[3], g_pauKiBugCheckData[4]);
2967
2968 VMMDevReqNtBugCheck *pReq = NULL;
2969 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq), VMMDevReq_NtBugCheck);
2970 if (RT_SUCCESS(rc))
2971 {
2972 pReq->uBugCheck = g_pauKiBugCheckData[0];
2973 pReq->auParameters[0] = g_pauKiBugCheckData[1];
2974 pReq->auParameters[1] = g_pauKiBugCheckData[2];
2975 pReq->auParameters[2] = g_pauKiBugCheckData[3];
2976 pReq->auParameters[3] = g_pauKiBugCheckData[4];
2977 VbglR0GRPerform(&pReq->header);
2978 VbglR0GRFree(&pReq->header);
2979 }
2980 }
2981 else
2982 {
2983 RTLogBackdoorPrintf("VBoxGuest: BugCheck!\n");
2984
2985 VMMDevRequestHeader *pReqHdr = NULL;
2986 int rc = VbglR0GRAlloc(&pReqHdr, sizeof(*pReqHdr), VMMDevReq_NtBugCheck);
2987 if (RT_SUCCESS(rc))
2988 {
2989 VbglR0GRPerform(pReqHdr);
2990 VbglR0GRFree(pReqHdr);
2991 }
2992 }
2993
2994 RT_NOREF(pvBuffer, cbBuffer);
2995}
2996
2997
2998/**
2999 * Sets the mouse notification callback.
3000 *
3001 * @returns VBox status code.
3002 * @param pDevExt Pointer to the device extension.
3003 * @param pNotify Pointer to the mouse notify struct.
3004 */
3005int VGDrvNativeSetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, PVBGLIOCSETMOUSENOTIFYCALLBACK pNotify)
3006{
3007 PVBOXGUESTDEVEXTWIN pDevExtWin = (PVBOXGUESTDEVEXTWIN)pDevExt;
3008 /* we need a lock here to avoid concurrency with the set event functionality */
3009 KIRQL OldIrql;
3010 KeAcquireSpinLock(&pDevExtWin->MouseEventAccessSpinLock, &OldIrql);
3011 pDevExtWin->Core.pfnMouseNotifyCallback = pNotify->u.In.pfnNotify;
3012 pDevExtWin->Core.pvMouseNotifyCallbackArg = pNotify->u.In.pvUser;
3013 KeReleaseSpinLock(&pDevExtWin->MouseEventAccessSpinLock, OldIrql);
3014 return VINF_SUCCESS;
3015}
3016
3017
3018/**
3019 * DPC handler.
3020 *
3021 * @param pDPC DPC descriptor.
3022 * @param pDevObj Device object.
3023 * @param pIrp Interrupt request packet.
3024 * @param pContext Context specific pointer.
3025 */
3026static void NTAPI vgdrvNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
3027{
3028 RT_NOREF3(pDPC, pIrp, pContext);
3029 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
3030 Log3Func(("pDevExt=0x%p\n", pDevExt));
3031
3032 /* Test & reset the counter. */
3033 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
3034 {
3035 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
3036 * i.e. to prevent the event from destroyed while we're using it */
3037 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
3038 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessSpinLock);
3039
3040 if (pDevExt->Core.pfnMouseNotifyCallback)
3041 pDevExt->Core.pfnMouseNotifyCallback(pDevExt->Core.pvMouseNotifyCallbackArg);
3042
3043 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessSpinLock);
3044 }
3045
3046 /* Process the wake-up list we were asked by the scheduling a DPC
3047 * in vgdrvNtIsrHandler(). */
3048 VGDrvCommonWaitDoWakeUps(&pDevExt->Core);
3049}
3050
3051
3052/**
3053 * ISR handler.
3054 *
3055 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
3056 * @param pInterrupt Interrupt that was triggered.
3057 * @param pServiceContext Context specific pointer.
3058 */
3059static BOOLEAN NTAPI vgdrvNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
3060{
3061 RT_NOREF1(pInterrupt);
3062 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
3063 if (pDevExt == NULL)
3064 return FALSE;
3065
3066 /*Log3Func(("pDevExt=0x%p, pVMMDevMemory=0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
3067
3068 /* Enter the common ISR routine and do the actual work. */
3069 BOOLEAN fIRQTaken = VGDrvCommonISR(&pDevExt->Core);
3070
3071 /* If we need to wake up some events we do that in a DPC to make
3072 * sure we're called at the right IRQL. */
3073 if (fIRQTaken)
3074 {
3075 Log3Func(("IRQ was taken! pInterrupt=0x%p, pDevExt=0x%p\n", pInterrupt, pDevExt));
3076 if (ASMAtomicUoReadU32( &pDevExt->Core.u32MousePosChangedSeq)
3077 || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
3078 {
3079 Log3Func(("Requesting DPC...\n"));
3080 IoRequestDpc(pDevExt->pDeviceObject, NULL /*pIrp*/, NULL /*pvContext*/);
3081 }
3082 }
3083 return fIRQTaken;
3084}
3085
3086
3087void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
3088{
3089 NOREF(pDevExt);
3090 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
3091 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
3092 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
3093}
3094
3095
3096/**
3097 * Hook for handling OS specfic options from the host.
3098 *
3099 * @returns true if handled, false if not.
3100 * @param pDevExt The device extension.
3101 * @param pszName The option name.
3102 * @param pszValue The option value.
3103 */
3104bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
3105{
3106 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
3107 return false;
3108}
3109
3110
3111/**
3112 * Implements RTL_QUERY_REGISTRY_ROUTINE for enumerating our registry key.
3113 */
3114static NTSTATUS NTAPI vgdrvNtRegistryEnumCallback(PWSTR pwszValueName, ULONG uValueType,
3115 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
3116{
3117 Log4(("vgdrvNtRegistryEnumCallback: pwszValueName=%ls uValueType=%#x Value=%.*Rhxs\n", pwszValueName, uValueType, cbValue, pvValue));
3118
3119 /*
3120 * Filter out general service config values.
3121 */
3122 if ( RTUtf16ICmpAscii(pwszValueName, "Type") == 0
3123 || RTUtf16ICmpAscii(pwszValueName, "Start") == 0
3124 || RTUtf16ICmpAscii(pwszValueName, "ErrorControl") == 0
3125 || RTUtf16ICmpAscii(pwszValueName, "Tag") == 0
3126 || RTUtf16ICmpAscii(pwszValueName, "ImagePath") == 0
3127 || RTUtf16ICmpAscii(pwszValueName, "DisplayName") == 0
3128 || RTUtf16ICmpAscii(pwszValueName, "Group") == 0
3129 || RTUtf16ICmpAscii(pwszValueName, "DependOnGroup") == 0
3130 || RTUtf16ICmpAscii(pwszValueName, "DependOnService") == 0
3131 )
3132 {
3133 return STATUS_SUCCESS;
3134 }
3135
3136 /*
3137 * Convert the value name.
3138 */
3139 size_t cch = RTUtf16CalcUtf8Len(pwszValueName);
3140 if (cch < 64 && cch > 0)
3141 {
3142 char szValueName[72];
3143 char *pszTmp = szValueName;
3144 int rc = RTUtf16ToUtf8Ex(pwszValueName, RTSTR_MAX, &pszTmp, sizeof(szValueName), NULL);
3145 if (RT_SUCCESS(rc))
3146 {
3147 /*
3148 * Convert the value.
3149 */
3150 char szValue[72];
3151 char *pszFree = NULL;
3152 char *pszValue = NULL;
3153 szValue[0] = '\0';
3154 switch (uValueType)
3155 {
3156 case REG_SZ:
3157 case REG_EXPAND_SZ:
3158 rc = RTUtf16CalcUtf8LenEx((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &cch);
3159 if (RT_SUCCESS(rc) && cch < _1K)
3160 {
3161 if (cch < sizeof(szValue))
3162 {
3163 pszValue = szValue;
3164 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
3165 }
3166 else
3167 {
3168 rc = RTUtf16ToUtf8Ex((PCRTUTF16)pvValue, cbValue / sizeof(RTUTF16), &pszValue, sizeof(szValue), NULL);
3169 if (RT_SUCCESS(rc))
3170 pszFree = pszValue;
3171 }
3172 if (RT_FAILURE(rc))
3173 {
3174 LogRel(("VBoxGuest: Failed to convert registry value '%ls' string data to UTF-8: %Rrc\n",
3175 pwszValueName, rc));
3176 pszValue = NULL;
3177 }
3178 }
3179 else if (RT_SUCCESS(rc))
3180 LogRel(("VBoxGuest: Registry value '%ls' has a too long value: %#x (uvalueType=%#x)\n",
3181 pwszValueName, cbValue, uValueType));
3182 else
3183 LogRel(("VBoxGuest: Registry value '%ls' has an invalid string value (cbValue=%#x, uvalueType=%#x)\n",
3184 pwszValueName, cbValue, uValueType));
3185 break;
3186
3187 case REG_DWORD:
3188 if (cbValue == sizeof(uint32_t))
3189 {
3190 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
3191 pszValue = szValue;
3192 }
3193 else
3194 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
3195 break;
3196
3197 case REG_QWORD:
3198 if (cbValue == sizeof(uint64_t))
3199 {
3200 RTStrFormatU32(szValue, sizeof(szValue), *(uint32_t const *)pvValue, 10, 0, 0, 0);
3201 pszValue = szValue;
3202 }
3203 else
3204 LogRel(("VBoxGuest: Registry value '%ls' has wrong length for REG_DWORD: %#x\n", pwszValueName, cbValue));
3205 break;
3206
3207 default:
3208 LogRel(("VBoxGuest: Ignoring registry value '%ls': Unsupported type %#x\n", pwszValueName, uValueType));
3209 break;
3210 }
3211 if (pszValue)
3212 {
3213 /*
3214 * Process it.
3215 */
3216 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pvUser;
3217 VGDrvCommonProcessOption(pDevExt, szValueName, pszValue);
3218 if (pszFree)
3219 RTStrFree(pszFree);
3220 }
3221 }
3222 }
3223 else if (cch > 0)
3224 LogRel(("VBoxGuest: Ignoring registery value '%ls': name too long\n", pwszValueName));
3225 else
3226 LogRel(("VBoxGuest: Ignoring registery value with bad name\n", pwszValueName));
3227 NOREF(pvEntryCtx);
3228 return STATUS_SUCCESS;
3229}
3230
3231
3232/**
3233 * Reads configuration from the registry and guest properties.
3234 *
3235 * We ignore failures and instead preserve existing configuration values.
3236 *
3237 * Thie routine will block.
3238 *
3239 * @param pDevExt The device extension.
3240 */
3241static void vgdrvNtReadConfiguration(PVBOXGUESTDEVEXTWIN pDevExt)
3242{
3243 /*
3244 * First the registry.
3245 *
3246 * Note! RTL_QUERY_REGISTRY_NOEXPAND is sensible (no environment) and also necessary to
3247 * avoid crash on NT 3.1 because RtlExpandEnvironmentStrings_U thinks its in ring-3
3248 * and tries to get the default heap from the PEB via the TEB. No TEB in ring-0.
3249 */
3250 RTL_QUERY_REGISTRY_TABLE aQuery[2];
3251 RT_ZERO(aQuery);
3252 aQuery[0].QueryRoutine = vgdrvNtRegistryEnumCallback;
3253 aQuery[0].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
3254 aQuery[0].Name = NULL;
3255 aQuery[0].EntryContext = NULL;
3256 aQuery[0].DefaultType = REG_NONE;
3257 NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES, L"VBoxGuest", &aQuery[0], pDevExt, NULL /*pwszzEnv*/);
3258 if (!NT_SUCCESS(rcNt))
3259 LogRel(("VBoxGuest: RtlQueryRegistryValues failed: %#x\n", rcNt));
3260
3261 /*
3262 * Read configuration from the host.
3263 */
3264 VGDrvCommonProcessOptionsFromHost(&pDevExt->Core);
3265}
3266
3267#ifdef VBOX_STRICT
3268
3269/**
3270 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
3271 */
3272static uint32_t vgdrvNtAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
3273{
3274 AssertPtrReturn(pu32Bits, 0);
3275 LogFlowFunc(("*pu32Bits=%#x, u32Mask=%#x\n", *(uint32_t *)pu32Bits, u32Mask));
3276 uint32_t u32Result = 0;
3277 uint32_t u32WorkingMask = u32Mask;
3278 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
3279
3280 while (iBitOffset > 0)
3281 {
3282 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
3283 if (fSet)
3284 u32Result |= 1 << (iBitOffset - 1);
3285 u32WorkingMask &= ~(1 << (iBitOffset - 1));
3286 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
3287 }
3288 LogFlowFunc(("Returning %#x\n", u32Result));
3289 return u32Result;
3290}
3291
3292
3293static void vgdrvNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
3294{
3295 ULONG u32Bits2 = u32Bits;
3296 uint32_t u32Result = vgdrvNtAtomicBitsTestAndClear(&u32Bits2, u32Mask);
3297 if ( u32Result != u32Exp
3298 || (u32Bits2 & u32Mask)
3299 || (u32Bits2 & u32Result)
3300 || ((u32Bits2 | u32Result) != u32Bits)
3301 )
3302 AssertLogRelMsgFailed(("TEST FAILED: u32Mask=%#x, u32Bits (before)=%#x, u32Bits (after)=%#x, u32Result=%#x, u32Exp=%#x\n",
3303 u32Mask, u32Bits, u32Bits2, u32Result));
3304}
3305
3306
3307static void vgdrvNtDoTests(void)
3308{
3309 vgdrvNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
3310 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
3311 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
3312 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
3313 vgdrvNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
3314 vgdrvNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
3315}
3316
3317#endif /* VBOX_STRICT */
3318
3319#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
3320
3321/*
3322 * DPC latency checker.
3323 */
3324
3325/**
3326 * One DPC latency sample.
3327 */
3328typedef struct DPCSAMPLE
3329{
3330 LARGE_INTEGER PerfDelta;
3331 LARGE_INTEGER PerfCounter;
3332 LARGE_INTEGER PerfFrequency;
3333 uint64_t u64TSC;
3334} DPCSAMPLE;
3335AssertCompileSize(DPCSAMPLE, 4*8);
3336
3337/**
3338 * The DPC latency measurement workset.
3339 */
3340typedef struct DPCDATA
3341{
3342 KDPC Dpc;
3343 KTIMER Timer;
3344 KSPIN_LOCK SpinLock;
3345
3346 ULONG ulTimerRes;
3347
3348 bool volatile fFinished;
3349
3350 /** The timer interval (relative). */
3351 LARGE_INTEGER DueTime;
3352
3353 LARGE_INTEGER PerfCounterPrev;
3354
3355 /** Align the sample array on a 64 byte boundrary just for the off chance
3356 * that we'll get cache line aligned memory backing this structure. */
3357 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
3358
3359 int cSamples;
3360 DPCSAMPLE aSamples[8192];
3361} DPCDATA;
3362
3363AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
3364
3365/**
3366 * DPC callback routine for the DPC latency measurement code.
3367 *
3368 * @param pDpc The DPC, not used.
3369 * @param pvDeferredContext Pointer to the DPCDATA.
3370 * @param SystemArgument1 System use, ignored.
3371 * @param SystemArgument2 System use, ignored.
3372 */
3373static VOID vgdrvNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
3374{
3375 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
3376 RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
3377
3378 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
3379
3380 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
3381 pData->fFinished = true;
3382 else
3383 {
3384 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
3385
3386 pSample->u64TSC = ASMReadTSC();
3387 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
3388 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
3389
3390 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
3391
3392 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
3393 }
3394
3395 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
3396}
3397
3398
3399/**
3400 * Handles the DPC latency checker request.
3401 *
3402 * @returns VBox status code.
3403 */
3404int VGDrvNtIOCtl_DpcLatencyChecker(void)
3405{
3406 /*
3407 * Allocate a block of non paged memory for samples and related data.
3408 */
3409 DPCDATA *pData = (DPCDATA *)RTMemAlloc(sizeof(DPCDATA));
3410 if (!pData)
3411 {
3412 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
3413 return VERR_NO_MEMORY;
3414 }
3415
3416 /*
3417 * Initialize the data.
3418 */
3419 KeInitializeDpc(&pData->Dpc, vgdrvNtDpcLatencyCallback, pData);
3420 KeInitializeTimer(&pData->Timer);
3421 KeInitializeSpinLock(&pData->SpinLock);
3422
3423 pData->fFinished = false;
3424 pData->cSamples = 0;
3425 pData->PerfCounterPrev.QuadPart = 0;
3426
3427 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
3428 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
3429
3430 /*
3431 * Start the DPC measurements and wait for a full set.
3432 */
3433 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
3434
3435 while (!pData->fFinished)
3436 {
3437 LARGE_INTEGER Interval;
3438 Interval.QuadPart = -100 * 1000 * 10;
3439 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
3440 }
3441
3442 ExSetTimerResolution(0, 0);
3443
3444 /*
3445 * Log everything to the host.
3446 */
3447 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
3448 for (int i = 0; i < pData->cSamples; i++)
3449 {
3450 DPCSAMPLE *pSample = &pData->aSamples[i];
3451
3452 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
3453 i,
3454 pSample->PerfDelta.QuadPart,
3455 pSample->PerfCounter.QuadPart,
3456 pSample->PerfFrequency.QuadPart,
3457 pSample->u64TSC);
3458 }
3459
3460 RTMemFree(pData);
3461 return VINF_SUCCESS;
3462}
3463
3464#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
3465
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