///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  debug.cpp: runtime support for debugging
//

#ifndef IN
#define IN
#endif
#ifndef OUT
#define OUT
#endif

// Define the maximum number of retries for packet sends.
//
#define BD_MAXIMUM_RETRIES 20

// Define packet waiting status codes.
//
#define BD_PACKET_RECEIVED 0
#define BD_PACKET_TIMEOUT 1
#define BD_PACKET_RESEND 2

typedef struct _STRING {
    UINT16  Length;
    UINT16  MaximumLength;
    PUINT8  Buffer;
} STRING, *PSTRING;

//======================================================================
// Selected structs and defines used by the KD protocol
//

//
// If the packet type is PACKET_TYPE_KD_DEBUG_IO, then
// the format of the packet data is as follows:
//

#define DbgKdPrintStringApi     0x00003230L

//
// For get string, the Null terminated prompt string
// immediately follows the message. The LengthOfStringRead
// field initially contains the maximum number of characters
// to read. Upon reply, this contains the number of bytes actually
// read. The data read immediately follows the message.
//
//
typedef struct _DBGKD_DEBUG_IO {
    UINT32 ApiNumber;
    UINT16 ProcessorLevel;
    UINT16 Processor;
    UINT32 LengthOfString;
    UINT32 LengthOfStringRead;
} DBGKD_DEBUG_IO, *PDBGKD_DEBUG_IO;

//
// DbgKd APIs are for the portable kernel debugger
//

//
// KD_PACKETS are the low level data format used in KD. All packets
// begin with a packet leader, byte count, packet type. The sequence
// for accepting a packet is:
//
//  - read 4 bytes to get packet leader.  If read times out (10 seconds)
//    with a short read, or if packet leader is incorrect, then retry
//    the read.
//
//  - next read 2 byte packet type.  If read times out (10 seconds) with
//    a short read, or if packet type is bad, then start again looking
//    for a packet leader.
//
//  - next read 4 byte packet Id.  If read times out (10 seconds)
//    with a short read, or if packet Id is not what we expect, then
//    ask for resend and restart again looking for a packet leader.
//
//  - next read 2 byte count.  If read times out (10 seconds) with
//    a short read, or if byte count is greater than PACKET_MAX_SIZE,
//    then start again looking for a packet leader.
//
//  - next read 4 byte packet data checksum.
//
//  - The packet data immediately follows the packet.  There should be
//    ByteCount bytes following the packet header.  Read the packet
//    data, if read times out (10 seconds) then start again looking for
//    a packet leader.
//

typedef struct _KD_PACKET {
    UINT32 PacketLeader;
    UINT16 PacketType;
    UINT16 ByteCount;
    UINT32 PacketId;
    UINT32 Checksum;
} KD_PACKET, *PKD_PACKET;

#define PACKET_MAX_SIZE     4000
#define INITIAL_PACKET_ID   0x80800000  // Don't use 0
#define SYNC_PACKET_ID      0x00000800  // Or in with INITIAL_PACKET_ID
                                        // to force a packet ID reset.

//
// Packet lead in sequence
//

#define PACKET_LEADER                   0x30303030 //0x77000077
#define PACKET_LEADER_BYTE              0x30

#define CONTROL_PACKET_LEADER           0x69696969
#define CONTROL_PACKET_LEADER_BYTE      0x69

//
// Packet Trailing Byte
//

#define PACKET_TRAILING_BYTE            0xAA

//
// Packet Types
//

#define PACKET_TYPE_KD_DEBUG_IO         3
#define PACKET_TYPE_KD_ACKNOWLEDGE      4       // Packet-control type
#define PACKET_TYPE_KD_RESEND           5       // Packet-control type
#define PACKET_TYPE_KD_RESET            6       // Packet-control type
#define PACKET_TYPE_MAX                 12

//
// Status Constants for reading data from comport
//

#define DBGKD_64BIT_PROTOCOL_VERSION2 6

//
// Communication functions (comio.c)
//

static
UINT32
BdComputeChecksum(
    IN PUINT8 Buffer,
    IN UINT32 Length
    );

static
UINT16
BdReceivePacketLeader(
    IN UINT32 PacketType,
    OUT PUINT32 PacketLeader
    );

static
void
BdSendControlPacket(
    IN UINT16 PacketType,
    IN UINT32 PacketId
    );

static
UINT32
BdReceivePacket(
    IN UINT32 ExpectedPacketType,
    OUT PSTRING MessageHeader,
    OUT PSTRING MessageData,
    OUT PUINT32 DataLength
    );

static
void
BdSendPacket(
    IN UINT32 PacketType,
    IN PSTRING MessageHeader,
    IN PSTRING MessageData
    );

static
UINT32
BdReceiveString(
    OUT PUINT8 Destination,
    IN UINT32 Length
    );

static
void
BdSendString(
    IN PUINT8 Source,
    IN UINT32 Length
    );

static
void
BdSendControlPacket(
    IN UINT16 PacketType,
    IN UINT32 PacketId
    );

// Debugger enabled and present.
//
static BOOL BdDebuggerNotPresent = 0;

// Next packet id to send and next packet id to expect.
//
static UINT32 BdPacketIdExpected;
static UINT32 BdNextPacketIdToSend;

// Number of retries and the retry count.
//
static UINT32 BdNumberRetries = BD_MAXIMUM_RETRIES;
static UINT32 BdRetryCount = BD_MAXIMUM_RETRIES;

//////////////////////////////////////////////////////////////////////////////

static
UINT32
BdComputeChecksum(
    IN PUINT8 Buffer,
    IN UINT32 Length
    )

//++
//
//Routine Description:
//
//  This routine computes the checksum of the specified buffer.
//
//Arguments:
//
//  Buffer - Supplies a pointer to the buffer.
//
//  Length - Supplies the length of the buffer.
//
//Return Value:
//
//  A UINT32 is return as the checksum for the input string.
//
//--  

{

    UINT32 Checksum = 0;

    while (Length > 0) {
        Checksum = Checksum + (UINT32)*Buffer++;
        Length--;
    }

    return Checksum;
}

static
UINT16
BdReceivePacketLeader(
                      IN UINT32 /* PacketType */,
                      OUT PUINT32 PacketLeader
                     )

//++
//
//Routine Description:
//
//  This routine waits for a packet header leader.
//
//Arguments:
//
//  PacketType - supplies the type of packet we are expecting.
//
//  PacketLeader - supplies a pointer to a ulong variable to receive
//                 packet leader bytes.
//
//Return Value:
//
//  BD_PACKET_RESEND - if resend is required.
//  BD_PAKCET_TIMEOUT - if timeout.
//  BD_PACKET_RECEIVED - if packet received.
//
//--  

{

    UINT8 Input, PreviousByte = 0;
    UINT32 PacketId = 0;
    UINT32 Index;
    UINT32 ReturnCode;

    // 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 = CP_GET_NODATA if it cannot find data
    // byte within 1 second. Kernel debugger's timeout period is 5 seconds.
    //
    Index = 0;
    do {
        ReturnCode = BdPortGetByte(&Input);
        if (ReturnCode == CP_GET_NODATA) {
            return BD_PACKET_TIMEOUT;
        } else if (ReturnCode == CP_GET_ERROR) {
            Index = 0;
            continue;

        } else {                    // if (ReturnCode == CP_GET_SUCCESS)
            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 {

                Index = 0;
            }
        }
    } while ( Index < 4 );

    // 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;
    }

    BdDebuggerNotPresent = 0;
    return BD_PACKET_RECEIVED;
}

static
void
BdSendControlPacket(
    IN UINT16 PacketType,
    IN UINT32 PacketId
    )

//++
//
//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;
    BdSendString((PUINT8)&PacketHeader, sizeof(KD_PACKET));
    return;
}

static
UINT32
BdReceivePacket(
    IN UINT32 PacketType,
    OUT PSTRING MessageHeader,
    OUT PSTRING MessageData,
    OUT PUINT32 DataLength
    )

//++
//
//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 BdrintString, 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.
//
//Return Value:
//
//  BD_PACKET_RESEND - if resend is required.
//  BD_PAKCET_TIMEOUT - if timeout.
//  BD_PACKET_RECEIVED - if packet received.
//
//--  

{

    UINT8 Input;
    UINT32 MessageLength;
    KD_PACKET PacketHeader;
    UINT32 ReturnCode;
    UINT32 Checksum;

WaitForPacketLeader:

    //
    // Read Packet Leader
    //

    ReturnCode = BdReceivePacketLeader(PacketType, &PacketHeader.PacketLeader);

    //
    // If we can successfully read packet leader, it has high possibility that
    // kernel debugger is alive.  So reset count.
    //

    if (ReturnCode != BD_PACKET_TIMEOUT) {
        BdNumberRetries = BdRetryCount;
    }
    if (ReturnCode != BD_PACKET_RECEIVED) {
        return ReturnCode;
    }

    //
    // Read packet type.
    //

    ReturnCode = BdReceiveString((PUINT8)&PacketHeader.PacketType,
                                 sizeof(PacketHeader.PacketType));

    if (ReturnCode == CP_GET_NODATA) {
        return BD_PACKET_TIMEOUT;

    } else if (ReturnCode == CP_GET_ERROR) {
        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 BD_PACKET_RESEND;
    }

    //
    // Read data length.
    //

    ReturnCode = BdReceiveString((PUINT8)&PacketHeader.ByteCount,
                                 sizeof(PacketHeader.ByteCount));

    if (ReturnCode == CP_GET_NODATA) {
        return BD_PACKET_TIMEOUT;
    } else if (ReturnCode == CP_GET_ERROR) {
        if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) {
            goto WaitForPacketLeader;
        } else {
            goto SendResendPacket;
        }
    }

    //
    // Read Packet Id.
    //

    ReturnCode = BdReceiveString((PUINT8)&PacketHeader.PacketId,
                                 sizeof(PacketHeader.PacketId));

    if (ReturnCode == CP_GET_NODATA) {
        return BD_PACKET_TIMEOUT;
    } else if (ReturnCode == CP_GET_ERROR) {
        if (PacketHeader.PacketLeader == CONTROL_PACKET_LEADER) {
            goto WaitForPacketLeader;
        } else {
            goto SendResendPacket;
        }
    }

    //
    // Read packet checksum.
    //

    ReturnCode = BdReceiveString((PUINT8)&PacketHeader.Checksum,
                                 sizeof(PacketHeader.Checksum));

    if (ReturnCode == CP_GET_NODATA) {
        return BD_PACKET_TIMEOUT;

    } else if (ReturnCode == CP_GET_ERROR) {
        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 !=
                (BdNextPacketIdToSend & ~SYNC_PACKET_ID))  {
                goto WaitForPacketLeader;

            } else if (PacketType == PACKET_TYPE_KD_ACKNOWLEDGE) {
                BdNextPacketIdToSend ^= 1;
                return BD_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.
            //

            BdNextPacketIdToSend = INITIAL_PACKET_ID;
            BdPacketIdExpected = INITIAL_PACKET_ID;
            BdSendControlPacket(PACKET_TYPE_KD_RESET, 0L);
            return BD_PACKET_RESEND;

        } else if (PacketHeader.PacketType == PACKET_TYPE_KD_RESEND) {
            return BD_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 == BdPacketIdExpected) {
            BdSendControlPacket(PACKET_TYPE_KD_RESEND, 0L);
            BdNextPacketIdToSend ^= 1;
            return BD_PACKET_RECEIVED;

        } else {
            BdSendControlPacket(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 = BdReceiveString(MessageHeader->Buffer, MessageLength);
    if (ReturnCode != CP_GET_SUCCESS) {
        goto SendResendPacket;
    }

    MessageHeader->Length = (UINT16)MessageLength;

    //
    // Read the message data.
    //

    ReturnCode = BdReceiveString(MessageData->Buffer, *DataLength);
    if (ReturnCode != CP_GET_SUCCESS) {
        goto SendResendPacket;
    }

    MessageData->Length = (UINT16)*DataLength;

    //
    // Read packet trailing byte
    //

    ReturnCode = BdPortGetByte(&Input);
    if (ReturnCode != CP_GET_SUCCESS || Input != PACKET_TRAILING_BYTE) {
        goto SendResendPacket;
    }

    //
    // Check PacketType is what we are waiting for.
    //

    if (PacketType != PacketHeader.PacketType) {
        BdSendControlPacket(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 != BdPacketIdExpected) {
            BdSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE,
                                 PacketHeader.PacketId
                                 );
            goto WaitForPacketLeader;
        }

    } else {
        goto SendResendPacket;
    }

    //
    // Check checksum is valid.
    //

    Checksum = BdComputeChecksum(MessageHeader->Buffer,
                                 MessageHeader->Length);


    Checksum += BdComputeChecksum(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.
    //

    BdSendControlPacket(PACKET_TYPE_KD_ACKNOWLEDGE,
                        PacketHeader.PacketId);

    //
    // We have successfully received the packet so update the
    // packet control variables and return success.
    //

    BdPacketIdExpected ^= 1;
    return BD_PACKET_RECEIVED;

SendResendPacket:
    BdSendControlPacket(PACKET_TYPE_KD_RESEND, 0L);
    goto WaitForPacketLeader;
}

static
void
BdSendPacket(
    IN UINT32 PacketType,
    IN PSTRING MessageHeader,
    IN PSTRING MessageData
    )

//++
//
//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.
//
//Return Value:
//
//  None.
//
//--  

{

    KD_PACKET PacketHeader;
    UINT32 MessageDataLength;
    UINT32 ReturnCode;
    PDBGKD_DEBUG_IO DebugIo;

    if (MessageData != NULL) {
        MessageDataLength = MessageData->Length;
        PacketHeader.Checksum = BdComputeChecksum(MessageData->Buffer,
                                                  MessageData->Length);

    } else {
        MessageDataLength = 0;
        PacketHeader.Checksum = 0;
    }

    PacketHeader.Checksum += BdComputeChecksum(MessageHeader->Buffer,
                                               MessageHeader->Length);

    //
    // Initialize and send the packet header.
    //

    PacketHeader.PacketLeader = PACKET_LEADER;
    PacketHeader.ByteCount = (UINT16)(MessageHeader->Length + MessageDataLength);
    PacketHeader.PacketType = (UINT16)PacketType;
    BdNumberRetries = BdRetryCount;
    do {
        if (BdNumberRetries == 0) {

            //
            // If the packet is not for reporting exception, we give up
            // and declare debugger not present.
            //

            if (PacketType == PACKET_TYPE_KD_DEBUG_IO) {
                DebugIo = (PDBGKD_DEBUG_IO)MessageHeader->Buffer;
                if (DebugIo->ApiNumber == DbgKdPrintStringApi) {
                    BdDebuggerNotPresent = 1;
                    BdNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID;
                    BdPacketIdExpected = INITIAL_PACKET_ID;
                    return;
                }
            }
        }

        //
        // Setting PacketId has to be in the do loop in case Packet Id was
        // reset.
        //

        PacketHeader.PacketId = BdNextPacketIdToSend;
        BdSendString((PUINT8)&PacketHeader, sizeof(KD_PACKET));

        //
        // Output message header.
        //

        BdSendString(MessageHeader->Buffer, MessageHeader->Length);

        //
        // Output message data.
        //

        if ( MessageDataLength ) {
            BdSendString(MessageData->Buffer, MessageData->Length);
        }

        //
        // Output a packet trailing byte
        //

        BdPortPutByte(PACKET_TRAILING_BYTE);

        //
        // Wait for the Ack Packet
        //

        ReturnCode = BdReceivePacket(PACKET_TYPE_KD_ACKNOWLEDGE,
                                     NULL,
                                     NULL,
                                     NULL);

        if (ReturnCode == BD_PACKET_TIMEOUT) {
            BdNumberRetries--;
        }

    } while (ReturnCode != BD_PACKET_RECEIVED);

    //
    // Reset Sync bit in packet id.  The packet we sent may have Sync bit set
    //

    BdNextPacketIdToSend &= ~SYNC_PACKET_ID;

    //
    // Since we are able to talk to debugger, the retrycount is set to
    // maximum value.
    //

    BdRetryCount = BD_MAXIMUM_RETRIES;
}

static
UINT32
BdReceiveString(
    OUT PUINT8 Destination,
    IN UINT32 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.
//
//Return Value:
//
//  CP_GET_SUCCESS is returned if string is successfully read from the
//      kernel debugger line.
//  CP_GET_ERROR is returned if error encountered during reading.
//  CP_GET_NODATA is returned if timeout.
//
//--  

{

    UINT8 Input;
    UINT32 ReturnCode;

    //
    // Read bytes until either an error is encountered or the entire string
    // has been read.
    //
    while (Length > 0) {
        ReturnCode = BdPortGetByte(&Input);
        if (ReturnCode != CP_GET_SUCCESS) {
            return ReturnCode;
        } else {
            *Destination++ = Input;
            Length -= 1;
        }
    }

    return CP_GET_SUCCESS;
}

static
void
BdSendString(
    IN PUINT8 Source,
    IN UINT32 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.
//
//--  

{

    UINT8 Output;

    //
    // Write bytes to the kernel debugger port.
    //

    while (Length > 0) {
        Output = *Source++;
        BdPortPutByte(Output);
        Length -= 1;
    }

    return;
}

void BdPrintString(char *Output, UINT Length)

//++
//
//Routine Description:
//
//  This routine prints a string.
//
//Arguments:
//
//  Output - Supplies a pointer to a string descriptor for the output string.
//
//--  

{

    STRING MessageData;
    STRING MessageHeader;
    DBGKD_DEBUG_IO DebugIo;

    if (BdDebuggerNotPresent) {
        return;
    }

    // If the total message length is greater than the maximum packet size,
    // then truncate the output string.
    //
    if ((sizeof(DBGKD_DEBUG_IO) + Length) > PACKET_MAX_SIZE) {
        Length = PACKET_MAX_SIZE - sizeof(DBGKD_DEBUG_IO);
    }

    // Construct the print string message and message descriptor.
    //
    DebugIo.ApiNumber = DbgKdPrintStringApi;
    DebugIo.ProcessorLevel = 0;
    DebugIo.Processor = 0;
    DebugIo.LengthOfString = Length;
    MessageHeader.Length = sizeof(DBGKD_DEBUG_IO);
    MessageHeader.Buffer = (PUINT8)&DebugIo;

    // Construct the print string data and data descriptor.
    //
    MessageData.Length = (UINT16)Length;
    MessageData.Buffer = (PUINT8)Output;

    // Send packet to the kernel debugger on the host machine.
    //
    BdSendPacket(PACKET_TYPE_KD_DEBUG_IO, &MessageHeader, &MessageData);
}

bool BdInitDebugger(bool present)
{
    // Configure debugger state based on presence of transport layer.
    //
    if (present) {
        BdDebuggerNotPresent = 0;

        // Initialize the ID for the NEXT packet to send and the Expect
        // ID of next incoming packet.
        //
        BdNextPacketIdToSend = INITIAL_PACKET_ID | SYNC_PACKET_ID;
        BdPacketIdExpected = INITIAL_PACKET_ID;

        // Number of retries and the retry count.
        //
        BdNumberRetries = 5;
        BdRetryCount = 5;

        return 1;
    }

    BdDebuggerNotPresent = 1;
    return 0;
}
