VirtualBox

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

Last change on this file since 44990 was 44990, checked in by vboxsync, 12 years ago

VBoxGuest-win*: More cleanups.

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