VirtualBox

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

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

vboxguest/win: propagate VERR_OUT_OF_RANGE, required by CrOpenGL

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.0 KB
Line 
1/** @file
2 *
3 * VBoxGuest - Windows specifics.
4 *
5 * Copyright (C) 2010 Oracle Corporation
6 *
7 * This file is part of VirtualBox Open Source Edition (OSE), as
8 * available from http://www.virtualbox.org. This file is free software;
9 * you can redistribute it and/or modify it under the terms of the GNU
10 * General Public License (GPL) as published by the Free Software
11 * Foundation, in version 2 as it comes in the "COPYING" file of the
12 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
13 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
14 */
15
16/*******************************************************************************
17* Header Files *
18*******************************************************************************/
19#define LOG_GROUP LOG_GROUP_SUP_DRV
20#include "VBoxGuest-win.h"
21#include "VBoxGuestInternal.h"
22
23#include <iprt/asm.h>
24#include <iprt/asm-amd64-x86.h>
25
26#include <VBox/log.h>
27#include <VBox/VBoxGuestLib.h>
28
29/*
30 * XP DDK #defines ExFreePool to ExFreePoolWithTag. The latter does not exist
31 * on NT4, so... The same for ExAllocatePool.
32 */
33#ifdef TARGET_NT4
34# undef ExAllocatePool
35# undef ExFreePool
36#endif
37
38/*******************************************************************************
39* Internal Functions *
40*******************************************************************************/
41RT_C_DECLS_BEGIN
42static NTSTATUS vboxguestwinAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj);
43static void vboxguestwinUnload(PDRIVER_OBJECT pDrvObj);
44static NTSTATUS vboxguestwinCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp);
45static NTSTATUS vboxguestwinClose(PDEVICE_OBJECT pDevObj, PIRP pIrp);
46static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
47static NTSTATUS vboxguestwinInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
48static NTSTATUS vboxguestwinRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue);
49static NTSTATUS vboxguestwinSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
50static NTSTATUS vboxguestwinShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp);
51static NTSTATUS vboxguestwinNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp);
52#ifdef DEBUG
53static void vboxguestwinDoTests(void);
54#endif
55RT_C_DECLS_END
56
57
58/*******************************************************************************
59* Exported Functions *
60*******************************************************************************/
61RT_C_DECLS_BEGIN
62ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath);
63RT_C_DECLS_END
64
65#ifdef ALLOC_PRAGMA
66#pragma alloc_text (INIT, DriverEntry)
67#pragma alloc_text (PAGE, vboxguestwinAddDevice)
68#pragma alloc_text (PAGE, vboxguestwinUnload)
69#pragma alloc_text (PAGE, vboxguestwinCreate)
70#pragma alloc_text (PAGE, vboxguestwinClose)
71#pragma alloc_text (PAGE, vboxguestwinIOCtl)
72#pragma alloc_text (PAGE, vboxguestwinShutdown)
73#pragma alloc_text (PAGE, vboxguestwinNotSupportedStub)
74#pragma alloc_text (PAGE, vboxguestwinScanPCIResourceList)
75#endif
76
77/** The detected Windows version. */
78winVersion_t g_winVersion;
79
80/**
81 * Driver entry point.
82 *
83 * @returns appropriate status code.
84 * @param pDrvObj Pointer to driver object.
85 * @param pRegPath Registry base path.
86 */
87ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath)
88{
89 NTSTATUS rc = STATUS_SUCCESS;
90
91 Log(("VBoxGuest::DriverEntry. Driver built: %s %s\n", __DATE__, __TIME__));
92
93 ULONG majorVersion;
94 ULONG minorVersion;
95 ULONG buildNumber;
96 BOOLEAN bCheckedBuild = PsGetVersion(&majorVersion, &minorVersion, &buildNumber, NULL);
97 Log(("VBoxGuest::DriverEntry: Running on Windows NT version %d.%d, build %d\n", majorVersion, minorVersion, buildNumber));
98 if (bCheckedBuild)
99 Log(("VBoxGuest::DriverEntry: Running on a Windows checked build (debug)!\n"));
100#ifdef DEBUG
101 vboxguestwinDoTests();
102#endif
103 switch (majorVersion)
104 {
105 case 6: /* Windows Vista or Windows 7 (based on minor ver) */
106 switch (minorVersion)
107 {
108 case 0: /* Note: Also could be Windows 2008 Server! */
109 g_winVersion = WINVISTA;
110 break;
111 case 1: /* Note: Also could be Windows 2008 Server R2! */
112 g_winVersion = WIN7;
113 break;
114 case 2:
115 g_winVersion = WIN8;
116 break;
117 default:
118 Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n",
119 majorVersion, minorVersion));
120 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
121 break;
122 }
123 break;
124 case 5:
125 switch (minorVersion)
126 {
127 case 2:
128 g_winVersion = WIN2K3;
129 break;
130 case 1:
131 g_winVersion = WINXP;
132 break;
133 case 0:
134 g_winVersion = WIN2K;
135 break;
136 default:
137 Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n",
138 majorVersion, minorVersion));
139 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
140 }
141 break;
142 case 4:
143 g_winVersion = WINNT4;
144 break;
145 default:
146 Log(("VBoxGuest::DriverEntry: At least Windows NT4 required!\n"));
147 rc = STATUS_DRIVER_UNABLE_TO_LOAD;
148 }
149
150 if (NT_SUCCESS(rc))
151 {
152 /*
153 * Setup the driver entry points in pDrvObj.
154 */
155 pDrvObj->DriverUnload = vboxguestwinUnload;
156 pDrvObj->MajorFunction[IRP_MJ_CREATE] = vboxguestwinCreate;
157 pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vboxguestwinClose;
158 pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vboxguestwinIOCtl;
159 pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vboxguestwinInternalIOCtl;
160 pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vboxguestwinShutdown;
161 pDrvObj->MajorFunction[IRP_MJ_READ] = vboxguestwinNotSupportedStub;
162 pDrvObj->MajorFunction[IRP_MJ_WRITE] = vboxguestwinNotSupportedStub;
163#ifdef TARGET_NT4
164 rc = vboxguestwinnt4CreateDevice(pDrvObj, NULL /* pDevObj */, pRegPath);
165#else
166 pDrvObj->MajorFunction[IRP_MJ_PNP] = vboxguestwinPnP;
167 pDrvObj->MajorFunction[IRP_MJ_POWER] = vboxguestwinPower;
168 pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vboxguestwinSystemControl;
169 pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)vboxguestwinAddDevice;
170#endif
171 }
172
173 Log(("VBoxGuest::DriverEntry returning %#x\n", rc));
174 return rc;
175}
176
177
178#ifndef TARGET_NT4
179/**
180 * Handle request from the Plug & Play subsystem.
181 *
182 * @returns NT status code
183 * @param pDrvObj Driver object
184 * @param pDevObj Device object
185 */
186static NTSTATUS vboxguestwinAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj)
187{
188 NTSTATUS rc;
189 Log(("VBoxGuest::vboxguestwinGuestAddDevice\n"));
190
191 /*
192 * Create device.
193 */
194 PDEVICE_OBJECT pDeviceObject = NULL;
195 PVBOXGUESTDEVEXT pDevExt = NULL;
196 UNICODE_STRING devName;
197 UNICODE_STRING win32Name;
198 RtlInitUnicodeString(&devName, VBOXGUEST_DEVICE_NAME_NT);
199 rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXT), &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
200 if (NT_SUCCESS(rc))
201 {
202 /*
203 * Create symbolic link (DOS devices).
204 */
205 RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS);
206 rc = IoCreateSymbolicLink(&win32Name, &devName);
207 if (NT_SUCCESS(rc))
208 {
209 /*
210 * Setup the device extension.
211 */
212 pDevExt = (PVBOXGUESTDEVEXT)pDeviceObject->DeviceExtension;
213 RtlZeroMemory(pDevExt, sizeof(VBOXGUESTDEVEXT));
214
215 KeInitializeSpinLock(&pDevExt->win.s.MouseEventAccessLock);
216
217 pDevExt->win.s.pDeviceObject = pDeviceObject;
218 pDevExt->win.s.prevDevState = STOPPED;
219 pDevExt->win.s.devState = STOPPED;
220
221 pDevExt->win.s.pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj);
222 if (pDevExt->win.s.pNextLowerDriver == NULL)
223 {
224 Log(("VBoxGuest::vboxguestwinGuestAddDevice: IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
225 rc = STATUS_DEVICE_NOT_CONNECTED;
226 }
227 }
228 else
229 Log(("VBoxGuest::vboxguestwinGuestAddDevice: IoCreateSymbolicLink failed with rc=%#x!\n", rc));
230 }
231 else
232 Log(("VBoxGuest::vboxguestwinGuestAddDevice: IoCreateDevice failed with rc=%#x!\n", rc));
233
234 if (NT_SUCCESS(rc))
235 {
236 /*
237 * If we reached this point we're fine with the basic driver setup,
238 * so continue to init our own things.
239 */
240#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
241 vboxguestwinBugCheckCallback(pDevExt); /* Ignore failure! */
242#endif
243 /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */
244 pDeviceObject->Flags |= DO_POWER_PAGABLE;
245
246 /* Driver is ready now. */
247 pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
248 }
249
250 /* Cleanup on error. */
251 if (NT_ERROR(rc))
252 {
253 if (pDevExt)
254 {
255 if (pDevExt->win.s.pNextLowerDriver)
256 IoDetachDevice(pDevExt->win.s.pNextLowerDriver);
257 }
258 IoDeleteSymbolicLink(&win32Name);
259 if (pDeviceObject)
260 IoDeleteDevice(pDeviceObject);
261 }
262
263 Log(("VBoxGuest::vboxguestwinGuestAddDevice: returning with rc = 0x%x\n", rc));
264 return rc;
265}
266#endif
267
268
269/**
270 * Debug helper to dump a device resource list.
271 *
272 * @param pResourceList list of device resources.
273 */
274static void vboxguestwinShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList)
275{
276#ifdef LOG_ENABLED
277 PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = pResourceList->PartialDescriptors;
278 ULONG nres = pResourceList->Count;
279 ULONG i;
280
281 for (i = 0; i < nres; ++i, ++resource)
282 {
283 ULONG uType = resource->Type;
284 static char* aszName[] =
285 {
286 "CmResourceTypeNull",
287 "CmResourceTypePort",
288 "CmResourceTypeInterrupt",
289 "CmResourceTypeMemory",
290 "CmResourceTypeDma",
291 "CmResourceTypeDeviceSpecific",
292 "CmResourceTypeBusNumber",
293 "CmResourceTypeDevicePrivate",
294 "CmResourceTypeAssignedResource",
295 "CmResourceTypeSubAllocateFrom",
296 };
297
298 Log(("VBoxGuest::vboxguestwinShowDeviceResources: Type %s",
299 uType < (sizeof(aszName) / sizeof(aszName[0]))
300 ? aszName[uType] : "Unknown"));
301
302 switch (uType)
303 {
304 case CmResourceTypePort:
305 case CmResourceTypeMemory:
306 Log(("VBoxGuest::vboxguestwinShowDeviceResources: Start %8X%8.8lX length %X\n",
307 resource->u.Port.Start.HighPart, resource->u.Port.Start.LowPart,
308 resource->u.Port.Length));
309 break;
310
311 case CmResourceTypeInterrupt:
312 Log(("VBoxGuest::vboxguestwinShowDeviceResources: Level %X, Vector %X, Affinity %X\n",
313 resource->u.Interrupt.Level, resource->u.Interrupt.Vector,
314 resource->u.Interrupt.Affinity));
315 break;
316
317 case CmResourceTypeDma:
318 Log(("VBoxGuest::vboxguestwinShowDeviceResources: Channel %d, Port %X\n",
319 resource->u.Dma.Channel, resource->u.Dma.Port));
320 break;
321
322 default:
323 Log(("\n"));
324 break;
325 }
326 }
327#endif
328}
329
330
331/**
332 * Global initialisation stuff (PnP + NT4 legacy).
333 *
334 * @param pDevObj Device object.
335 * @param pIrp Request packet.
336 */
337#ifndef TARGET_NT4
338NTSTATUS vboxguestwinInit(PDEVICE_OBJECT pDevObj, PIRP pIrp)
339#else
340NTSTATUS vboxguestwinInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath)
341#endif
342{
343 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
344#ifndef TARGET_NT4
345 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
346#endif
347
348 Log(("VBoxGuest::vboxguestwinInit\n"));
349
350 int rc = STATUS_SUCCESS;
351#ifdef TARGET_NT4
352 /*
353 * Let's have a look at what our PCI adapter offers.
354 */
355 Log(("VBoxGuest::vboxguestwinInit: Starting to scan PCI resources of VBoxGuest ...\n"));
356
357 /* Assign the PCI resources. */
358 PCM_RESOURCE_LIST pResourceList = NULL;
359 UNICODE_STRING classNameString;
360 RtlInitUnicodeString(&classNameString, L"VBoxGuestAdapter");
361 rc = HalAssignSlotResources(pRegPath, &classNameString,
362 pDrvObj, pDevObj,
363 PCIBus, pDevExt->win.s.busNumber, pDevExt->win.s.slotNumber,
364 &pResourceList);
365 if (pResourceList && pResourceList->Count > 0)
366 vboxguestwinShowDeviceResources(&pResourceList->List[0].PartialResourceList);
367 if (NT_SUCCESS(rc))
368 rc = vboxguestwinScanPCIResourceList(pResourceList, pDevExt);
369#else
370 if (pStack->Parameters.StartDevice.AllocatedResources->Count > 0)
371 vboxguestwinShowDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList);
372 if (NT_SUCCESS(rc))
373 rc = vboxguestwinScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated,
374 pDevExt);
375#endif
376 if (NT_SUCCESS(rc))
377 {
378 /*
379 * Map physical address of VMMDev memory into MMIO region
380 * and init the common device extension bits.
381 */
382 void *pvMMIOBase = NULL;
383 uint32_t cbMMIO = 0;
384 rc = vboxguestwinMapVMMDevMemory(pDevExt,
385 pDevExt->win.s.vmmDevPhysMemoryAddress,
386 pDevExt->win.s.vmmDevPhysMemoryLength,
387 &pvMMIOBase,
388 &cbMMIO);
389 if (NT_SUCCESS(rc))
390 {
391 pDevExt->pVMMDevMemory = (VMMDevMemory *)pvMMIOBase;
392
393 Log(("VBoxGuest::vboxguestwinInit: pvMMIOBase = 0x%p, pDevExt = 0x%p, pDevExt->pVMMDevMemory = 0x%p\n",
394 pvMMIOBase, pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));
395
396 int vrc = VBoxGuestInitDevExt(pDevExt,
397 pDevExt->IOPortBase,
398 pvMMIOBase, cbMMIO,
399 vboxguestwinVersionToOSType(g_winVersion),
400 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
401 if (RT_FAILURE(vrc))
402 {
403 Log(("VBoxGuest::vboxguestwinInit: Could not init device extension, rc = %Rrc!\n", vrc));
404 rc = STATUS_DEVICE_CONFIGURATION_ERROR;
405 }
406 }
407 else
408 Log(("VBoxGuest::vboxguestwinInit: Could not map physical address of VMMDev, rc = 0x%x!\n", rc));
409 }
410
411 if (NT_SUCCESS(rc))
412 {
413 int vrc = VbglGRAlloc((VMMDevRequestHeader **)&pDevExt->win.s.pPowerStateRequest,
414 sizeof (VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
415 if (RT_FAILURE(vrc))
416 {
417 Log(("VBoxGuest::vboxguestwinInit: Alloc for pPowerStateRequest failed, rc = %Rrc\n", vrc));
418 rc = STATUS_UNSUCCESSFUL;
419 }
420 }
421
422 if (NT_SUCCESS(rc))
423 {
424 /*
425 * Register DPC and ISR.
426 */
427 Log(("VBoxGuest::vboxguestwinInit: Initializing DPC/ISR ...\n"));
428
429 IoInitializeDpcRequest(pDevExt->win.s.pDeviceObject, vboxguestwinDpcHandler);
430#ifdef TARGET_NT4
431 ULONG uInterruptVector;
432 KIRQL irqLevel;
433 /* Get an interrupt vector. */
434 /* Only proceed if the device provides an interrupt. */
435 if ( pDevExt->win.s.interruptLevel
436 || pDevExt->win.s.interruptVector)
437 {
438 Log(("VBoxGuest::vboxguestwinInit: Getting interrupt vector (HAL): Bus: %u, IRQL: %u, Vector: %u\n",
439 pDevExt->win.s.busNumber, pDevExt->win.s.interruptLevel, pDevExt->win.s.interruptVector));
440
441 uInterruptVector = HalGetInterruptVector(PCIBus,
442 pDevExt->win.s.busNumber,
443 pDevExt->win.s.interruptLevel,
444 pDevExt->win.s.interruptVector,
445 &irqLevel,
446 &pDevExt->win.s.interruptAffinity);
447 Log(("VBoxGuest::vboxguestwinInit: HalGetInterruptVector returns vector %u\n", uInterruptVector));
448 if (uInterruptVector == 0)
449 Log(("VBoxGuest::vboxguestwinInit: No interrupt vector found!\n"));
450 }
451 else
452 Log(("VBoxGuest::vboxguestwinInit: Device does not provide an interrupt!\n"));
453#endif
454 if (pDevExt->win.s.interruptVector)
455 {
456 Log(("VBoxGuest::vboxguestwinInit: Connecting interrupt ...\n"));
457
458 rc = IoConnectInterrupt(&pDevExt->win.s.pInterruptObject, /* Out: interrupt object. */
459 (PKSERVICE_ROUTINE)vboxguestwinIsrHandler, /* Our ISR handler. */
460 pDevExt, /* Device context. */
461 NULL, /* Optional spinlock. */
462#ifdef TARGET_NT4
463 uInterruptVector, /* Interrupt vector. */
464 irqLevel, /* Interrupt level. */
465 irqLevel, /* Interrupt level. */
466#else
467 pDevExt->win.s.interruptVector, /* Interrupt vector. */
468 (KIRQL)pDevExt->win.s.interruptLevel, /* Interrupt level. */
469 (KIRQL)pDevExt->win.s.interruptLevel, /* Interrupt level. */
470#endif
471 pDevExt->win.s.interruptMode, /* LevelSensitive or Latched. */
472 TRUE, /* Shareable interrupt. */
473 pDevExt->win.s.interruptAffinity, /* CPU affinity. */
474 FALSE); /* Don't save FPU stack. */
475 if (NT_ERROR(rc))
476 Log(("VBoxGuest::vboxguestwinInit: Could not connect interrupt, rc = 0x%x\n", rc));
477 }
478 else
479 Log(("VBoxGuest::vboxguestwinInit: No interrupt vector found!\n"));
480 }
481
482
483#ifdef VBOX_WITH_HGCM
484 Log(("VBoxGuest::vboxguestwinInit: Allocating kernel session data ...\n"));
485 int vrc = VBoxGuestCreateKernelSession(pDevExt, &pDevExt->win.s.pKernelSession);
486 if (RT_FAILURE(vrc))
487 {
488 Log(("VBoxGuest::vboxguestwinInit: Failed to allocated kernel session data! rc = %Rrc\n", rc));
489 rc = STATUS_UNSUCCESSFUL;
490 }
491#endif
492
493 if (RT_SUCCESS(rc))
494 {
495 ULONG ulValue = 0;
496 NTSTATUS s = vboxguestwinRegistryReadDWORD(RTL_REGISTRY_SERVICES, L"VBoxGuest", L"LoggingEnabled",
497 &ulValue);
498 if (NT_SUCCESS(s))
499 {
500 pDevExt->fLoggingEnabled = ulValue >= 0xFF;
501 if (pDevExt->fLoggingEnabled)
502 Log(("Logging to release log enabled (0x%x)", ulValue));
503 }
504
505 /* Ready to rumble! */
506 Log(("VBoxGuest::vboxguestwinInit: Device is ready!\n"));
507 VBOXGUEST_UPDATE_DEVSTATE(pDevExt, WORKING);
508 }
509 else
510 {
511 pDevExt->win.s.pInterruptObject = NULL;
512 }
513
514 Log(("VBoxGuest::vboxguestwinInit: Returned with rc = 0x%x\n", rc));
515 return rc;
516}
517
518
519/**
520 * Cleans up hardware resources.
521 * Do not delete DevExt here.
522 *
523 * @param pDrvObj Driver object.
524 */
525NTSTATUS vboxguestwinCleanup(PDEVICE_OBJECT pDevObj)
526{
527 Log(("VBoxGuest::vboxguestwinCleanup\n"));
528
529 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
530 if (pDevExt)
531 {
532
533#if 0 /* @todo: test & enable cleaning global session data */
534#ifdef VBOX_WITH_HGCM
535 if (pDevExt->win.s.pKernelSession)
536 {
537 VBoxGuestCloseSession(pDevExt, pDevExt->win.s.pKernelSession);
538 pDevExt->win.s.pKernelSession = NULL;
539 }
540#endif
541#endif
542
543 if (pDevExt->win.s.pInterruptObject)
544 {
545 IoDisconnectInterrupt(pDevExt->win.s.pInterruptObject);
546 pDevExt->win.s.pInterruptObject = NULL;
547 }
548
549 /* @todo: cleanup the rest stuff */
550
551
552#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION
553 hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */
554#endif
555 /* According to MSDN we have to unmap previously mapped memory. */
556 vboxguestwinUnmapVMMDevMemory(pDevExt);
557 }
558 return STATUS_SUCCESS;
559}
560
561
562/**
563 * Unload the driver.
564 *
565 * @param pDrvObj Driver object.
566 */
567static void vboxguestwinUnload(PDRIVER_OBJECT pDrvObj)
568{
569 Log(("VBoxGuest::vboxguestwinGuestUnload\n"));
570#ifdef TARGET_NT4
571 vboxguestwinCleanup(pDrvObj->DeviceObject);
572
573 /* Destroy device extension and clean up everything else. */
574 if (pDrvObj->DeviceObject && pDrvObj->DeviceObject->DeviceExtension)
575 VBoxGuestDeleteDevExt((PVBOXGUESTDEVEXT)pDrvObj->DeviceObject->DeviceExtension);
576
577 /*
578 * I don't think it's possible to unload a driver which processes have
579 * opened, at least we'll blindly assume that here.
580 */
581 UNICODE_STRING win32Name;
582 RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS);
583 NTSTATUS rc = IoDeleteSymbolicLink(&win32Name);
584
585 IoDeleteDevice(pDrvObj->DeviceObject);
586#else /* TARGET_NT4 */
587 /* On a PnP driver this routine will be called after
588 * IRP_MN_REMOVE_DEVICE (where we already did the cleanup),
589 * so don't do anything here (yet). */
590#endif
591
592 Log(("VBoxGuest::vboxguestwinGuestUnload: returning\n"));
593}
594
595
596/**
597 * Create (i.e. Open) file entry point.
598 *
599 * @param pDevObj Device object.
600 * @param pIrp Request packet.
601 */
602static NTSTATUS vboxguestwinCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
603{
604 /** @todo AssertPtrReturn(pIrp); */
605 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
606 /** @todo AssertPtrReturn(pStack); */
607 PFILE_OBJECT pFileObj = pStack->FileObject;
608 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
609 NTSTATUS rc = STATUS_SUCCESS;
610
611 if (pDevExt->win.s.devState != WORKING)
612 {
613 Log(("VBoxGuest::vboxguestwinGuestCreate: device is not working currently: %d!\n",
614 pDevExt->win.s.devState));
615 rc = STATUS_UNSUCCESSFUL;
616 }
617 else if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
618 {
619 /*
620 * We are not remotely similar to a directory...
621 * (But this is possible.)
622 */
623 Log(("VBoxGuest::vboxguestwinGuestCreate: Uhm, we're not a directory!\n"));
624 rc = STATUS_NOT_A_DIRECTORY;
625 }
626 else
627 {
628#ifdef VBOX_WITH_HGCM
629 if (pFileObj)
630 {
631 Log(("VBoxGuest::vboxguestwinGuestCreate: File object type = %d\n",
632 pFileObj->Type));
633
634 int vrc;
635 PVBOXGUESTSESSION pSession;
636 if (pFileObj->Type == 5 /* File Object */)
637 {
638 /*
639 * Create a session object if we have a valid file object. This session object
640 * exists for every R3 process.
641 */
642 vrc = VBoxGuestCreateUserSession(pDevExt, &pSession);
643 }
644 else
645 {
646 /* ... otherwise we've been called from R0! */
647 vrc = VBoxGuestCreateKernelSession(pDevExt, &pSession);
648 }
649 if (RT_SUCCESS(vrc))
650 pFileObj->FsContext = pSession;
651 }
652#endif
653 }
654
655 /* Complete the request! */
656 pIrp->IoStatus.Information = 0;
657 pIrp->IoStatus.Status = rc;
658 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
659
660 Log(("VBoxGuest::vboxguestwinGuestCreate: Returning 0x%x\n", rc));
661 return rc;
662}
663
664
665/**
666 * Close file entry point.
667 *
668 * @param pDevObj Device object.
669 * @param pIrp Request packet.
670 */
671static NTSTATUS vboxguestwinClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
672{
673 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
674 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
675 PFILE_OBJECT pFileObj = pStack->FileObject;
676
677 Log(("VBoxGuest::vboxguestwinGuestClose: pDevExt=0x%p pFileObj=0x%p FsContext=0x%p\n",
678 pDevExt, pFileObj, pFileObj->FsContext));
679
680#ifdef VBOX_WITH_HGCM
681 /* Close both, R0 and R3 sessions. */
682 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
683 if (pSession)
684 VBoxGuestCloseSession(pDevExt, pSession);
685#endif
686
687 pFileObj->FsContext = NULL;
688 pIrp->IoStatus.Information = 0;
689 pIrp->IoStatus.Status = STATUS_SUCCESS;
690 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
691
692 return STATUS_SUCCESS;
693}
694
695
696/**
697 * Device I/O Control entry point.
698 *
699 * @param pDevObj Device object.
700 * @param pIrp Request packet.
701 */
702static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
703{
704 NTSTATUS Status = STATUS_SUCCESS;
705 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
706 PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
707 unsigned int uCmd = (unsigned int)pStack->Parameters.DeviceIoControl.IoControlCode;
708
709 char *pBuf = (char *)pIrp->AssociatedIrp.SystemBuffer; /* All requests are buffered. */
710 size_t cbData = pStack->Parameters.DeviceIoControl.InputBufferLength;
711 unsigned cbOut = 0;
712
713 /* Do we have a file object associated?*/
714 PFILE_OBJECT pFileObj = pStack->FileObject;
715 PVBOXGUESTSESSION pSession = NULL;
716 if (pFileObj) /* ... then we might have a session object as well! */
717 pSession = (PVBOXGUESTSESSION)pFileObj->FsContext;
718
719 Log(("VBoxGuest::vboxguestwinIOCtl: uCmd=%u, pDevExt=0x%p, pSession=0x%p\n",
720 uCmd, pDevExt, pSession));
721
722 /* We don't have a session associated with the file object? So this seems
723 * to be a kernel call then. */
724 /** @todo r=bird: What on earth is this supposed to be? Each kernel session
725 * shall have its own context of course, no hacks, pleeease. */
726 if (pSession == NULL)
727 {
728 Log(("VBoxGuest::vboxguestwinIOCtl: Using kernel session data ...\n"));
729 pSession = pDevExt->win.s.pKernelSession;
730 }
731
732 /*
733 * First process Windows specific stuff which cannot be handled
734 * by the common code used on all other platforms. In the default case
735 * we then finally handle the common cases.
736 */
737 switch (uCmd)
738 {
739#ifdef VBOX_WITH_VRDP_SESSION_HANDLING
740 case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION:
741 {
742 LogRel(("VBoxGuest::vboxguestwinIOCtl: ENABLE_VRDP_SESSION: Currently: %sabled\n",
743 pDevExt->fVRDPEnabled? "en": "dis"));
744 if (!pDevExt->fVRDPEnabled)
745 {
746 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
747
748 pDevExt->fVRDPEnabled = true;
749 LogRel(("VBoxGuest::vboxguestwinIOCtl: ENABLE_VRDP_SESSION: Current active console ID: 0x%08X\n",
750 pSharedUserData->ActiveConsoleId));
751 pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId;
752 pSharedUserData->ActiveConsoleId = 2;
753 }
754 break;
755 }
756
757 case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION:
758 {
759 LogRel(("VBoxGuest::vboxguestwinIOCtl: DISABLE_VRDP_SESSION: Currently: %sabled\n",
760 pDevExt->fVRDPEnabled? "en": "dis"));
761 if (pDevExt->fVRDPEnabled)
762 {
763 KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA;
764
765 pDevExt->fVRDPEnabled = false;
766 Log(("VBoxGuest::vboxguestwinIOCtl: DISABLE_VRDP_SESSION: Current active console ID: 0x%08X\n",
767 pSharedUserData->ActiveConsoleId));
768 pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId;
769 pDevExt->ulOldActiveConsoleId = 0;
770 }
771 break;
772 }
773#else
774 /* Add at least one (bogus) fall through case to shut up MSVC! */
775 case 0:
776#endif
777 default:
778 {
779 /*
780 * Process the common IOCtls.
781 */
782 size_t cbDataReturned;
783 int vrc = VBoxGuestCommonIOCtl(uCmd, pDevExt, pSession, pBuf, cbData, &cbDataReturned);
784
785 Log(("VBoxGuest::vboxguestwinGuestDeviceControl: rc=%Rrc, pBuf=0x%p, cbData=%u, cbDataReturned=%u\n",
786 vrc, pBuf, cbData, cbDataReturned));
787
788 if (RT_SUCCESS(vrc))
789 {
790 if (RT_UNLIKELY(cbDataReturned > cbData))
791 {
792 Log(("VBoxGuest::vboxguestwinGuestDeviceControl: Too much output data %u - expected %u!\n", cbDataReturned, cbData));
793 cbDataReturned = cbData;
794 Status = STATUS_BUFFER_TOO_SMALL;
795 }
796 if (cbDataReturned > 0)
797 cbOut = cbDataReturned;
798 }
799 else
800 {
801 if ( vrc == VERR_NOT_SUPPORTED
802 || vrc == VERR_INVALID_PARAMETER)
803 {
804 Status = STATUS_INVALID_PARAMETER;
805 }
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::vboxguestwinGuestDeviceControl: 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 vboxguestwinInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
838{
839 NTSTATUS Status = STATUS_SUCCESS;
840 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)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->win.s.MouseEventAccessLock, &OldIrql);
865 pDevExt->MouseNotifyCallback = *pInfo;
866 KeReleaseSpinLock(&pDevExt->win.s.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 vboxguestwinIOCtl(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 vboxguestwinSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
898{
899 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
900
901 Log(("VBoxGuest::vboxguestwinGuestSystemControl\n"));
902
903 /* Always pass it on to the next driver. */
904 IoSkipCurrentIrpStackLocation(pIrp);
905
906 return IoCallDriver(pDevExt->win.s.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 vboxguestwinShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp)
918{
919 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
920
921 Log(("VBoxGuest::vboxguestwinGuestShutdown\n"));
922
923 VMMDevPowerStateRequest *pReq = pDevExt->win.s.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::vboxguestwinGuestShutdown: 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 vboxguestwinNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp)
948{
949 Log(("VBoxGuest::vboxguestwinGuestNotSupportedStub\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 vboxguestwinDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
968{
969 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension;
970 Log(("VBoxGuest::vboxguestwinGuestDpcHandler: pDevExt=0x%p\n", pDevExt));
971
972 /* test & reset the counter */
973 if (ASMAtomicXchgU32(&pDevExt->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->win.s.MouseEventAccessLock);
979
980 if (pDevExt->MouseNotifyCallback.pfnNotify)
981 pDevExt->MouseNotifyCallback.pfnNotify(pDevExt->MouseNotifyCallback.pvUser);
982
983 KeReleaseSpinLockFromDpcLevel(&pDevExt->win.s.MouseEventAccessLock);
984 }
985
986 /* Process the wake-up list we were asked by the scheduling a DPC
987 * in vboxguestwinIsrHandler(). */
988 VBoxGuestWaitDoWakeUps(pDevExt);
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 vboxguestwinIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext)
1000{
1001 PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pServiceContext;
1002 if (pDevExt == NULL)
1003 return FALSE;
1004
1005 /*Log(("VBoxGuest::vboxguestwinGuestIsrHandler: 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);
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::vboxguestwinGuestIsrHandler: IRQ was taken! pInterrupt = 0x%p, pDevExt = 0x%p\n",
1016 pInterrupt, pDevExt));
1017 if (ASMAtomicUoReadU32(&pDevExt->u32MousePosChangedSeq) || !RTListIsEmpty(&pDevExt->WakeUpList))
1018 {
1019 Log(("VBoxGuest::vboxguestwinGuestIsrHandler: Requesting DPC ...\n"));
1020 IoRequestDpc(pDevExt->win.s.pDeviceObject, pDevExt->win.s.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 vboxguestwinRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName,
1053 PULONG puValue)
1054{
1055 if (!pwszPath || !pwszName || !puValue)
1056 return STATUS_INVALID_PARAMETER;
1057
1058 ULONG ulDefault = *puValue;
1059
1060 RTL_QUERY_REGISTRY_TABLE tblQuery[2];
1061 RtlZeroMemory(tblQuery, sizeof(tblQuery));
1062 /** @todo Add RTL_QUERY_REGISTRY_TYPECHECK! */
1063 tblQuery[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
1064 tblQuery[0].Name = pwszName;
1065 tblQuery[0].EntryContext = puValue;
1066 tblQuery[0].DefaultType = REG_DWORD;
1067 tblQuery[0].DefaultData = &ulDefault;
1068 tblQuery[0].DefaultLength = sizeof(ULONG);
1069
1070 return RtlQueryRegistryValues(ulRoot,
1071 pwszPath,
1072 &tblQuery[0],
1073 NULL /* Context */,
1074 NULL /* Environment */);
1075}
1076
1077
1078/**
1079 * Helper to scan the PCI resource list and remember stuff.
1080 *
1081 * @param pResList Resource list
1082 * @param pDevExt Device extension
1083 */
1084NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXT pDevExt)
1085{
1086 /* Enumerate the resource list. */
1087 Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Found %d resources\n",
1088 pResList->List->PartialResourceList.Count));
1089
1090 NTSTATUS rc = STATUS_SUCCESS;
1091 PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL;
1092 ULONG rangeCount = 0;
1093 ULONG cMMIORange = 0;
1094 PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->win.s.pciBaseAddress;
1095 for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++)
1096 {
1097 pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i];
1098 switch (pPartialData->Type)
1099 {
1100 case CmResourceTypePort:
1101 {
1102 /* Overflow protection. */
1103 if (rangeCount < PCI_TYPE0_ADDRESSES)
1104 {
1105 Log(("VBoxGuest::vboxguestwinScanPCIResourceList: I/O range: Base = %08x:%08x, Length = %08x\n",
1106 pPartialData->u.Port.Start.HighPart,
1107 pPartialData->u.Port.Start.LowPart,
1108 pPartialData->u.Port.Length));
1109
1110 /* Save the IO port base. */
1111 /** @todo Not so good. */
1112 pDevExt->IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart;
1113
1114 /* Save resource information. */
1115 pBaseAddress->RangeStart = pPartialData->u.Port.Start;
1116 pBaseAddress->RangeLength = pPartialData->u.Port.Length;
1117 pBaseAddress->RangeInMemory = FALSE;
1118 pBaseAddress->ResourceMapped = FALSE;
1119
1120 Log(("VBoxGuest::vboxguestwinScanPCIResourceList: I/O range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
1121 pPartialData->u.Port.Start.HighPart,
1122 pPartialData->u.Port.Start.LowPart,
1123 pPartialData->u.Port.Length));
1124
1125 /* Next item ... */
1126 rangeCount++; pBaseAddress++;
1127 }
1128 break;
1129 }
1130
1131 case CmResourceTypeInterrupt:
1132 {
1133 Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Interrupt: Level = %x, Vector = %x, Mode = %x\n",
1134 pPartialData->u.Interrupt.Level,
1135 pPartialData->u.Interrupt.Vector,
1136 pPartialData->Flags));
1137
1138 /* Save information. */
1139 pDevExt->win.s.interruptLevel = pPartialData->u.Interrupt.Level;
1140 pDevExt->win.s.interruptVector = pPartialData->u.Interrupt.Vector;
1141 pDevExt->win.s.interruptAffinity = pPartialData->u.Interrupt.Affinity;
1142
1143 /* Check interrupt mode. */
1144 if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
1145 {
1146 pDevExt->win.s.interruptMode = Latched;
1147 }
1148 else
1149 {
1150 pDevExt->win.s.interruptMode = LevelSensitive;
1151 }
1152 break;
1153 }
1154
1155 case CmResourceTypeMemory:
1156 {
1157 /* Overflow protection. */
1158 if (rangeCount < PCI_TYPE0_ADDRESSES)
1159 {
1160 Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Memory range: Base = %08x:%08x, Length = %08x\n",
1161 pPartialData->u.Memory.Start.HighPart,
1162 pPartialData->u.Memory.Start.LowPart,
1163 pPartialData->u.Memory.Length));
1164
1165 /* We only care about read/write memory. */
1166 /** @todo Reconsider memory type. */
1167 if ( cMMIORange == 0 /* Only care about the first MMIO range (!!!). */
1168 && (pPartialData->Flags & VBOX_CM_PRE_VISTA_MASK) == CM_RESOURCE_MEMORY_READ_WRITE)
1169 {
1170 /* Save physical MMIO base + length for VMMDev. */
1171 pDevExt->win.s.vmmDevPhysMemoryAddress = pPartialData->u.Memory.Start;
1172 pDevExt->win.s.vmmDevPhysMemoryLength = (ULONG)pPartialData->u.Memory.Length;
1173
1174 /* Save resource information. */
1175 pBaseAddress->RangeStart = pPartialData->u.Memory.Start;
1176 pBaseAddress->RangeLength = pPartialData->u.Memory.Length;
1177 pBaseAddress->RangeInMemory = TRUE;
1178 pBaseAddress->ResourceMapped = FALSE;
1179
1180 Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
1181 pPartialData->u.Memory.Start.HighPart,
1182 pPartialData->u.Memory.Start.LowPart,
1183 pPartialData->u.Memory.Length));
1184
1185 /* Next item ... */
1186 rangeCount++; pBaseAddress++; cMMIORange++;
1187 }
1188 else
1189 {
1190 Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Ignoring memory: Flags = %08x\n",
1191 pPartialData->Flags));
1192 }
1193 }
1194 break;
1195 }
1196
1197 default:
1198 {
1199 Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Unhandled resource found, type = %d\n", pPartialData->Type));
1200 break;
1201 }
1202 }
1203 }
1204
1205 /* Memorize the number of resources found. */
1206 pDevExt->win.s.pciAddressCount = rangeCount;
1207 return rc;
1208}
1209
1210
1211/**
1212 * Maps the I/O space from VMMDev to virtual kernel address space.
1213 *
1214 * @return NTSTATUS
1215 *
1216 * @param pDevExt The device extension.
1217 * @param physicalAdr Physical address to map.
1218 * @param ulLength Length (in bytes) to map.
1219 * @param ppvMMIOBase Pointer of mapped I/O base.
1220 * @param pcbMMIO Length of mapped I/O base.
1221 */
1222NTSTATUS vboxguestwinMapVMMDevMemory(PVBOXGUESTDEVEXT pDevExt, PHYSICAL_ADDRESS physicalAdr, ULONG ulLength,
1223 void **ppvMMIOBase, uint32_t *pcbMMIO)
1224{
1225 AssertPtrReturn(pDevExt, VERR_INVALID_POINTER);
1226 AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER);
1227 /* pcbMMIO is optional. */
1228
1229 NTSTATUS rc = STATUS_SUCCESS;
1230 if (physicalAdr.LowPart > 0) /* We're mapping below 4GB. */
1231 {
1232 VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(physicalAdr, ulLength, MmNonCached);
1233 Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: pVMMDevMemory = 0x%x\n", pVMMDevMemory));
1234 if (pVMMDevMemory)
1235 {
1236 Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: VMMDevMemory: Version = 0x%x, Size = %d\n",
1237 pVMMDevMemory->u32Version, pVMMDevMemory->u32Size));
1238
1239 /* Check version of the structure; do we have the right memory version? */
1240 if (pVMMDevMemory->u32Version != VMMDEV_MEMORY_VERSION)
1241 {
1242 Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: Wrong version (%u), refusing operation!\n",
1243 pVMMDevMemory->u32Version));
1244
1245 /* Not our version, refuse operation and unmap the memory. */
1246 vboxguestwinUnmapVMMDevMemory(pDevExt);
1247 rc = STATUS_UNSUCCESSFUL;
1248 }
1249 else
1250 {
1251 /* Save results. */
1252 *ppvMMIOBase = pVMMDevMemory;
1253 if (pcbMMIO) /* Optional. */
1254 *pcbMMIO = pVMMDevMemory->u32Size;
1255
1256 Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n",
1257 *ppvMMIOBase));
1258 }
1259 }
1260 else
1261 rc = STATUS_UNSUCCESSFUL;
1262 }
1263 return rc;
1264}
1265
1266
1267/**
1268 * Unmaps the VMMDev I/O range from kernel space.
1269 *
1270 * @param pDevExt The device extension.
1271 */
1272void vboxguestwinUnmapVMMDevMemory(PVBOXGUESTDEVEXT pDevExt)
1273{
1274 Log(("VBoxGuest::vboxguestwinUnmapVMMDevMemory: pVMMDevMemory = 0x%x\n", pDevExt->pVMMDevMemory));
1275 if (pDevExt->pVMMDevMemory)
1276 {
1277 MmUnmapIoSpace((void*)pDevExt->pVMMDevMemory, pDevExt->win.s.vmmDevPhysMemoryLength);
1278 pDevExt->pVMMDevMemory = NULL;
1279 }
1280
1281 pDevExt->win.s.vmmDevPhysMemoryAddress.QuadPart = 0;
1282 pDevExt->win.s.vmmDevPhysMemoryLength = 0;
1283}
1284
1285
1286VBOXOSTYPE vboxguestwinVersionToOSType(winVersion_t winVer)
1287{
1288 VBOXOSTYPE enmOsType;
1289 switch (winVer)
1290 {
1291 case WINNT4:
1292 enmOsType = VBOXOSTYPE_WinNT4;
1293 break;
1294
1295 case WIN2K:
1296 enmOsType = VBOXOSTYPE_Win2k;
1297 break;
1298
1299 case WINXP:
1300#if ARCH_BITS == 64
1301 enmOsType = VBOXOSTYPE_WinXP_x64;
1302#else
1303 enmOsType = VBOXOSTYPE_WinXP;
1304#endif
1305 break;
1306
1307 case WIN2K3:
1308#if ARCH_BITS == 64
1309 enmOsType = VBOXOSTYPE_Win2k3_x64;
1310#else
1311 enmOsType = VBOXOSTYPE_Win2k3;
1312#endif
1313 break;
1314
1315 case WINVISTA:
1316#if ARCH_BITS == 64
1317 enmOsType = VBOXOSTYPE_WinVista_x64;
1318#else
1319 enmOsType = VBOXOSTYPE_WinVista;
1320#endif
1321 break;
1322
1323 case WIN7:
1324#if ARCH_BITS == 64
1325 enmOsType = VBOXOSTYPE_Win7_x64;
1326#else
1327 enmOsType = VBOXOSTYPE_Win7;
1328#endif
1329 break;
1330
1331 case WIN8:
1332#if ARCH_BITS == 64
1333 enmOsType = VBOXOSTYPE_Win8_x64;
1334#else
1335 enmOsType = VBOXOSTYPE_Win8;
1336#endif
1337 break;
1338
1339 default:
1340 /* We don't know, therefore NT family. */
1341 enmOsType = VBOXOSTYPE_WinNT;
1342 break;
1343 }
1344 return enmOsType;
1345}
1346
1347#ifdef DEBUG
1348
1349/**
1350 * A quick implementation of AtomicTestAndClear for uint32_t and multiple bits.
1351 */
1352static uint32_t vboxugestwinAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask)
1353{
1354 AssertPtrReturn(pu32Bits, 0);
1355 LogFlowFunc(("*pu32Bits=0x%x, u32Mask=0x%x\n", *(long *)pu32Bits,
1356 u32Mask));
1357 uint32_t u32Result = 0;
1358 uint32_t u32WorkingMask = u32Mask;
1359 int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1360
1361 while (iBitOffset > 0)
1362 {
1363 bool fSet = ASMAtomicBitTestAndClear(pu32Bits, iBitOffset - 1);
1364 if (fSet)
1365 u32Result |= 1 << (iBitOffset - 1);
1366 u32WorkingMask &= ~(1 << (iBitOffset - 1));
1367 iBitOffset = ASMBitFirstSetU32 (u32WorkingMask);
1368 }
1369 LogFlowFunc(("Returning 0x%x\n", u32Result));
1370 return u32Result;
1371}
1372
1373
1374static void vboxguestwinTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits,
1375 uint32_t u32Exp)
1376{
1377 ULONG u32Bits2 = u32Bits;
1378 uint32_t u32Result = vboxugestwinAtomicBitsTestAndClear(&u32Bits2, u32Mask);
1379 if ( u32Result != u32Exp
1380 || (u32Bits2 & u32Mask)
1381 || (u32Bits2 & u32Result)
1382 || ((u32Bits2 | u32Result) != u32Bits)
1383 )
1384 AssertLogRelMsgFailed(("%s: TEST FAILED: u32Mask=0x%x, u32Bits (before)=0x%x, u32Bits (after)=0x%x, u32Result=0x%x, u32Exp=ox%x\n",
1385 __PRETTY_FUNCTION__, u32Mask, u32Bits, u32Bits2,
1386 u32Result));
1387}
1388
1389
1390static void vboxguestwinDoTests()
1391{
1392 vboxguestwinTestAtomicTestAndClearBitsU32(0x00, 0x23, 0);
1393 vboxguestwinTestAtomicTestAndClearBitsU32(0x11, 0, 0);
1394 vboxguestwinTestAtomicTestAndClearBitsU32(0x11, 0x22, 0);
1395 vboxguestwinTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1);
1396 vboxguestwinTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10);
1397 vboxguestwinTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22);
1398}
1399
1400#endif /* DEBUG */
1401
1402#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
1403#pragma pack(1)
1404typedef struct DPCSAMPLE
1405{
1406 LARGE_INTEGER PerfDelta;
1407 LARGE_INTEGER PerfCounter;
1408 LARGE_INTEGER PerfFrequency;
1409 uint64_t u64TSC;
1410} DPCSAMPLE;
1411
1412typedef struct DPCDATA
1413{
1414 KDPC Dpc;
1415 KTIMER Timer;
1416 KSPIN_LOCK SpinLock;
1417
1418 ULONG ulTimerRes;
1419
1420 LARGE_INTEGER DueTime;
1421
1422 BOOLEAN fFinished;
1423
1424 LARGE_INTEGER PerfCounterPrev;
1425
1426 int iSampleCount;
1427 DPCSAMPLE aSamples[8192];
1428} DPCDATA;
1429#pragma pack(1)
1430
1431#define VBOXGUEST_DPC_TAG 'DPCS'
1432
1433static VOID DPCDeferredRoutine(struct _KDPC *Dpc,
1434 PVOID DeferredContext,
1435 PVOID SystemArgument1,
1436 PVOID SystemArgument2)
1437{
1438 DPCDATA *pData = (DPCDATA *)DeferredContext;
1439
1440 KeAcquireSpinLockAtDpcLevel(&pData->SpinLock);
1441
1442 if (pData->iSampleCount >= RT_ELEMENTS(pData->aSamples))
1443 {
1444 pData->fFinished = 1;
1445 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
1446 return;
1447 }
1448
1449 DPCSAMPLE *pSample = &pData->aSamples[pData->iSampleCount++];
1450
1451 pSample->u64TSC = ASMReadTSC();
1452 pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency);
1453 pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart;
1454
1455 pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart;
1456
1457 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1458
1459 KeReleaseSpinLockFromDpcLevel(&pData->SpinLock);
1460}
1461
1462int VBoxGuestCommonIOCtl_DPC(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession,
1463 void *pvData, size_t cbData, size_t *pcbDataReturned)
1464{
1465 int rc = VINF_SUCCESS;
1466
1467 /* Allocate a non paged memory for samples and related data. */
1468 DPCDATA *pData = (DPCDATA *)ExAllocatePoolWithTag(NonPagedPool, sizeof(DPCDATA), VBOXGUEST_DPC_TAG);
1469
1470 if (!pData)
1471 {
1472 RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n");
1473 return VERR_NO_MEMORY;
1474 }
1475
1476 KeInitializeDpc(&pData->Dpc, DPCDeferredRoutine, pData);
1477 KeInitializeTimer(&pData->Timer);
1478 KeInitializeSpinLock(&pData->SpinLock);
1479
1480 pData->fFinished = 0;
1481 pData->iSampleCount = 0;
1482 pData->PerfCounterPrev.QuadPart = 0;
1483
1484 pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1);
1485 pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10;
1486
1487 /* Start the DPC measurements. */
1488 KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc);
1489
1490 while (!pData->fFinished)
1491 {
1492 LARGE_INTEGER Interval;
1493 Interval.QuadPart = -100 * 1000 * 10;
1494 KeDelayExecutionThread(KernelMode, TRUE, &Interval);
1495 }
1496
1497 ExSetTimerResolution(0, 0);
1498
1499 /* Log everything to the host. */
1500 RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes);
1501 int i;
1502 for (i = 0; i < pData->iSampleCount; i++)
1503 {
1504 DPCSAMPLE *pSample = &pData->aSamples[i];
1505
1506 RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n",
1507 i,
1508 pSample->PerfDelta.QuadPart,
1509 pSample->PerfCounter.QuadPart,
1510 pSample->PerfFrequency.QuadPart,
1511 pSample->u64TSC);
1512 }
1513
1514 ExFreePoolWithTag(pData, VBOXGUEST_DPC_TAG);
1515 return rc;
1516}
1517#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
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