VBoxGuest-win.cpp revision 5ea88591f1ca09476c7e97c758f9c52e1dbb6626
/** @file
*
* VBoxGuest - Windows specifics.
*
* Copyright (C) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP_DRV
#include "VBoxGuest-win.h"
#include "VBoxGuestInternal.h"
#include <VBox/VBoxGuestLib.h>
#include <VBoxGuestInternal.h>
#ifdef TARGET_NT4
/*
* XP DDK #defines ExFreePool to ExFreePoolWithTag. The latter does not exist
* on NT4, so... The same for ExAllocatePool.
*/
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/*******************************************************************************
* Entry Points *
*******************************************************************************/
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef DEBUG
#endif
/*******************************************************************************
* Exported Functions *
*******************************************************************************/
#ifdef ALLOC_PRAGMA
#endif
/** The detected Windows version. */
/**
* Driver entry point.
*
* @returns appropriate status code.
* @param pDrvObj Pointer to driver object.
* @param pRegPath Registry base path.
*/
{
Log(("VBoxGuest::DriverEntry: Running on Windows NT version %d.%d, build %d\n", majorVersion, minorVersion, buildNumber));
if (bCheckedBuild)
Log(("VBoxGuest::DriverEntry: Running on a Windows checked build (debug)!\n"));
#ifdef DEBUG
#endif
switch (majorVersion)
{
case 6: /* Windows Vista or Windows 7 (based on minor ver) */
switch (minorVersion)
{
case 0: /* Note: Also could be Windows 2008 Server! */
break;
case 1: /* Note: Also could be Windows 2008 Server R2! */
g_winVersion = WIN7;
break;
default:
Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n",
break;
}
break;
case 5:
switch (minorVersion)
{
case 2:
break;
case 1:
break;
case 0:
break;
default:
Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n",
}
break;
case 4:
break;
default:
Log(("VBoxGuest::DriverEntry: At least Windows NT4 required!\n"));
}
if (NT_SUCCESS(rc))
{
/*
* Setup the driver entry points in pDrvObj.
*/
#ifdef TARGET_NT4
#else
#endif
}
return rc;
}
#ifndef TARGET_NT4
/**
* Handle request from the Plug & Play subsystem.
*
* @returns NT status code
* @param pDrvObj Driver object
* @param pDevObj Device object
*/
{
Log(("VBoxGuest::vboxguestwinGuestAddDevice\n"));
/*
* Create device.
*/
rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXT), &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
if (NT_SUCCESS(rc))
{
/*
* Create symbolic link (DOS devices).
*/
if (NT_SUCCESS(rc))
{
/*
* Setup the device extension.
*/
{
Log(("VBoxGuest::vboxguestwinGuestAddDevice: IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n"));
}
}
else
}
else
if (NT_SUCCESS(rc))
{
/*
* If we reached this point we're fine with the basic driver setup,
* so continue to init our own things.
*/
#endif
/* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */
/* Driver is ready now. */
}
/* Cleanup on error. */
{
if (pDevExt)
{
}
if (pDeviceObject)
}
return rc;
}
#endif
/**
* Debug helper to dump a device resource list.
*
* @param pResourceList list of device resources.
*/
{
#ifdef LOG_ENABLED
ULONG i;
{
static char* aszName[] =
{
"CmResourceTypeNull",
"CmResourceTypePort",
"CmResourceTypeInterrupt",
"CmResourceTypeMemory",
"CmResourceTypeDma",
"CmResourceTypeDeviceSpecific",
"CmResourceTypeBusNumber",
"CmResourceTypeDevicePrivate",
"CmResourceTypeAssignedResource",
"CmResourceTypeSubAllocateFrom",
};
Log(("VBoxGuest::vboxguestwinShowDeviceResources: Type %s",
switch (uType)
{
case CmResourceTypePort:
case CmResourceTypeMemory:
Log(("VBoxGuest::vboxguestwinShowDeviceResources: Start %8X%8.8lX length %X\n",
break;
case CmResourceTypeInterrupt:
Log(("VBoxGuest::vboxguestwinShowDeviceResources: Level %X, Vector %X, Affinity %X\n",
break;
case CmResourceTypeDma:
Log(("VBoxGuest::vboxguestwinShowDeviceResources: Channel %d, Port %X\n",
break;
default:
Log(("\n"));
break;
}
}
#endif
}
/**
* Global initialisation stuff (PnP + NT4 legacy).
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
#ifndef TARGET_NT4
#else
#endif
{
#ifndef TARGET_NT4
#endif
Log(("VBoxGuest::vboxguestwinInit\n"));
int rc = STATUS_SUCCESS;
#ifdef TARGET_NT4
/*
* Let's have a look at what our PCI adapter offers.
*/
Log(("VBoxGuest::vboxguestwinInit: Starting to scan PCI resources of VBoxGuest ...\n"));
/* Assign the PCI resources. */
if (NT_SUCCESS(rc))
#else
vboxguestwinShowDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList);
if (NT_SUCCESS(rc))
pDevExt);
#endif
if (NT_SUCCESS(rc))
{
/*
* Map physical address of VMMDev memory into MMIO region
* and init the common device extension bits.
*/
void *pvMMIOBase = NULL;
&cbMMIO);
if (NT_SUCCESS(rc))
{
Log(("VBoxGuest::vboxguestwinInit: pvMMIOBase = 0x%p, pDevExt = 0x%p, pDevExt->pVMMDevMemory = 0x%p\n",
if (RT_FAILURE(vrc))
{
}
}
else
}
if (NT_SUCCESS(rc))
{
sizeof (VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus);
if (RT_FAILURE(vrc))
{
}
}
if (NT_SUCCESS(rc))
{
/*
* Register DPC and ISR.
*/
#ifdef TARGET_NT4
/* Get an interrupt vector. */
/* Only proceed if the device provides an interrupt. */
{
}
else
Log(("VBoxGuest::vboxguestwinInit: Device does not provide an interrupt!\n"));
#endif
{
pDevExt, /* Device context. */
NULL, /* Optional spinlock. */
TRUE, /* Shareable interrupt. */
FALSE); /* Don't save FPU stack. */
}
else
Log(("VBoxGuest::vboxguestwinInit: No interrupt vector found!\n"));
}
#ifdef VBOX_WITH_HGCM
/* Initialize the HGCM event notification semaphore. */
/* Preallocated constant timeout 250ms for HGCM async waiter. */
if (RT_FAILURE(vrc))
{
}
#endif
if (RT_SUCCESS(rc))
{
/* Ready to rumble! */
Log(("VBoxGuest::vboxguestwinInit: Device is ready!\n"));
}
return rc;
}
/**
* Cleans up all data (like device extension and guest mapping).
*
* @param pDrvObj Driver object.
*/
{
Log(("VBoxGuest::vboxguestwinCleanup\n"));
if (pDevExt)
{
#endif
/* According to MSDN we have to unmap previously mapped memory. */
/* Destroy device extension and clean up everything else. */
}
return STATUS_SUCCESS;
}
/**
* Unload the driver.
*
* @param pDrvObj Driver object.
*/
{
Log(("VBoxGuest::vboxguestwinGuestUnload\n"));
#ifdef TARGET_NT4
/*
* I don't think it's possible to unload a driver which processes have
* opened, at least we'll blindly assume that here.
*/
#else /* TARGET_NT4 */
/* On a PnP driver this routine will be called after
* IRP_MN_REMOVE_DEVICE (where we already did the cleanup),
* so don't do anything here (yet). */
#endif
Log(("VBoxGuest::vboxguestwinGuestUnload: returning\n"));
}
/**
* Create (i.e. Open) file entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
Log(("VBoxGuest::vboxguestwinGuestCreate\n"));
/** @todo AssertPtrReturn(pIrp); */
/** @todo AssertPtrReturn(pStack); */
/*
* We are not remotely similar to a directory...
* (But this is possible.)
*/
{
Log(("VBoxGuest::vboxguestwinGuestCreate: Uhm, we're not a directory!\n"));
}
else
{
#ifdef VBOX_WITH_HGCM
if (pFileObj)
{
int vrc;
{
/*
* Create a session object if we have a valid file object. This session object
* exists for every R3 process.
*/
}
else
{
/* ... otherwise we've been called from R0! */
}
if (RT_SUCCESS(vrc))
}
#endif
}
/* Complete the request! */
return rc;
}
/**
* Close file entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
Log(("VBoxGuest::vboxguestwinGuestClose: pDevExt=0x%p pFileObj=0x%p FsContext=0x%p\n",
#ifdef VBOX_WITH_HGCM
/* Close both, R0 and R3 sessions. */
if (pSession)
#endif
return STATUS_SUCCESS;
}
/**
* Device I/O Control entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
unsigned cbOut = 0;
/* Do we have a file object associated?*/
if (pFileObj) /* ... then we might have a session object as well! */
Log(("VBoxGuest::vboxguestwinIOCtl: uCmd=%u, pDevExt=0x%p, pSession=0x%p\n",
/* We don't have a session associated with the file object? So this seems
* to be a kernel call then. */
{
Log(("VBoxGuest::vboxguestwinIOCtl: Using kernel session data ...\n"));
}
/*
* First process Windows specific stuff which cannot be handled
* by the common code used on all other platforms. In the default case
* we then finally handle the common cases.
*/
switch (uCmd)
{
{
LogRel(("VBoxGuest::vboxguestwinIOCtl: ENABLE_VRDP_SESSION: Currently: %sabled\n",
if (!pDevExt->fVRDPEnabled)
{
LogRel(("VBoxGuest::vboxguestwinIOCtl: ENABLE_VRDP_SESSION: Current active console ID: 0x%08X\n",
}
break;
}
{
LogRel(("VBoxGuest::vboxguestwinIOCtl: DISABLE_VRDP_SESSION: Currently: %sabled\n",
if (pDevExt->fVRDPEnabled)
{
Log(("VBoxGuest::vboxguestwinIOCtl: DISABLE_VRDP_SESSION: Current active console ID: 0x%08X\n",
pDevExt->ulOldActiveConsoleId = 0;
}
break;
}
#else
/* Add at least one (bogus) fall through case to shut up MSVC! */
case 0:
#endif
default:
{
/*
* Process the common IOCtls.
*/
Log(("VBoxGuest::vboxguestwinGuestDeviceControl: rc=%Rrc, pBuf=0x%p, cbData=%u, cbDataReturned=%u\n",
if (RT_SUCCESS(vrc))
{
{
Log(("VBoxGuest::vboxguestwinGuestDeviceControl: Too much output data %u - expected %u!\n", cbDataReturned, cbData));
}
if (cbDataReturned > 0)
}
else
{
if ( vrc == VERR_NOT_SUPPORTED
|| vrc == VERR_INVALID_PARAMETER)
}
break;
}
}
//Log(("VBoxGuest::vboxguestwinGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status));
return Status;
}
/**
* IRP_MJ_SYSTEM_CONTROL handler.
*
* @returns NT status code
* @param pDevObj Device object.
* @param pIrp IRP.
*/
{
Log(("VBoxGuest::vboxguestwinGuestSystemControl\n"));
/* Always pass it on to the next driver. */
}
/**
* IRP_MJ_SHUTDOWN handler.
*
* @returns NT status code
* @param pDevObj Device object.
* @param pIrp IRP.
*/
{
Log(("VBoxGuest::vboxguestwinGuestShutdown\n"));
if (pReq)
{
if (RT_FAILURE(rc))
{
Log(("VBoxGuest::vboxguestwinGuestShutdown: Error performing request to VMMDev! "
"rc = %Rrc\n", rc));
}
}
return STATUS_SUCCESS;
}
/**
* Stub function for functions we don't implemented.
*
* @returns STATUS_NOT_SUPPORTED
* @param pDevObj Device object.
* @param pIrp IRP.
*/
{
Log(("VBoxGuest::vboxguestwinGuestNotSupportedStub\n"));
return STATUS_NOT_SUPPORTED;
}
/**
* DPC handler
*
* @param dpc DPC descriptor.
* @param pDevObj Device object.
* @param irp Interrupt request packet.
* @param context Context specific pointer.
*/
{
/* Unblock handlers waiting for arrived events.
*
* Events are very low things, there is one event flag (1 or more bit)
* for each event. Each event is processed by exactly one handler.
*
* Assume that we trust additions and that other drivers will
* handle its respective events without trying to fetch all events.
*
* Anyway design assures that wrong event processing will affect only guest.
*
* Event handler calls VMMDev IOCTL for waiting an event.
* It supplies event mask. IOCTL blocks on EventNotification.
* Here we just signal an the EventNotification to all waiting
* threads, the IOCTL handler analyzes events and either
* return to caller or blocks again.
*
* If we do not have too many events this is a simple and good
* approach. Other way is to have as many Event objects as the callers
* and wake up only callers waiting for the specific event.
*
* Now with the 'wake up all' appoach we probably do not need the DPC
* handler and can signal event directly from ISR.
*
*/
#ifdef VBOX_WITH_HGCM
if (pDevExt)
#endif
}
/**
* ISR handler.
*
* @return BOOLEAN Indicates whether the IRQ came from us (TRUE) or not (FALSE).
* @param interrupt Interrupt that was triggered.
* @param serviceContext Context specific pointer.
*/
{
/*Log(("VBoxGuest::vboxguestwinGuestIsrHandler: pDevExt = 0x%p, pVMMDevMemory = 0x%p\n",
pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/
if (VBoxGuestCommonISR(pDevExt))
{
/*Log(("VBoxGuest::vboxguestwinGuestIsrHandler: IRQ was taken! pDeviceObject = 0x%p, pCurrentIrp = 0x%p\n",
}
return fIRQTaken;
}
/*
* Overriden routine for mouse polling events. Not
* used at the moment on Windows.
*
* @param pDevExt Device extension structure.
*/
{
}
/**
* Helper to scan the PCI resource list and remember stuff.
*
* @param pResList Resource list
* @param pDevExt Device extension
*/
{
/* Enumerate the resource list. */
Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Found %d resources\n",
ULONG rangeCount = 0;
ULONG cMMIORange = 0;
{
switch (pPartialData->Type)
{
case CmResourceTypePort:
{
/* Overflow protection. */
if (rangeCount < PCI_TYPE0_ADDRESSES)
{
Log(("VBoxGuest::vboxguestwinScanPCIResourceList: I/O range: Base = %08x:%08x, Length = %08x\n",
/* Save the IO port base. */
/** @todo Not so good. */
/* Save resource information. */
Log(("VBoxGuest::vboxguestwinScanPCIResourceList: I/O range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
/* Next item ... */
rangeCount++; pBaseAddress++;
}
break;
}
case CmResourceTypeInterrupt:
{
Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Interrupt: Level = %x, Vector = %x, Mode = %x\n",
pPartialData->Flags));
/* Save information. */
/* Check interrupt mode. */
{
}
else
{
}
break;
}
case CmResourceTypeMemory:
{
/* Overflow protection. */
if (rangeCount < PCI_TYPE0_ADDRESSES)
{
Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Memory range: Base = %08x:%08x, Length = %08x\n",
/** @todo Reconsider memory type. */
if ( cMMIORange == 0 /* Only care about the first MMIO range (!!!). */
{
/* Save physical MMIO base + length for VMMDev. */
/* Save resource information. */
Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n",
/* Next item ... */
}
else
{
Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Ignoring memory: Flags = %08x\n",
pPartialData->Flags));
}
}
break;
}
default:
{
Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Unhandled resource found, type = %d\n", pPartialData->Type));
break;
}
}
}
/* Memorize the number of resources found. */
return rc;
}
NTSTATUS vboxguestwinMapVMMDevMemory(PVBOXGUESTDEVEXT pDevExt, PHYSICAL_ADDRESS physicalAdr, ULONG ulLength,
{
/* pcbMMIO is optional. */
{
if (pVMMDevMemory)
{
Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: VMMDevMemory: Version = 0x%x, Size = %d\n",
/* Check version of the structure; do we have the right memory version? */
{
Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: Wrong version (%u), refusing operation!\n",
/* Not our version, refuse operation and unmap the memory. */
}
else
{
/* Save results. */
if (pcbMMIO) /* Optional. */
Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n",
*ppvMMIOBase));
}
}
else
}
return rc;
}
{
if (pDevExt->pVMMDevMemory)
{
}
}
{
switch (winVer)
{
case WINNT4:
return VBOXOSTYPE_WinNT4;
case WIN2K:
return VBOXOSTYPE_Win2k;
case WINXP:
return VBOXOSTYPE_WinXP;
case WIN2K3:
return VBOXOSTYPE_Win2k3;
case WINVISTA:
return VBOXOSTYPE_WinVista;
case WIN7:
return VBOXOSTYPE_Win7;
default:
break;
}
/* We don't know, therefore NT family. */
return VBOXOSTYPE_WinNT;
}
#ifdef DEBUG
/** A quick implementation of AtomicTestAndClear for uint32_t and multiple
* bits.
*/
{
AssertPtrReturn(pu32Bits, 0);
u32Mask));
while (iBitOffset > 0)
{
if (fSet)
}
return u32Result;
}
{
)
AssertLogRelMsgFailed(("%s: TEST FAILED: u32Mask=0x%x, u32Bits (before)=0x%x, u32Bits (after)=0x%x, u32Result=0x%x, u32Exp=ox%x\n",
u32Result));
}
{
vboxguestwinTestAtomicTestAndClearBitsU32(0x11, 0, 0);
}
#endif