/*++ Copyright (c) 1990 Microsoft Corporation Module Name: packet.c Abstract: This module implements the packet io APIs Author: Wesley Witt (wesw) 8-Mar-1992 Environment: NT 3.1 --*/ #include "precomp.h" #pragma hdrstop extern KDDEBUGGER_DATA64 KdDebuggerData; VOID TRACE_PRINT(PCHAR szFormat, ...); jmp_buf JumpBuffer; BOOL DmKdBreakIn = FALSE; BOOL KdResync = FALSE; BOOL InitialBreak = FALSE; ULONG MaxRetries = 5; ULONG DmKdPacketExpected = INITIAL_PACKET_ID; ULONG DmKdNextPacketToSend = INITIAL_PACKET_ID; BOOL ValidUnaccessedPacket = FALSE; UCHAR DmKdPacket[PACKET_MAX_SIZE]; KD_PACKET PacketHeader; UCHAR DmKdBreakinPacket[1] = { BREAKIN_PACKET_BYTE }; UCHAR DmKdPacketTrailingByte[1] = { PACKET_TRAILING_BYTE }; UCHAR StringBuffer[512]; BOOL DmKdApi64; BOOL DmKdPtr64; extern BOOL DmKdCacheDecodePTEs; extern HANDLE DmKdComPort; extern DWORD PollThreadId; extern CRITICAL_SECTION csPacket; extern BOOL fPacketTrace; VOID DmKdHandlePromptString(PDBGKD_DEBUG_IO IoMessage) { DEBUG_EVENT64 de; ULONG len = min( sizeof(StringBuffer)-1, IoMessage->u.GetString.LengthOfPromptString ); memcpy( StringBuffer, (PUCHAR)(IoMessage+1), len ); StringBuffer[len]='\0'; de.dwDebugEventCode = INPUT_DEBUG_STRING_EVENT; de.dwProcessId = 1; de.dwThreadId = 1; de.u.DebugString.nDebugStringLength = (WORD)len; de.u.DebugString.fUnicode = FALSE; de.u.DebugString.lpDebugStringData = (UINT_PTR)StringBuffer; ReleaseApiLock(); if (GetCurrentThreadId() != PollThreadId) { StringBuffer[0] = 'i'; IoMessage->u.GetString.LengthOfStringRead = 1; } else { NotifyEM( &de, HTHDXFromPIDTID(1, 1), 0, 0 ); IoMessage->u.GetString.LengthOfStringRead = de.u.DebugString.nDebugStringLength; } TakeApiLock(); DmKdWritePacket(IoMessage, sizeof(*IoMessage), PACKET_TYPE_KD_DEBUG_IO, (PVOID) de.u.DebugString.lpDebugStringData, (USHORT) IoMessage->u.GetString.LengthOfStringRead); MHFree( (PVOID) de.u.DebugString.lpDebugStringData); } VOID DmKdWriteControlPacket(IN USHORT PacketType, IN ULONG PacketId OPTIONAL) /*++ Routine Description: This function writes a control packet to target machine. N.B. a CONTROL Packet header is sent with the following information: PacketLeader - indicates it's a control packet PacketType - indicates the type of the control packet ByteCount - aways zero to indicate no data following the header PacketId - Valid ONLY for PACKET_TYPE_KD_ACKNOWLEDGE to indicate which packet is acknowledged. Arguments: PacketType - Supplies the type of the control packet. PacketId - Supplies the PacketId. Used by Acknowledge packet only. Return Value: None. --*/ { DWORD BytesWritten; BOOL rc; KD_PACKET Packet; assert( PacketType < PACKET_TYPE_MAX ); Packet.PacketLeader = CONTROL_PACKET_LEADER; Packet.ByteCount = 0; Packet.PacketType = PacketType; if ( ARGUMENT_PRESENT(PacketId) ) { Packet.PacketId = PacketId; } do { // Write the control packet header rc = DmKdWriteComPort((PUCHAR)&Packet, sizeof(Packet), &BytesWritten); } while ( (!rc) || BytesWritten != sizeof(Packet) ); } ULONG DmKdComputeChecksum (IN PUCHAR Buffer, IN ULONG Length) /*++ Routine Description: This routine computes the checksum for the string passed in. Arguments: Buffer - Supplies a pointer to the string. Length - Supplies the length of the string. Return Value: A ULONG is return as the checksum for the input string. --*/ { ULONG Checksum = 0; while (Length > 0) { Checksum = Checksum + (ULONG)*Buffer++; Length--; } return Checksum; } BOOL DmKdSynchronizeTarget (VOID) /*++ Routine Description: This routine keeps on sending reset packet to target until reset packet is acknowledged by a reset packet from target. N.B. This routine is intended to be used by kernel debugger at startup time (ONLY) to get packet control variables on both target and host back in synchronization. Also, reset request will cause kernel to reset its control variables AND resend us its previous packet (with the new packet id). Return Value: None. --*/ { ULONG Retries = 0; USHORT Index; UCHAR DataByte, PreviousDataByte; USHORT PacketType = 0; ULONG TimeoutCount = 0; COMMTIMEOUTS CommTimeouts; COMMTIMEOUTS OldTimeouts; DWORD BytesRead; BOOL rc; EnterCriticalSection(&csSynchronizeTargetInterlock); TRACE_PRINT( "Synchronizing with the target machine...\n" ); // Get the old time out values and hold them. // We then set a new total timeout value of // five seconds on the read. (In millisecond intervals. GetCommTimeouts( DmKdComPort, &OldTimeouts ); CommTimeouts = OldTimeouts; CommTimeouts.ReadIntervalTimeout = 0; CommTimeouts.ReadTotalTimeoutMultiplier = 0; CommTimeouts.ReadTotalTimeoutConstant = 500; SetCommTimeouts( DmKdComPort, &CommTimeouts ); while (TRUE) { Timeout: Retries++; DmKdWriteControlPacket(PACKET_TYPE_KD_RESET, 0L); // Read packet leader Index = 0; do { if (Retries > MaxRetries) { // SetCommTimeouts( DmKdComPort,&OldTimeouts ); LeaveCriticalSection(&csSynchronizeTargetInterlock); return FALSE; } // Check user input for control_c. If user types control_c, // we will send a breakin packet to the target. Hopefully, // target will send us a StateChange packet and // if we don't get response from kernel in 3 seconds we // will resend the reset packet if user does not type ctrl_c. // Otherwise, we send breakin character and wait for data again. rc = DmKdReadComPort(&DataByte, 1, &BytesRead); if ((!rc) || (BytesRead != 1)) { if (DmKdBreakIn) { DmKdSendBreakin(); TimeoutCount = 0; continue; } TimeoutCount++; // if we have been waiting for 3 seconds, resend RESYNC packet if (TimeoutCount != 6) { continue; } TimeoutCount = 0; TRACE_PRINT("SYNCTARGET: Timeout.\n"); goto Timeout; } if (rc && BytesRead == 1 && ( DataByte == PACKET_LEADER_BYTE || DataByte == CONTROL_PACKET_LEADER_BYTE)) { if ( Index == 0 ) { PreviousDataByte = DataByte; Index++; } else if ( DataByte == PreviousDataByte ) { Index++; } else { PreviousDataByte = DataByte; Index = 1; } } else { Index = 0; } } while ( Index < 4 ); if (DataByte == CONTROL_PACKET_LEADER_BYTE) { // Read 2 byte Packet type rc = DmKdReadComPort((PUCHAR)&PacketType,sizeof(PacketType),&BytesRead); if (rc && BytesRead == sizeof(PacketType) && PacketType == PACKET_TYPE_KD_RESET ) { DmKdPacketExpected = INITIAL_PACKET_ID; DmKdNextPacketToSend = INITIAL_PACKET_ID; SetCommTimeouts( DmKdComPort,&OldTimeouts ); LeaveCriticalSection(&csSynchronizeTargetInterlock); return TRUE; } } // If we receive Data Packet leader, it means target has not // receive our reset packet. So we loop back and send it again. // N.B. We need to wait until target finishes sending the packet. // Otherwise, we may be sending the reset packet while the target // is sending the packet. This might cause target loss the reset packet. while (DataByte != PACKET_TRAILING_BYTE) { DmKdReadComPort(&DataByte, 1, &BytesRead); if (BytesRead != 1) { break; } } } // SetCommTimeouts( DmKdComPort,&OldTimeouts ); LeaveCriticalSection(&csSynchronizeTargetInterlock); return TRUE; } VOID DmKdSendBreakin( VOID ) /*++ Routine Description: Send a breakin packet to the target, unless some other packet is already being transmitted, in which case do nothing. --*/ { DWORD BytesWritten; BOOL rc; TRACE_PRINT("Send Break in ...\n"); do { rc = DmKdWriteComPort(&DmKdBreakinPacket[0], sizeof(DmKdBreakinPacket), &BytesWritten); } while ((!rc) || (BytesWritten != sizeof(DmKdBreakinPacket))); DmKdBreakIn = FALSE; } BOOL DmKdWritePacket( IN PVOID PacketData, IN size_t PacketDataLength, IN USHORT PacketType, IN PVOID MorePacketData OPTIONAL, IN USHORT MorePacketDataLength OPTIONAL ) { DWORD BytesWritten; BOOL rc; KD_PACKET Packet; USHORT TotalBytesToWrite; BOOL Received; DWORD retries = 0; assert( PacketType < PACKET_TYPE_MAX ); DEBUG_PRINT_3( "WRITE: Write type %x, packet id=%lx, ml=%hu\n", PacketType, DmKdNextPacketToSend, MorePacketDataLength); if ( ARGUMENT_PRESENT(MorePacketData) ) { TotalBytesToWrite = PacketDataLength + MorePacketDataLength; Packet.Checksum = DmKdComputeChecksum(MorePacketData, MorePacketDataLength); } else { TotalBytesToWrite = (USHORT) PacketDataLength; Packet.Checksum = 0; } Packet.Checksum += DmKdComputeChecksum(PacketData, PacketDataLength); Packet.PacketLeader = PACKET_LEADER; Packet.ByteCount = TotalBytesToWrite; Packet.PacketType = PacketType; ResendPacket: Packet.PacketId = DmKdNextPacketToSend; // Write the packet header rc = DmKdWriteComPort((PUCHAR)&Packet, sizeof(Packet), &BytesWritten); if ( (!rc) || BytesWritten != sizeof(Packet) ){ // an error occured writing the header, so write it again TRACE_PRINT("WRITE: Packet header error.\n"); retries++; if (retries == MaxRetries) { return FALSE; } goto ResendPacket; } // Write the primary packet data rc = DmKdWriteComPort(PacketData, PacketDataLength, &BytesWritten); if ( (!rc) || BytesWritten != PacketDataLength ){ // an error occured writing the primary packet data, so write it again TRACE_PRINT("WRITE: Message header error.\n"); retries++; if (retries == MaxRetries) { return FALSE; } goto ResendPacket; } // If secondary packet data was specified (WriteMemory, SetContext...) // then write it as well. if ( ARGUMENT_PRESENT(MorePacketData) ) { rc = DmKdWriteComPort(MorePacketData, MorePacketDataLength, &BytesWritten); if ( (!rc) || BytesWritten != MorePacketDataLength ){ // an error occured writing the secondary packet data, so write it again TRACE_PRINT("WRITE: Message data error.\n"); retries++; if (retries == MaxRetries) { return FALSE; } goto ResendPacket; } } // Output a packet trailing byte do { rc = DmKdWriteComPort(&DmKdPacketTrailingByte[0], sizeof(DmKdPacketTrailingByte), &BytesWritten); } while ((!rc) || (BytesWritten != sizeof(DmKdPacketTrailingByte))); // Wait for ACK Received = DmKdWaitForPacket(PACKET_TYPE_KD_ACKNOWLEDGE, NULL); if ( Received == FALSE ) { TRACE_PRINT("WRITE: Wait for ACK failed. Resend Packet.\n"); retries++; if (retries == MaxRetries) { return FALSE; } goto ResendPacket; } return TRUE; } BOOL DmKdReadPacketLeader(IN ULONG PacketType, OUT PULONG PacketLeader) { DWORD BytesRead; BOOL rc; USHORT Index; UCHAR DataByte, PreviousDataByte; Index = 0; do { if (DmKdBreakIn) { if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { DmKdSendBreakin( ); DmKdBreakIn = FALSE; LeaveCriticalSection(&csPacket); longjmp( JumpBuffer, 1 ); } } if (KdResync) { KdResync = FALSE; TRACE_PRINT(" Resync packet id ...\n"); DmKdSynchronizeTarget( ); TRACE_PRINT(" Done.\n"); LeaveCriticalSection(&csPacket); longjmp( JumpBuffer, 1 ); } rc = DmKdReadComPort(&DataByte, 1, &BytesRead); if (rc && BytesRead == 1 && ( DataByte == PACKET_LEADER_BYTE || DataByte == CONTROL_PACKET_LEADER_BYTE)) { if ( Index == 0 ) { PreviousDataByte = DataByte; Index++; } else if ( DataByte == PreviousDataByte ) { Index++; } else { PreviousDataByte = DataByte; Index = 1; } } else { Index = 0; if (BytesRead == 0) { TRACE_PRINT("READ: Timeout.\n"); if (InitialBreak) { DmKdSendBreakin( ); InitialBreak = FALSE; } return(FALSE); } } } while ( Index < 2 ); if ( DataByte != CONTROL_PACKET_LEADER_BYTE ) { *PacketLeader = PACKET_LEADER; } else { *PacketLeader = CONTROL_PACKET_LEADER; } return TRUE; } BOOL DmKdWaitForPacket(IN USHORT PacketType, OUT PVOID Packet) { PDBGKD_DEBUG_IO IoMessage; DWORD BytesRead; BOOL rc; UCHAR DataByte; ULONG Checksum; ULONG SyncBit; if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { DEBUG_PRINT_1("READ: wait for ACK packet with id = %lx\n", DmKdNextPacketToSend); } else { TRACE_PRINT("READ: Wait for type %x packet exp id = %lx\n", PacketType, DmKdPacketExpected); } if (PacketType != PACKET_TYPE_KD_ACKNOWLEDGE) { if (ValidUnaccessedPacket) { DEBUG_PRINT("READ: Grab packet from buffer.\n"); goto ReadBuffered; } } // First read a packet leader WaitForPacketLeader: ValidUnaccessedPacket = FALSE; if (!DmKdReadPacketLeader(PacketType, &PacketHeader.PacketLeader)) { return FALSE; } if (InitialBreak) { DmKdSendBreakin( ); InitialBreak = FALSE; } // Read packetLeader ONLY read two Packet Leader bytes. This do loop // filters out the remaining leader byte. do { rc = DmKdReadComPort(&DataByte, 1, &BytesRead); if ((rc) && BytesRead == 1) { if (DataByte == PACKET_LEADER_BYTE || DataByte == CONTROL_PACKET_LEADER_BYTE) { continue; } else { *(PUCHAR)&PacketHeader.PacketType = DataByte; break; } } else { goto WaitForPacketLeader; } }while (TRUE); // Now we have valid packet leader. Read rest of the packet type. rc = DmKdReadComPort(((PUCHAR)&PacketHeader.PacketType) + 1, sizeof(PacketHeader.PacketType) - 1, &BytesRead); if ((!rc) || BytesRead != sizeof(PacketHeader.PacketType) - 1) { // If we cannot read the packet type and if the packet leader // indicates this is a data packet, we need to ask for resend. // Otherwise we simply ignore the incomplete packet. if (PacketHeader.PacketLeader == PACKET_LEADER) { DmKdWriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); TRACE_PRINT("READ: Data packet header Type error (short read).\n"); } goto WaitForPacketLeader; } // Check the Packet type. if (PacketHeader.PacketType >= PACKET_TYPE_MAX ) { TRACE_PRINT("READ: Received INVALID packet type [%x]\n",PacketHeader.PacketType); if (PacketHeader.PacketLeader == PACKET_LEADER) { DmKdWriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); } goto WaitForPacketLeader; } // Read ByteCount rc = DmKdReadComPort((PUCHAR)&PacketHeader.ByteCount, sizeof(PacketHeader.ByteCount), &BytesRead); if ((!rc) || BytesRead != sizeof(PacketHeader.ByteCount)) { // If we cannot read the packet type and if the packet leader // indicates this is a data packet, we need to ask for resend. // Otherwise we simply ignore the incomplete packet. if (PacketHeader.PacketLeader == PACKET_LEADER) { DmKdWriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); TRACE_PRINT("READ: Data packet header ByteCount error (short read).\n"); } goto WaitForPacketLeader; } // Check ByteCount if (PacketHeader.ByteCount > PACKET_MAX_SIZE ) { if (PacketHeader.PacketLeader == PACKET_LEADER) { DmKdWriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); TRACE_PRINT("READ: Data packet header ByteCount error (short read).\n"); } goto WaitForPacketLeader; } // Read Packet Id rc = DmKdReadComPort((PUCHAR)&PacketHeader.PacketId, sizeof(PacketHeader.PacketId), &BytesRead); if ((!rc) || BytesRead != sizeof(PacketHeader.PacketId)) { // If we cannot read the packet Id and if the packet leader // indicates this is a data packet, we need to ask for resend. // Otherwise we simply ignore the incomplete packet. if (PacketHeader.PacketLeader == PACKET_LEADER) { DmKdWriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); DEBUG_PRINT("READ: Data packet header Id error (short read).\n"); } goto WaitForPacketLeader; } if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER ) { if (PacketHeader.PacketType == PACKET_TYPE_KD_ACKNOWLEDGE ) { // If we received an expected ACK packet and we are not // waiting for any new packet, update outgoing packet id // and return. If we are NOT waiting for ACK packet // we will keep on waiting. If the ACK packet // is not for the packet we send, ignore it and keep on waiting. if (PacketHeader.PacketId != DmKdNextPacketToSend) { TRACE_PRINT("READ: Received unmatched packet id = %lx, Type = %x\n", PacketHeader.PacketId, PacketHeader.PacketType); goto WaitForPacketLeader; } else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { DmKdNextPacketToSend ^= 1; TRACE_PRINT("Received correct ACK packet.\n"); return TRUE; } else { goto WaitForPacketLeader; } } else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESET) { // if we received Reset packet, reset the packet control variables and resend earlier packet. DmKdNextPacketToSend = INITIAL_PACKET_ID; DmKdPacketExpected = INITIAL_PACKET_ID; DmKdWriteControlPacket(PACKET_TYPE_KD_RESET, 0L); return FALSE; } else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESEND) { TRACE_PRINT("READ: Received RESEND packet\n"); return FALSE; } else { // Invalid packet header, ignore it. TRACE_PRINT("READ: Received Control packet with UNKNOWN type\n"); goto WaitForPacketLeader; } // The packet header is for data packet (not control packet). } else { // Read Packet Checksum. (for Data Packet Only). rc = DmKdReadComPort((PUCHAR)&PacketHeader.Checksum, sizeof(PacketHeader.Checksum), &BytesRead); if ((!rc) || BytesRead != sizeof(PacketHeader.Checksum)) { DmKdWriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); TRACE_PRINT("READ: Data packet header checksum error (short read).\n"); goto WaitForPacketLeader; } if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { // if we are waiting for ACK packet ONLY // and we receive a data packet header, check if the packet id // is what we expected. If yes, assume the acknowledge is lost (but // sent), ask sender to resend and return with PACKET_RECEIVED. if (PacketHeader.PacketId == DmKdPacketExpected) { DmKdNextPacketToSend ^= 1; TRACE_PRINT("READ: Received VALID data packet while waiting for ACK.\n"); } else { TRACE_PRINT("READ: Received Data packet with unmatched ID = %lx\n"); // ,PacketHeader.PacketId); DmKdWriteControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, PacketHeader.PacketId); goto WaitForPacketLeader; } } } // we are waiting for data packet and we received the packet header // for data packet. Perform the following checkings to make sure // it is the packet we are waiting for. if ((PacketHeader.PacketId & ~SYNC_PACKET_ID) != INITIAL_PACKET_ID && (PacketHeader.PacketId & ~SYNC_PACKET_ID) != (INITIAL_PACKET_ID ^ 1)) { TRACE_PRINT("READ: Received INVALID packet Id.\n"); goto AskForResend; } rc = DmKdReadComPort(DmKdPacket, PacketHeader.ByteCount, &BytesRead); if ( (!rc) || BytesRead != PacketHeader.ByteCount ) { TRACE_PRINT("READ: Data packet error (short read).\n"); goto AskForResend; } // Make sure the next byte is packet trailing byte rc = DmKdReadComPort(&DataByte, sizeof(DataByte), &BytesRead); if ( (!rc) || BytesRead != sizeof(DataByte) || DataByte != PACKET_TRAILING_BYTE ) { TRACE_PRINT("READ: Packet trailing byte timeout.\n"); goto AskForResend; } // Make sure the checksum is valid. Checksum = DmKdComputeChecksum(DmKdPacket, PacketHeader.ByteCount); if (Checksum != PacketHeader.Checksum) { TRACE_PRINT("READ: Checksum error.\n"); goto AskForResend; } // We have a valid data packet. If the packetid is bad, we just // ack the packet to the sender will step ahead. If packetid is bad // but SYNC_PACKET_ID bit is set, we sync up. If packetid is good, // or SYNC_PACKET_ID is set, we take the packet. TRACE_PRINT("READ: Received Packet with id = %lx\n", PacketHeader.PacketId); SyncBit = PacketHeader.PacketId & SYNC_PACKET_ID; PacketHeader.PacketId = PacketHeader.PacketId & ~SYNC_PACKET_ID; // Ack the packet. SYNC_PACKET_ID bit will ALWAYS be OFF. DmKdWriteControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, PacketHeader.PacketId); // Check the incoming packet Id. if ((PacketHeader.PacketId != DmKdPacketExpected) && (SyncBit != SYNC_PACKET_ID)) { DEBUG_PRINT_1("READ: Unexpected Packet Id (Acked) (%lx)\n", DmKdPacketExpected); goto WaitForPacketLeader; } else { if (SyncBit == SYNC_PACKET_ID) { // We know SyncBit is set, so reset Expected Ids TRACE_PRINT("READ: Got Sync Id, reset PacketId.\n"); DmKdPacketExpected = PacketHeader.PacketId; DmKdNextPacketToSend = INITIAL_PACKET_ID; } DmKdPacketExpected ^= 1; } // If this is an internal packet. IO, or Resend, then handle it. if (PacketHeader.PacketType == PACKET_TYPE_KD_DEBUG_IO) { IoMessage = (PDBGKD_DEBUG_IO)DmKdPacket; if (IoMessage->ApiNumber == DbgKdPrintStringApi) { ULONG len = min( sizeof(StringBuffer)-1, IoMessage->u.PrintString.LengthOfString ); memcpy( StringBuffer, (PUCHAR)(IoMessage+1), len ); StringBuffer[len]='\0'; // If we are in the poll thread, then we just print the message, otherwise we queue it so the poll thread will take care of the printing. if ( GetCurrentThreadId() == PollThreadId ) { DMPrintShellMsg( "%s", StringBuffer ); } else { AddQueue( QT_DEBUGSTRING, 0, 0, (DWORD_PTR)StringBuffer, len+1 ); } } else if (IoMessage->ApiNumber == DbgKdGetStringApi) { DmKdHandlePromptString(IoMessage); } if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { return TRUE; } goto WaitForPacketLeader; } if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { ValidUnaccessedPacket = TRUE; TRACE_PRINT("READ: Packet Read ahead.\n"); return TRUE; } ReadBuffered: // Check PacketType is what we are waiting for. if (PacketType == PACKET_TYPE_KD_STATE_CHANGE32 || PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { if (PacketHeader.PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { DmKdApi64 = TRUE; } else if (PacketHeader.PacketType == PACKET_TYPE_KD_STATE_CHANGE32) { PacketType = PACKET_TYPE_KD_STATE_CHANGE32; DmKdApi64 = FALSE; } } if (!DmKdApi64 && PacketType == PACKET_TYPE_KD_STATE_MANIPULATE) { DBGKD_MANIPULATE_STATE64 Packet64; DWORD AdditionalDataSize; DbgkdManipulateState32To64((PDBGKD_MANIPULATE_STATE32)&DmKdPacket, &Packet64, &AdditionalDataSize); if (Packet64.ApiNumber == DbgKdGetVersionApi) { DbgkdGetVersion32To64(&((PDBGKD_MANIPULATE_STATE32)&DmKdPacket)->u.GetVersion32, &Packet64.u.GetVersion64, &KdDebuggerData); } else if (AdditionalDataSize) { // Move the trailing data to make room for the larger packet header MoveMemory(DmKdPacket + sizeof(DBGKD_MANIPULATE_STATE64), DmKdPacket + sizeof(DBGKD_MANIPULATE_STATE32), AdditionalDataSize); } *(PDBGKD_MANIPULATE_STATE64)DmKdPacket = Packet64; } if (PacketType != PacketHeader.PacketType) { TRACE_PRINT("READ: Unexpected Packet type (Acked).\n"); goto WaitForPacketLeader; } *(PVOID *)Packet = &DmKdPacket; ValidUnaccessedPacket = FALSE; return TRUE; AskForResend: DmKdWriteControlPacket(PACKET_TYPE_KD_RESEND, 0L); if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { return TRUE; } TRACE_PRINT("READ: Ask for resend.\n"); goto WaitForPacketLeader; } DWORD DmKdWaitStateChange(OUT PDBGKD_WAIT_STATE_CHANGE64 StateChange, OUT PVOID Buffer, IN ULONG BufferLength) /*++ Routine Description: This function causes the calling user interface to wait for a state change to occur in the system being debugged. Once a state change occurs, the user interface can either continue the system using DmKdContinue, or it can manipulate system state using anyone of the DmKd state manipulation APIs. Arguments: StateChange - Supplies the address of state change record that will contain the state change information. Buffer - Supplies the address of a buffer that returns additional information. BufferLength - Supplies the length of Buffer. Return Value: STATUS_SUCCESS - A state change occured. Valid state change information was returned. --*/ { BOOL rc; PVOID LocalStateChange; DWORD st; UCHAR *pt; DWORD i; ULONG SizeofStateChange; USHORT PacketType; DEBUG_PRINT( "Waiting for a state change\n" ); EnterCriticalSection(&csPacket); st = (DWORD)STATUS_UNSUCCESSFUL; while ( st != STATUS_SUCCESS ) { // Waiting for a state change message. Copy the message to the callers buffer, and then free the packet entry. do { PacketType = DmKdApi64 ? PACKET_TYPE_KD_STATE_CHANGE64 : PACKET_TYPE_KD_STATE_CHANGE32; rc = DmKdWaitForPacket(PacketType, &LocalStateChange); if (!rc && MaxRetries==1) { LeaveCriticalSection(&csPacket); return STATUS_TIMEOUT; } } while ( rc == FALSE); // BUGBUG - for some reaso, this no longer works. #if 0 #if defined(TARGET_i386) if (((PDBGKD_WAIT_STATE_CHANGE64)LocalStateChange)->Processor != IMAGE_FILE_MACHINE_I386) #elif defined(TARGET_ALPHA) || defined(TARGET_AXP64) if (((PDBGKD_WAIT_STATE_CHANGE64)LocalStateChange)->Processor != IMAGE_FILE_MACHINE_ALPHA && ((PDBGKD_WAIT_STATE_CHANGE64)LocalStateChange)->Processor != IMAGE_FILE_MACHINE_ALPHA64) #else #error "need machine support here" #endif { DMPrintShellMsg( "DMKD: Debugger configuration does not match target machine\n"); return STATUS_UNSUCCESSFUL; } #endif st = STATUS_SUCCESS; if (DmKdApi64) { *StateChange = *(PDBGKD_WAIT_STATE_CHANGE64)LocalStateChange; SizeofStateChange = sizeof(PDBGKD_WAIT_STATE_CHANGE64); } else { WaitStateChange32To64((PDBGKD_WAIT_STATE_CHANGE32)LocalStateChange, StateChange); SizeofStateChange = sizeof(PDBGKD_WAIT_STATE_CHANGE32); } switch ( (USHORT) StateChange->NewState ) { case DbgKdExceptionStateChange: if (BufferLength < (PacketHeader.ByteCount - SizeofStateChange)) { st = STATUS_BUFFER_OVERFLOW; } else { pt = (UCHAR *)LocalStateChange + SizeofStateChange; memcpy(Buffer, pt, PacketHeader.ByteCount - SizeofStateChange); } break; case DbgKdLoadSymbolsStateChange: if ( BufferLength < StateChange->u.LoadSymbols.PathNameLength ) { st = (DWORD)STATUS_BUFFER_OVERFLOW; } else { pt = ((UCHAR *) LocalStateChange) + PacketHeader.ByteCount - (int)StateChange->u.LoadSymbols.PathNameLength; memcpy(Buffer, pt, (int)StateChange->u.LoadSymbols.PathNameLength); } break; default: assert(FALSE); } DEBUG_PRINT_1( "state change = %x\n", StateChange->NewState ); LeaveCriticalSection(&csPacket); #if defined(TARGET_i386) if (vs.MinorVersion < CONTEXT_SIZE_NT5_VERSION) { ZeroMemory(StateChange->Context.ExtendedRegisters, sizeof(StateChange->Context.ExtendedRegisters)); } #endif return st; } return st; } VOID TRACE_PRINT(PCHAR szFormat, ...) { va_list marker; char buf[500]; int n; #if DBG if (!fPacketTrace && nVerbose < 5) #else if (!fPacketTrace) #endif { return; } va_start( marker, szFormat ); n = _vsnprintf(buf, sizeof(buf), szFormat, marker ); va_end( marker); if (n == -1) { buf[sizeof(buf)-1] = '\0'; } #if DBG if (nVerbose >= 5) { OutputDebugString( buf ); } #endif if (fPacketTrace) { DMPrintShellMsg( "%s", buf ); } } NTSTATUS DmKdReadDebuggerDataBlock(ULONG64 Address, OUT PDBGKD_DEBUG_DATA_HEADER64 DataBlock, ULONG SizeToRead) { NTSTATUS Status; ULONG Result; KDDEBUGGER_DATA32 LocalData; PKDDEBUGGER_DATA64 DebuggerData = (PKDDEBUGGER_DATA64) DataBlock; if (DmKdApi64) { Status = DmKdReadMemoryWrapper(Address, DataBlock, SizeToRead, &Result); } else { Status = DmKdReadMemoryWrapper(Address, &LocalData, sizeof(LocalData), &Result); if (Result == sizeof(LocalData)) { Result = SizeToRead; } } if (!NT_SUCCESS(Status)) { return Status; } if (Result != SizeToRead) { return STATUS_UNSUCCESSFUL; } if (!DmKdApi64) { DebuggerData32To64(&LocalData, DebuggerData); } else { // Sign extended for X86 if (!DmKdPtr64) { LIST_ENTRY64 List64; // Extend the header so it doesn't get whacked ListEntry32To64((PLIST_ENTRY32)(&DebuggerData->Header.List), &List64); DebuggerData->Header.List = List64; // None of the values are sign extended. Sign extend them now. DebuggerData64To32(DebuggerData, &LocalData); DebuggerData32To64(&LocalData, DebuggerData); } } return STATUS_SUCCESS; } NTSTATUS DmKdReadDebuggerDataHeader(ULONG64 Address, OUT PDBGKD_DEBUG_DATA_HEADER64 DataHeader) { NTSTATUS Status; ULONG Result; ULONG SizeToRead; DBGKD_DEBUG_DATA_HEADER32 OldHeader; LIST_ENTRY64 List64; if (DmKdApi64) { SizeToRead = sizeof(*DataHeader); Status = DmKdReadMemoryWrapper(Address, DataHeader, SizeToRead, &Result); } else { SizeToRead = sizeof(OldHeader); Status = DmKdReadMemoryWrapper(Address, &OldHeader, SizeToRead, &Result); } if (!NT_SUCCESS(Status)) { return Status; } if (Result != SizeToRead) { return STATUS_UNSUCCESSFUL; } if (DmKdApi64) { // translate the LIST_ENTRY if necessary if (!DmKdPtr64) { ListEntry32To64( (PLIST_ENTRY32)(&DataHeader->List), &List64); DataHeader->List = List64; } } else { ListEntry32To64(&OldHeader.List, &List64); DataHeader->OwnerTag = OldHeader.OwnerTag; //DataHeader->Size = OldHeader.Size; DataHeader->Size = sizeof(KDDEBUGGER_DATA64); } return STATUS_SUCCESS; } NTSTATUS DmKdReadListEntry(ULONG64 Address, PLIST_ENTRY64 List64) { NTSTATUS Status = STATUS_SUCCESS; ULONG Result; ULONG SizeToRead; LIST_ENTRY32 List32; extern BOOL fCrashDump; if (DmKdPtr64) { SizeToRead = sizeof(LIST_ENTRY64); Status = DmKdReadMemoryWrapper(Address, List64, SizeToRead, &Result); } else { SizeToRead = sizeof(LIST_ENTRY32); Status = DmKdReadMemoryWrapper(Address, &List32, SizeToRead, &Result); } if (!NT_SUCCESS(Status)) { return Status; } if (Result != SizeToRead) { return STATUS_UNSUCCESSFUL; } if (!DmKdPtr64) { ListEntry32To64(&List32, List64); } return STATUS_SUCCESS; } NTSTATUS DmKdReadPointer(ULONG64 Address, PULONG64 Pointer64) { NTSTATUS Status; ULONG Result; ULONG SizeToRead; ULONG Pointer32; if (DmKdPtr64) { SizeToRead = sizeof(ULONG64); Status = DmKdReadMemoryWrapper(Address, Pointer64, SizeToRead, &Result); } else { SizeToRead = sizeof(ULONG32); Status = DmKdReadMemoryWrapper(Address, &Pointer32, SizeToRead, &Result); } if (!NT_SUCCESS(Status)) { return Status; } if (Result != SizeToRead) { return STATUS_UNSUCCESSFUL; } if (!DmKdPtr64) { *Pointer64 = SE32To64(Pointer32); } return STATUS_SUCCESS; } LDR_DATA_TABLE_ENTRY32 b32; NTSTATUS DmKdReadLoaderEntry(ULONG64 Address, PLDR_DATA_TABLE_ENTRY64 b64) { NTSTATUS Status = STATUS_SUCCESS; ULONG Result; ULONG SizeToRead; extern BOOL fCrashDump; if (DmKdPtr64) { SizeToRead = sizeof(LDR_DATA_TABLE_ENTRY64); Status = DmKdReadMemoryWrapper(Address, b64, SizeToRead, &Result); } else { SizeToRead = sizeof(LDR_DATA_TABLE_ENTRY32); Status = DmKdReadMemoryWrapper(Address, &b32, SizeToRead, &Result); } if (!NT_SUCCESS(Status)) { return Status; } if (Result != SizeToRead) { return STATUS_UNSUCCESSFUL; } if (!DmKdPtr64) { LdrDataTableEntry32To64(&b32, b64); } return STATUS_SUCCESS; }