/*++ Copyright (c) 1993 Microsoft Corporation Module Name: cxport.c Abstract: Common Transport Environment utility functions for the NT environment Author: Mike Massa (mikemas) Aug 11, 1993 Revision History: Who When What -------- -------- ---------------------------------------------- mikemas 08-11-93 created Notes: --*/ #pragma warning(push) #pragma warning(disable:4115) // named type definition in parentheses ntddk.h #include #pragma warning(pop) #include #include #include // // Mark pageable code // #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, CTELogEvent) #endif // ALLOC_PRAGMA // // Local variables // ULONG CTEpTimeIncrement = 0; // used to convert kernel clock ticks to 100ns. LIST_ENTRY CTEBlockListHead; KSPIN_LOCK CTEBlockSpinLock; // Used in the conversion of 100ns times to milliseconds. static LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758}; // // Macros // //++ // // LARGE_INTEGER // CTEConvertMillisecondsTo100ns( // IN LARGE_INTEGER MsTime // ); // // Routine Description: // // Converts time expressed in hundreds of nanoseconds to milliseconds. // // Arguments: // // MsTime - Time in milliseconds. // // Return Value: // // Time in hundreds of nanoseconds. // //-- #define CTEConvertMillisecondsTo100ns(MsTime) \ RtlExtendedIntegerMultiply(MsTime, 10000) //++ // // LARGE_INTEGER // CTEConvert100nsToMilliseconds( // IN LARGE_INTEGER HnsTime // ); // // Routine Description: // // Converts time expressed in hundreds of nanoseconds to milliseconds. // // Arguments: // // HnsTime - Time in hundreds of nanoseconds. // // Return Value: // // Time in milliseconds. // //-- #define SHIFT10000 13 extern LARGE_INTEGER Magic10000; #define CTEConvert100nsToMilliseconds(HnsTime) \ RtlExtendedMagicDivide((HnsTime), Magic10000, SHIFT10000) // // Local functions // VOID CTEpInitialize( VOID ) /*++ Routine Description: Initializes internal module state. Arguments: None. Return Value: None. --*/ { CTEpTimeIncrement = KeQueryTimeIncrement(); KeInitializeSpinLock(&CTEBlockSpinLock); InitializeListHead(&CTEBlockListHead); } VOID CTEpEventHandler( IN PVOID Context ) /*++ Routine Description: Internal handler for scheduled CTE Events. Conforms to calling convention for ExWorkerThread handlers. Calls the CTE handler registered for this event. Arguments: Context - Work item to process. Return Value: None. --*/ { CTEEvent *Event; CTELockHandle Handle; CTEEventRtn Handler; PVOID Arg; #if DBG KIRQL StartingIrql; StartingIrql = KeGetCurrentIrql(); #endif Event = (CTEEvent *) Context; CTEGetLock(&(Event->ce_lock), &Handle); ASSERT(Event->ce_scheduled); Handler = Event->ce_handler; Arg = Event->ce_arg; Event->ce_scheduled = 0; CTEFreeLock(&(Event->ce_lock), Handle); Handler(Event, Arg); #if DBG if (KeGetCurrentIrql() != StartingIrql) { DbgPrint( "CTEpEventHandler: routine %lx , event %lx returned at raised IRQL\n", Handler, Event ); DbgBreakPoint(); } #endif } VOID CTEpTimerHandler( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: Internal handler for scheduled CTE Timers. Conforms to calling convention for NT DPC handlers. Calls the CTE handler registered for this timer. Arguments: Dpc - Pointer to the DPC routine being run. DeferredContext - Private context for this instance of the DPC. SystemArgument1 - Additional argument. SystemArgument2 - Additional argument. Return Value: None. --*/ { CTETimer *Timer; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument1); UNREFERENCED_PARAMETER(SystemArgument2); Timer = (CTETimer *) DeferredContext; (*Timer->t_handler)((CTEEvent *)Timer, Timer->t_arg); } // // Exported functions. // int CTEInitialize( VOID ) /*++ Routine Description: An empty initialization routine for backward compatibility. Arguments: None. Return Value: Non-zero. --*/ { return 1; } void CTEInitEvent( CTEEvent *Event, CTEEventRtn Handler ) /*++ Routine Description: Initializes a CTE Event variable. Arguments: Event - Event variable to initialize. Handler - Handler routine for this event variable. Return Value: None. --*/ { ASSERT(Handler != NULL); Event->ce_handler = Handler; Event->ce_scheduled = 0; CTEInitLock(&(Event->ce_lock)); ExInitializeWorkItem(&(Event->ce_workitem), CTEpEventHandler, Event); } int CTEScheduleCriticalEvent( IN CTEEvent *Event, IN void *Argument OPTIONAL ) /*++ Routine Description: Schedules a routine to be executed later in a different context. In the NT environment, the event is implemented using Executive worker threads. Arguments: Event - Pointer to a CTE Event variable Argument - An argument to pass to the event handler when it is called Return Value: 0 if the event could not be scheduled. Nonzero otherwise. --*/ { CTELockHandle Handle; CTEGetLock(&(Event->ce_lock), &Handle); if (!(Event->ce_scheduled)) { Event->ce_scheduled = 1; Event->ce_arg = Argument; ExQueueWorkItem( &(Event->ce_workitem), CriticalWorkQueue ); } CTEFreeLock(&(Event->ce_lock), Handle); return(1); } int CTEScheduleEvent( IN CTEEvent *Event, IN void *Argument OPTIONAL ) /*++ Routine Description: Schedules a routine to be executed later in a different context. In the NT environment, the event is implemented using Executive worker threads. Arguments: Event - Pointer to a CTE Event variable Argument - An argument to pass to the event handler when it is called Return Value: 0 if the event could not be scheduled. Nonzero otherwise. --*/ { CTELockHandle Handle; CTEGetLock(&(Event->ce_lock), &Handle); if (!(Event->ce_scheduled)) { Event->ce_scheduled = 1; Event->ce_arg = Argument; ExQueueWorkItem( &(Event->ce_workitem), DelayedWorkQueue ); } CTEFreeLock(&(Event->ce_lock), Handle); return(1); } int CTEScheduleDelayedEvent( IN CTEEvent *Event, IN void *Argument OPTIONAL ) /*++ Routine Description: Schedules a routine to be executed later in a different context. In the NT environment, the event is implemented using Executive worker threads. Arguments: Event - Pointer to a CTE Event variable Argument - An argument to pass to the event handler when it is called Return Value: 0 if the event could not be scheduled. Nonzero otherwise. --*/ { CTELockHandle Handle; CTEGetLock(&(Event->ce_lock), &Handle); if (!(Event->ce_scheduled)) { Event->ce_scheduled = 1; Event->ce_arg = Argument; ExQueueWorkItem( &(Event->ce_workitem), DelayedWorkQueue ); } CTEFreeLock(&(Event->ce_lock), Handle); return(1); } void CTEInitTimer( CTETimer *Timer ) /*++ Routine Description: Initializes a CTE Timer variable. Arguments: Timer - Timer variable to initialize. Return Value: None. --*/ { Timer->t_handler = NULL; Timer->t_arg = NULL; KeInitializeDpc(&(Timer->t_dpc), CTEpTimerHandler, Timer); KeInitializeTimer(&(Timer->t_timer)); } void * CTEStartTimer( CTETimer *Timer, unsigned long DueTime, CTEEventRtn Handler, void *Context ) /*++ Routine Description: Sets a CTE Timer for expiration. Arguments: Timer - Pointer to a CTE Timer variable. DueTime - Time in milliseconds after which the timer should expire. Handler - Timer expiration handler routine. Context - Argument to pass to the handler. Return Value: 0 if the timer could not be set. Nonzero otherwise. --*/ { LARGE_INTEGER LargeDueTime; ASSERT(Handler != NULL); // // Convert milliseconds to hundreds of nanoseconds and negate to make // an NT relative timeout. // LargeDueTime.HighPart = 0; LargeDueTime.LowPart = DueTime; LargeDueTime = CTEConvertMillisecondsTo100ns(LargeDueTime); LargeDueTime.QuadPart = -LargeDueTime.QuadPart; Timer->t_handler = Handler; Timer->t_arg = Context; KeSetTimer( &(Timer->t_timer), LargeDueTime, &(Timer->t_dpc) ); return((void *) 1); } unsigned long CTESystemUpTime( void ) /*++ Routine Description: Provides the time since system boot in milliseconds. Arguments: None. Return Value: The time since boot in milliseconds. --*/ { LARGE_INTEGER TickCount; // // Get tick count and convert to hundreds of nanoseconds. // #pragma warning(push) #pragma warning(disable:4127) // condition expression constant KeQueryTickCount(&TickCount); #pragma warning(pop) TickCount = RtlExtendedIntegerMultiply( TickCount, (LONG) CTEpTimeIncrement ); TickCount = CTEConvert100nsToMilliseconds(TickCount); return(TickCount.LowPart); } uint CTEBlock( IN CTEBlockStruc *BlockEvent ) { NTSTATUS Status; Status = KeWaitForSingleObject( &(BlockEvent->cbs_event), UserRequest, KernelMode, FALSE, NULL ); if (!NT_SUCCESS(Status)) { BlockEvent->cbs_status = Status; } return(BlockEvent->cbs_status); } uint CTEBlockWithTracker( IN CTEBlockStruc *BlockEvent, IN CTEBlockTracker *BlockTracker, IN void *Context ) { uint Status; CTEInsertBlockTracker(BlockTracker, Context); Status = CTEBlock(BlockEvent); CTERemoveBlockTracker(BlockTracker); return Status; } void CTEInsertBlockTracker( IN CTEBlockTracker *BlockTracker, IN void *Context ) { KIRQL OldIrql; BlockTracker->cbt_thread = KeGetCurrentThread(); BlockTracker->cbt_context = Context; KeAcquireSpinLock(&CTEBlockSpinLock, &OldIrql); InsertTailList(&CTEBlockListHead, &BlockTracker->cbt_link); KeReleaseSpinLock(&CTEBlockSpinLock, OldIrql); } void CTERemoveBlockTracker( IN CTEBlockTracker *BlockTracker ) { KIRQL OldIrql; KeAcquireSpinLock(&CTEBlockSpinLock, &OldIrql); RemoveEntryList(&BlockTracker->cbt_link); KeReleaseSpinLock(&CTEBlockSpinLock, OldIrql); } void CTESignal( IN CTEBlockStruc *BlockEvent, IN uint Status ) { BlockEvent->cbs_status = Status; KeSetEvent(&(BlockEvent->cbs_event), 0, FALSE); return; } BOOLEAN CTEInitString( IN OUT PNDIS_STRING DestinationString, IN char *SourceString ) /*++ Routine Description: Converts a C style ASCII string to an NDIS_STRING. Resources needed for the NDIS_STRING are allocated and must be freed by a call to CTEFreeString. Arguments: DestinationString - A pointer to an NDIS_STRING variable with no associated data buffer. SourceString - The C style ASCII string source. Return Value: TRUE if the initialization succeeded. FALSE otherwise. --*/ { STRING AnsiString; ULONG UnicodeLength; RtlInitString(&AnsiString, SourceString); // calculate size of unicoded ansi string + 2 for NULL terminator UnicodeLength = RtlAnsiStringToUnicodeSize(&AnsiString) + 2; // allocate storage for the unicode string DestinationString->Buffer = ExAllocatePool(NonPagedPool, UnicodeLength); if (DestinationString->Buffer == NULL) { DestinationString->MaximumLength = 0; return(FALSE); } DestinationString->MaximumLength = (USHORT) UnicodeLength; // Finally, convert the string to unicode RtlAnsiStringToUnicodeString(DestinationString, &AnsiString, FALSE); return(TRUE); } BOOLEAN CTEAllocateString( PNDIS_STRING String, unsigned short MaximumLength ) /*++ Routine Description: Allocates a data buffer for Length characters in an uninitialized NDIS_STRING. The allocated space must be freed by a call to CTEFreeString. Arguments: String - A pointer to an NDIS_STRING variable with no associated data buffer. Length - The maximum length of the string. In Unicode, this is a byte count. Return Value: TRUE if the initialization succeeded. FALSE otherwise. --*/ { String->Buffer = ExAllocatePool( NonPagedPool, MaximumLength + sizeof(UNICODE_NULL) ); if (String->Buffer == NULL) { return(FALSE); } String->Length = 0; String->MaximumLength = MaximumLength + sizeof(UNICODE_NULL); return(TRUE); } LONG CTELogEvent( IN PVOID LoggerId, IN ULONG EventCode, IN ULONG UniqueEventValue, IN USHORT NumStrings, IN PVOID StringsList, OPTIONAL IN ULONG DataSize, IN PVOID Data OPTIONAL ) /*++ Routine Description: This function allocates an I/O error log record, fills it in and writes it to the I/O error log. Arguments: LoggerId - Pointer to the driver object logging this event. EventCode - Identifies the error message. UniqueEventValue - Identifies this instance of a given error message. NumStrings - Number of unicode strings in strings list. DataSize - Number of bytes of data. Strings - Array of pointers to unicode strings (PWCHAR). Data - Binary dump data for this message, each piece being aligned on word boundaries. Return Value: TDI_SUCCESS - The error was successfully logged. TDI_BUFFER_TOO_SMALL - The error data was too large to be logged. TDI_NO_RESOURCES - Unable to allocate memory. Notes: This code is paged and may not be called at raised IRQL. --*/ { PIO_ERROR_LOG_PACKET ErrorLogEntry; ULONG PaddedDataSize; ULONG PacketSize; ULONG TotalStringsSize = 0; USHORT i; PWCHAR *Strings; PWCHAR Tmp; PAGED_CODE(); Strings = (PWCHAR *) StringsList; // // Sum up the length of the strings // for (i=0; i CTE_MAX_EVENT_LOG_DATA_SIZE) { return(TDI_BUFFER_TOO_SMALL); // Too much error data } // // Now add in the size of the log packet, but subtract 4 from the data // since the packet struct contains a ULONG for data. // if (PacketSize > sizeof(ULONG)) { PacketSize += sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); } else { PacketSize += sizeof(IO_ERROR_LOG_PACKET); } ASSERT(PacketSize <= ERROR_LOG_MAXIMUM_SIZE); ErrorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry( (PDRIVER_OBJECT) LoggerId, (UCHAR) PacketSize ); if (ErrorLogEntry == NULL) { return(TDI_NO_RESOURCES); } // // Fill in the necessary log packet fields. // ErrorLogEntry->UniqueErrorValue = UniqueEventValue; ErrorLogEntry->ErrorCode = EventCode; ErrorLogEntry->NumberOfStrings = NumStrings; ErrorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) + (USHORT) PaddedDataSize - sizeof(ULONG); ErrorLogEntry->DumpDataSize = (USHORT) PaddedDataSize; // // Copy the Dump Data to the packet // if (DataSize > 0) { RtlMoveMemory( (PVOID) ErrorLogEntry->DumpData, Data, DataSize ); } // // Copy the strings to the packet. // Tmp = (PWCHAR) ((char *) ErrorLogEntry + ErrorLogEntry->StringOffset + PaddedDataSize); for (i=0; i