/*++
Copyright (c) 1998-2001  Microsoft Corporation

Module Name:

    kd1394io.c

Abstract:

    1394 Kernel Debugger DLL

Author:

    George Chrysanthakopoulos (georgioc) Nov-1999

Revision   History:
Date       Who       What
---------- --------- ------------------------------------------------------------
06/19/2001 pbinder   cleanup
--*/

#define _KD1394IO_C
#include "pch.h"
#undef _KD1394IO_C

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEKD, KdpComputeChecksum)
#pragma alloc_text(PAGEKD, KdpSendControlPacket)
#pragma alloc_text(PAGEKD, KdReceivePacket)
#pragma alloc_text(PAGEKD, KdSendPacket)
#endif

//
// KdpRetryCount controls the number of retries before we give
// up and assume kernel debugger is not present.
// KdpNumberRetries is the number of retries left.  Initially,
// it is set to 5 such that booting NT without debugger won't be
// delayed to long.
//
ULONG KdCompNumberRetries = 5;
ULONG KdCompRetryCount = 5;

ULONG KdPacketId = 0;

ULONG
KdpComputeChecksum(
    IN PUCHAR   Buffer,
    IN ULONG    Length
    )
/*++

Routine Description:

    This routine computes the checksum for the string passed in.

Arguments:

    Buffer - Supplies a pointer to the string.

    Length - Supplies the length of the string.

Return Value:

    A ULONG is return as the checksum for the input string.

--*/
{
    ULONG   Checksum = 0;

    while (Length > 0) {

        Checksum = Checksum + (ULONG)*Buffer++;
        Length--;
    }

    return(Checksum);
} // KdpComputeChecksum

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 (ARGUMENT_PRESENT((PVOID)(ULONG_PTR)PacketId)) {

        PacketHeader.PacketId = PacketId;
    }
    PacketHeader.PacketId = 0;
    PacketHeader.ByteCount = 0;
    PacketHeader.Checksum = 0;
    PacketHeader.PacketType = PacketType;

    // setup our send packet
    RtlZeroMemory(&Kd1394Data->SendPacket, sizeof(DEBUG_1394_SEND_PACKET));
    Kd1394Data->SendPacket.Length = 0;

    RtlCopyMemory( &Kd1394Data->SendPacket.PacketHeader[0],
                   &PacketHeader,
                   sizeof(KD_PACKET)
                   );

    Kd1394Data->SendPacket.TransferStatus = STATUS_PENDING;

    return;
} // KdpSendControlPacket

ULONG
KdReceivePacket(
    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.  = 2 = CP_GET_ERROR
    KDP_PACKET_TIMEOUT - if timeout.            = 1 = CP_GET_NODATA
    KDP_PACKET_RECEIVED - if packet received.   = 0 = CP_GET_SUCCESS

--*/
{
    UCHAR       Input;
    ULONG       MessageLength;
    KD_PACKET   PacketHeader;
    ULONG       ReturnCode;
    ULONG       Checksum;
    ULONG       Status;

// this dispatch gets called with PacketType != PACKET_TYPE_KD_POLL_BREAKIN for
// the number of times specified in KdCompNumberRetries (??). if we always timeout
// then we'll get called with PacketType == PACKET_TYPE_KD_POLL_BREAKIN

    // make sure our link is enabled..
    Dbg1394_EnablePhysicalAccess(Kd1394Data);

    //
    // Just check for breakin packet and return
    //
    if (PacketType == PACKET_TYPE_KD_POLL_BREAKIN) {

        // let's peak into our receive packet and see if it's a breakin
        if ((Kd1394Data->ReceivePacket.TransferStatus == STATUS_PENDING) &&
            (Kd1394Data->ReceivePacket.Packet[0] == BREAKIN_PACKET_BYTE)) {

            *KdDebuggerNotPresent = FALSE;
            SharedUserData->KdDebuggerEnabled |= 0x00000002;

            // we have a breakin packet
            Kd1394Data->ReceivePacket.TransferStatus = STATUS_SUCCESS;
            return(KDP_PACKET_RECEIVED);
        }
        else {

            return(KDP_PACKET_TIMEOUT);
        }
    }

WaitForPacketLeader:

    // read in our packet, if available...
    ReturnCode = Dbg1394_ReadPacket( Kd1394Data,
                                     &PacketHeader,
                                     MessageHeader,
                                     MessageData,
                                     TRUE
                                     );


    //
    // 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) {

        // see if it's a breakin packet...
        if ((PacketHeader.PacketLeader & 0xFF) == BREAKIN_PACKET_BYTE) {

            KdContext->KdpControlCPending = TRUE;
            return(KDP_PACKET_RESEND);
        }

        return(ReturnCode);
    }

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

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

    MessageData->Length = (USHORT)*DataLength;
    MessageHeader->Length = (USHORT)MessageLength;

    //
    // Check PacketType is what we are waiting for.
    //
    if (PacketType != PacketHeader.PacketType) {

        goto SendResendPacket;
    }

    //
    // Check checksum is valid.
    //
    Checksum = KdpComputeChecksum(MessageHeader->Buffer, MessageHeader->Length);
    Checksum += KdpComputeChecksum(MessageData->Buffer, MessageData->Length);

    if (Checksum != PacketHeader.Checksum) {

        goto SendResendPacket;
    }

    return(KDP_PACKET_RECEIVED);

SendResendPacket:

    KdpSendControlPacket(PACKET_TYPE_KD_RESEND, 0L);
    goto WaitForPacketLeader;
} // KdReceivePacket

void
KdSendPacket(
    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;
    ULONG                       ReturnCode;
    PDBGKD_DEBUG_IO             DebugIo;
    PDBGKD_WAIT_STATE_CHANGE64  StateChange;
    PDBGKD_FILE_IO              FileIo;
    BOOLEAN                     bException = FALSE;

    PacketHeader.Checksum = 0;

    if (ARGUMENT_PRESENT(MessageData)) {

        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;

    PacketHeader.PacketId = KdPacketId;

    KdPacketId++;

    KdCompNumberRetries = KdCompRetryCount;

    // setup our send packet
    RtlZeroMemory(&Kd1394Data->SendPacket, sizeof(DEBUG_1394_SEND_PACKET));
    Kd1394Data->SendPacket.Length = 0;

    // copy our packet header...
    RtlCopyMemory( &Kd1394Data->SendPacket.PacketHeader[0],
                   &PacketHeader,
                   sizeof(KD_PACKET)
                   );

    // setup our message header
    if (MessageHeader) {

        RtlCopyMemory( &Kd1394Data->SendPacket.Packet[0],
                       MessageHeader->Buffer,
                       MessageHeader->Length
                       );

        Kd1394Data->SendPacket.Length += MessageHeader->Length;
    }

    // setup our message data
    if (ARGUMENT_PRESENT(MessageData)) {

        RtlCopyMemory( &Kd1394Data->SendPacket.Packet[Kd1394Data->SendPacket.Length],
                       MessageData->Buffer,
                       MessageData->Length
                       );

        Kd1394Data->SendPacket.Length += MessageData->Length;
    }

    Kd1394Data->SendPacket.TransferStatus = STATUS_PENDING;

    do {

        // make sure our link is enabled..
        Dbg1394_EnablePhysicalAccess(Kd1394Data);

        if (KdCompNumberRetries == 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) {

                    *KdDebuggerNotPresent = TRUE;
                    SharedUserData->KdDebuggerEnabled &= ~0x00000002;

                    Kd1394Data->SendPacket.TransferStatus = STATUS_SUCCESS;
                    return;
                }
            }
            else if (PacketType == PACKET_TYPE_KD_STATE_CHANGE64) {

                StateChange = (PDBGKD_WAIT_STATE_CHANGE64)MessageHeader->Buffer;

                if (StateChange->NewState == DbgKdLoadSymbolsStateChange) {

                    *KdDebuggerNotPresent = TRUE;
                    SharedUserData->KdDebuggerEnabled &= ~0x00000002;

                    Kd1394Data->SendPacket.TransferStatus = STATUS_SUCCESS;
                    return;
                }
            }
            else if (PacketType == PACKET_TYPE_KD_FILE_IO) {
                
                FileIo = (PDBGKD_FILE_IO)MessageHeader->Buffer;

                if (FileIo->ApiNumber == DbgKdCreateFileApi) {

                    *KdDebuggerNotPresent = TRUE;
                    SharedUserData->KdDebuggerEnabled &= ~0x00000002;

                    Kd1394Data->SendPacket.TransferStatus = STATUS_SUCCESS;
                    return;
                }
            }
            else {

                bException = TRUE;
            }
        }

        ReturnCode = KDP_PACKET_TIMEOUT;

        {
            ULONG                   count = 0;
            volatile NTSTATUS       *pStatus;

            pStatus = &Kd1394Data->ReceivePacket.TransferStatus;

            //
            // now sit here and poll for a response from the target machine
            //
            do {

                // make sure our link is enabled..
                Dbg1394_EnablePhysicalAccess(Kd1394Data);

                //
                // while in this loop check if the host layed in a request.
                // If they did, mark it read and double buffer it
                //
                count++;
                if (Kd1394Data->SendPacket.TransferStatus != STATUS_PENDING) {

                    ReturnCode = KDP_PACKET_RECEIVED;
                    break;
                }

                if ((*pStatus == STATUS_PENDING) && (!bException)) {

                    ReturnCode = KDP_PACKET_RECEIVED;
                    break;
                }

            } while (count < TIMEOUT_COUNT);
        }

        if (ReturnCode == KDP_PACKET_TIMEOUT) {

            KdCompNumberRetries--;
        }

    } while (ReturnCode != KDP_PACKET_RECEIVED);

    //
    // Since we are able to talk to debugger, the retrycount is set to
    // maximum value.
    //
    KdCompRetryCount = KdContext->KdpDefaultRetries;

    return;
} // KdSendPacket