reactos/sdk/lib/drivers/wdf/shared/core/fxdriver.cpp
2021-06-11 15:33:08 +03:00

657 lines
18 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Module Name:
FxDriver.cpp
Abstract:
This is the main driver framework.
Author:
Environment:
Both kernel and user mode
Revision History:
--*/
#include "coreprivshared.hpp"
#include "fxiotarget.hpp"
// Tracing support
extern "C" {
// #include "FxDriver.tmh"
}
FxDriver::FxDriver(
__in MdDriverObject ArgDriverObject,
__in PWDF_DRIVER_CONFIG DriverConfig,
__in PFX_DRIVER_GLOBALS FxDriverGlobals
) :
FxNonPagedObject(FX_TYPE_DRIVER, sizeof(FxDriver), FxDriverGlobals),
m_DriverObject(ArgDriverObject),
m_CallbackMutexLock(FxDriverGlobals)
{
RtlInitUnicodeString(&m_RegistryPath, NULL);
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
m_ExecutionLevel = WdfExecutionLevelDispatch;
#else
m_ExecutionLevel = WdfExecutionLevelPassive;
#endif
m_SynchronizationScope = WdfSynchronizationScopeNone;
m_CallbackLockPtr = NULL;
m_CallbackLockObjectPtr = NULL;
m_DisposeList = NULL;
//
// These are initialized up front so that sub objects
// can have the right configuration
//
WDF_DRIVER_CONFIG_INIT(&m_Config, NULL);
// Only copy the smallest of what is specified and our size
RtlCopyMemory(&m_Config, DriverConfig, min(sizeof(m_Config), DriverConfig->Size) );
m_DebuggerConnected = FALSE;
#if FX_IS_USER_MODE
m_DriverParametersKey = NULL;
#endif
}
FxDriver::~FxDriver()
{
// Make it always present right now even on free builds
if (IsDisposed() == FALSE) {
DoTraceLevelMessage(
GetDriverGlobals(), TRACE_LEVEL_FATAL, TRACINGDRIVER,
"FxDriver 0x%p not disposed: this maybe a driver reference count "
"problem with WDFDRIVER 0x%p", this, GetObjectHandleUnchecked());
FxVerifierBugCheck(GetDriverGlobals(),
WDF_OBJECT_ERROR,
(ULONG_PTR) GetObjectHandleUnchecked(),
(ULONG_PTR) this);
}
//
// Free the memory for the registry path if required.
//
if (m_RegistryPath.Buffer) {
FxPoolFree(m_RegistryPath.Buffer);
}
if (m_DisposeList != NULL) {
m_DisposeList->DeleteObject();
}
#if FX_IS_USER_MODE
//
// Close the R/W handle to the driver's service parameters key
// that we opened during Initialize.
//
if (m_DriverParametersKey != NULL) {
NTSTATUS status = FxRegKey::_Close(m_DriverParametersKey);
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDRIVER,
"Cannot close Driver Parameters key %!STATUS!",
status);
}
m_DriverParametersKey = NULL;
}
//
// The host-created driver object holds a reference to this
// FxDriver object. Clear it, since this object was deleted.
//
ClearDriverObjectFxDriver();
#endif
}
BOOLEAN
FxDriver::Dispose(
VOID
)
{
if (m_DisposeList != NULL) {
m_DisposeList->WaitForEmpty();
}
return FxNonPagedObject::Dispose(); // __super call
}
VOID
FxDriver::Unload(
__in MdDriverObject DriverObject
)
{
PFX_DRIVER_GLOBALS pFxDriverGlobals;
FxDriver *pDriver;
pDriver = FxDriver::GetFxDriver(DriverObject);
if (pDriver == NULL) {
return;
}
pFxDriverGlobals = pDriver->GetDriverGlobals();
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDRIVER,
"Unloading WDFDRIVER %p, PDRIVER_OBJECT_UM %p",
pDriver->GetHandle(), DriverObject);
//
// Invoke the driver if they specified an unload routine.
//
if (pDriver->m_DriverUnload.Method) {
pDriver->m_DriverUnload.Invoke(pDriver->GetHandle());
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDRIVER,
"Driver unload routine Exit WDFDRIVER %p, PDRIVER_OBJECT_UM %p",
pDriver->GetHandle(), DriverObject);
}
//
// Delete the FxDriver object.
//
// This releases the FxDriver reference. Must be called at PASSIVE
//
pDriver->DeleteObject();
pFxDriverGlobals->Driver = NULL;
FxDestroy(pFxDriverGlobals);
}
VOID
FxDriver::_InitializeDriverName(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PCUNICODE_STRING RegistryPath
)
/*++
Routine Description:
Create a CHAR version of the service name contained in RegistryPath.
Arguments:
RegistryPath - the path to the service name in the registry.
Return Value:
None
--*/
{
PWCHAR pCur, pBegin, pEnd;
RtlZeroMemory(&FxDriverGlobals->Public.DriverName[0],
sizeof(FxDriverGlobals->Public.DriverName) );
if (RegistryPath == NULL) {
return;
}
pBegin = RegistryPath->Buffer;
//
// pEnd is one past the end of the string, while pCur is a pointer to the
// last character of the string. We will decrement pCur down towards the
// beginning of the string.
//
pEnd = pBegin + (RegistryPath->Length / sizeof(WCHAR));
pCur = pEnd - 1;
for ( ; *pCur != L'\\' && pCur != pBegin; pCur--) {
DO_NOTHING();
}
if (pCur != pBegin && *pCur == L'\\') {
size_t regLen;
ULONG i;
pCur++;
//
// Can't use wcslen becuase this is a UNICODE_STRING which means that it
// does not necessarily have a terminating NULL in the buffer.
//
regLen = pEnd - pCur;
if (regLen > WDF_DRIVER_GLOBALS_NAME_LEN-1) {
regLen = WDF_DRIVER_GLOBALS_NAME_LEN-1;
}
for (i = 0; i < regLen; i++) {
FxDriverGlobals->Public.DriverName[i] = (CHAR) pCur[i];
}
}
else {
NTSTATUS status;
#if FX_CORE_MODE==FX_CORE_KERNEL_MODE
status = RtlStringCbCopyA(FxDriverGlobals->Public.DriverName,
sizeof(FxDriverGlobals->Public.DriverName),
"WDF");
#else // USER_MODE
HRESULT hr;
hr = StringCbCopyA(FxDriverGlobals->Public.DriverName,
sizeof(FxDriverGlobals->Public.DriverName),
"WDF");
if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
status = WinErrorToNtStatus(HRESULT_CODE(hr));
}
else {
status = SUCCEEDED(hr) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
}
#endif
UNREFERENCED_PARAMETER(status);
ASSERT(NT_SUCCESS(status));
}
}
VOID
FxDriver::_InitializeTag(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PWDF_DRIVER_CONFIG Config
)
/*++
Routine Description:
Tries to create a tag for the driver based off of its name. This tag is
used for allocations made on behalf of the driver so that it is easier to
track which allocations belong to which image.
Arguments:
Config - driver writer provided config. in the future, may contain a tag
value in it
Return Value:
None
--*/
{
PCHAR pBegin;
size_t length;
UNREFERENCED_PARAMETER(Config);
length = strlen(FxDriverGlobals->Public.DriverName);
pBegin = &FxDriverGlobals->Public.DriverName[0];
if (length >= 3) {
//
// If the driver name begins with "WDF" (case insensitive), start after
// "WDF"
//
if ((pBegin[0] == 'w' || pBegin[0] == 'W') &&
(pBegin[1] == 'd' || pBegin[1] == 'D') &&
(pBegin[2] == 'f' || pBegin[2] == 'F')) {
length -=3;
pBegin += 3;
}
}
if (length <= 2) {
//
// 2 or less characters is not a unique enough tag, just use the default
// tag.
//
FxDriverGlobals->Tag = FX_TAG;
}
else {
if (length > sizeof(ULONG)) {
length = sizeof(ULONG);
}
//
// This copies the bytes in the right order (so that they appear correct
// when dumped as a sequence of chars)
//
RtlCopyMemory(&FxDriverGlobals->Tag,
pBegin,
length);
FxDriverGlobals->Public.DriverTag = FxDriverGlobals->Tag;
}
}
_Must_inspect_result_
NTSTATUS
FxDriver::Initialize(
__in PCUNICODE_STRING ArgRegistryPath,
__in PWDF_DRIVER_CONFIG Config,
__in_opt PWDF_OBJECT_ATTRIBUTES DriverAttributes
)
{
PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
NTSTATUS status;
// WDFDRIVER can not be deleted by the device driver
MarkNoDeleteDDI();
MarkDisposeOverride(ObjectDoNotLock);
//
// Configure Constraints
//
ConfigureConstraints(DriverAttributes);
if (m_DriverObject.GetObject() == NULL) {
return STATUS_UNSUCCESSFUL;
}
// Allocate FxDisposeList
status = FxDisposeList::_Create(FxDriverGlobals, m_DriverObject.GetObject(), &m_DisposeList);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Store FxDriver in Driver object extension
//
status = AllocateDriverObjectExtensionAndStoreFxDriver();
if (!NT_SUCCESS(status)) {
return status;
}
//
// Store away the callback functions.
//
if ((Config->DriverInitFlags & WdfDriverInitNoDispatchOverride) == 0) {
//
// Caller doesn't want to override the dispatch table. That
// means that they want to create everything and still be in
// control (or at least the port driver will take over)
//
m_DriverDeviceAdd.Method = Config->EvtDriverDeviceAdd;
m_DriverUnload.Method = Config->EvtDriverUnload;
}
if (ArgRegistryPath != NULL) {
USHORT length;
length = ArgRegistryPath->Length + sizeof(UNICODE_NULL);
m_RegistryPath.Length = ArgRegistryPath->Length;
m_RegistryPath.MaximumLength = length;
m_RegistryPath.Buffer = (PWSTR) FxPoolAllocate(
GetDriverGlobals(), PagedPool, length);
if (m_RegistryPath.Buffer != NULL) {
RtlCopyMemory(m_RegistryPath.Buffer,
ArgRegistryPath->Buffer,
ArgRegistryPath->Length);
//
// other parts of WDF assumes m_RegistryPath.Buffer is
// a null terminated string. make sure it is.
//
m_RegistryPath.Buffer[length/sizeof(WCHAR)- 1] = UNICODE_NULL;
}
else {
//
// We failed to allocate space for the registry path
// so set the length to 0.
//
m_RegistryPath.Length = 0;
m_RegistryPath.MaximumLength = 0;
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
if (NT_SUCCESS(status)) {
if ((Config->DriverInitFlags & WdfDriverInitNoDispatchOverride) == 0) {
UCHAR i;
//
// Set up dispatch routines.
//
if (Config->DriverInitFlags & WdfDriverInitNonPnpDriver) {
//
// NT4 style drivers must clear the AddDevice field in the
// driver object so that they can be unloaded while still
// having device objects around.
//
// If AddDevice is set, NT considers the driver a pnp driver
// and will not allow net stop to unload the driver.
//
m_DriverObject.SetDriverExtensionAddDevice(NULL);
//
// Unload for an NT4 driver is still optional if the driver
// does not want to be stopped (through net stop for
// instance).
//
if (Config->EvtDriverUnload != NULL) {
m_DriverObject.SetDriverUnload(Unload);
}
else {
m_DriverObject.SetDriverUnload(NULL);
}
}
else {
//
// PnP driver, set our routines up
//
m_DriverObject.SetDriverExtensionAddDevice(AddDevice);
m_DriverObject.SetDriverUnload(Unload);
}
//
// For any major control code that we use a remove lock, the
// following locations must be updated:
//
// 1) FxDevice::_RequiresRemLock() which decides if the remlock
// is required
// 2) FxDefaultIrpHandler::Dispatch might need to be changed if
// there is catchall generic post processing that must be done
// 3) Whereever the major code irp handler completes the irp or
// sends it down the stack. A good design would have all
// spots in the irp handler where this is done to call a
// common function.
//
WDFCASSERT(IRP_MN_REMOVE_DEVICE != 0x0);
#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
if (FxDevice::_RequiresRemLock(i, 0x0) == FxDeviceRemLockNotRequired) {
m_DriverObject.SetMajorFunction(i, FxDevice::Dispatch);
}
else {
m_DriverObject.SetMajorFunction(i, FxDevice::DispatchWithLock);
}
}
#else // USER_MODE
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
if (FxDevice::_RequiresRemLock(i, 0x0) == FxDeviceRemLockNotRequired) {
m_DriverObject.SetMajorFunction(i, FxDevice::DispatchUm);
}
else {
m_DriverObject.SetMajorFunction(i, FxDevice::DispatchWithLockUm);
}
}
#endif
}
//
// Determine if the debugger is connected.
//
#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
if (KD_DEBUGGER_ENABLED == TRUE && KD_DEBUGGER_NOT_PRESENT == FALSE) {
m_DebuggerConnected = TRUE;
}
#endif
//
// Log this notable event after tracing has been initialized.
//
if ((Config->DriverInitFlags & WdfDriverInitNonPnpDriver) &&
Config->EvtDriverUnload == NULL) {
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGDRIVER,
"Driver Object %p, reg path %wZ cannot be "
"unloaded, no DriverUnload routine specified",
m_DriverObject.GetObject(), &m_RegistryPath);
}
#if FX_IS_USER_MODE
//
// Open a R/W handle to the driver's service parameters key
//
status = OpenParametersKey();
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDRIVER,
"Cannot open Driver Parameters key %!STATUS!",
status);
}
#endif
}
return status;
}
_Must_inspect_result_
FxString *
FxDriver::GetRegistryPath(
VOID
)
{
FxString *pString;
pString = new(GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES)
FxString(GetDriverGlobals());
if (pString != NULL) {
NTSTATUS status;
status = pString->Assign(m_RegistryPath.Buffer);
if (!NT_SUCCESS(status)) {
pString->Release();
pString = NULL;
}
}
return pString;
}
VOID
FxDriver::ConfigureConstraints(
__in_opt PWDF_OBJECT_ATTRIBUTES DriverAttributes
)
/*++
Routine Description:
Determine the pointer to the proper lock to acquire
for event callbacks from the FxDriver to the device driver
depending on the configured locking model.
Arguments:
DriverAttributes - caller supplied scope and level, used only if they
are not InheritFromParent.
Returns:
None
--*/
{
BOOLEAN automaticLockingRequired;
automaticLockingRequired = FALSE;
// Initialize the mutex lock
m_CallbackMutexLock.Initialize(this);
MarkPassiveCallbacks(ObjectDoNotLock);
m_CallbackLockPtr = &m_CallbackMutexLock;
m_CallbackLockObjectPtr = this;
//
// Use the caller supplied scope and level only if they are not
// InheritFromParent.
//
if (DriverAttributes != NULL) {
if (DriverAttributes->ExecutionLevel !=
WdfExecutionLevelInheritFromParent) {
m_ExecutionLevel = DriverAttributes->ExecutionLevel;
}
if (DriverAttributes->SynchronizationScope !=
WdfSynchronizationScopeInheritFromParent) {
m_SynchronizationScope = DriverAttributes->SynchronizationScope;
}
}
//
// If the driver asks for any synchronization, we synchronize the
// WDFDRIVER object's own callbacks as well.
//
// (No option to extend synchronization for regular operations
// across all WDFDEVICE objects)
//
if (m_SynchronizationScope == WdfSynchronizationScopeDevice ||
m_SynchronizationScope == WdfSynchronizationScopeQueue) {
automaticLockingRequired = TRUE;
}
//
// No FxDriver events are delivered from a thread that is
// not already at PASSIVE_LEVEL, so we don't need to
// allocate an FxSystemWorkItem if the execution level
// constraint is WdfExecutionLevelPassive.
//
// If any events are added FxDriver that could occur on a thread
// that is above PASSIVE_LEVEL, then an FxSystemWorkItem would
// need to be allocated to deliver those events similar to the
// code in FxIoQueue.
//
//
// Configure FxDriver event callback locks
//
if (automaticLockingRequired) {
m_DriverDeviceAdd.SetCallbackLockPtr(m_CallbackLockPtr);
}
else {
m_DriverDeviceAdd.SetCallbackLockPtr(NULL);
}
}