936 lines
29 KiB
C++
936 lines
29 KiB
C++
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
// halkd.cpp: runtime support for debugging
|
||
|
//
|
||
|
// For more information see:
|
||
|
// \nt\base\ntos\kd64
|
||
|
// \nt\base\boot\kdcom
|
||
|
// \nt\base\boot\kd1394
|
||
|
// \nt\base\boot\kdusb2
|
||
|
// \nt\sdktools\debuggers\ntsd64
|
||
|
//
|
||
|
#include "hal.h"
|
||
|
#include "halkd.h"
|
||
|
|
||
|
extern "C" void * __cdecl memcpy(void *, const void *, size_t);
|
||
|
extern "C" void * __cdecl memset(void *, int, size_t);
|
||
|
|
||
|
//
|
||
|
// Debugger Debugging
|
||
|
//
|
||
|
#define KDDBG if (0) kdprintf
|
||
|
#define KDDBG2 if (0) kdprintf
|
||
|
|
||
|
//
|
||
|
#define CP_GET_SUCCESS 0
|
||
|
#define CP_GET_NODATA 1
|
||
|
#define CP_GET_ERROR 2
|
||
|
|
||
|
////////////////////////////////////////////////////////// COM PORT Constants.
|
||
|
//
|
||
|
#define COM1_PORT 0x03f8
|
||
|
#define COM2_PORT 0x02f8
|
||
|
#define COM3_PORT 0x03e8
|
||
|
#define COM4_PORT 0x02e8
|
||
|
|
||
|
#define COM_DAT 0x00
|
||
|
#define COM_IEN 0x01 // interrupt enable register
|
||
|
#define COM_FCR 0x02 // FIFO Control Register
|
||
|
#define COM_LCR 0x03 // line control registers
|
||
|
#define COM_MCR 0x04 // modem control reg
|
||
|
#define COM_LSR 0x05 // line status register
|
||
|
#define COM_MSR 0x06 // modem status register
|
||
|
#define COM_SCR 0x07 // scratch register
|
||
|
#define COM_DLL 0x00 // divisor latch least sig
|
||
|
#define COM_DLM 0x01 // divisor latch most sig
|
||
|
|
||
|
const UINT16 BaudRate = 1; // 115200 bps
|
||
|
|
||
|
#define COM_DATRDY 0x01
|
||
|
#define COM_OUTRDY 0x20
|
||
|
|
||
|
#define LC_DLAB 0x80
|
||
|
|
||
|
#define CLOCK_RATE 0x1C200 // USART clock rate
|
||
|
|
||
|
#define MC_DTRRTS 0x03 // Control bits to assert DTR and RTS
|
||
|
#define MS_DSRCTSCD 0xB0 // Status bits for DSR, CTS and CD
|
||
|
#define MS_CD 0x80
|
||
|
|
||
|
#define SERIAL_MCR_LOOP 0x10 // enables loopback testing mode
|
||
|
#define SERIAL_MCR_OUT1 0x04 // general purpose output.
|
||
|
#define SERIAL_MSR_CTS 0x10 // (complemented) state of clear to send (CTS).
|
||
|
#define SERIAL_MSR_DSR 0x20 // (complemented) state of data set ready (DSR).
|
||
|
#define SERIAL_MSR_RI 0x40 // (complemented) state of ring indicator (RI).
|
||
|
#define SERIAL_MSR_DCD 0x80 // (complemented) state of data carrier detect (DCD).
|
||
|
|
||
|
//
|
||
|
// Globals
|
||
|
//
|
||
|
static UINT16 KdBasePort = COM2_PORT;
|
||
|
static ULONG KdCompPacketIdExpected = 0;
|
||
|
static ULONG KdCompNextPacketIdToSend = 0;
|
||
|
static BOOL KdStateChange64Sent = FALSE;
|
||
|
|
||
|
////////////////////////////////////////////////// Serial Port Input & Output.
|
||
|
//
|
||
|
static UINT8 KdReadInt8(UINT16 port)
|
||
|
{
|
||
|
__asm {
|
||
|
mov eax,0;
|
||
|
mov dx,port;
|
||
|
in al,dx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void KdWriteInt8(UINT16 port, UINT8 value)
|
||
|
{
|
||
|
__asm {
|
||
|
mov dx,port;
|
||
|
mov al,value;
|
||
|
out dx,al;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// http://byterunner.com/16550.html
|
||
|
|
||
|
bool KdpComInit(Struct_Microsoft_Singularity_BootInfo *bi)
|
||
|
// Initializes the communication port (baud rate, parity etc.)
|
||
|
{
|
||
|
KdBasePort = bi->DebugBasePort;
|
||
|
if (KdBasePort < 0x100) {
|
||
|
KdBasePort = 0;
|
||
|
return FALSE;
|
||
|
}
|
||
|
KdCompNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID;
|
||
|
KdCompPacketIdExpected = INITIAL_PACKET_ID;
|
||
|
|
||
|
// turn off interrupts
|
||
|
KdWriteInt8(KdBasePort + COM_LCR, 0x00);
|
||
|
KdWriteInt8(KdBasePort + COM_IEN, 0x00);
|
||
|
|
||
|
// Turn on DTS/RTS
|
||
|
KdWriteInt8(KdBasePort + COM_MCR, MC_DTRRTS); // Needed for VirtualPC PIPE/Serial
|
||
|
|
||
|
// Turn on FIFO
|
||
|
//KdWriteInt8(KdBasePort + COM_FCR, 1);
|
||
|
|
||
|
// Set the baud rate
|
||
|
KdWriteInt8(KdBasePort + COM_LCR, LC_DLAB); // Divisor latch access bit
|
||
|
KdWriteInt8(KdBasePort + COM_DLM, (UINT8)(BaudRate >> 8));
|
||
|
KdWriteInt8(KdBasePort + COM_DLL, (UINT8)(BaudRate & 0xFF));
|
||
|
|
||
|
// initialize the LCR
|
||
|
KdWriteInt8(KdBasePort + COM_LCR, 0x03);
|
||
|
// 8 data bits, 1 stop bit, no parity, no break
|
||
|
|
||
|
// See if the 16450/16550 scratch register is available.
|
||
|
// If not, we'll assume the serial port doesn't really exist.
|
||
|
KdWriteInt8(KdBasePort + COM_SCR, 0xff);
|
||
|
UINT8 a1 = KdReadInt8(KdBasePort + COM_SCR);
|
||
|
KdWriteInt8(KdBasePort + COM_SCR, 0x00);
|
||
|
UINT8 a2 = KdReadInt8(KdBasePort + COM_SCR);
|
||
|
|
||
|
return (bool) ((a1 == (UINT8)0xff) && (a2 == (UINT8)0x00));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Define wait timeout value.
|
||
|
//
|
||
|
#define TIMEOUT_COUNT 1024 * 10
|
||
|
// #define TIMEOUT_COUNT 1024 * 200
|
||
|
//#define TIMEOUT_COUNT 15
|
||
|
|
||
|
static KDP_STATUS CpGetByte(OUT PUCHAR Input, BOOL WaitForByte)
|
||
|
{
|
||
|
UCHAR lsr;
|
||
|
UCHAR value;
|
||
|
ULONG limitcount = WaitForByte ? TIMEOUT_COUNT : 1;
|
||
|
|
||
|
UCHAR msr;
|
||
|
msr = KdReadInt8(KdBasePort + COM_MSR);
|
||
|
KDDBG2("MSR %02x\n", msr);
|
||
|
|
||
|
while (limitcount != 0) {
|
||
|
limitcount--;
|
||
|
|
||
|
lsr = KdReadInt8(KdBasePort + COM_LSR);
|
||
|
KDDBG2("LSR %02x\n", lsr);
|
||
|
if (lsr & COM_DATRDY) {
|
||
|
value = KdReadInt8(KdBasePort + COM_DAT);
|
||
|
*Input = value & 0xff;
|
||
|
return KDP_PACKET_RECEIVED;
|
||
|
}
|
||
|
}
|
||
|
return KDP_PACKET_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
// Fetch a byte from the debug port and return it.
|
||
|
// N.B. It is assumed that necessary multiprocessor synchronization has been
|
||
|
// performed before this routine is called.
|
||
|
//
|
||
|
static KDP_STATUS KdCompGetByte(OUT PUCHAR Input)
|
||
|
{
|
||
|
KDP_STATUS stat;
|
||
|
KDDBG2("KdCompGetByte\n");
|
||
|
stat = CpGetByte(Input, TRUE);
|
||
|
KDDBG2("KdCompGetByte status %d\n", stat);
|
||
|
|
||
|
return stat;
|
||
|
}
|
||
|
|
||
|
// Write a byte to the debug port.
|
||
|
// N.B. It is assumed that necessary multiprocessor synchronization has been
|
||
|
// performed before this routine is called.
|
||
|
//
|
||
|
static VOID KdCompPutByte(IN UCHAR Output)
|
||
|
{
|
||
|
KDDBG2("KdCompPutByte %02x\n", Output);
|
||
|
// wait for the com port to be ready
|
||
|
while ((KdReadInt8( KdBasePort + COM_LSR ) & COM_OUTRDY) == 0);
|
||
|
|
||
|
KDDBG2("KdCompPutByte ready\n");
|
||
|
|
||
|
// write a single char
|
||
|
KdWriteInt8(KdBasePort + COM_DAT, Output);
|
||
|
KDDBG2("KdCompPutByte done\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
// Fetch a byte from the debug port and return it if one is available.
|
||
|
// N.B. It is assumed that necessary multiprocessor synchronization has been
|
||
|
// performed before this routine is called.
|
||
|
//
|
||
|
static KDP_STATUS KdCompPollByte(OUT PUCHAR Input)
|
||
|
{
|
||
|
KDDBG2("KdCompPollByte\n");
|
||
|
KDP_STATUS status = CpGetByte(Input, FALSE);
|
||
|
KDDBG2("KdCompPollByte %d\n", status);
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
// Wait for a packet header leader (receive it into PacketLeader ULONG).
|
||
|
//
|
||
|
static
|
||
|
KDP_STATUS
|
||
|
KdCompReceivePacketLeader(
|
||
|
OUT PULONG PacketLeader,
|
||
|
IN OUT PKD_CONTEXT KdContext
|
||
|
)
|
||
|
{
|
||
|
|
||
|
UCHAR Input;
|
||
|
UCHAR PreviousByte = 0;
|
||
|
ULONG PacketId = 0;
|
||
|
ULONG 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 = KdCompGetByte(&Input);
|
||
|
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
|
||
|
KdpSendString(
|
||
|
IN PCHAR Source,
|
||
|
IN ULONG Length
|
||
|
)
|
||
|
// 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.
|
||
|
{
|
||
|
|
||
|
UCHAR Output;
|
||
|
|
||
|
KDDBG2("KdpSendString len %d\n", Length);
|
||
|
|
||
|
//
|
||
|
// Write bytes to the kernel debugger port.
|
||
|
//
|
||
|
|
||
|
while (Length > 0) {
|
||
|
Output = *Source++;
|
||
|
KdCompPutByte(Output);
|
||
|
Length -= 1;
|
||
|
}
|
||
|
KDDBG2("KdpSendString done\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
KDP_STATUS
|
||
|
KdpReceiveString(
|
||
|
OUT PCHAR Destination,
|
||
|
IN ULONG Length
|
||
|
)
|
||
|
// 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.
|
||
|
{
|
||
|
|
||
|
UCHAR Input;
|
||
|
KDP_STATUS ReturnCode;
|
||
|
KDDBG2("KdpReceiveString len %d\n", Length);
|
||
|
|
||
|
//
|
||
|
// Read bytes until either an error is encountered or the entire string
|
||
|
// has been read.
|
||
|
//
|
||
|
while (Length > 0) {
|
||
|
KdpSpin();
|
||
|
|
||
|
ReturnCode = KdCompGetByte(&Input);
|
||
|
if (ReturnCode != KDP_PACKET_RECEIVED) {
|
||
|
KDDBG("KdpReceiveString return %d\n", ReturnCode);
|
||
|
return ReturnCode;
|
||
|
}
|
||
|
else {
|
||
|
*Destination++ = Input;
|
||
|
Length -= 1;
|
||
|
}
|
||
|
}
|
||
|
KDDBG2("KdpReceiveString return %d\n", KDP_PACKET_RECEIVED);
|
||
|
return KDP_PACKET_RECEIVED;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
VOID
|
||
|
KdpSendControlPacket(
|
||
|
IN USHORT PacketType,
|
||
|
IN ULONG 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;
|
||
|
KdpSendString((PCHAR)&PacketHeader, sizeof(KD_PACKET));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
void
|
||
|
KdpComSendPacket(
|
||
|
IN ULONG 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;
|
||
|
ULONG MessageDataLength;
|
||
|
KDP_STATUS ReturnCode;
|
||
|
KDDBG2("KdpComSendPacket %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 = (USHORT)(MessageHeader->Length + MessageDataLength);
|
||
|
PacketHeader.PacketType = (USHORT)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;
|
||
|
DWORD dwDrained = 0;
|
||
|
KdCompNextPacketIdToSend |= SYNC_PACKET_ID;
|
||
|
KdStateChange64Sent = TRUE;
|
||
|
|
||
|
while (KdCompGetByte(&uDummy) == 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;
|
||
|
}
|
||
|
}
|
||
|
#if 0
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
//
|
||
|
// Setting PacketId has to be in the do loop in case Packet Id was
|
||
|
// reset.
|
||
|
//
|
||
|
|
||
|
PacketHeader.PacketId = KdCompNextPacketIdToSend;
|
||
|
KdpSendString((PCHAR)&PacketHeader, sizeof(KD_PACKET));
|
||
|
|
||
|
//
|
||
|
// Output message header.
|
||
|
//
|
||
|
|
||
|
KdpSendString(MessageHeader->Buffer, MessageHeader->Length);
|
||
|
|
||
|
//
|
||
|
// Output message data.
|
||
|
//
|
||
|
|
||
|
if ( MessageDataLength ) {
|
||
|
KdpSendString(MessageData->Buffer, MessageData->Length);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Output a packet trailing byte
|
||
|
//
|
||
|
|
||
|
KdCompPutByte(PACKET_TRAILING_BYTE);
|
||
|
|
||
|
//
|
||
|
// Wait for the Ack Packet
|
||
|
//
|
||
|
|
||
|
ReturnCode = KdpComReceivePacket(
|
||
|
PACKET_TYPE_KD_ACKNOWLEDGE,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
KdContext
|
||
|
);
|
||
|
if (ReturnCode == KDP_PACKET_TIMEOUT) {
|
||
|
KDDBG2("TIMEOUT\n");
|
||
|
KdCompNumberRetries--;
|
||
|
}
|
||
|
KdpSpin();
|
||
|
} 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("KdpComSendPacket %d done\n", PacketType);
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
KDP_STATUS
|
||
|
KdpComReceivePacket(
|
||
|
IN ULONG PacketType,
|
||
|
OUT PSTRING MessageHeader,
|
||
|
OUT PSTRING MessageData,
|
||
|
OUT PULONG 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 ULONG 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;
|
||
|
ULONG MessageLength;
|
||
|
KD_PACKET PacketHeader;
|
||
|
KDP_STATUS ReturnCode;
|
||
|
ULONG Checksum;
|
||
|
|
||
|
KDDBG2("KdpComReceivePacket %d\n", PacketType);
|
||
|
|
||
|
WaitForPacketLeader:
|
||
|
|
||
|
KdpSpin();
|
||
|
|
||
|
//
|
||
|
// 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 = KdpReceiveString((PCHAR)&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 = KdpReceiveString((PCHAR)&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 = KdpReceiveString((PCHAR)&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 = KdpReceiveString((PCHAR)&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 > (USHORT)PACKET_MAX_SIZE) ||
|
||
|
(PacketHeader.ByteCount < (USHORT)MessageLength)) {
|
||
|
goto SendResendPacket;
|
||
|
}
|
||
|
*DataLength = PacketHeader.ByteCount - MessageLength;
|
||
|
|
||
|
//
|
||
|
// Read the message header.
|
||
|
//
|
||
|
ReturnCode = KdpReceiveString(MessageHeader->Buffer, MessageLength);
|
||
|
if (ReturnCode != KDP_PACKET_RECEIVED) {
|
||
|
goto SendResendPacket;
|
||
|
}
|
||
|
MessageHeader->Length = (USHORT)MessageLength;
|
||
|
|
||
|
//
|
||
|
// Read the message data.
|
||
|
//
|
||
|
ReturnCode = KdpReceiveString(MessageData->Buffer, *DataLength);
|
||
|
if (ReturnCode != KDP_PACKET_RECEIVED) {
|
||
|
goto SendResendPacket;
|
||
|
}
|
||
|
MessageData->Length = (USHORT)*DataLength;
|
||
|
|
||
|
//
|
||
|
// Read packet trailing byte
|
||
|
//
|
||
|
ReturnCode = KdCompGetByte(&Input);
|
||
|
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("KdpComReceivePacket - 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 KdpComPollBreakIn()
|
||
|
{
|
||
|
KDDBG2("KdpComPollBreakIn\n");
|
||
|
UCHAR Input;
|
||
|
ULONG Status = KdCompPollByte(&Input);
|
||
|
KDDBG2("KdCompPollByte 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.
|