singrdk/base/Kernel/Native/HalKdSerial.cpp

792 lines
25 KiB
C++

//////////////////////////////////////////////////////////////////////////////
//
// 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.