/*++ Copyright (c) 1991 - 2001 Microsoft Corporation Module Name: ## ## ###### #### ## #### ##### ##### ## ## ## ## ## ## # ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##### ##### ## ## ## ## ## ## ## # ## ## #### ## #### ##### ## #### ## ## Abstract: Utility driver functions. Author: Wesley Witt (wesw) 23-Jan-2002 Environment: Kernel mode only. Notes: --*/ #include "internal.h" #include #include #if DBG ULONG WdDebugLevel; #endif ULONG OsMajorVersion; ULONG OsMinorVersion; NTSTATUS CompleteRequest( PIRP Irp, NTSTATUS Status, ULONG_PTR Information ) /*++ Routine Description: This routine completes as outstanding I/O request. Arguments: Irp - Pointer to an IRP structure that describes the requested I/O operation. Status - NT status value Information - Informational, request specific data Return Value: NT status code. --*/ { PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); KIRQL CancelIrql; if (IrpSp->MajorFunction == IRP_MJ_READ || IrpSp->MajorFunction == IRP_MJ_WRITE) { IoAcquireCancelSpinLock( &CancelIrql ); IoSetCancelRoutine( Irp, NULL ); IoReleaseCancelSpinLock( CancelIrql ); } Irp->IoStatus.Information = Information; Irp->IoStatus.Status = Status; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return Status; } NTSTATUS ForwardRequest( IN PIRP Irp, IN PDEVICE_OBJECT TargetObject ) /*++ Routine Description: This routine forwards the IRP to another driver. Arguments: Irp - Pointer to an IRP structure that describes the requested I/O operation. TargetObject - Target device object to receive the request packet Return Value: NT status code. --*/ { IoSkipCurrentIrpStackLocation( Irp ); return IoCallDriver( TargetObject, Irp ); } VOID WdDebugPrint( IN ULONG DebugLevel, IN PSTR DebugMessage, IN ... ) /*++ Routine Description: This routine prints a formatted string to the debugger. Arguments: DebugLevel - Debug level that controls when a message is printed DebugMessage - String that is printed ... - Arguments that are used by the DebugMessage Return Value: None. --*/ { va_list arg_ptr; char buf[512]; char *s = buf; #if DBG if ((DebugLevel != 0xffffffff) && ((WdDebugLevel == 0) || ((WdDebugLevel & DebugLevel) == 0))) { return; } #endif va_start( arg_ptr, DebugMessage ); strcpy( s, "WD: " ); s += strlen(s); _vsnprintf( s, sizeof(buf)-1-strlen(s), DebugMessage, arg_ptr ); DbgPrint( buf ); } #if DBG VOID GetOsVersion( VOID ) /*++ Routine Description: This routine gets the current OS version information Arguments: None. Return Value: None. --*/ { RTL_OSVERSIONINFOW VersionInformation; VersionInformation.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW); RtlGetVersion( &VersionInformation ); OsMajorVersion = VersionInformation.dwMajorVersion; OsMinorVersion = VersionInformation.dwMinorVersion; } VOID FormatTime( ULONG TimeStamp, PSTR TimeBuf ) /*++ Routine Description: This routine formats a timestamp word into a string. Arguments: TimeStamp - Timestamp word TimeBuf - Buffer to place the resulting string Return Value: None. --*/ { static char mnames[] = { "JanFebMarAprMayJunJulAugSepOctNovDec" }; LARGE_INTEGER MyTime; TIME_FIELDS TimeFields; RtlSecondsSince1970ToTime( TimeStamp, &MyTime ); ExSystemTimeToLocalTime( &MyTime, &MyTime ); RtlTimeToTimeFields( &MyTime, &TimeFields ); strncpy( TimeBuf, &mnames[(TimeFields.Month - 1) * 3], 3 ); sprintf( &TimeBuf[3], " %02d, %04d @ %02d:%02d:%02d", TimeFields.Day, TimeFields.Year, TimeFields.Hour, TimeFields.Minute, TimeFields.Second ); } VOID PrintDriverVersion( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine locates the NT image headers from the base of a loaded driver. Arguments: DeviceType - Miniport device type (see saio.h for the enumeration) DriverObject - Pointer to the DRIVER_OBJECT structure Return Value: None. --*/ { PIMAGE_NT_HEADERS NtHeaders; ULONG TimeStamp; CHAR buf[32]; NtHeaders = RtlpImageNtHeader( DriverObject->DriverStart ); if (NtHeaders) { TimeStamp = NtHeaders->FileHeader.TimeDateStamp; FormatTime( TimeStamp, buf ); } } #endif NTSTATUS WdSignalCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Event ) /*++ Routine Description: This routine is used to signal the completion of an I/O request and is used ONLY by CallLowerDriverAndWait. Arguments: DeviceObject - Pointer to the miniport's device object Irp - I/O request packet Event - Event to be signaled when the I/O is completed Return Value: NT status code --*/ { KeSetEvent( (PKEVENT)Event, IO_NO_INCREMENT, FALSE ); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS CallLowerDriverAndWait( IN PIRP Irp, IN PDEVICE_OBJECT TargetObject ) /*++ Routine Description: This routine calls a lower driver and waits for the I/O to complete. Arguments: Irp - I/O request packet TargetObject - Pointer to the target device object Return Value: NT status code --*/ { KEVENT event; KeInitializeEvent( &event, NotificationEvent, FALSE ); IoCopyCurrentIrpStackLocationToNext( Irp ); IoSetCompletionRoutine( Irp, WdSignalCompletion, &event, TRUE, TRUE, TRUE ); IoCallDriver( TargetObject, Irp ); KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL ); return Irp->IoStatus.Status; } NTSTATUS OpenParametersRegistryKey( IN PUNICODE_STRING RegistryPath, IN ULONG AccessMode, OUT PHANDLE RegistryHandle ) /*++ Routine Description: This routine opens the driver's paramaters registry key for I/O. Arguments: RegistryPath - Full path to the root of the driver's registry tree. AccessMode - Specifies how the handle is to be opened (READ/WRITE/etc). RegistryHandle - Output parameter that receives the registry handle. Return Value: NT status code --*/ { NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING unicodeString; HANDLE serviceKey = NULL; __try { InitializeObjectAttributes( &objectAttributes, RegistryPath, OBJ_CASE_INSENSITIVE, NULL, NULL ); status = ZwOpenKey( &serviceKey, AccessMode, &objectAttributes ); if (!NT_SUCCESS(status)) { ERROR_RETURN( "ZwOpenKey failed", status ); } RtlInitUnicodeString( &unicodeString, L"Parameters" ); InitializeObjectAttributes( &objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, serviceKey, NULL ); status = ZwOpenKey( RegistryHandle, AccessMode, &objectAttributes ); if (!NT_SUCCESS(status)) { ERROR_RETURN( "ZwOpenKey failed", status ); } status = STATUS_SUCCESS; } __finally { if (serviceKey) { ZwClose( serviceKey ); } if (!NT_SUCCESS(status)) { if (*RegistryHandle) { ZwClose( *RegistryHandle ); } } } return status; } NTSTATUS CreateParametersRegistryKey( IN PUNICODE_STRING RegistryPath, OUT PHANDLE parametersKey ) /*++ Routine Description: This routine creates the driver's paramaters registry key for I/O. Arguments: RegistryPath - Full path to the root of the driver's registry tree. RegistryHandle - Output parameter that receives the registry handle. Return Value: NT status code --*/ { NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING unicodeString; HANDLE serviceKey = NULL; ULONG Disposition; __try { parametersKey = NULL; InitializeObjectAttributes( &objectAttributes, RegistryPath, OBJ_CASE_INSENSITIVE, NULL, NULL ); status = ZwOpenKey( &serviceKey, KEY_READ | KEY_WRITE, &objectAttributes ); if (!NT_SUCCESS(status)) { ERROR_RETURN( "ZwOpenKey failed", status ); } RtlInitUnicodeString( &unicodeString, L"Parameters" ); InitializeObjectAttributes( &objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, serviceKey, NULL ); status = ZwCreateKey( parametersKey, KEY_READ | KEY_WRITE, &objectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &Disposition ); if (!NT_SUCCESS(status)) { ERROR_RETURN( "ZwCreateKey failed", status ); } status = STATUS_SUCCESS; } __finally { if (serviceKey) { ZwClose( serviceKey ); } if (!NT_SUCCESS(status)) { if (parametersKey) { ZwClose( parametersKey ); } } } return status; } NTSTATUS ReadRegistryValue( IN PUNICODE_STRING RegistryPath, IN PWSTR ValueName, OUT PKEY_VALUE_FULL_INFORMATION *KeyInformation ) /*++ Routine Description: This routine reads a registry arbitrary value from the device's parameter registry data. The necessary memory is allocated by this function and must be freed by the caller. Arguments: RegistryPath - String containing the path to the driver's registry data ValueName - Value name in the registry KeyInformation - Pointer to a PKEY_VALUE_FULL_INFORMATION pointer that is allocated by this function Return Value: NT status code --*/ { NTSTATUS status; UNICODE_STRING unicodeString; HANDLE parametersKey = NULL; ULONG keyValueLength; __try { *KeyInformation = NULL; status = OpenParametersRegistryKey( RegistryPath, KEY_READ, ¶metersKey ); if (!NT_SUCCESS(status)) { ERROR_RETURN( "OpenParametersRegistryKey failed", status ); } RtlInitUnicodeString( &unicodeString, ValueName ); status = ZwQueryValueKey( parametersKey, &unicodeString, KeyValueFullInformation, NULL, 0, &keyValueLength ); if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) { ERROR_RETURN( "ZwQueryValueKey failed", status ); } *KeyInformation = (PKEY_VALUE_FULL_INFORMATION) ExAllocatePool( NonPagedPool, keyValueLength ); if (*KeyInformation == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; ERROR_RETURN( "Failed to allocate pool for registry data", status ); } status = ZwQueryValueKey( parametersKey, &unicodeString, KeyValueFullInformation, *KeyInformation, keyValueLength, &keyValueLength ); if (!NT_SUCCESS(status)) { ERROR_RETURN( "ZwQueryValueKey failed", status ); } status = STATUS_SUCCESS; } __finally { if (parametersKey) { ZwClose( parametersKey ); } if (!NT_SUCCESS(status)) { if (*KeyInformation) { ExFreePool( *KeyInformation ); } *KeyInformation = NULL; } } return status; } NTSTATUS WriteRegistryValue( IN PUNICODE_STRING RegistryPath, IN PWSTR ValueName, IN ULONG RegistryType, IN PVOID RegistryValue, IN ULONG RegistryValueLength ) /*++ Routine Description: This routine reads a registry arbitrary value from the device's parameter registry data. The necessary memory is allocated by this function and must be freed by the caller. Arguments: RegistryPath - String containing the path to the driver's registry data ValueName - Value name in the registry KeyInformation - Pointer to a PKEY_VALUE_FULL_INFORMATION pointer that is allocated by this function Return Value: NT status code --*/ { NTSTATUS status; UNICODE_STRING unicodeString; HANDLE parametersKey = NULL; __try { status = OpenParametersRegistryKey( RegistryPath, KEY_READ | KEY_WRITE, ¶metersKey ); if (!NT_SUCCESS(status)) { status = CreateParametersRegistryKey( RegistryPath, ¶metersKey ); if (!NT_SUCCESS(status)) { ERROR_RETURN( "CreateParametersRegistryKey failed", status ); } } RtlInitUnicodeString( &unicodeString, ValueName ); status = ZwSetValueKey( parametersKey, &unicodeString, 0, RegistryType, RegistryValue, RegistryValueLength ); if (!NT_SUCCESS(status)) { ERROR_RETURN( "ZwQueryValueKey failed", status ); } status = STATUS_SUCCESS; } __finally { if (parametersKey) { ZwClose( parametersKey ); } } return status; } NTSTATUS WriteEventLogEntry ( IN PDEVICE_EXTENSION DeviceExtension, IN ULONG ErrorCode, IN PVOID InsertionStrings, OPTIONAL IN ULONG StringCount, OPTIONAL IN PVOID DumpData, OPTIONAL IN ULONG DataSize OPTIONAL ) /*++ Routine Description: Writes an entry into the system eventlog. Arguments: DeviceExtension - Pointer to a device extension object ErrorCode - Eventlog errorcode as specified in eventmsg.mc InsertionStrings - String to insert into the eventlog message StringCount - Number of InsertionStrings DumpData - Additional data to be include in the message DataSize - Size of the DumpData Return Value: NT status code --*/ { #define ERROR_PACKET_SIZE sizeof(IO_ERROR_LOG_PACKET) NTSTATUS status = STATUS_SUCCESS; ULONG totalPacketSize; ULONG i, stringSize = 0; PWCHAR *strings, temp; PIO_ERROR_LOG_PACKET logEntry; UNICODE_STRING unicodeString; __try { // // Calculate total string length, including NULL. // strings = (PWCHAR *) InsertionStrings; for (i=0; i= ERROR_LOG_MAXIMUM_SIZE) { ERROR_RETURN( "WriteEventLogEntry: Error Log Entry too large", STATUS_UNSUCCESSFUL ); } // // Allocate the error log packet // logEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry( DeviceExtension->DeviceObject, (UCHAR)totalPacketSize ); if (!logEntry) { ERROR_RETURN( "IoAllocateErrorLogEntry failed", STATUS_INSUFFICIENT_RESOURCES ); } RtlZeroMemory( logEntry, totalPacketSize ); // // Fill out the packet // //logEntry->MajorFunctionCode = 0; //logEntry->RetryCount = 0; //logEntry->UniqueErrorValue = 0; //logEntry->FinalStatus = 0; //logEntry->SequenceNumber = ErrorLogCount++; //logEntry->IoControlCode = 0; //logEntry->DeviceOffset.QuadPart = 0; logEntry->DumpDataSize = (USHORT) DataSize; logEntry->NumberOfStrings = (USHORT) StringCount; logEntry->EventCategory = 0x1; logEntry->ErrorCode = ErrorCode; if (StringCount) { logEntry->StringOffset = (USHORT) (ERROR_PACKET_SIZE + DataSize); } // // Copy Dump Data // if (DataSize) { RtlCopyMemory( (PVOID)logEntry->DumpData, DumpData, DataSize ); } // // Copy String Data // temp = (PWCHAR)((PUCHAR)logEntry + logEntry->StringOffset); for (i=0; i