|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
loadunld.c
Abstract:
This module contains the code to implement the NtLoadDriver and NtUnLoadDriver system services for the NT I/O system.
Author:
Darryl E. Havens (darrylh) 5-Apr-1992
Environment:
Kernel mode only
Revision History:
--*/
#include "iomgr.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NtLoadDriver)
#pragma alloc_text(PAGE, NtUnloadDriver)
#endif
NTSTATUS NtLoadDriver( IN PUNICODE_STRING DriverServiceName )
/*++
Routine Description:
This service dynamically loads a device or file system driver into the currently running system. It requires that the caller have the appropriate privilege to execute this service.
Arguments:
DriverServiceName - Specifies the name of the node in the registry associated with the driver to be loaded.
Return Value:
The status returned is the final completion status of the load operation.
--*/
{ KPROCESSOR_MODE requestorMode; UNICODE_STRING driverServiceName; PWCHAR nameBuffer = (PWCHAR) NULL; LOAD_PACKET loadPacket; PETHREAD CurrentThread;
PAGED_CODE();
//
// Get the previous mode; i.e., the mode of the caller.
//
CurrentThread = PsGetCurrentThread (); requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
if (requestorMode != KernelMode) {
//
// The caller's access mode is not kernel so check to ensure that
// the caller has the privilege to load a driver and probe and
// capture the name of the driver service entry.
//
if (!SeSinglePrivilegeCheck( SeLoadDriverPrivilege, requestorMode )) { return STATUS_PRIVILEGE_NOT_HELD; }
//
// The caller has the appropriate privilege to load and unload
// drivers, so capture the driver service name string so that it
// can be used to locate the driver from the registry node.
//
try {
driverServiceName = ProbeAndReadUnicodeString( DriverServiceName );
if (!driverServiceName.Length) { return STATUS_INVALID_PARAMETER; }
ProbeForRead( driverServiceName.Buffer, driverServiceName.Length, sizeof( WCHAR ) );
nameBuffer = ExAllocatePoolWithQuota( PagedPool, driverServiceName.Length );
RtlCopyMemory( nameBuffer, driverServiceName.Buffer, driverServiceName.Length );
driverServiceName.Buffer = nameBuffer;
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred while attempting to capture the
// input name string or while attempting to allocate the name
// string buffer. Simply clean everything up and return an
// appropriate error status code.
//
if (nameBuffer) { ExFreePool( nameBuffer ); } return GetExceptionCode(); } } else { driverServiceName = *DriverServiceName; }
//
// Because drivers may wish to create a system thread and execute in
// its context, the remainder of this service must be executed in the
// context of the primary system process. This is accomplished by
// queueing a request to one of the EX worker threads and having it
// invoke the I/O system routine to complete this work.
//
// Fill in a request packet and queue it to the worker thread then, so
// that it can actually do the load.
//
KeInitializeEvent( &loadPacket.Event, NotificationEvent, FALSE ); loadPacket.DriverObject = (PDRIVER_OBJECT) NULL; loadPacket.DriverServiceName = &driverServiceName;
if (PsGetCurrentProcessByThread(CurrentThread) == PsInitialSystemProcess) {
//
// If we are already in the system process, just use this thread.
//
IopLoadUnloadDriver(&loadPacket);
} else {
ExInitializeWorkItem( &loadPacket.WorkQueueItem, IopLoadUnloadDriver, &loadPacket );
ExQueueWorkItem( &loadPacket.WorkQueueItem, DelayedWorkQueue );
KeWaitForSingleObject( &loadPacket.Event, UserRequest, KernelMode, FALSE, (PLARGE_INTEGER) NULL );
}
//
// The load operation is now complete. If a name buffer was allocated,
// deallocate it now, and return the final status of the load operation.
//
if (nameBuffer) { ExFreePool( nameBuffer ); }
return loadPacket.FinalStatus; }
NTSTATUS IopCheckUnloadDriver( IN PDRIVER_OBJECT driverObject, OUT PBOOLEAN unloadDriver ) { PDEVICE_OBJECT deviceObject; KIRQL irql;
//
// Check to see whether the driver has already been marked for an unload
// operation by anyone in the past.
//
irql = KeAcquireQueuedSpinLock( LockQueueIoDatabaseLock );
if ((driverObject->DeviceObject == NULL && (driverObject->Flags & DRVO_UNLOAD_INVOKED)) || (!(driverObject->Flags & DRVO_BASE_FILESYSTEM_DRIVER) && driverObject->DeviceObject && driverObject->DeviceObject->DeviceObjectExtension->ExtensionFlags & DOE_UNLOAD_PENDING)) {
//
// The driver has already been marked for unload or is being
// unloaded. Simply return a successful completion status since
// the driver is on its way out and therefore has been "marked for
// unload".
//
KeReleaseQueuedSpinLock( LockQueueIoDatabaseLock, irql );
ObDereferenceObject( driverObject ); return STATUS_SUCCESS; }
//
// The driver exists, and it implements unload, and it has not, so far,
// been marked for an unload operation. Simply mark all of the devices
// that the driver owns as being marked for unload. While this is going
// on, count the references for each of the devices. If all of the
// devices have a zero reference count, then tell the driver that it
// should unload itself.
//
deviceObject = driverObject->DeviceObject; *unloadDriver = TRUE;
while (deviceObject) { deviceObject->DeviceObjectExtension->ExtensionFlags |= DOE_UNLOAD_PENDING; if (deviceObject->ReferenceCount || deviceObject->AttachedDevice) { *unloadDriver = FALSE; } deviceObject = deviceObject->NextDevice; }
//
// If this is a base filesystem driver then delay the unload until all its device objects
// are deleted.
//
if (driverObject->Flags & DRVO_BASE_FILESYSTEM_DRIVER && driverObject->DeviceObject) { *unloadDriver = FALSE; }
if (*unloadDriver) { driverObject->Flags |= DRVO_UNLOAD_INVOKED; }
KeReleaseQueuedSpinLock( LockQueueIoDatabaseLock, irql ); return STATUS_UNSUCCESSFUL; }
NTSTATUS NtUnloadDriver( IN PUNICODE_STRING DriverServiceName ) { return (IopUnloadDriver(DriverServiceName, FALSE)); }
NTSTATUS IopUnloadDriver( IN PUNICODE_STRING DriverServiceName, IN BOOLEAN InvokedByPnpMgr ) /*++
Routine Description:
This service dynamically unloads a device or file system driver from the currently running system. It requires that the caller have the appropriate privilege to execute this service.
Arguments:
DriverServiceName - Specifies the name of the node in the registry associated with the driver to be unloaded.
Return Value:
The status returned is the final completion status of the operation.
--*/
{ KPROCESSOR_MODE requestorMode; UNICODE_STRING driverServiceName; PWCHAR nameBuffer = (PWCHAR) NULL; NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; HANDLE keyHandle; UNICODE_STRING driverName; HANDLE driverHandle; PDRIVER_OBJECT driverObject; BOOLEAN unloadDriver;
PAGED_CODE();
//
// Get the previous mode; i.e., the mode of the caller.
//
requestorMode = KeGetPreviousMode();
if ((requestorMode != KernelMode) && (InvokedByPnpMgr == FALSE)) {
//
// The caller's access mode is not kernel so check to ensure that
// the caller has the privilege to unload a driver and probe and
// capture the name of the driver service entry.
//
if (!SeSinglePrivilegeCheck( SeLoadDriverPrivilege, requestorMode )) { return STATUS_PRIVILEGE_NOT_HELD; }
//
// The caller has the appropriate privilege to load and unload
// drivers, so capture the driver service name string so that it
// can be used to locate the driver from the registry node.
//
try {
driverServiceName = ProbeAndReadUnicodeString( DriverServiceName );
if (!driverServiceName.Length) { return STATUS_INVALID_PARAMETER; }
ProbeForRead( driverServiceName.Buffer, driverServiceName.Length, sizeof( WCHAR ) );
nameBuffer = ExAllocatePoolWithQuota( PagedPool, driverServiceName.Length );
RtlCopyMemory( nameBuffer, driverServiceName.Buffer, driverServiceName.Length );
driverServiceName.Buffer = nameBuffer;
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred while attempting to capture the
// input name string or while attempting to allocate the name
// string buffer. Simply clean everything up and return an
// appropriate error status code.
//
if (nameBuffer) { ExFreePool( nameBuffer ); } return GetExceptionCode(); }
//
// Now that the caller's parameters have been captured and everything
// appears to have checked out, actually attempt to unload the driver.
// This is done with a previous mode of kernel so that drivers will
// not fail to unload because the caller didn't happen to have access
// to some resource that the driver needs in order to complete its
// unload operation.
//
status = ZwUnloadDriver( &driverServiceName ); ExFreePool( nameBuffer ); return status; }
//
// The caller's mode is now kernel mode. Attempt to actually unload the
// driver specified by the indicated registry node. Begin by opening
// the registry node for this driver.
//
status = IopOpenRegistryKey( &keyHandle, (HANDLE) NULL, DriverServiceName, KEY_READ, FALSE ); if (!NT_SUCCESS( status )) { return status; }
//
// Get the optional object name for this driver from the value for this
// key. If one exists, then its name overrides the default name of the
// driver.
//
status = IopGetDriverNameFromKeyNode( keyHandle, &driverName ); NtClose( keyHandle ); if (!NT_SUCCESS( status )) { return status; }
//
// Now attempt to open the driver object for the specified driver.
//
InitializeObjectAttributes( &objectAttributes, &driverName, OBJ_CASE_INSENSITIVE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL );
status = ObOpenObjectByName( &objectAttributes, IoDriverObjectType, KernelMode, NULL, FILE_READ_DATA, (PVOID) NULL, &driverHandle );
//
// Perform some common cleanup by getting rid of buffers that have been
// allocated up to this point so that error conditions do not have as
// much work to do on each exit path.
//
ExFreePool( driverName.Buffer );
//
// If the driver object could not be located in the first place, then
// return now before attempting to do anything else.
//
if (!NT_SUCCESS( status )) { return status; }
//
// The driver object was located, so convert the handle into a pointer
// so that the driver object itself can be examined.
//
status = ObReferenceObjectByHandle( driverHandle, 0, IoDriverObjectType, KernelMode, (PVOID *) &driverObject, NULL ); NtClose( driverHandle );
if (!NT_SUCCESS( status )) { return status; }
//
// Check to see whether or not this driver implements unload. Also,
// if the driver has no section associated with it, then it was loaded
// be the OS loader and therefore cannot be unloaded. If either is true,
// return an appropriate error status code.
//
if (driverObject->DriverUnload == (PDRIVER_UNLOAD) NULL || !driverObject->DriverSection) { ObDereferenceObject( driverObject ); return STATUS_INVALID_DEVICE_REQUEST; }
if (!InvokedByPnpMgr && !IopIsLegacyDriver(driverObject)) {
ObDereferenceObject( driverObject ); return STATUS_INVALID_DEVICE_REQUEST; }
//
// Check to see whether the driver has already been marked for an unload
// operation by anyone in the past.
//
status = IopCheckUnloadDriver(driverObject,&unloadDriver);
if ( NT_SUCCESS(status) ) { return status; }
if (unloadDriver) {
if (PsGetCurrentProcess() == PsInitialSystemProcess) {
//
// The current thread is alrady executing in the context of the
// system process, so simply invoke the driver's unload routine.
//
driverObject->DriverUnload( driverObject );
} else {
//
// The current thread is not executing in the context of the system
// process, which is required in order to invoke the driver's unload
// routine. Queue a worker item to one of the worker threads to
// get into the appropriate process context and then invoke the
// routine.
//
LOAD_PACKET loadPacket;
KeInitializeEvent( &loadPacket.Event, NotificationEvent, FALSE ); loadPacket.DriverObject = driverObject; ExInitializeWorkItem( &loadPacket.WorkQueueItem, IopLoadUnloadDriver, &loadPacket ); ExQueueWorkItem( &loadPacket.WorkQueueItem, DelayedWorkQueue ); (VOID) KeWaitForSingleObject( &loadPacket.Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); } ObMakeTemporaryObject( driverObject ); ObDereferenceObject( driverObject ); }
//
// The driver has either been unloaded, or it has successfully been
// marked for an unload operation. Simply dereference the pointer to
// the object and return success.
//
ObDereferenceObject( driverObject ); return STATUS_SUCCESS; }
|