VirtualBox

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

Last change on this file since 50722 was 46876, checked in by vboxsync, 11 years ago

added Win8.1 OS type

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.5 KB
Line 
1/* $Id: VBoxGuest-win.cpp 46876 2013-07-01 10:09:35Z vboxsync $ */
2/** @file
3 * VBoxGuest - Windows specifics.
4 */
5
6/*
7 * Copyright (C) 2010-2013 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
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_SUP_DRV
22#include "VBoxGuest-win.h"
23#include "VBoxGuestInternal.h"
24
25#include <iprt/asm.h>
26#include <iprt/asm-amd64-x86.h>
27
28#include <VBox/log.h>
29#include <VBox/VBoxGuestLib.h>
30#include <iprt/string.h>
31
32/*
33 * XP DDK #defines ExFreePool to ExFreePoolWithTag. The latter does not exist
34 * on NT4, so... The same for ExAllocatePool.
35 */
36#ifdef TARGET_NT4
37# undef ExAllocatePool
38# undef ExFreePool
39#endif
40
41
42/*******************************************************************************
43* Internal Functions *
44*******************************************************************************/
45RT_C_DECLS_BEGIN
46static NTSTATUS vbgdNtAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
47static void vbgdNtUnload(PDRIVER_OBJECT pDrvObj);
48static NTSTATUS vbgdNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
49static NTSTATUS vbgdNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
50static NTSTATUS vbgdNtIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
51static NTSTATUS vbgdNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
52static NTSTATUS vbgdNtRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue);
53static NTSTATUS vbgdNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
54static NTSTATUS vbgdNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
55static NTSTATUS vbgdNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
56#ifdef DEBUG
57static void vbgdNtDoTests(void);
58#endif
59RT_C_DECLS_END
60
61
62/*******************************************************************************
63* Exported Functions *
64*******************************************************************************/
65RT_C_DECLS_BEGIN
66ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
67RT_C_DECLS_END
68
69#ifdef ALLOC_PRAGMA
70# pragma alloc_text(INIT, DriverEntry)
71# pragma alloc_text(PAGE, vbgdNtAddDevice)
72# pragma alloc_text(PAGE, vbgdNtUnload)
73# pragma alloc_text(PAGE, vbgdNtCreate)
74# pragma alloc_text(PAGE, vbgdNtClose)
75# pragma alloc_text(PAGE, vbgdNtShutdown)
76# pragma alloc_text(PAGE, vbgdNtNotSupportedStub)
77# pragma alloc_text(PAGE, vbgdNtScanPCIResourceList)
78#endif
79
80
81/*******************************************************************************
82* Global Variables *
83*******************************************************************************/
84/** The detected NT (windows) version. */
85VBGDNTVER g_enmVbgdNtVer = VBGDNTVER_INVALID;
86
87
88
89/**
90 * Driver entry point.
91 *
92 * @returns appropriate status code.
93 * @param pDrvObj Pointer to driver object.
94 * @param pRegPath Registry base path.
95 */
96ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
97{
98 NTSTATUS rc = STATUS_SUCCESS;
99
100 Log(("VBoxGuest::DriverEntry. Driver built: %s %s\n", __DATE__, __TIME__));
101
102 /*
103 * Check if the the NT version is supported and initializing
104 * g_enmVbgdNtVer in the process.
105 */
106 ULONG ulMajorVer;
107 ULONG ulMinorVer;
108 ULONG ulBuildNo;
109 BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL);
110 Log(("VBoxGuest::DriverEntry: Running on Windows NT version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo));
111 if (fCheckedBuild)
112 Log(("VBoxGuest::DriverEntry: Running on a Windows checked build (debug)!\n"));
113#ifdef DEBUG
114 vbgdNtDoTests();
115#endif
116 switch (ulMajorVer)
117 {
118 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
119 switch (ulMinorVer)
120 {
121 case 0: /* Note: Also could be Windows 2008 Server! */
122 g_enmVbgdNtVer = VBGDNTVER_WINVISTA;
123 break;
124 case 1: /* Note: Also could be Windows 2008 Server R2! */
125 g_enmVbgdNtVer = VBGDNTVER_WIN7;
126 break;
127 case 2:
128 g_enmVbgdNtVer = VBGDNTVER_WIN8;
129 break;
130 case 3:
131 g_enmVbgdNtVer = VBGDNTVER_WIN81;
132 break;
133 default:
134 Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n", ulMajorVer, ulMinorVer));
135 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
136 break;
137 }
138 break;
139 case 5:
140 switch (ulMinorVer)
141 {
142 case 2:
143 g_enmVbgdNtVer = VBGDNTVER_WIN2K3;
144 break;
145 case 1:
146 g_enmVbgdNtVer = VBGDNTVER_WINXP;
147 break;
148 case 0:
149 g_enmVbgdNtVer = VBGDNTVER_WIN2K;
150 break;
151 default:
152 Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n", ulMajorVer, ulMinorVer));
153 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
154 }
155 break;
156 case 4:
157 g_enmVbgdNtVer = VBGDNTVER_WINNT4;
158 break;
159 default:
160 if (ulMajorVer < 4)
161 Log(("VBoxGuest::DriverEntry: At least Windows NT4 required! (%u.%u)\n", ulMajorVer, ulMinorVer));
162 else
163 Log(("VBoxGuest::DriverEntry: Too new version %u.%u!\n", ulMajorVer, ulMinorVer));
164 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
165 break;
166 }
167
168 if (NT_SUCCESS(rc))
169 {
170 /*
171 * Setup the driver entry points in pDrvObj.
172 */
173 pDrvObj->DriverUnload = vbgdNtUnload;
174 pDrvObj->MajorFunction[IRP_MJ_CREATE] = vbgdNtCreate;
175 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vbgdNtClose;
176 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vbgdNtIOCtl;
177 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vbgdNtInternalIOCtl;
178 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vbgdNtShutdown;
179 pDrvObj->MajorFunction[IRP_MJ_READ] = vbgdNtNotSupportedStub;
180 pDrvObj->MajorFunction[IRP_MJ_WRITE] = vbgdNtNotSupportedStub;
181#ifdef TARGET_NT4
182 rc = vbgdNt4CreateDevice(pDrvObj, NULL /* pDevObj */, pRegPath);
183#else
184 pDrvObj->MajorFunction[IRP_MJ_PNP] = vbgdNtPnP;
185 pDrvObj->MajorFunction[IRP_MJ_POWER] = vbgdNtPower;
186 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vbgdNtSystemControl;
187 pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)vbgdNtAddDevice;
188#endif
189 }
190
191 Log(("VBoxGuest::DriverEntry returning %#x\n", rc));
192 return rc;
193}
194
195
196#ifndef TARGET_NT4
197/**
198 * Handle request from the Plug & Play subsystem.
199 *
200 * @returns NT status code
201 * @param pDrvObj Driver object
202 * @param pDevObj Device object
203 */
204static NTSTATUS vbgdNtAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
205{
206 NTSTATUS rc;
207 Log(("VBoxGuest::vbgdNtGuestAddDevice\n"));
208
209 /*
210 * Create device.
211 */
212 UNICODE_STRING DevName;
213 RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT);
214 PDEVICE_OBJECT pDeviceObject = NULL;
215 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
216 if (NT_SUCCESS(rc))
217 {
218 /*
219 * Create symbolic link (DOS devices).
220 */
221 UNICODE_STRING DosName;
222 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
223 rc = IoCreateSymbolicLink(&DosName, &DevName);
224 if (NT_SUCCESS(rc))
225 {
226 /*
227 * Setup the device extension.
228 */
229 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension;
230 RT_ZERO(*pDevExt);
231
232 KeInitializeSpinLock(&pDevExt->MouseEventAccessLock);
233
234 pDevExt->pDeviceObject = pDeviceObject;
235 pDevExt->prevDevState = STOPPED;
236 pDevExt->devState = STOPPED;
237
238 pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
239 if (pDevExt->pNextLowerDriver != NULL)
240 {
241 /*
242 * If we reached this point we're fine with the basic driver setup,
243 * so continue to init our own things.
244 */
245#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
246 vbgdNtBugCheckCallback(pDevExt); /* Ignore failure! */
247#endif
248 if (NT_SUCCESS(rc))
249 {
250 /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */
251 pDeviceObject->Flags |= DO_POWER_PAGABLE;
252
253 /* Driver is ready now. */
254 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
255 Log(("VBoxGuest::vbgdNtGuestAddDevice: returning with rc = 0x%x (success)\n", rc));
256 return rc;
257 }
258
259 IoDetachDevice(pDevExt->pNextLowerDriver);
260 }
261 else
262 {
263 Log(("VBoxGuest::vbgdNtGuestAddDevice: IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
264 rc = STATUS_DEVICE_NOT_CONNECTED;
265 }
266
267 /* bail out */
268 IoDeleteSymbolicLink(&DosName);
269 }
270 else
271 Log(("VBoxGuest::vbgdNtGuestAddDevice: IoCreateSymbolicLink failed with rc=%#x!\n", rc));
272 IoDeleteDevice(pDeviceObject);
273 }
274 else
275 Log(("VBoxGuest::vbgdNtGuestAddDevice: IoCreateDevice failed with rc=%#x!\n", rc));
276 Log(("VBoxGuest::vbgdNtGuestAddDevice: returning with rc = 0x%x\n", rc));
277 return rc;
278}
279#endif
280
281
282/**
283 * Debug helper to dump a device resource list.
284 *
285 * @param pResourceList list of device resources.
286 */
287static void vbgdNtShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList)
288{
289#ifdef LOG_ENABLED
290 PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pResourceList->PartialDescriptors;
291 ULONG cResources = pResourceList->Count;
292
293 for (ULONG i = 0; i < cResources; ++i, ++pResource)
294 {
295 ULONG uType = pResource->Type;
296 static char const * const s_apszName[] =
297 {
298 "CmResourceTypeNull",
299 "CmResourceTypePort",
300 "CmResourceTypeInterrupt",
301 "CmResourceTypeMemory",
302 "CmResourceTypeDma",
303 "CmResourceTypeDeviceSpecific",
304 "CmResourceTypeBusNumber",
305 "CmResourceTypeDevicePrivate",
306 "CmResourceTypeAssignedResource",
307 "CmResourceTypeSubAllocateFrom",
308 };
309
310 Log(("VBoxGuest::vbgdNtShowDeviceResources: Type %s",
311 uType < RT_ELEMENTS(s_apszName) ? s_apszName[uType] : "Unknown"));
312
313 switch (uType)
314 {
315 case CmResourceTypePort:
316 case CmResourceTypeMemory:
317 Log(("VBoxGuest::vbgdNtShowDeviceResources: Start %8X%8.8lX length %X\n",
318 pResource->u.Port.Start.HighPart, pResource->u.Port.Start.LowPart,
319 pResource->u.Port.Length));
320 break;
321
322 case CmResourceTypeInterrupt:
323 Log(("VBoxGuest::vbgdNtShowDeviceResources: Level %X, Vector %X, Affinity %X\n",
324 pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector,
325 pResource->u.Interrupt.Affinity));
326 break;
327
328 case CmResourceTypeDma:
329 Log(("VBoxGuest::vbgdNtShowDeviceResources: Channel %d, Port %X\n",
330 pResource->u.Dma.Channel, pResource->u.Dma.Port));
331 break;
332
333 default:
334 Log(("\n"));
335 break;
336 }
337 }
338#endif
339}
340
341
342/**
343 * Global initialisation stuff (PnP + NT4 legacy).
344 *
345 * @param pDevObj Device object.
346 * @param pIrp Request packet.
347 */
348#ifndef TARGET_NT4
349NTSTATUS vbgdNtInit(PDEVICE_OBJECT pDevObj, PIRP pIrp)
350#else
351NTSTATUS vbgdNtInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath)
352#endif
353{
354 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
355#ifndef TARGET_NT4
356 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
357#endif
358
359 Log(("VBoxGuest::vbgdNtInit\n"));
360
361 int rc = STATUS_SUCCESS;
362#ifdef TARGET_NT4
363 /*
364 * Let's have a look at what our PCI adapter offers.
365 */
366 Log(("VBoxGuest::vbgdNtInit: Starting to scan PCI resources of VBoxGuest ...\n"));
367
368 /* Assign the PCI resources. */
369 PCM_RESOURCE_LIST pResourceList = NULL;
370 UNICODE_STRING classNameString;
371 RtlInitUnicodeString(&classNameString, L"VBoxGuestAdapter");
372 rc = HalAssignSlotResources(pRegPath, &classNameString,
373 pDrvObj, pDevObj,
374 PCIBus, pDevExt->busNumber, pDevExt->slotNumber,
375 &pResourceList);
376 if (pResourceList && pResourceList->Count > 0)
377 vbgdNtShowDeviceResources(&pResourceList->List[0].PartialResourceList);
378 if (NT_SUCCESS(rc))
379 rc = vbgdNtScanPCIResourceList(pResourceList, pDevExt);
380#else
381 if (pStack->Parameters.StartDevice.AllocatedResources->Count > 0)
382 vbgdNtShowDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList);
383 if (NT_SUCCESS(rc))
384 rc = vbgdNtScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated, pDevExt);
385#endif
386 if (NT_SUCCESS(rc))
387 {
388 /*
389 * Map physical address of VMMDev memory into MMIO region
390 * and init the common device extension bits.
391 */
392 void *pvMMIOBase = NULL;
393 uint32_t cbMMIO = 0;
394 rc = vbgdNtMapVMMDevMemory(pDevExt,
395 pDevExt->vmmDevPhysMemoryAddress,
396 pDevExt->vmmDevPhysMemoryLength,
397 &pvMMIOBase,
398 &cbMMIO);
399 if (NT_SUCCESS(rc))
400 {
401 pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
402
403 Log(("VBoxGuest::vbgdNtInit: pvMMIOBase = 0x%p, pDevExt = 0x%p, pDevExt->Core.pVMMDevMemory = 0x%p\n",
404 pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL));
405
406 int vrc = VBoxGuestInitDevExt(&pDevExt->Core,
407 pDevExt->Core.IOPortBase,
408 pvMMIOBase, cbMMIO,
409 vbgdNtVersionToOSType(g_enmVbgdNtVer),
410 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
411 if (RT_FAILURE(vrc))
412 {
413 Log(("VBoxGuest::vbgdNtInit: Could not init device extension, rc = %Rrc!\n", vrc));
414 rc = STATUS_DEVICE_CONFIGURATION_ERROR;
415 }
416 }
417 else
418 Log(("VBoxGuest::vbgdNtInit: Could not map physical address of VMMDev, rc = 0x%x!\n", rc));
419 }
420
421 if (NT_SUCCESS(rc))
422 {
423 int vrc = VbglGRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest,
424 sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
425 if (RT_FAILURE(vrc))
426 {
427 Log(("VBoxGuest::vbgdNtInit: Alloc for pPowerStateRequest failed, rc = %Rrc\n", vrc));
428 rc = STATUS_UNSUCCESSFUL;
429 }
430 }
431
432 if (NT_SUCCESS(rc))
433 {
434 /*
435 * Register DPC and ISR.
436 */
437 Log(("VBoxGuest::vbgdNtInit: Initializing DPC/ISR ...\n"));
438
439 IoInitializeDpcRequest(pDevExt->pDeviceObject, vbgdNtDpcHandler);
440#ifdef TARGET_NT4
441 ULONG uInterruptVector;
442 KIRQL irqLevel;
443 /* Get an interrupt vector. */
444 /* Only proceed if the device provides an interrupt. */
445 if ( pDevExt->interruptLevel
446 || pDevExt->interruptVector)
447 {
448 Log(("VBoxGuest::vbgdNtInit: Getting interrupt vector (HAL): Bus: %u, IRQL: %u, Vector: %u\n",
449 pDevExt->busNumber, pDevExt->interruptLevel, pDevExt->interruptVector));
450
451 uInterruptVector = HalGetInterruptVector(PCIBus,
452 pDevExt->busNumber,
453 pDevExt->interruptLevel,
454 pDevExt->interruptVector,
455 &irqLevel,
456 &pDevExt->interruptAffinity);
457 Log(("VBoxGuest::vbgdNtInit: HalGetInterruptVector returns vector %u\n", uInterruptVector));
458 if (uInterruptVector == 0)
459 Log(("VBoxGuest::vbgdNtInit: No interrupt vector found!\n"));
460 }
461 else
462 Log(("VBoxGuest::vbgdNtInit: Device does not provide an interrupt!\n"));
463#endif
464 if (pDevExt->interruptVector)
465 {
466 Log(("VBoxGuest::vbgdNtInit: Connecting interrupt ...\n"));
467
468 rc = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */
469 (PKSERVICE_ROUTINE)vbgdNtIsrHandler, /* Our ISR handler. */
470 pDevExt, /* Device context. */
471 NULL, /* Optional spinlock. */
472#ifdef TARGET_NT4
473 uInterruptVector, /* Interrupt vector. */
474 irqLevel, /* Interrupt level. */
475 irqLevel, /* Interrupt level. */
476#else
477 pDevExt->interruptVector, /* Interrupt vector. */
478 (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */
479 (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */
480#endif
481 pDevExt->interruptMode, /* LevelSensitive or Latched. */
482 TRUE, /* Shareable interrupt. */
483 pDevExt->interruptAffinity, /* CPU affinity. */
484 FALSE); /* Don't save FPU stack. */
485 if (NT_ERROR(rc))
486 Log(("VBoxGuest::vbgdNtInit: Could not connect interrupt, rc = 0x%x\n", rc));
487 }
488 else
489 Log(("VBoxGuest::vbgdNtInit: No interrupt vector found!\n"));
490 }
491
492
493#ifdef VBOX_WITH_HGCM
494 Log(("VBoxGuest::vbgdNtInit: Allocating kernel session data ...\n"));
495 int vrc = VBoxGuestCreateKernelSession(&pDevExt->Core, &pDevExt->pKernelSession);
496 if (RT_FAILURE(vrc))
497 {
498 Log(("VBoxGuest::vbgdNtInit: Failed to allocated kernel session data! rc = %Rrc\n", rc));
499 rc = STATUS_UNSUCCESSFUL;
500 }
501#endif
502
503 if (RT_SUCCESS(rc))
504 {
505 ULONG ulValue = 0;
506 NTSTATUS rcNt = vbgdNtRegistryReadDWORD(RTL_REGISTRY_SERVICES, L"VBoxGuest", L"LoggingEnabled", &ulValue);
507 if (NT_SUCCESS(rcNt))
508 {
509 pDevExt->Core.fLoggingEnabled = ulValue >= 0xFF;
510 if (pDevExt->Core.fLoggingEnabled)
511 Log(("Logging to release log enabled (0x%x)", ulValue));
512 }
513
514 /* Ready to rumble! */
515 Log(("VBoxGuest::vbgdNtInit: Device is ready!\n"));
516 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, WORKING);
517 }
518 else
519 pDevExt->pInterruptObject = NULL;
520
521 /** @todo r=bird: The error cleanup here is completely missing. We'll leak a
522 * whole bunch of things... */
523
524 Log(("VBoxGuest::vbgdNtInit: Returned with rc = 0x%x\n", rc));
525 return rc;
526}
527
528
529/**
530 * Cleans up hardware resources.
531 * Do not delete DevExt here.
532 *
533 * @param pDrvObj Driver object.
534 */
535NTSTATUS vbgdNtCleanup(PDEVICE_OBJECT pDevObj)
536{
537 Log(("VBoxGuest::vbgdNtCleanup\n"));
538
539 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
540 if (pDevExt)
541 {
542
543#if 0 /* @todo: test & enable cleaning global session data */
544#ifdef VBOX_WITH_HGCM
545 if (pDevExt->pKernelSession)
546 {
547 VBoxGuestCloseSession(pDevExt, pDevExt->pKernelSession);
548 pDevExt->pKernelSession = NULL;
549 }
550#endif
551#endif
552
553 if (pDevExt->pInterruptObject)
554 {
555 IoDisconnectInterrupt(pDevExt->pInterruptObject);
556 pDevExt->pInterruptObject = NULL;
557 }
558
559 /** @todo: cleanup the rest stuff */
560
561
562#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
563 hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */
564#endif
565 /* According to MSDN we have to unmap previously mapped memory. */
566 vbgdNtUnmapVMMDevMemory(pDevExt);
567 }
568 return STATUS_SUCCESS;
569}
570
571
572/**
573 * Unload the driver.
574 *
575 * @param pDrvObj Driver object.
576 */
577static void vbgdNtUnload(PDRIVER_OBJECT pDrvObj)
578{
579 Log(("VBoxGuest::vbgdNtGuestUnload\n"));
580#ifdef TARGET_NT4
581 vbgdNtCleanup(pDrvObj->DeviceObject);
582
583 /* Destroy device extension and clean up everything else. */
584 if (pDrvObj->DeviceObject && pDrvObj->DeviceObject->DeviceExtension)
585 VBoxGuestDeleteDevExt((PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension);
586
587 /*
588 * I don't think it's possible to unload a driver which processes have
589 * opened, at least we'll blindly assume that here.
590 */
591 UNICODE_STRING DosName;
592 RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS);
593 NTSTATUS rc = IoDeleteSymbolicLink(&DosName);
594
595 IoDeleteDevice(pDrvObj->DeviceObject);
596#else /* !TARGET_NT4 */
597 /* On a PnP driver this routine will be called after
598 * IRP_MN_REMOVE_DEVICE (where we already did the cleanup),
599 * so don't do anything here (yet). */
600#endif /* !TARGET_NT4 */
601
602 Log(("VBoxGuest::vbgdNtGuestUnload: returning\n"));
603}
604
605
606/**
607 * Create (i.e. Open) file entry point.
608 *
609 * @param pDevObj Device object.
610 * @param pIrp Request packet.
611 */
612static NTSTATUS vbgdNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
613{
614 /** @todo AssertPtrReturn(pIrp); */
615 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
616 /** @todo AssertPtrReturn(pStack); */
617 PFILE_OBJECT pFileObj = pStack->FileObject;
618 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
619 NTSTATUS rc = STATUS_SUCCESS;
620
621 if (pDevExt->devState != WORKING)
622 {
623 Log(("VBoxGuest::vbgdNtGuestCreate: device is not working currently: %d!\n", pDevExt->devState));
624 rc = STATUS_UNSUCCESSFUL;
625 }
626 else if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
627 {
628 /*
629 * We are not remotely similar to a directory...
630 * (But this is possible.)
631 */
632 Log(("VBoxGuest::vbgdNtGuestCreate: Uhm, we're not a directory!\n"));
633 rc = STATUS_NOT_A_DIRECTORY;
634 }
635 else
636 {
637#ifdef VBOX_WITH_HGCM
638 if (pFileObj)
639 {
640 Log(("VBoxGuest::vbgdNtGuestCreate: File object type = %d\n",
641 pFileObj->Type));
642
643 int vrc;
644 PVBOXGUESTSESSION pSession;
645 if (pFileObj->Type == 5 /* File Object */)
646 {
647 /*
648 * Create a session object if we have a valid file object. This session object
649 * exists for every R3 process.
650 */
651 vrc = VBoxGuestCreateUserSession(&pDevExt->Core, &pSession);
652 }
653 else
654 {
655 /* ... otherwise we've been called from R0! */
656 vrc = VBoxGuestCreateKernelSession(&pDevExt->Core, &pSession);
657 }
658 if (RT_SUCCESS(vrc))
659 pFileObj->FsContext = pSession;
660 }
661#endif
662 }
663
664 /* Complete the request! */
665 pIrp->IoStatus.Information = 0;
666 pIrp->IoStatus.Status = rc;
667 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
668
669 Log(("VBoxGuest::vbgdNtGuestCreate: Returning 0x%x\n", rc));
670 return rc;
671}
672
673
674/**
675 * Close file entry point.
676 *
677 * @param pDevObj Device object.
678 * @param pIrp Request packet.
679 */
680static NTSTATUS vbgdNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
681{
682 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
683 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
684 PFILE_OBJECT pFileObj = pStack->FileObject;
685
686 Log(("VBoxGuest::vbgdNtGuestClose: pDevExt=0x%p pFileObj=0x%p FsContext=0x%p\n",
687 pDevExt, pFileObj, pFileObj->FsContext));
688
689#ifdef VBOX_WITH_HGCM
690 /* Close both, R0 and R3 sessions. */
691 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
692 if (pSession)
693 VBoxGuestCloseSession(&pDevExt->Core, pSession);
694#endif
695
696 pFileObj->FsContext = NULL;
697 pIrp->IoStatus.Information = 0;
698 pIrp->IoStatus.Status = STATUS_SUCCESS;
699 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
700
701 return STATUS_SUCCESS;
702}
703
704
705/**
706 * Device I/O Control entry point.
707 *
708 * @param pDevObj Device object.
709 * @param pIrp Request packet.
710 */
711static NTSTATUS vbgdNtIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
712{
713 NTSTATUS Status = STATUS_SUCCESS;
714 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
715 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
716 unsigned int uCmd = (unsigned int)pStack->Parameters.DeviceIoControl.IoControlCode;
717
718 char *pBuf = (char *)pIrp->AssociatedIrp.SystemBuffer; /* All requests are buffered. */
719 size_t cbData = pStack->Parameters.DeviceIoControl.InputBufferLength;
720 size_t cbOut = 0;
721
722 /* Do we have a file object associated?*/
723 PFILE_OBJECT pFileObj = pStack->FileObject;
724 PVBOXGUESTSESSION pSession = NULL;
725 if (pFileObj) /* ... then we might have a session object as well! */
726 pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
727
728 Log(("VBoxGuest::vbgdNtIOCtl: uCmd=%u, pDevExt=0x%p, pSession=0x%p\n",
729 uCmd, pDevExt, pSession));
730
731 /* We don't have a session associated with the file object? So this seems
732 * to be a kernel call then. */
733 /** @todo r=bird: What on earth is this supposed to be? Each kernel session
734 * shall have its own context of course, no hacks, pleeease. */
735 if (pSession == NULL)
736 {
737 Log(("VBoxGuest::vbgdNtIOCtl: Using kernel session data ...\n"));
738 pSession = pDevExt->pKernelSession;
739 }
740
741 /*
742 * First process Windows specific stuff which cannot be handled
743 * by the common code used on all other platforms. In the default case
744 * we then finally handle the common cases.
745 */
746 switch (uCmd)
747 {
748#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
749 case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION:
750 {
751 LogRel(("VBoxGuest::vbgdNtIOCtl: ENABLE_VRDP_SESSION: Currently: %sabled\n",
752 pDevExt->fVRDPEnabled? "en": "dis"));
753 if (!pDevExt->fVRDPEnabled)
754 {
755 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
756
757 pDevExt->fVRDPEnabled = true;
758 LogRel(("VBoxGuest::vbgdNtIOCtl: ENABLE_VRDP_SESSION: Current active console ID: 0x%08X\n",
759 pSharedUserData->ActiveConsoleId));
760 pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId;
761 pSharedUserData->ActiveConsoleId = 2;
762 }
763 break;
764 }
765
766 case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION:
767 {
768 LogRel(("VBoxGuest::vbgdNtIOCtl: DISABLE_VRDP_SESSION: Currently: %sabled\n",
769 pDevExt->fVRDPEnabled? "en": "dis"));
770 if (pDevExt->fVRDPEnabled)
771 {
772 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
773
774 pDevExt->fVRDPEnabled = false;
775 Log(("VBoxGuest::vbgdNtIOCtl: DISABLE_VRDP_SESSION: Current active console ID: 0x%08X\n",
776 pSharedUserData->ActiveConsoleId));
777 pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId;
778 pDevExt->ulOldActiveConsoleId = 0;
779 }
780 break;
781 }
782#else
783 /* Add at least one (bogus) fall through case to shut up MSVC! */
784 case 0:
785#endif
786 default:
787 {
788 /*
789 * Process the common IOCtls.
790 */
791 size_t cbDataReturned;
792 int vrc = VBoxGuestCommonIOCtl(uCmd, &pDevExt->Core, pSession, pBuf, cbData, &cbDataReturned);
793
794 Log(("VBoxGuest::vbgdNtGuestDeviceControl: rc=%Rrc, pBuf=0x%p, cbData=%u, cbDataReturned=%u\n",
795 vrc, pBuf, cbData, cbDataReturned));
796
797 if (RT_SUCCESS(vrc))
798 {
799 if (RT_UNLIKELY(cbDataReturned > cbData))
800 {
801 Log(("VBoxGuest::vbgdNtGuestDeviceControl: Too much output data %u - expected %u!\n", cbDataReturned, cbData));
802 cbDataReturned = cbData;
803 Status = STATUS_BUFFER_TOO_SMALL;
804 }
805 if (cbDataReturned > 0)
806 cbOut = cbDataReturned;
807 }
808 else
809 {
810 if ( vrc == VERR_NOT_SUPPORTED
811 || vrc == VERR_INVALID_PARAMETER)
812 Status = STATUS_INVALID_PARAMETER;
813 else if (vrc == VERR_OUT_OF_RANGE)
814 Status = STATUS_INVALID_BUFFER_SIZE;
815 else
816 Status = STATUS_UNSUCCESSFUL;
817 }
818 break;
819 }
820 }
821
822 pIrp->IoStatus.Status = Status;
823 pIrp->IoStatus.Information = cbOut;
824
825 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
826
827 //Log(("VBoxGuest::vbgdNtGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status));
828 return Status;
829}
830
831/**
832 * Internal Device I/O Control entry point.
833 *
834 * @param pDevObj Device object.
835 * @param pIrp Request packet.
836 */
837static NTSTATUS vbgdNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
838{
839 NTSTATUS Status = STATUS_SUCCESS;
840 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
841 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
842 unsigned int uCmd = (unsigned int)pStack->Parameters.DeviceIoControl.IoControlCode;
843 bool fProcessed = false;
844 unsigned Info = 0;
845
846 /*
847 * Override common behavior of some operations.
848 */
849 /** @todo r=bird: Better to add dedicated worker functions for this! */
850 switch (uCmd)
851 {
852 case VBOXGUEST_IOCTL_SET_MOUSE_NOTIFY_CALLBACK:
853 {
854 PVOID pvBuf = pStack->Parameters.Others.Argument1;
855 size_t cbData = (size_t)pStack->Parameters.Others.Argument2;
856 fProcessed = true;
857 if (cbData != sizeof(VBoxGuestMouseSetNotifyCallback))
858 {
859 AssertFailed();
860 Status = STATUS_INVALID_PARAMETER;
861 break;
862 }
863
864 VBoxGuestMouseSetNotifyCallback *pInfo = (VBoxGuestMouseSetNotifyCallback*)pvBuf;
865
866 /* we need a lock here to avoid concurrency with the set event functionality */
867 KIRQL OldIrql;
868 KeAcquireSpinLock(&pDevExt->MouseEventAccessLock, &OldIrql);
869 pDevExt->Core.MouseNotifyCallback = *pInfo;
870 KeReleaseSpinLock(&pDevExt->MouseEventAccessLock, OldIrql);
871
872 Status = STATUS_SUCCESS;
873 break;
874 }
875
876 default:
877 break;
878 }
879 if (fProcessed)
880 {
881 pIrp->IoStatus.Status = Status;
882 pIrp->IoStatus.Information = Info;
883
884 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
885 return Status;
886 }
887
888 /*
889 * No override, go to common code.
890 */
891 return vbgdNtIOCtl(pDevObj, pIrp);
892}
893
894
895/**
896 * IRP_MJ_SYSTEM_CONTROL handler.
897 *
898 * @returns NT status code
899 * @param pDevObj Device object.
900 * @param pIrp IRP.
901 */
902NTSTATUS vbgdNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
903{
904 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
905
906 Log(("VBoxGuest::vbgdNtGuestSystemControl\n"));
907
908 /* Always pass it on to the next driver. */
909 IoSkipCurrentIrpStackLocation(pIrp);
910
911 return IoCallDriver(pDevExt->pNextLowerDriver, pIrp);
912}
913
914
915/**
916 * IRP_MJ_SHUTDOWN handler.
917 *
918 * @returns NT status code
919 * @param pDevObj Device object.
920 * @param pIrp IRP.
921 */
922NTSTATUS vbgdNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
923{
924 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
925
926 Log(("VBoxGuest::vbgdNtGuestShutdown\n"));
927
928 VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest;
929 if (pReq)
930 {
931 pReq->header.requestType = VMMDevReq_SetPowerStatus;
932 pReq->powerState = VMMDevPowerState_PowerOff;
933
934 int rc = VbglGRPerform(&pReq->header);
935 if (RT_FAILURE(rc))
936 {
937 Log(("VBoxGuest::vbgdNtGuestShutdown: Error performing request to VMMDev! "
938 "rc = %Rrc\n", rc));
939 }
940 }
941 return STATUS_SUCCESS;
942}
943
944
945/**
946 * Stub function for functions we don't implemented.
947 *
948 * @returns STATUS_NOT_SUPPORTED
949 * @param pDevObj Device object.
950 * @param pIrp IRP.
951 */
952NTSTATUS vbgdNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
953{
954 Log(("VBoxGuest::vbgdNtGuestNotSupportedStub\n"));
955
956 pIrp->IoStatus.Information = 0;
957 pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
958 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
959
960 return STATUS_NOT_SUPPORTED;
961}
962
963
964/**
965 * DPC handler.
966 *
967 * @param pDPC DPC descriptor.
968 * @param pDevObj Device object.
969 * @param pIrp Interrupt request packet.
970 * @param pContext Context specific pointer.
971 */
972void vbgdNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
973{
974 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension;
975 Log(("VBoxGuest::vbgdNtGuestDpcHandler: pDevExt=0x%p\n", pDevExt));
976
977 /* test & reset the counter */
978 if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0))
979 {
980 /* we need a lock here to avoid concurrency with the set event ioctl handler thread,
981 * i.e. to prevent the event from destroyed while we're using it */
982 Assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
983 KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessLock);
984
985 if (pDevExt->Core.MouseNotifyCallback.pfnNotify)
986 pDevExt->Core.MouseNotifyCallback.pfnNotify(pDevExt->Core.MouseNotifyCallback.pvUser);
987
988 KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessLock);
989 }
990
991 /* Process the wake-up list we were asked by the scheduling a DPC
992 * in vbgdNtIsrHandler(). */
993 VBoxGuestWaitDoWakeUps(&pDevExt->Core);
994}
995
996
997/**
998 * ISR handler.
999 *
1000 * @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
1001 * @param pInterrupt Interrupt that was triggered.
1002 * @param pServiceContext Context specific pointer.
1003 */
1004BOOLEAN vbgdNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
1005{
1006 PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext;
1007 if (pDevExt == NULL)
1008 return FALSE;
1009
1010 /*Log(("VBoxGuest::vbgdNtGuestIsrHandler: pDevExt = 0x%p, pVMMDevMemory = 0x%p\n",
1011 pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
1012
1013 /* Enter the common ISR routine and do the actual work. */
1014 BOOLEAN fIRQTaken = VBoxGuestCommonISR(&pDevExt->Core);
1015
1016 /* If we need to wake up some events we do that in a DPC to make
1017 * sure we're called at the right IRQL. */
1018 if (fIRQTaken)
1019 {
1020 Log(("VBoxGuest::vbgdNtGuestIsrHandler: IRQ was taken! pInterrupt = 0x%p, pDevExt = 0x%p\n",
1021 pInterrupt, pDevExt));
1022 if (ASMAtomicUoReadU32(&pDevExt->Core.u32MousePosChangedSeq) || !RTListIsEmpty(&pDevExt->Core.WakeUpList))
1023 {
1024 Log(("VBoxGuest::vbgdNtGuestIsrHandler: Requesting DPC ...\n"));
1025 IoRequestDpc(pDevExt->pDeviceObject, pDevExt->pCurrentIrp, NULL);
1026 }
1027 }
1028 return fIRQTaken;
1029}
1030
1031
1032/**
1033 * Overridden routine for mouse polling events.
1034 *
1035 * @param pDevExt Device extension structure.
1036 */
1037void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1038{
1039 NOREF(pDevExt);
1040 /* nothing to do here - i.e. since we can not KeSetEvent from ISR level,
1041 * we rely on the pDevExt->u32MousePosChangedSeq to be set to a non-zero value on a mouse event
1042 * and queue the DPC in our ISR routine in that case doing KeSetEvent from the DPC routine */
1043}
1044
1045
1046/**
1047 * Queries (gets) a DWORD value from the registry.
1048 *
1049 * @return NTSTATUS
1050 * @param ulRoot Relative path root. See RTL_REGISTRY_SERVICES or RTL_REGISTRY_ABSOLUTE.
1051 * @param pwszPath Path inside path root.
1052 * @param pwszName Actual value name to look up.
1053 * @param puValue On input this can specify the default value (if RTL_REGISTRY_OPTIONAL is
1054 * not specified in ulRoot), on output this will retrieve the looked up
1055 * registry value if found.
1056 */
1057NTSTATUS vbgdNtRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue)
1058{
1059 if (!pwszPath || !pwszName || !puValue)
1060 return STATUS_INVALID_PARAMETER;
1061
1062 ULONG ulDefault = *puValue;
1063
1064 RTL_QUERY_REGISTRY_TABLE tblQuery[2];
1065 RtlZeroMemory(tblQuery, sizeof(tblQuery));
1066 /** @todo Add RTL_QUERY_REGISTRY_TYPECHECK! */
1067 tblQuery[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
1068 tblQuery[0].Name = pwszName;
1069 tblQuery[0].EntryContext = puValue;
1070 tblQuery[0].DefaultType = REG_DWORD;
1071 tblQuery[0].DefaultData = &ulDefault;
1072 tblQuery[0].DefaultLength = sizeof(ULONG);
1073
1074 return RtlQueryRegistryValues(ulRoot,
1075 pwszPath,
1076 &tblQuery[0],
1077 NULL /* Context */,
1078 NULL /* Environment */);
1079}
1080
1081
1082/**
1083 * Helper to scan the PCI resource list and remember stuff.
1084 *
1085 * @param pResList Resource list
1086 * @param pDevExt Device extension
1087 */
1088NTSTATUS vbgdNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt)
1089{
1090 /* Enumerate the resource list. */
1091 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Found %d resources\n",
1092 pResList->List->PartialResourceList.Count));
1093
1094 NTSTATUS rc = STATUS_SUCCESS;
1095 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
1096 ULONG rangeCount = 0;
1097 ULONG cMMIORange = 0;
1098 PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->pciBaseAddress;
1099 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
1100 {
1101 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
1102 switch (pPartialData->Type)
1103 {
1104 case CmResourceTypePort:
1105 {
1106 /* Overflow protection. */
1107 if (rangeCount < PCI_TYPE0_ADDRESSES)
1108 {
1109 Log(("VBoxGuest::vbgdNtScanPCIResourceList: I/O range: Base = %08x:%08x, Length = %08x\n",
1110 pPartialData->u.Port.Start.HighPart,
1111 pPartialData->u.Port.Start.LowPart,
1112 pPartialData->u.Port.Length));
1113
1114 /* Save the IO port base. */
1115 /** @todo Not so good.
1116 * Update/bird: What is not so good? That we just consider the last range? */
1117 pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
1118
1119 /* Save resource information. */
1120 pBaseAddress->RangeStart = pPartialData->u.Port.Start;
1121 pBaseAddress->RangeLength = pPartialData->u.Port.Length;
1122 pBaseAddress->RangeInMemory = FALSE;
1123 pBaseAddress->ResourceMapped = FALSE;
1124
1125 Log(("VBoxGuest::vbgdNtScanPCIResourceList: I/O range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
1126 pPartialData->u.Port.Start.HighPart,
1127 pPartialData->u.Port.Start.LowPart,
1128 pPartialData->u.Port.Length));
1129
1130 /* Next item ... */
1131 rangeCount++; pBaseAddress++;
1132 }
1133 break;
1134 }
1135
1136 case CmResourceTypeInterrupt:
1137 {
1138 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Interrupt: Level = %x, Vector = %x, Mode = %x\n",
1139 pPartialData->u.Interrupt.Level,
1140 pPartialData->u.Interrupt.Vector,
1141 pPartialData->Flags));
1142
1143 /* Save information. */
1144 pDevExt->interruptLevel = pPartialData->u.Interrupt.Level;
1145 pDevExt->interruptVector = pPartialData->u.Interrupt.Vector;
1146 pDevExt->interruptAffinity = pPartialData->u.Interrupt.Affinity;
1147
1148 /* Check interrupt mode. */
1149 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
1150 pDevExt->interruptMode = Latched;
1151 else
1152 pDevExt->interruptMode = LevelSensitive;
1153 break;
1154 }
1155
1156 case CmResourceTypeMemory:
1157 {
1158 /* Overflow protection. */
1159 if (rangeCount < PCI_TYPE0_ADDRESSES)
1160 {
1161 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Memory range: Base = %08x:%08x, Length = %08x\n",
1162 pPartialData->u.Memory.Start.HighPart,
1163 pPartialData->u.Memory.Start.LowPart,
1164 pPartialData->u.Memory.Length));
1165
1166 /* We only care about read/write memory. */
1167 /** @todo Reconsider memory type. */
1168 if ( cMMIORange == 0 /* Only care about the first MMIO range (!!!). */
1169 && (pPartialData->Flags & VBOX_CM_PRE_VISTA_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
1170 {
1171 /* Save physical MMIO base + length for VMMDev. */
1172 pDevExt->vmmDevPhysMemoryAddress = pPartialData->u.Memory.Start;
1173 pDevExt->vmmDevPhysMemoryLength = (ULONG)pPartialData->u.Memory.Length;
1174
1175 /* Save resource information. */
1176 pBaseAddress->RangeStart = pPartialData->u.Memory.Start;
1177 pBaseAddress->RangeLength = pPartialData->u.Memory.Length;
1178 pBaseAddress->RangeInMemory = TRUE;
1179 pBaseAddress->ResourceMapped = FALSE;
1180
1181 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
1182 pPartialData->u.Memory.Start.HighPart,
1183 pPartialData->u.Memory.Start.LowPart,
1184 pPartialData->u.Memory.Length));
1185
1186 /* Next item ... */
1187 rangeCount++; pBaseAddress++; cMMIORange++;
1188 }
1189 else
1190 {
1191 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Ignoring memory: Flags = %08x\n",
1192 pPartialData->Flags));
1193 }
1194 }
1195 break;
1196 }
1197
1198 default:
1199 {
1200 Log(("VBoxGuest::vbgdNtScanPCIResourceList: Unhandled resource found, type = %d\n", pPartialData->Type));
1201 break;
1202 }
1203 }
1204 }
1205
1206 /* Memorize the number of resources found. */
1207 pDevExt->pciAddressCount = rangeCount;
1208 return rc;
1209}
1210
1211
1212/**
1213 * Maps the I/O space from VMMDev to virtual kernel address space.
1214 *
1215 * @return NTSTATUS
1216 *
1217 * @param pDevExt The device extension.
1218 * @param PhysAddr Physical address to map.
1219 * @param cbToMap Number of bytes to map.
1220 * @param ppvMMIOBase Pointer of mapped I/O base.
1221 * @param pcbMMIO Length of mapped I/O base.
1222 */
1223NTSTATUS vbgdNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap,
1224 void **ppvMMIOBase, uint32_t *pcbMMIO)
1225{
1226 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
1227 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
1228 /* pcbMMIO is optional. */
1229
1230 NTSTATUS rc = STATUS_SUCCESS;
1231 if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */
1232 {
1233 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached);
1234 Log(("VBoxGuest::vbgdNtMapVMMDevMemory: pVMMDevMemory = 0x%x\n", pVMMDevMemory));
1235 if (pVMMDevMemory)
1236 {
1237 Log(("VBoxGuest::vbgdNtMapVMMDevMemory: VMMDevMemory: Version = 0x%x, Size = %d\n",
1238 pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
1239
1240 /* Check version of the structure; do we have the right memory version? */
1241 if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION)
1242 {
1243 /* Save results. */
1244 *ppvMMIOBase = pVMMDevMemory;
1245 if (pcbMMIO) /* Optional. */
1246 *pcbMMIO = pVMMDevMemory->u32Size;
1247
1248 Log(("VBoxGuest::vbgdNtMapVMMDevMemory: VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n",
1249 *ppvMMIOBase));
1250 }
1251 else
1252 {
1253 /* Not our version, refuse operation and unmap the memory. */
1254 Log(("VBoxGuest::vbgdNtMapVMMDevMemory: Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version));
1255 vbgdNtUnmapVMMDevMemory(pDevExt);
1256 rc = STATUS_UNSUCCESSFUL;
1257 }
1258 }
1259 else
1260 rc = STATUS_UNSUCCESSFUL;
1261 }
1262 return rc;
1263}
1264
1265
1266/**
1267 * Unmaps the VMMDev I/O range from kernel space.
1268 *
1269 * @param pDevExt The device extension.
1270 */
1271void vbgdNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt)
1272{
1273 Log(("VBoxGuest::vbgdNtUnmapVMMDevMemory: pVMMDevMemory = 0x%x\n", pDevExt->Core.pVMMDevMemory));
1274 if (pDevExt->Core.pVMMDevMemory)
1275 {
1276 MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->vmmDevPhysMemoryLength);
1277 pDevExt->Core.pVMMDevMemory = NULL;
1278 }
1279
1280 pDevExt->vmmDevPhysMemoryAddress.QuadPart = 0;
1281 pDevExt->vmmDevPhysMemoryLength = 0;
1282}
1283
1284
1285VBOXOSTYPE vbgdNtVersionToOSType(VBGDNTVER enmNtVer)
1286{
1287 VBOXOSTYPE enmOsType;
1288 switch (enmNtVer)
1289 {
1290 case VBGDNTVER_WINNT4:
1291 enmOsType = VBOXOSTYPE_WinNT4;
1292 break;
1293
1294 case VBGDNTVER_WIN2K:
1295 enmOsType = VBOXOSTYPE_Win2k;
1296 break;
1297
1298 case VBGDNTVER_WINXP:
1299#if ARCH_BITS == 64
1300 enmOsType = VBOXOSTYPE_WinXP_x64;
1301#else
1302 enmOsType = VBOXOSTYPE_WinXP;
1303#endif
1304 break;
1305
1306 case VBGDNTVER_WIN2K3:
1307#if ARCH_BITS == 64
1308 enmOsType = VBOXOSTYPE_Win2k3_x64;
1309#else
1310 enmOsType = VBOXOSTYPE_Win2k3;
1311#endif
1312 break;
1313
1314 case VBGDNTVER_WINVISTA:
1315#if ARCH_BITS == 64
1316 enmOsType = VBOXOSTYPE_WinVista_x64;
1317#else
1318 enmOsType = VBOXOSTYPE_WinVista;
1319#endif
1320 break;
1321
1322 case VBGDNTVER_WIN7:
1323#if ARCH_BITS == 64
1324 enmOsType = VBOXOSTYPE_Win7_x64;
1325#else
1326 enmOsType = VBOXOSTYPE_Win7;
1327#endif
1328 break;
1329
1330 case VBGDNTVER_WIN8:
1331#if ARCH_BITS == 64
1332 enmOsType = VBOXOSTYPE_Win8_x64;
1333#else
1334 enmOsType = VBOXOSTYPE_Win8;
1335#endif
1336 break;
1337
1338 case VBGDNTVER_WIN81:
1339#if ARCH_BITS == 64
1340 enmOsType = VBOXOSTYPE_Win81_x64;
1341#else
1342 enmOsType = VBOXOSTYPE_Win81;
1343#endif
1344 break;
1345
1346 default:
1347 /* We don't know, therefore NT family. */
1348 enmOsType = VBOXOSTYPE_WinNT;
1349 break;
1350 }
1351 return enmOsType;
1352}
1353
1354#ifdef DEBUG
1355
1356/**
1357 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
1358 */
1359static uint32_t vboxugestwinAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
1360{
1361 AssertPtrReturn(pu32Bits, 0);
1362 LogFlowFunc(("*pu32Bits=0x%x, u32Mask=0x%x\n", *(uint32_t *)pu32Bits, u32Mask));
1363 uint32_t u32Result = 0;
1364 uint32_t u32WorkingMask = u32Mask;
1365 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1366
1367 while (iBitOffset > 0)
1368 {
1369 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
1370 if (fSet)
1371 u32Result |= 1 << (iBitOffset - 1);
1372 u32WorkingMask &= ~(1 << (iBitOffset - 1));
1373 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1374 }
1375 LogFlowFunc(("Returning 0x%x\n", u32Result));
1376 return u32Result;
1377}
1378
1379
1380static void vbgdNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp)
1381{
1382 ULONG u32Bits2 = u32Bits;
1383 uint32_t u32Result = vboxugestwinAtomicBitsTestAndClear(&u32Bits2, u32Mask);
1384 if ( u32Result != u32Exp
1385 || (u32Bits2 & u32Mask)
1386 || (u32Bits2 & u32Result)
1387 || ((u32Bits2 | u32Result) != u32Bits)
1388 )
1389 AssertLogRelMsgFailed(("%s: TEST FAILED: u32Mask=0x%x, u32Bits (before)=0x%x, u32Bits (after)=0x%x, u32Result=0x%x, u32Exp=ox%x\n",
1390 __PRETTY_FUNCTION__, u32Mask, u32Bits, u32Bits2,
1391 u32Result));
1392}
1393
1394
1395static void vbgdNtDoTests(void)
1396{
1397 vbgdNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
1398 vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0, 0);
1399 vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
1400 vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
1401 vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
1402 vbgdNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
1403}
1404
1405#endif /* DEBUG */
1406
1407#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
1408
1409/*
1410 * DPC latency checker.
1411 */
1412
1413/**
1414 * One DPC latency sample.
1415 */
1416typedef struct DPCSAMPLE
1417{
1418 LARGE_INTEGER PerfDelta;
1419 LARGE_INTEGER PerfCounter;
1420 LARGE_INTEGER PerfFrequency;
1421 uint64_t u64TSC;
1422} DPCSAMPLE;
1423AssertCompileSize(DPCSAMPLE, 4*8);
1424
1425/**
1426 * The DPC latency measurement workset.
1427 */
1428typedef struct DPCDATA
1429{
1430 KDPC Dpc;
1431 KTIMER Timer;
1432 KSPIN_LOCK SpinLock;
1433
1434 ULONG ulTimerRes;
1435
1436 bool volatile fFinished;
1437
1438 /** The timer interval (relative). */
1439 LARGE_INTEGER DueTime;
1440
1441 LARGE_INTEGER PerfCounterPrev;
1442
1443 /** Align the sample array on a 64 byte boundrary just for the off chance
1444 * that we'll get cache line aligned memory backing this structure. */
1445 uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7];
1446
1447 int cSamples;
1448 DPCSAMPLE aSamples[8192];
1449} DPCDATA;
1450
1451AssertCompileMemberAlignment(DPCDATA, aSamples, 64);
1452
1453# define VBOXGUEST_DPC_TAG 'DPCS'
1454
1455
1456/**
1457 * DPC callback routine for the DPC latency measurement code.
1458 *
1459 * @param pDpc The DPC, not used.
1460 * @param pvDeferredContext Pointer to the DPCDATA.
1461 * @param SystemArgument1 System use, ignored.
1462 * @param SystemArgument2 System use, ignored.
1463 */
1464static VOID vbgdNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
1465{
1466 DPCDATA *pData = (DPCDATA *)pvDeferredContext;
1467
1468 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
1469
1470 if (pData->cSamples >= RT_ELEMENTS(pData->aSamples))
1471 pData->fFinished = true;
1472 else
1473 {
1474 DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++];
1475
1476 pSample->u64TSC = ASMReadTSC();
1477 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
1478 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
1479
1480 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
1481
1482 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1483 }
1484
1485 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
1486}
1487
1488
1489/**
1490 * Handles the DPC latency checker request.
1491 *
1492 * @returns VBox status code.
1493 */
1494int VbgdNtIOCtl_DpcLatencyChecker(void)
1495{
1496 /*
1497 * Allocate a block of non paged memory for samples and related data.
1498 */
1499 DPCDATA *pData = (DPCDATA *)ExAllocatePoolWithTag(NonPagedPool, sizeof(DPCDATA), VBOXGUEST_DPC_TAG);
1500 if (!pData)
1501 {
1502 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
1503 return VERR_NO_MEMORY;
1504 }
1505
1506 /*
1507 * Initialize the data.
1508 */
1509 KeInitializeDpc(&pData->Dpc, vbgdNtDpcLatencyCallback, pData);
1510 KeInitializeTimer(&pData->Timer);
1511 KeInitializeSpinLock(&pData->SpinLock);
1512
1513 pData->fFinished = false;
1514 pData->cSamples = 0;
1515 pData->PerfCounterPrev.QuadPart = 0;
1516
1517 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
1518 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
1519
1520 /*
1521 * Start the DPC measurements and wait for a full set.
1522 */
1523 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1524
1525 while (!pData->fFinished)
1526 {
1527 LARGE_INTEGER Interval;
1528 Interval.QuadPart = -100 * 1000 * 10;
1529 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
1530 }
1531
1532 ExSetTimerResolution(0, 0);
1533
1534 /*
1535 * Log everything to the host.
1536 */
1537 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
1538 for (int i = 0; i < pData->cSamples; i++)
1539 {
1540 DPCSAMPLE *pSample = &pData->aSamples[i];
1541
1542 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
1543 i,
1544 pSample->PerfDelta.QuadPart,
1545 pSample->PerfCounter.QuadPart,
1546 pSample->PerfFrequency.QuadPart,
1547 pSample->u64TSC);
1548 }
1549
1550 ExFreePoolWithTag(pData, VBOXGUEST_DPC_TAG);
1551 return VINF_SUCCESS;
1552}
1553
1554#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
1555
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