/*++ Copyright (c) 1998 Microsoft Corporation Module Name: init.c Abstract: This module performs initialization for the SPUD device driver. Author: John Ballard (jballard) 21-Oct-1996 Revision History: Keith Moore (keithmo) 04-Feb-1998 Cleanup, added much needed comments. --*/ #include "spudp.h" // // Private constants. // #define REGISTRY_SPUD_INFORMATION L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Spud" #define REGISTRY_PARAMETERS L"Parameters" #define REGISTRY_DO_NOT_LOAD L"DoNotLoad" #if DBG #define REGISTRY_BREAK_ON_STARTUP L"BreakOnStartup" #define REGISTRY_USE_PRIVATE_ASSERT L"UsePrivateAssert" #endif #if ALLOW_UNLOAD #define REGISTRY_ENABLE_UNLOAD L"EnableUnload" #endif // // Private globals. // BOOLEAN SpudpFailLoad; #if ALLOW_UNLOAD BOOLEAN SpudpEnableUnload; #endif // // Private prototypes. // VOID SpudpReadRegistry( VOID ); NTSTATUS SpudpOpenRegistry( IN PUNICODE_STRING BaseName, OUT PHANDLE ParametersHandle ); ULONG SpudpReadSingleParameter( IN HANDLE ParametersHandle, IN PWCHAR ValueName, IN LONG DefaultValue ); #if ALLOW_UNLOAD VOID SpudpUnload( IN PDRIVER_OBJECT DriverObject ); #endif #ifdef ALLOC_PRAGMA #pragma alloc_text( INIT, DriverEntry ) #pragma alloc_text( INIT, SpudpReadRegistry ) #pragma alloc_text( INIT, SpudpOpenRegistry ) #pragma alloc_text( INIT, SpudpReadSingleParameter ) #if ALLOW_UNLOAD #pragma alloc_text( PAGE, SpudpUnload ) #endif #endif // // Public functions. // NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This is the initialization routine for the SPUD driver. Arguments: DriverObject - Pointer to driver object created by the system. Return Value: The function value is the final status from the initialization operation. --*/ { NTSTATUS status; UNICODE_STRING deviceName; // // Sanity check. // PAGED_CODE(); // // Read any configuration information from the registry. // SpudpReadRegistry(); // // If we're configured to fail the load, then bail. // if( SpudpFailLoad ) { return STATUS_INVALID_DEVICE_REQUEST; } // // Create and initialize our device object. // RtlInitUnicodeString( &deviceName, SPUD_DEVICE_NAME ); status = IoCreateDevice( DriverObject, // DriverObject 0, // DeviceExtension &deviceName, // DeviceName FILE_DEVICE_NAMED_PIPE, // DeviceType 0, // DeviceCharacteristics TRUE, // Exclusive &SpudSelfDeviceObject // DeviceObject ); if( !NT_SUCCESS(status) ) { KdPrint(( "SPUD DriverEntry: unable to create device object: %X\n", status )); return status; } DriverObject->MajorFunction[IRP_MJ_CREATE] = SpudIrpCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = SpudIrpClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = SpudIrpCleanup; DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = SpudIrpQuery; #if ALLOW_UNLOAD if( SpudpEnableUnload ) { DriverObject->DriverUnload = SpudpUnload; KdPrint(( "SPUD DriverEntry: unload enabled\n" )); } #endif // // Initialize the context manager. // status = SpudInitializeContextManager(); if( !NT_SUCCESS(status) ) { IoDeleteDevice( SpudSelfDeviceObject ); return status; } // // Initialize other global data. // status = SpudInitializeData(); if( !NT_SUCCESS(status) ) { SpudTerminateContextManager(); IoDeleteDevice( SpudSelfDeviceObject ); return status; } // // Add our service table to the system. // if( !KeAddSystemServiceTable( SpudServiceTable, // Base NULL, // Count SpudServiceLimit, // Limit SpudArgumentTable, // Number IIS_SERVICE_INDEX // Index ) ) { SpudTerminateContextManager(); IoDeleteDevice( SpudSelfDeviceObject ); return STATUS_INSUFFICIENT_RESOURCES; } return status; } // DriverEntry // // Private functions. // VOID SpudpReadRegistry( VOID ) /*++ Routine Description: Reads the SPUD section of the registry. Any values listed in the registry override defaults. Arguments: None. Return Value: None -- if anything fails, the default value is used. --*/ { HANDLE parametersHandle; NTSTATUS status; UNICODE_STRING registryPath; CLONG i; // // Sanity check. // PAGED_CODE(); // // Open the registry. // RtlInitUnicodeString( ®istryPath, REGISTRY_SPUD_INFORMATION ); status = SpudpOpenRegistry( ®istryPath, ¶metersHandle ); if( status != STATUS_SUCCESS ) { return; } #if DBG // // Force a breakpoint if so requested. // if( SpudpReadSingleParameter( parametersHandle, REGISTRY_BREAK_ON_STARTUP, 0 ) != 0 ) { DbgBreakPoint(); } // // Enable private assert function if requested. Note that the // default value is TRUE for free builds and FALSE for checked // builds. // SpudUsePrivateAssert = ( *(PULONG)&NtBuildNumber & 0xF0000000 ) == 0xF0000000; SpudUsePrivateAssert = SpudpReadSingleParameter( parametersHandle, REGISTRY_USE_PRIVATE_ASSERT, (LONG)SpudUsePrivateAssert ) != 0; #endif #if ALLOW_UNLOAD // // Enable driver unload on checked builds only if the proper // value is in the registry. NEVER enable driver unload on free // builds. // SpudpEnableUnload = SpudpReadSingleParameter( parametersHandle, REGISTRY_ENABLE_UNLOAD, (LONG)SpudpEnableUnload ) != 0; #endif // // Fail Load if so requested. // if( SpudpReadSingleParameter( parametersHandle, REGISTRY_DO_NOT_LOAD, 0 ) != 0 ) { SpudpFailLoad = TRUE; KdPrint(("Spud.sys load aborted! DoNotLoad is configured in the registry.\n")); } else { SpudpFailLoad = FALSE; KdPrint(("Spud.sys load enabled! DoNotLoad is configured in the registry.\n")); } // // Cleanup. // ZwClose( parametersHandle ); } // SpudpReadRegistry NTSTATUS SpudpOpenRegistry( IN PUNICODE_STRING BaseName, OUT PHANDLE ParametersHandle ) /*++ Routine Description: This routine is called by SPUD to open the registry. If the registry tree exists, then it opens it and returns STATUS_SUCCESS. Arguments: BaseName - Where in the registry to start looking for the information. LinkageHandle - Returns the handle used to read linkage information. ParametersHandle - Returns the handle used to read other parameters. Return Value: The status of the request. --*/ { HANDLE configHandle; NTSTATUS status; PWSTR parametersString = REGISTRY_PARAMETERS; UNICODE_STRING parametersKeyName; OBJECT_ATTRIBUTES objectAttributes; ULONG disposition; // // Sanity check. // PAGED_CODE(); // // Open the registry for the initial string. // InitializeObjectAttributes( &objectAttributes, BaseName, // name OBJ_CASE_INSENSITIVE, // attributes NULL, // root NULL // security descriptor ); status = ZwOpenKey( &configHandle, KEY_READ, &objectAttributes ); if( !NT_SUCCESS(status) ) { return STATUS_UNSUCCESSFUL; } // // Now open the parameters key. // RtlInitUnicodeString( ¶metersKeyName, parametersString ); InitializeObjectAttributes( &objectAttributes, ¶metersKeyName, // name OBJ_CASE_INSENSITIVE, // attributes configHandle, // root NULL // security descriptor ); status = ZwOpenKey( ParametersHandle, KEY_READ, &objectAttributes ); if( !NT_SUCCESS(status) ) { ZwClose( configHandle ); return status; } // // All keys successfully opened. // ZwClose( configHandle ); return STATUS_SUCCESS; } // SpudpOpenRegistry ULONG SpudpReadSingleParameter( IN HANDLE ParametersHandle, IN PWCHAR ValueName, IN LONG DefaultValue ) /*++ Routine Description: This routine is called by SPUD to read a single parameter from the registry. If the parameter is found it is stored in Data. Arguments: ParametersHandle - A pointer to the open registry. ValueName - The name of the value to search for. DefaultValue - The default value. Return Value: The value to use; will be the default if the value is not found or is not in the correct range. --*/ { static ULONG informationBuffer[32]; // declare ULONG to get it aligned PKEY_VALUE_FULL_INFORMATION information = (PKEY_VALUE_FULL_INFORMATION)informationBuffer; UNICODE_STRING valueKeyName; ULONG informationLength; LONG returnValue; NTSTATUS status; // // Sanity check. // PAGED_CODE(); // // Read the registry value. // RtlInitUnicodeString( &valueKeyName, ValueName ); status = ZwQueryValueKey( ParametersHandle, &valueKeyName, KeyValueFullInformation, (PVOID)information, sizeof (informationBuffer), &informationLength ); if( (status == STATUS_SUCCESS) && (information->DataLength == sizeof(ULONG)) ) { RtlMoveMemory( (PVOID)&returnValue, ((PUCHAR)information) + information->DataOffset, sizeof(ULONG) ); if (returnValue < 0) { returnValue = DefaultValue; } } else { returnValue = DefaultValue; } return returnValue; } // SpudpReadSingleParameter #if ALLOW_UNLOAD VOID SpudpUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Unload routine. Arguments: DriverObject - Pointer to target driver object. Return Value: None. --*/ { PKSERVICE_TABLE_DESCRIPTOR serviceTable; ULONG i; // // Sanity check. // PAGED_CODE(); UNREFERENCED_PARAMETER( DriverObject ); // // Yank the system service table. What a hack. // // Note that this can never be perfectly synchronized, and you // risk a blue-screen everytime you unload the driver. Only // initiate an unload if you're absolutely sure the server is // idle. This is the reason this code is conditionally compiled // and will never see the light of day in a public, retail build. // serviceTable = *KeServiceDescriptorTable; serviceTable[IIS_SERVICE_INDEX].Base = NULL; serviceTable[IIS_SERVICE_INDEX].Count = NULL; serviceTable[IIS_SERVICE_INDEX].Limit = 0; serviceTable[IIS_SERVICE_INDEX].Number = NULL; try { serviceTable += NUMBER_SERVICE_TABLES; for( i = 0 ; i < 1000 ; i++ ) { if( serviceTable->Base == SpudServiceTable && serviceTable->Count == NULL && serviceTable->Limit == SpudServiceLimit && serviceTable->Number == SpudArgumentTable ) { serviceTable->Base = NULL; serviceTable->Count = NULL; serviceTable->Limit = 0; serviceTable->Number = NULL; break; } serviceTable = (PKSERVICE_TABLE_DESCRIPTOR)( (PUCHAR)serviceTable + sizeof(ULONG_PTR) ); } } except( EXCEPTION_EXECUTE_HANDLER ) { NOTHING; } // // Dereference the I/O completion port. // if( SpudCompletionPort != NULL ) { TRACE_OB_DEREFERENCE( SpudCompletionPort ); ObDereferenceObject(SpudCompletionPort); SpudCompletionPort = NULL; } // // Destroy the non-paged data. // if( SpudNonpagedData != NULL ) { ExDeleteNPagedLookasideList( &SpudNonpagedData->ReqContextList ); ExDeleteResourceLite( &SpudNonpagedData->ReqHandleTableLock ); SPUD_FREE_POOL( SpudNonpagedData ); SpudNonpagedData = NULL; } // // Nuke the device object. // IoDeleteDevice( SpudSelfDeviceObject ); SpudSelfDeviceObject = NULL; // // Free the trace log. // #if ENABLE_OB_TRACING if( SpudTraceLog != NULL ) { DestroyRefTraceLog( SpudTraceLog ); SpudTraceLog = NULL; } #endif } // SpudpUnload #endif // ALLOW_UNLOAD