/*++ Copyright (c) 1995,1996 Microsoft Corporation :ts=4 Module Name: dbg.c Abstract: Debug Code for UHCD. Environment: kernel mode only Notes: Revision History: 10-08-95 : created --*/ #include "wdm.h" #include "stdarg.h" #include "stdio.h" #include "usbdi.h" #include "hcdi.h" #include "uhcd.h" // debug compile defines // DEBUG# sets the level of spew 0 = none // > 2 turns on debug heap code // NTKERN_TRACE puts ' in the format buffer so that spew goes // to ntkern buffer #if DBG // This turns the looping assert so that testers can't hit g // past our assert before we can look at it. // Win9x testers tend to do this but NT testers are conditioned not // to so we only enable it for a DEBUG1 compile (win9x) ULONG UHCD_Debug_Asserts = #ifdef DEBUG1 1; #else 0; #endif // this flag causes us to write a ' in the format string // so that the string goes to the NTKERN buffer // this trick causes problems with driver verifier on NT // and the trace buffer isn't in NT anyway ULONG UHCD_W98_Debug_Trace = #ifdef NTKERN_TRACE 1; #else 0; #endif // set the debug output spew level based on the DEBUG# define // if compiled for W98 DEBUG1 is default // if compiled for NT zero should be default ULONG UHCD_Debug_Trace_Level = #ifdef DEBUG3 #define DEBUG_HEAP 3; #else #ifdef DEBUG2 2; #else #ifdef DEBUG1 1; #else 0; #endif // DEBUG1 #endif // DEBUG2 #endif // DEBUG3 LONG UHCD_TotalAllocatedHeapSpace = 0; ULONG _cdecl UHCD_KdPrintX( ULONG l, PCH Format, ... ) { va_list list; int i; int arg[5]; if (UHCD_Debug_Trace_Level >= l) { if (l <= 1) { // if flag is set override the ' // so that the level 1 strings are // printed on the debugger terminal if (UHCD_W98_Debug_Trace) { DbgPrint("UHCD.SYS: "); *Format = ' '; } else { DbgPrint("'UHCD.SYS: "); } } else { DbgPrint("'UHCD.SYS: "); } va_start(list, Format); for (i=0; i<4; i++) arg[i] = va_arg(list, int); DbgPrint(Format, arg[0], arg[1], arg[2], arg[3]); } return 0; } #if 0 ULONG UHCD_DebugCommand( IN ULONG Command, IN ULONG Paramater1 ) /*++ Routine Description: This routine performs a specific debug function. Arguments: Command - debug function Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise --*/ { USBD_STATUS status = USBD_STATUS_SUCCESS; switch (Command) { // case DBG_DUMP_FRAME_LIST: break; default: status = USBD_STATUS_INVALID_PARAMETER; } return status; } #endif VOID UHCD_Debug_DumpTD( IN struct _HW_TRANSFER_DESCRIPTOR *Transfer ) /*++ Routine Description: Dump a transfer descriptor to the debug terminal Arguments: Return Value: --*/ { if (UHCD_Debug_Trace_Level < 3) return; UHCD_KdPrint((2, "'TD DESCRIPTOR @va %0x\n", Transfer)); UHCD_KdPrint((2, "'TD ActualLength %0x\n", Transfer->ActualLength)); UHCD_KdPrint((2, "'TD Reserved_1 %0x\n", Transfer->Reserved_1)); UHCD_KdPrint((2, "'TD Active %0x\n", Transfer->Active)); UHCD_KdPrint((2, "'TD StatusField %0x\n", Transfer->StatusField)); UHCD_KdPrint((2, "'TD InterruptOnComplete %0x\n", Transfer->InterruptOnComplete)); UHCD_KdPrint((2, "'TD ShortPacketDetect %0x\n", Transfer->ShortPacketDetect)); UHCD_KdPrint((2, "'TD Isochronous %0x\n", Transfer->Isochronous)); UHCD_KdPrint((2, "'TD LowSpeedControl %0x\n", Transfer->LowSpeedControl)); UHCD_KdPrint((2, "'TD ErrorCounter %0x\n", Transfer->ErrorCounter)); UHCD_KdPrint((2, "'TD ReservedMBZ %0x\n", Transfer->ReservedMBZ)); UHCD_KdPrint((2, "'TD PID %0x\n", Transfer->PID)); UHCD_KdPrint((2, "'TD Address %0x\n", Transfer->Address)); UHCD_KdPrint((2, "'TD Endpoint %0x\n", Transfer->Endpoint)); UHCD_KdPrint((2, "'TD RetryToggle %0x\n", Transfer->RetryToggle)); UHCD_KdPrint((2, "'TD Reserved_2 %0x\n", Transfer->Reserved_2)); UHCD_KdPrint((2, "'TD MaxLength %0x\n", Transfer->MaxLength)); } VOID UHCD_Assert( IN PVOID FailedAssertion, IN PVOID FileName, IN ULONG LineNumber, IN PCHAR Message ) /*++ Routine Description: Debug Assert function. Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: --*/ { assert_loop: // just call the NT assert function and stop // in the debugger. RtlAssert( FailedAssertion, FileName, LineNumber, Message ); // loop here to prevent users from going past // are assert before we can look at it // we only do thi sif the W98 DEBUG mode is set TRAP(); if (UHCD_Debug_Asserts) { goto assert_loop; } return; } VOID UHCD_CheckSystemBuffer( IN PUCHAR VirtualAddress, IN ULONG Length ) /*++ Routine Description: Verify that this virtual address points to physically contiguous memory. Arguments: VirtualAddress - virtual address of the system buffer Length - length of buffer Return Value: --*/ { UHCD_ASSERT(Length <= PAGE_SIZE); UHCD_ASSERT( ADDRESS_AND_SIZE_TO_SPAN_PAGES( VirtualAddress, Length ) <= 1 ); } // // tag buffer we use to mark heap blocks we allocate // typedef struct _HEAP_TAG_BUFFER { ULONG Sig; ULONG Length; } HEAP_TAG_BUFFER, *PHEAP_TAG_BUFFER; PVOID UHCD_Debug_GetHeap( IN POOL_TYPE PoolType, IN ULONG NumberOfBytes, IN ULONG Signature, IN PLONG TotalAllocatedHeapSpace ) /*++ Routine Description: Debug routine, used to debug heap problems. We are using this since most NT debug functions are not supported by NTKERN. Arguments: PoolType - pool type passed to ExAllocatePool NumberOfBytes - number of bytes for item Signature - four byte signature supplied by caller TotalAllocatedHeapSpace - pointer to variable where client stores the total accumulated heap space allocated. Return Value: pointer to allocated memory --*/ { PUCHAR p; #ifdef DEBUG_HEAP ULONG *stk; PHEAP_TAG_BUFFER tagBuffer; // we call ExAllocatePoolWithTag but no tag will be added // when running under NTKERN #ifdef _M_IX86 _asm mov stk, ebp #endif p = (PUCHAR) ExAllocatePoolWithTag(PoolType, NumberOfBytes + sizeof(HEAP_TAG_BUFFER), Signature); if (p) { tagBuffer = (PHEAP_TAG_BUFFER) p; tagBuffer->Sig = Signature; tagBuffer->Length = NumberOfBytes; p += sizeof(HEAP_TAG_BUFFER); *TotalAllocatedHeapSpace += NumberOfBytes; } // LOGENTRY(LOG_MISC, (PUCHAR) &Signature, 0, 0, 0); // LOGENTRY(LOG_MISC, 'GetH', p, NumberOfBytes, stk[1] & 0x00FFFFFF); #else p = (PUCHAR) ExAllocatePoolWithTag(PoolType, NumberOfBytes, Signature); #endif /* DEBUG_HEAP */ return p; } VOID UHCD_Debug_RetHeap( IN PVOID P, IN ULONG Signature, IN PLONG TotalAllocatedHeapSpace ) /*++ Routine Description: Debug routine, used to debug heap problems. We are using this since most NT debug functions are not supported by NTKERN. Arguments: P - pointer to free Return Value: none. --*/ { #ifdef DEBUG_HEAP PHEAP_TAG_BUFFER tagBuffer; ULONG *stk; UHCD_ASSERT(P != 0); #ifdef _M_IX86 _asm mov stk, ebp #endif tagBuffer = (PHEAP_TAG_BUFFER) ((PUCHAR)P - sizeof(HEAP_TAG_BUFFER)); *TotalAllocatedHeapSpace -= tagBuffer->Length; // LOGENTRY(LOG_MISC, (PUCHAR) &Signature, 0, 0, 0); // LOGENTRY(LOG_MISC, 'RetH', P, tagBuffer->Length, stk[1] & 0x00FFFFFF); UHCD_ASSERT(*TotalAllocatedHeapSpace >= 0); UHCD_ASSERT(tagBuffer->Sig == Signature); // fill the buffer with bad data RtlFillMemory(P, tagBuffer->Length, 0xff); tagBuffer->Sig = UHCD_FREE_TAG; // free the original block ExFreePool(tagBuffer); #else ExFreePool(P); #endif /* DEBUG_HEAP */ } #if DBG VOID UHCD_PrintPowerMessage( PUCHAR Label, UCHAR MinorFunction ) /*++ Routine Description: Arguments: Return Value: NT status code --*/ { UHCD_KdPrint((2, "'(%s) ", Label)); switch (MinorFunction) { case IRP_MN_WAIT_WAKE: UHCD_KdPrint((2, "'IRP_MN_WAIT_WAKE\n")); break; case IRP_MN_SET_POWER: UHCD_KdPrint((2, "'IRP_MN_SET_POWER\n")); break; case IRP_MN_QUERY_POWER: UHCD_KdPrint((2, "'IRP_MN_QUERY_POWER\n")); break; } } VOID UHCD_PrintPnPMessage( PUCHAR Label, UCHAR MinorFunction ) /*++ Routine Description: Arguments: Return Value: NT status code --*/ { UHCD_KdPrint((2, "'(%s) ", Label)); switch (MinorFunction) { case IRP_MN_START_DEVICE: UHCD_KdPrint((2, "'IRP_MN_START_DEVICE\n")); break; case IRP_MN_STOP_DEVICE: UHCD_KdPrint((2, "'IRP_MN_STOP_DEVICE\n")); break; case IRP_MN_REMOVE_DEVICE: UHCD_KdPrint((2, "'IRP_MN_REMOVE_DEVICE\n")); break; case IRP_MN_QUERY_STOP_DEVICE: UHCD_KdPrint((2, "'IRP_MN_QUERY_STOP_DEVICE\n")); break; case IRP_MN_CANCEL_STOP_DEVICE: UHCD_KdPrint((2, "'IRP_MN_CANCEL_STOP_DEVICE\n")); break; case IRP_MN_QUERY_REMOVE_DEVICE: UHCD_KdPrint((2, "'IRP_MN_QUERY_REMOVE_DEVICE\n")); break; case IRP_MN_CANCEL_REMOVE_DEVICE: UHCD_KdPrint((2, "'IRP_MN_CANCEL_REMOVE_DEVICE\n")); break; } } #endif #endif /* DBG */ #ifdef DEBUG_LOG KSPIN_LOCK LogSpinLock; struct UHCD_LOG_ENTRY { ULONG le_sig; // Identifying string ULONG_PTR le_info1; // entry specific info ULONG_PTR le_info2; // entry specific info ULONG_PTR le_info3; // entry specific info }; /* USBD_LOG_ENTRY */ struct UHCD_LOG_ENTRY *HcdLStart = 0; // No log yet struct UHCD_LOG_ENTRY *HcdLPtr; struct UHCD_LOG_ENTRY *HcdLEnd; #ifdef PROFILE ULONG LogMask = LOG_PROFILE; #else ULONG LogMask = 0xFFFFFFFF; #endif VOID UHCD_Debug_LogEntry( IN ULONG Mask, IN ULONG Sig, IN ULONG_PTR Info1, IN ULONG_PTR Info2, IN ULONG_PTR Info3 ) /*++ Routine Description: Adds an Entry to UHCD log. Arguments: Return Value: None. --*/ { KIRQL irql; typedef union _SIG { struct { UCHAR Byte0; UCHAR Byte1; UCHAR Byte2; UCHAR Byte3; } b; ULONG l; } SIG, *PSIG; SIG sig, rsig; if (HcdLStart == 0) { return; } if ((Mask & LogMask) == 0) { return; } irql = KeGetCurrentIrql(); if (irql < DISPATCH_LEVEL) { KeAcquireSpinLock(&LogSpinLock, &irql); } else { KeAcquireSpinLockAtDpcLevel(&LogSpinLock); } if (HcdLPtr > HcdLStart) { HcdLPtr -= 1; // Decrement to next entry } else { HcdLPtr = HcdLEnd; } //RtlCopyMemory(HcdLPtr->le_name, Name, 4); // LPtr->le_ret = (stk[1] & 0x00ffffff) | (CurVMID()<<24); sig.l = Sig; rsig.b.Byte0 = sig.b.Byte3; rsig.b.Byte1 = sig.b.Byte2; rsig.b.Byte2 = sig.b.Byte1; rsig.b.Byte3 = sig.b.Byte0; HcdLPtr->le_sig = rsig.l; HcdLPtr->le_info1 = Info1; HcdLPtr->le_info2 = Info2; HcdLPtr->le_info3 = Info3; UHCD_ASSERT(HcdLPtr >= HcdLStart); if (irql < DISPATCH_LEVEL) { KeReleaseSpinLock(&LogSpinLock, irql); } else { KeReleaseSpinLockFromDpcLevel(&LogSpinLock); } return; } VOID UHCD_LogInit( ) /*++ Routine Description: Init the debug log - remember interesting information in a circular buffer Arguments: Return Value: None. --*/ { #ifdef MAX_DEBUG ULONG logSize = 4096*6; #else ULONG logSize = 4096*3; #endif KeInitializeSpinLock(&LogSpinLock); HcdLStart = ExAllocatePoolWithTag(NonPagedPool, logSize, UHCD_TAG); if (HcdLStart) { HcdLPtr = HcdLStart; // Point the end (and first entry) 1 entry from the end of the segment HcdLEnd = HcdLStart + (logSize / sizeof(struct UHCD_LOG_ENTRY)) - 1; } else { TRAP(); } return; } VOID UHCD_LogFree( ) /*++ Routine Description: Arguments: Return Value: None. --*/ { if (HcdLStart) { ExFreePool(HcdLStart); } return; } VOID UHCD_LogTD( IN ULONG s, IN PULONG p ) { LOGENTRY(LOG_MISC, s, p, 0, 0); LOGENTRY(LOG_MISC, s, *p, *(p+1), *(p+2)); LOGENTRY(LOG_MISC, s, *(p+3), *(p+4), *(p+5)); } #ifdef DEBUG_LOG_IO #undef WRITE_PORT_USHORT #undef WRITE_PORT_ULONG #undef WRITE_PORT_UCHAR #undef READ_PORT_USHORT #undef READ_PORT_ULONG #undef READ_PORT_UCHAR USHORT UhcdLogIoUshort(PUSHORT Port, USHORT Val, BOOLEAN Write) { USHORT rval = 0; if (Write) { LOGENTRY(LOG_IO, 'WrUS', Port, (ULONG_PTR)Val & 0x0000ffff, 0); WRITE_PORT_USHORT(Port, Val); } else { rval = READ_PORT_USHORT(Port); LOGENTRY(LOG_IO, 'RdUS', Port, (ULONG_PTR)rval & 0x0000ffff, 0); } return rval; } ULONG UhcdLogIoUlong(PULONG Port, ULONG Val, BOOLEAN Write) { ULONG rval = 0; if (Write) { LOGENTRY(LOG_IO, 'WrUL', Port, Val, 0); WRITE_PORT_ULONG(Port, Val); } else { rval = READ_PORT_ULONG(Port); LOGENTRY(LOG_IO, 'RdUL', Port, rval, 0); } return rval; } UCHAR UhcdLogIoUchar(PUCHAR Port, UCHAR Val, BOOLEAN Write) { UCHAR rval = 0; if (Write) { LOGENTRY(LOG_IO, 'WrUC', Port, (ULONG_PTR)Val & 0x000000ff, 0); WRITE_PORT_UCHAR(Port, Val); } else { rval = READ_PORT_UCHAR(Port); LOGENTRY(LOG_IO, 'RdUC', Port, (ULONG_PTR)rval & 0x000000ff, 0); } return rval; } #endif // DEBUG_LOG_IO #endif /* DEBUG_LOG */