////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: halkdserial.cpp: Serial-port transport routines. // // Note: Kernel Only // #include "hal.h" #include "halkd.h" // // Debugger Debugging // #define KDDBG if (0) kdprintf #define KDDBG2 if (0) kdprintf // // Globals // static UINT32 KdCompPacketIdExpected = INITIAL_PACKET_ID; static UINT32 KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; static BOOL KdStateChange64Sent = false; ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // PLATFORM LAYER - this is the cut point for abstracting the serial port // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// KDP_STATUS KdpSerialGetByte(OUT PUCHAR Input, BOOL WaitForByte); void KdpSerialPutByte(IN UCHAR Output); //++ // //Routine Description: // // This routine reads a string from the kernel debugger port. // //Arguments: // // Destination - Supplies a pointer to the input string. // // Length - Supplies the length of the string to be read. //-- static KDP_STATUS KdpSerialReceiveString(OUT PUCHAR Destination, IN UINT32 Length, IN BOOL WaitForInput = true) { KDDBG2("KdpSerialReceiveString len %d\n", Length); UCHAR Input; UINT32 ReqLength = Length; UINT32 ReturnCode; // // Read bytes until either a error is encountered or the entire string // has been read. // while (Length > 0) { KdpSpin(); ReturnCode = KdpSerialGetByte(&Input, WaitForInput); if (ReturnCode != KDP_PACKET_RECEIVED) { break; } else { *Destination++ = Input; Length -= 1; } } KDDBG2("KdpSerialReceiveString left %d\n", Length); return (Length == 0) ? KDP_PACKET_RECEIVED : KDP_PACKET_TIMEOUT; } //++ // //Routine Description: // // This routine writes a string to the kernel debugger port. // //Arguments: // // Source - Supplies a pointer to the output string. // // Length - Supplies the length of the string to be written. // //Return Value: // // None. // //-- static void KdpSerialSendString(IN PUCHAR Source, IN UINT32 Length) { KDDBG2("KdpSerialSendString len %d\n", Length); UINT32 ReqLength = (UINT32)Length; // // Write bytes to the kernel debugger port. // UCHAR Output; while (Length > 0) { Output = *Source++; KdpSerialPutByte(Output); Length -= 1; } KDDBG2("KdpSerialSendString done\n"); } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // CHANNEL LAYER - all this code should be neutral to the actual channel // (i.e. nothing specific to serial port, only reading and writing bytes) // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // Wait for a packet header leader (receive it into PacketLeader ULONG). // static KDP_STATUS KdCompReceivePacketLeader( OUT UINT32 *PacketLeader, IN OUT PKD_CONTEXT KdContext ) { UCHAR Input; UCHAR PreviousByte = 0; UINT32 PacketId = 0; UINT32 Index; KDP_STATUS ReturnCode; BOOLEAN BreakinDetected = false; KDDBG2("KdCompReceivePacketLeader\n"); // // NOTE - With all the interrupts being off, it is very hard // to implement the actual timeout code. (Maybe, by reading the CMOS.) // Here we use a loop count to wait about 3 seconds. The CpGetByte // will return with error code = KDP_PACKET_TIMEOUT if it cannot find data // byte within 1 second. Kernel debugger's timeout period is 5 seconds. // Index = 0; do { ReturnCode = KdpSerialReceiveString(&Input, 1); if (ReturnCode == KDP_PACKET_TIMEOUT) { if (BreakinDetected) { KdContext->KdpControlCPending = true; return KDP_PACKET_RESEND; } else { KDDBG2("KdCompReceivePackerLeader returning KDP_PACKET_TIMEOUT\n"); return KDP_PACKET_TIMEOUT; } } else if (ReturnCode == KDP_PACKET_RESEND) { Index = 0; continue; } else { // if (ReturnCode == KDP_PACKET_RECEIVED) if ( Input == PACKET_LEADER_BYTE || Input == CONTROL_PACKET_LEADER_BYTE ) { if ( Index == 0 ) { PreviousByte = Input; Index++; } else if (Input == PreviousByte) { Index++; } else { PreviousByte = Input; Index = 1; } } else { // // If we detect breakin character, we need to verify it // validity. (It is possible that we missed a packet leader // and the breakin character is simply a data byte in the // packet.) // Since kernel debugger send out breakin character ONLY // when it is waiting for State Change packet. The breakin // character should not be followed by any other character // except packet leader byte. // if ( Input == BREAKIN_PACKET_BYTE ) { BreakinDetected = true; } else { // // The following statement is ABSOLUTELY necessary. // BreakinDetected = false; } Index = 0; } } } while ( Index < 4 ); if (BreakinDetected) { KdContext->KdpControlCPending = true; } // // return the packet leader and false to indicate no resend is needed. // if ( Input == PACKET_LEADER_BYTE ) { *PacketLeader = PACKET_LEADER; } else { *PacketLeader = CONTROL_PACKET_LEADER; } KdDebuggerNotPresent = false; #if 0 SharedUserData->KdDebuggerEnabled |= 0x00000002; #endif return KDP_PACKET_RECEIVED; } static void KdpSendControlPacket( IN UINT16 PacketType, IN UINT32 PacketId OPTIONAL ) // Routine Description: // This routine sends a control packet to the host machine that is running the // kernel debugger and waits for an ACK. // // Arguments: // PacketType - Supplies the type of packet to send. // PacketId - Supplies packet id, optionally. // // Return Value: // None. { KD_PACKET PacketHeader; // // Initialize and send the packet header. // PacketHeader.PacketLeader = CONTROL_PACKET_LEADER; if (PacketId != 0) { PacketHeader.PacketId = PacketId; } PacketHeader.ByteCount = 0; PacketHeader.Checksum = 0; PacketHeader.PacketType = PacketType; KdpSerialSendString((PUCHAR)&PacketHeader, sizeof(KD_PACKET)); return; } ////////////////////////////////////////////////////////////////////////////// // void KdpSerialSendPacket( IN UINT32 PacketType, IN PSTRING MessageHeader, IN PSTRING MessageData OPTIONAL, IN OUT PKD_CONTEXT KdContext ) // Routine Description: // This routine sends a packet to the host machine that is running the // kernel debugger and waits for an ACK. // // Arguments: // PacketType - Supplies the type of packet to send. // MessageHeader - Supplies a pointer to a string descriptor that describes // the message information. // MessageData - Supplies a pointer to a string descriptor that describes // the optional message data. // KdContext - Supplies a pointer to the kernel debugger context. // // Return Value: // None. { KD_PACKET PacketHeader; UINT32 MessageDataLength; KDP_STATUS ReturnCode; KDDBG2("KdpSerialSendPacket %d\n", PacketType); if (MessageData != NULL) { MessageDataLength = MessageData->Length; PacketHeader.Checksum = KdpComputeChecksum(MessageData->Buffer, MessageData->Length); } else { MessageDataLength = 0; PacketHeader.Checksum = 0; } PacketHeader.Checksum += KdpComputeChecksum(MessageHeader->Buffer, MessageHeader->Length); // // Initialize and send the packet header. // PacketHeader.PacketLeader = PACKET_LEADER; PacketHeader.ByteCount = (UINT16)(MessageHeader->Length + MessageDataLength); PacketHeader.PacketType = (UINT16)PacketType; KdCompNumberRetries = KdCompRetryCount; // // We sync on first STATE_CHANGE64 message like NT. If this // is the first such message, drain receive pipe as nothing // said before this instant is interesting (and any buffered // packets may interact badly with SendWaitContinue). // if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64 && !KdStateChange64Sent) { // UCHAR uDummy; uint32 dwDrained = 0; KdCompNextPacketIdToSend |= SYNC_PACKET_ID; KdStateChange64Sent = true; while (KdpSerialReceiveString(&uDummy, 1, false) == KDP_PACKET_RECEIVED) { dwDrained++; } } do { KDDBG2("LOOP %d/%d\n", KdCompNumberRetries, KdCompRetryCount); if (KdCompNumberRetries == 0) { KDDBG("KdCompNumberRetries == 0\n"); // // If the packet is not for reporting exception, we give up // and declare debugger not present. // if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64) { PDBGKD_ANY_WAIT_STATE_CHANGE StateChange = (PDBGKD_ANY_WAIT_STATE_CHANGE)MessageHeader->Buffer; if (StateChange->NewState == DbgKdLoadSymbolsStateChange) { KdDebuggerNotPresent = true; //SharedUserData->KdDebuggerEnabled &= ~0x00000002; KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; KdCompPacketIdExpected = INITIAL_PACKET_ID; return; } } else if (PacketType == PACKET_TYPE_KD_DEBUG_IO) { PDBGKD_DEBUG_IO DebugIo = (PDBGKD_DEBUG_IO)MessageHeader->Buffer; if (DebugIo->ApiNumber == DbgKdPrintStringApi) { KdDebuggerNotPresent = true; //SharedUserData->KdDebuggerEnabled &= ~0x00000002; KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; KdCompPacketIdExpected = INITIAL_PACKET_ID; return; } } else if (PacketType == PACKET_TYPE_KD_FILE_IO) { PDBGKD_FILE_IO FileIo; FileIo = (PDBGKD_FILE_IO)MessageHeader->Buffer; if (FileIo->ApiNumber == DbgKdCreateFileApi) { KdDebuggerNotPresent = true; //SharedUserData->KdDebuggerEnabled &= ~0x00000002; KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID; KdCompPacketIdExpected = INITIAL_PACKET_ID; return; } } } // // Setting PacketId has to be in the do loop in case Packet Id was // reset. // PacketHeader.PacketId = KdCompNextPacketIdToSend; KdpSerialSendString((PUCHAR)&PacketHeader, sizeof(KD_PACKET)); // // Output message header. // KdpSerialSendString((PUCHAR)MessageHeader->Buffer, MessageHeader->Length); // // Output message data. // if ( MessageDataLength ) { KdpSerialSendString((PUCHAR)MessageData->Buffer, MessageData->Length); } // // Output a packet trailing byte // { UCHAR b = PACKET_TRAILING_BYTE; KdpSerialSendString(&b, 1); } // // Wait for the Ack Packet // ReturnCode = KdpSerialReceivePacket( PACKET_TYPE_KD_ACKNOWLEDGE, NULL, NULL, NULL, KdContext ); if (ReturnCode == KDP_PACKET_TIMEOUT) { KDDBG2("TIMEOUT\n"); KdCompNumberRetries--; } } while (ReturnCode != KDP_PACKET_RECEIVED); KDDBG2("KD: PACKET_RECEIVED\n"); // // Reset Sync bit in packet id. The packet we sent may have Sync bit set // KdCompNextPacketIdToSend &= ~SYNC_PACKET_ID; // // Since we are able to talk to debugger, the retrycount is set to // maximum value. // KdCompRetryCount = KdContext->KdpDefaultRetries; KDDBG2("KdpSerialSendPacket %d done\n", PacketType); } ////////////////////////////////////////////////////////////////////////////// // KDP_STATUS KdpSerialReceivePacket( IN UINT32 PacketType, OUT PSTRING MessageHeader, OUT PSTRING MessageData, OUT UINT32 *DataLength, IN OUT PKD_CONTEXT KdContext ) // Routine Description: // This routine receives a packet from the host machine that is running // the kernel debugger UI. This routine is ALWAYS called after packet being // sent by caller. It first waits for ACK packet for the packet sent and // then waits for the packet desired. // // N.B. If caller is KdPrintString, the parameter PacketType is // PACKET_TYPE_KD_ACKNOWLEDGE. In this case, this routine will return // right after the ack packet is received. // // Arguments: // PacketType - Supplies the type of packet that is excepted. // MessageHeader - Supplies a pointer to a string descriptor for the input // message. // MessageData - Supplies a pointer to a string descriptor for the input data. // DataLength - Supplies pointer to UINT32 to receive length of recv. data. // KdContext - Supplies a pointer to the kernel debugger context. // // Return Value: // KDP_PACKET_RESEND - if resend is required. // KDP_PAKCET_TIMEOUT - if timeout. // KDP_PACKET_RECEIVED - if packet received. { UCHAR Input; UINT32 MessageLength; KD_PACKET PacketHeader; KDP_STATUS ReturnCode; UINT32 Checksum; KDDBG2("KdpSerialReceivePacket %d\n", PacketType); WaitForPacketLeader: // // Read Packet Leader // ReturnCode = KdCompReceivePacketLeader(&PacketHeader.PacketLeader, KdContext); KDDBG2("KdCompReceivePacketLeader returned %d\n", ReturnCode); // // If we can successfully read packet leader, it has high possibility that // kernel debugger is alive. So reset count. // if (ReturnCode != KDP_PACKET_TIMEOUT) { KdCompNumberRetries = KdCompRetryCount; } if (ReturnCode != KDP_PACKET_RECEIVED) { return ReturnCode; } // // Read packet type. // ReturnCode = KdpSerialReceiveString((PUCHAR)&PacketHeader.PacketType, sizeof(PacketHeader.PacketType)); if (ReturnCode == KDP_PACKET_TIMEOUT) { return KDP_PACKET_TIMEOUT; } else if (ReturnCode == KDP_PACKET_RESEND) { if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { // // If read error and it is for a control packet, simply // pretend that we have not seen this packet. Hopefully // we will receive the packet we desire which automatically acks // the packet we just sent. // goto WaitForPacketLeader; } else { // // if read error while reading data packet, we have to ask // kernel debugger to resend us the packet. // goto SendResendPacket; } } // // if the packet we received is a resend request, we return true and // let caller resend the packet. // if ( PacketHeader.PacketLeader == CONTROL_PACKET_LEADER && PacketHeader.PacketType == PACKET_TYPE_KD_RESEND ) { return KDP_PACKET_RESEND; } // // Read data length. // ReturnCode = KdpSerialReceiveString((PUCHAR)&PacketHeader.ByteCount, sizeof(PacketHeader.ByteCount)); if (ReturnCode == KDP_PACKET_TIMEOUT) { return KDP_PACKET_TIMEOUT; } else if (ReturnCode == KDP_PACKET_RESEND) { if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { goto WaitForPacketLeader; } else { goto SendResendPacket; } } // // Read Packet Id. // ReturnCode = KdpSerialReceiveString((PUCHAR)&PacketHeader.PacketId, sizeof(PacketHeader.PacketId)); if (ReturnCode == KDP_PACKET_TIMEOUT) { return KDP_PACKET_TIMEOUT; } else if (ReturnCode == KDP_PACKET_RESEND) { if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { goto WaitForPacketLeader; } else { goto SendResendPacket; } } // // Read packet checksum. // ReturnCode = KdpSerialReceiveString((PUCHAR)&PacketHeader.Checksum, sizeof(PacketHeader.Checksum)); if (ReturnCode == KDP_PACKET_TIMEOUT) { return KDP_PACKET_TIMEOUT; } else if (ReturnCode == KDP_PACKET_RESEND) { if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) { goto WaitForPacketLeader; } else { goto SendResendPacket; } } // // A complete packet header is received. Check its validity and // perform appropriate action depending on packet type. // 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 != (KdCompNextPacketIdToSend & ~SYNC_PACKET_ID)) { goto WaitForPacketLeader; } else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) { KdCompNextPacketIdToSend ^= 1; return KDP_PACKET_RECEIVED; } 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. // KdCompNextPacketIdToSend = INITIAL_PACKET_ID; KdCompPacketIdExpected = INITIAL_PACKET_ID; KdpSendControlPacket(PACKET_TYPE_KD_RESET, 0L); return KDP_PACKET_RESEND; } else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESEND) { return KDP_PACKET_RESEND; } else { // // Invalid packet header, ignore it. // goto WaitForPacketLeader; } // // The packet header is for data packet (not control packet). // } else 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 == KdCompPacketIdExpected) { KdpSendControlPacket(PACKET_TYPE_KD_RESEND, 0L); KdCompNextPacketIdToSend ^= 1; return KDP_PACKET_RECEIVED; } else { KdpSendControlPacket(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 checks to make sure // it is the packet we are waiting for. // // // Check ByteCount received is valid // MessageLength = MessageHeader->MaximumLength; if ((PacketHeader.ByteCount > (UINT16)PACKET_MAX_SIZE) || (PacketHeader.ByteCount < (UINT16)MessageLength)) { goto SendResendPacket; } *DataLength = PacketHeader.ByteCount - MessageLength; // // Read the message header. // ReturnCode = KdpSerialReceiveString((PUCHAR)MessageHeader->Buffer, MessageLength); if (ReturnCode != KDP_PACKET_RECEIVED) { goto SendResendPacket; } MessageHeader->Length = (UINT16)MessageLength; // // Read the message data. // ReturnCode = KdpSerialReceiveString((PUCHAR)MessageData->Buffer, *DataLength); if (ReturnCode != KDP_PACKET_RECEIVED) { goto SendResendPacket; } MessageData->Length = (UINT16)*DataLength; // // Read packet trailing byte // ReturnCode = KdpSerialReceiveString(&Input, 1); if (ReturnCode != KDP_PACKET_RECEIVED || Input != PACKET_TRAILING_BYTE) { goto SendResendPacket; } // // Check PacketType is what we are waiting for. // if (PacketType != PacketHeader.PacketType) { KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, PacketHeader.PacketId); goto WaitForPacketLeader; } // // Check PacketId is valid. // if (PacketHeader.PacketId == INITIAL_PACKET_ID || PacketHeader.PacketId == (INITIAL_PACKET_ID ^ 1)) { if (PacketHeader.PacketId != KdCompPacketIdExpected) { KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, PacketHeader.PacketId); goto WaitForPacketLeader; } } else { goto SendResendPacket; } // // Check checksum is valid. // Checksum = KdpComputeChecksum(MessageHeader->Buffer, MessageHeader->Length); Checksum += KdpComputeChecksum(MessageData->Buffer, MessageData->Length); if (Checksum != PacketHeader.Checksum) { goto SendResendPacket; } // // Send Acknowledge byte and the Id of the packet received. // Then, update the ExpectId for next incoming packet. // KdpSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE, PacketHeader.PacketId); // // We have successfully received the packet so update the // packet control variables and return success. // KdCompPacketIdExpected ^= 1; KDDBG2("KdpSerialReceivePacket - got one!\n"); return KDP_PACKET_RECEIVED; SendResendPacket: KdpSendControlPacket(PACKET_TYPE_KD_RESEND, 0L); goto WaitForPacketLeader; } // Returns true if a breakin packet is pending. // A packet is present if: There is a valid character which matches BREAK_CHAR. bool KdpSerialPollBreakIn() { KDDBG2("KdpSerialPollBreakIn\n"); UCHAR Input; UINT32 Status = KdpSerialReceiveString(&Input, 1, false); KDDBG2("KdpSerialPollByte STATUS %d Input %02x\n", Status, Input); if ((Status == KDP_PACKET_RECEIVED) && (Input == BREAKIN_PACKET_BYTE)) { KDDBG("KDP_PACKET_RECEIVED\n"); KdDebuggerNotPresent = false; return true; } return false; } // ///////////////////////////////////////////////////////////////// End of File.