/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    api.c

Abstract:

    This module implements the boot bebugger platform independent remote APIs.

Author:

    Mark Lucovsky (markl) 31-Aug-1990

Revision History:

--*/

#include "bd.h"

VOID
BdGetVersion(
    IN PDBGKD_MANIPULATE_STATE64 m
    )

/*++

Routine Description:

    This function returns to the caller a general information packet
    that contains useful information to a debugger.  This packet is also
    used for a debugger to determine if the writebreakpointex and
    readbreakpointex apis are available.

Arguments:

    m - Supplies the state manipulation message.

Return Value:

    None.

--*/

{

    STRING messageHeader;

    messageHeader.Length = sizeof(*m);
    messageHeader.Buffer = (PCHAR)m;
    RtlZeroMemory(&m->u.GetVersion64, sizeof(m->u.GetVersion64));

    //
    // the current build number
    //
    // - 4 - tells the debugger this is a "special" OS - the boot loader.
    // The boot loader has a lot of special cases associated with it, like
    // the lack of the DebuggerDataBlock, lack of ntoskrnl, etc ...
    //

    m->u.GetVersion64.MinorVersion = (short)NtBuildNumber;
    m->u.GetVersion64.MajorVersion = 0x400 |
                                     (short)((NtBuildNumber >> 28) & 0xFFFFFFF);

    //
    // Kd protocol version number.
    //

    m->u.GetVersion64.ProtocolVersion = DBGKD_64BIT_PROTOCOL_VERSION2;
    m->u.GetVersion64.Flags = DBGKD_VERS_FLAG_DATA;

#if defined(_M_IX86)

    m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_I386;

#elif defined(_M_MRX000)

    m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_R4000;

#elif defined(_M_ALPHA)

    m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_ALPHA;

#elif defined(_M_PPC)

    m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_POWERPC;

#elif defined(_IA64_)

    m->u.GetVersion64.MachineType = IMAGE_FILE_MACHINE_IA64;
    m->u.GetVersion64.Flags |= DBGKD_VERS_FLAG_PTR64;

#else

#error( "unknown target machine" );

#endif

    m->u.GetVersion64.MaxPacketType = (UCHAR)(PACKET_TYPE_KD_FILE_IO + 1);;
    m->u.GetVersion64.MaxStateChange = (UCHAR)(DbgKdLoadSymbolsStateChange + 1);;
    m->u.GetVersion64.MaxManipulate = (UCHAR)(DbgKdSetBusDataApi + 1);


    //
    // address of the loader table
    //

    m->u.GetVersion64.PsLoadedModuleList = 0;
    m->u.GetVersion64.KernBase = 0;
    //m->u.GetVersion64.ThCallbackStack = 0;
    //m->u.GetVersion64.KiCallUserMode = 0;
    //m->u.GetVersion64.KeUserCallbackDispatcher = 0;
    //m->u.GetVersion64.NextCallback = 0;

#if defined(_X86_)

    //m->u.GetVersion64.FramePointer = 0;

#endif

    //m->u.GetVersion64.BreakpointWithStatus = 0;
    m->u.GetVersion64.DebuggerDataList = 0;

    //
    // the usual stuff
    //

    m->ReturnStatus = STATUS_SUCCESS;
    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &messageHeader,
                 NULL);

    return;
}

VOID
BdGetContext(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response of a get context state
    manipulation message.  Its function is to return the current
    context.

Arguments:

    m - Supplies the state manipulation message.

    AdditionalData - Supplies any additional data for the message.

    Context - Supplies the current context.

Return Value:

    None.

--*/

{

    PDBGKD_GET_CONTEXT a = &m->u.GetContext;
    STRING MessageHeader;

    m->ReturnStatus = STATUS_SUCCESS;
    AdditionalData->Length = sizeof(CONTEXT);
    BdCopyMemory(AdditionalData->Buffer, (PCHAR)Context, sizeof(CONTEXT));

    //
    // Send reply packet.
    //

    MessageHeader.Length = sizeof(*m);
    MessageHeader.Buffer = (PCHAR)m;
    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &MessageHeader,
                 AdditionalData);

    return;
}

VOID
BdSetContext(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response of a set context state
    manipulation message.  Its function is set the current
    context.

Arguments:

    m - Supplies the state manipulation message.

    AdditionalData - Supplies any additional data for the message.

    Context - Supplies the current context.

Return Value:

    None.

--*/

{

    PDBGKD_SET_CONTEXT a = &m->u.SetContext;
    STRING MessageHeader;

    m->ReturnStatus = STATUS_SUCCESS;
    BdCopyMemory((PCHAR)Context, AdditionalData->Buffer, sizeof(CONTEXT));

    //
    // Send reply packet.
    //

    MessageHeader.Length = sizeof(*m);
    MessageHeader.Buffer = (PCHAR)m;
    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &MessageHeader,
                 NULL);
}

VOID
BdReadVirtualMemory(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response to a read virtual memory 32-bit
    state manipulation message. Its function is to read virtual memory
    and return.

Arguments:

    m - Supplies a pointer to the state manipulation message.

    AdditionalData - Supplies a pointer to a descriptor for the data to read.

    Context - Supplies a pointer to the current context.

Return Value:

    None.

--*/

{

    ULONG Length;
    STRING MessageHeader;

    //
    // Trim the transfer count to fit in a single message.
    //

    Length = min(m->u.ReadMemory.TransferCount,
                 PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64));

    //
    // Move the data to the destination buffer.
    //

    AdditionalData->Length = (USHORT)BdMoveMemory((PCHAR)AdditionalData->Buffer,
                                                  (PCHAR)m->u.ReadMemory.TargetBaseAddress,
                                                  Length);

    //
    // If all the data is read, then return a success status. Otherwise,
    // return an unsuccessful status.
    //

    m->ReturnStatus = STATUS_SUCCESS;
    if (Length != AdditionalData->Length) {
        m->ReturnStatus = STATUS_UNSUCCESSFUL;
    }

    //
    // Set the actual number of bytes read, initialize the message header,
    // and send the reply packet to the host debugger.
    //

    m->u.ReadMemory.ActualBytesRead = AdditionalData->Length;
    MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
    MessageHeader.Buffer = (PCHAR)m;
    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                  &MessageHeader,
                  AdditionalData);

    return;
}

VOID
BdWriteVirtualMemory(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response of a write virtual memory 32-bit
    state manipulation message. Its function is to write virtual memory
    and return.

Arguments:

    m - Supplies a pointer to the state manipulation message.

    AdditionalData - Supplies a pointer to a descriptor for the data to write.

    Context - Supplies a pointer to the current context.

Return Value:

    None.

--*/

{

    ULONG Length;
    STRING MessageHeader;

    //
    // Move the data to the destination buffer.
    //

    Length = BdMoveMemory((PCHAR)m->u.WriteMemory.TargetBaseAddress,
                          (PCHAR)AdditionalData->Buffer,
                          AdditionalData->Length);

    //
    // If all the data is written, then return a success status. Otherwise,
    // return an unsuccessful status.
    //

    m->ReturnStatus = STATUS_SUCCESS;
    if (Length != AdditionalData->Length) {
        m->ReturnStatus = STATUS_UNSUCCESSFUL;
    }

    //
    // Set the actual number of bytes written, initialize the message header,
    // and send the reply packet to the host debugger.
    //

    m->u.WriteMemory.ActualBytesWritten = Length;
    MessageHeader.Length = sizeof(DBGKD_MANIPULATE_STATE64);
    MessageHeader.Buffer = (PCHAR)m;
    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &MessageHeader,
                 NULL);

    return;
}

VOID
BdWriteBreakpoint(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response of a write breakpoint state
    manipulation message.  Its function is to write a breakpoint
    and return a handle to the breakpoint.

Arguments:

    m - Supplies the state manipulation message.

    AdditionalData - Supplies any additional data for the message.

    Context - Supplies the current context.

Return Value:

    None.

--*/

{
    PDBGKD_WRITE_BREAKPOINT64 a = &m->u.WriteBreakPoint;
    STRING MessageHeader;


    a->BreakPointHandle = BdAddBreakpoint(a->BreakPointAddress);
    if (a->BreakPointHandle != 0) {
        m->ReturnStatus = STATUS_SUCCESS;

    } else {
        m->ReturnStatus = STATUS_UNSUCCESSFUL;
    }

    //
    // Send reply packet.
    //

    MessageHeader.Length = sizeof(*m);
    MessageHeader.Buffer = (PCHAR)m;
    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &MessageHeader,
                 NULL);

    return;
}

VOID
BdRestoreBreakpoint(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response of a restore breakpoint state
    manipulation message.  Its function is to restore a breakpoint
    using the specified handle.

Arguments:

    m - Supplies the state manipulation message.

    AdditionalData - Supplies any additional data for the message.

    Context - Supplies the current context.

Return Value:

    None.

--*/

{

    PDBGKD_RESTORE_BREAKPOINT a = &m->u.RestoreBreakPoint;
    STRING MessageHeader;

    if (BdDeleteBreakpoint(a->BreakPointHandle)) {
        m->ReturnStatus = STATUS_SUCCESS;

    } else {
        m->ReturnStatus = STATUS_UNSUCCESSFUL;
    }

    //
    // Send reply packet.
    //

    MessageHeader.Length = sizeof(*m);
    MessageHeader.Buffer = (PCHAR)m;
    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &MessageHeader,
                 NULL);
}

VOID
BdReadPhysicalMemory(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response to a read physical memory
    state manipulation message. Its function is to read physical memory
    and return.

Arguments:

    m - Supplies the state manipulation message.

    AdditionalData - Supplies any additional data for the message.

    Context - Supplies the current context.

Return Value:

    None.

--*/

{

    PDBGKD_READ_MEMORY64 a = &m->u.ReadMemory;
    ULONG Length;
    STRING MessageHeader;
    PVOID VirtualAddress;
    PHYSICAL_ADDRESS Source;
    PUCHAR Destination;
    USHORT NumberBytes;
    USHORT BytesLeft;

    //
    // Trim transfer count to fit in a single message.
    //

    Length = min(a->TransferCount,
                 PACKET_MAX_SIZE - sizeof(DBGKD_MANIPULATE_STATE64));

    //
    // Since the BdTranslatePhysicalAddress only maps in one physical
    // page at a time, we need to break the memory move up into smaller
    // moves which don't cross page boundaries.  There are two cases we
    // need to deal with.  The area to be moved may start and end on the
    // same page, or it may start and end on different pages (with an
    // arbitrary number of pages in between)
    //

    Source.QuadPart = (ULONG_PTR)a->TargetBaseAddress;
    Destination = AdditionalData->Buffer;
    BytesLeft = (USHORT)Length;
    if(PAGE_ALIGN((PUCHAR)a->TargetBaseAddress) ==
       PAGE_ALIGN((PUCHAR)(a->TargetBaseAddress)+Length)) {

        //
        // Memory move starts and ends on the same page.
        //

        VirtualAddress=BdTranslatePhysicalAddress(Source);
        if (VirtualAddress == NULL) {
            AdditionalData->Length = 0;

        } else {
            AdditionalData->Length = (USHORT)BdMoveMemory(Destination,
                                                          VirtualAddress,
                                                          BytesLeft);

            BytesLeft -= AdditionalData->Length;
        }

    } else {

        //
        // Memory move spans page boundaries
        //

        VirtualAddress=BdTranslatePhysicalAddress(Source);
        if (VirtualAddress == NULL) {
            AdditionalData->Length = 0;

        } else {
            NumberBytes = (USHORT)(PAGE_SIZE - BYTE_OFFSET(VirtualAddress));
            AdditionalData->Length = (USHORT)BdMoveMemory(Destination,
                                                          VirtualAddress,
                                                          NumberBytes);

            Source.LowPart += NumberBytes;
            Destination += NumberBytes;
            BytesLeft -= NumberBytes;
            while(BytesLeft > 0) {

                //
                // Transfer a full page or the last bit,
                // whichever is smaller.
                //

                VirtualAddress = BdTranslatePhysicalAddress(Source);
                if (VirtualAddress == NULL) {
                    break;

                } else {
                    NumberBytes = (USHORT) ((PAGE_SIZE < BytesLeft) ? PAGE_SIZE : BytesLeft);
                    AdditionalData->Length += (USHORT)BdMoveMemory(
                                                    Destination,
                                                    VirtualAddress,
                                                    NumberBytes);

                    Source.LowPart += NumberBytes;
                    Destination += NumberBytes;
                    BytesLeft -= NumberBytes;
                }
            }
        }
    }

    if (Length == AdditionalData->Length) {
        m->ReturnStatus = STATUS_SUCCESS;

    } else {
        m->ReturnStatus = STATUS_UNSUCCESSFUL;
    }

    a->ActualBytesRead = AdditionalData->Length;

    //
    // Send reply packet.
    //

    MessageHeader.Length = sizeof(*m);
    MessageHeader.Buffer = (PCHAR)m;
    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &MessageHeader,
                 AdditionalData);

    return;
}

VOID
BdWritePhysicalMemory(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response to a write physical memory
    state manipulation message. Its function is to write physical memory
    and return.

Arguments:

    m - Supplies the state manipulation message.

    AdditionalData - Supplies any additional data for the message.

    Context - Supplies the current context.

Return Value:

    None.

--*/

{

    PDBGKD_WRITE_MEMORY64 a = &m->u.WriteMemory;
    ULONG Length;
    STRING MessageHeader;
    PVOID VirtualAddress;
    PHYSICAL_ADDRESS Destination;
    PUCHAR Source;
    USHORT NumberBytes;
    USHORT BytesLeft;

    MessageHeader.Length = sizeof(*m);
    MessageHeader.Buffer = (PCHAR)m;

    //
    // Since the BdTranslatePhysicalAddress only maps in one physical
    // page at a time, we need to break the memory move up into smaller
    // moves which don't cross page boundaries.  There are two cases we
    // need to deal with.  The area to be moved may start and end on the
    // same page, or it may start and end on different pages (with an
    // arbitrary number of pages in between)
    //

    Destination.QuadPart = (ULONG_PTR)a->TargetBaseAddress;
    Source = AdditionalData->Buffer;
    BytesLeft = (USHORT) a->TransferCount;
    if(PAGE_ALIGN(Destination.QuadPart) ==
       PAGE_ALIGN(Destination.QuadPart+BytesLeft)) {

        //
        // Memory move starts and ends on the same page.
        //

        VirtualAddress=BdTranslatePhysicalAddress(Destination);
        Length = (USHORT)BdMoveMemory(VirtualAddress,
                                      Source,
                                      BytesLeft);

        BytesLeft -= (USHORT) Length;

    } else {

        //
        // Memory move spans page boundaries
        //

        VirtualAddress=BdTranslatePhysicalAddress(Destination);
        NumberBytes = (USHORT) (PAGE_SIZE - BYTE_OFFSET(VirtualAddress));
        Length = (USHORT)BdMoveMemory(VirtualAddress,
                                      Source,
                                      NumberBytes);

        Source += NumberBytes;
        Destination.LowPart += NumberBytes;
        BytesLeft -= NumberBytes;
        while(BytesLeft > 0) {

            //
            // Transfer a full page or the last bit, whichever is smaller.
            //

            VirtualAddress = BdTranslatePhysicalAddress(Destination);
            NumberBytes = (USHORT) ((PAGE_SIZE < BytesLeft) ? PAGE_SIZE : BytesLeft);
            Length += (USHORT)BdMoveMemory(VirtualAddress,
                                           Source,
                                           NumberBytes);

            Source += NumberBytes;
            Destination.LowPart += NumberBytes;
            BytesLeft -= NumberBytes;
        }
    }


    if (Length == AdditionalData->Length) {
        m->ReturnStatus = STATUS_SUCCESS;

    } else {
        m->ReturnStatus = STATUS_UNSUCCESSFUL;
    }

    a->ActualBytesWritten = Length;
    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &MessageHeader,
                 NULL);

    return;
}

NTSTATUS
BdWriteBreakPointEx(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response of a write breakpoint state 'ex'
    manipulation message.  Its function is to clear breakpoints, write
    new breakpoints, and continue the target system.  The clearing of
    breakpoints is conditional based on the presence of breakpoint handles.
    The setting of breakpoints is conditional based on the presence of
    valid, non-zero, addresses.  The continueing of the target system
    is conditional based on a non-zero continuestatus.

    This api allows a debugger to clear breakpoints, add new breakpoint,
    and continue the target system all in one api packet.  This reduces the
    amount of traffic across the wire and greatly improves source stepping.


Arguments:

    m - Supplies the state manipulation message.

    AdditionalData - Supplies any additional data for the message.

    Context - Supplies the current context.

Return Value:

    None.

--*/

{

    PDBGKD_BREAKPOINTEX         a = &m->u.BreakPointEx;
    PDBGKD_WRITE_BREAKPOINT64   b;
    STRING                      MessageHeader;
    ULONG                       i;
    DBGKD_WRITE_BREAKPOINT64    BpBuf[BREAKPOINT_TABLE_SIZE];


    MessageHeader.Length = sizeof(*m);
    MessageHeader.Buffer = (PCHAR)m;

    //
    // verify that the packet size is correct
    //

    if (AdditionalData->Length !=
                         a->BreakPointCount*sizeof(DBGKD_WRITE_BREAKPOINT64)) {
        m->ReturnStatus = STATUS_UNSUCCESSFUL;
        BdSendPacket(
                      PACKET_TYPE_KD_STATE_MANIPULATE,
                      &MessageHeader,
                      AdditionalData
                      );
        return m->ReturnStatus;
    }

    BdMoveMemory((PUCHAR)BpBuf,
                  AdditionalData->Buffer,
                  a->BreakPointCount*sizeof(DBGKD_WRITE_BREAKPOINT64));

    //
    // assume success
    //
    m->ReturnStatus = STATUS_SUCCESS;

    //
    // loop thru the breakpoint handles passed in from the debugger and
    // clear any breakpoint that has a non-zero handle
    //

    b = BpBuf;
    for (i=0; i<a->BreakPointCount; i++,b++) {
        if (b->BreakPointHandle) {
            if (!BdDeleteBreakpoint(b->BreakPointHandle)) {
                m->ReturnStatus = STATUS_UNSUCCESSFUL;
            }

            b->BreakPointHandle = 0;
        }
    }

    //
    // loop thru the breakpoint addesses passed in from the debugger and
    // add any new breakpoints that have a non-zero address
    //

    b = BpBuf;
    for (i=0; i<a->BreakPointCount; i++,b++) {
        if (b->BreakPointAddress) {
            b->BreakPointHandle = BdAddBreakpoint( b->BreakPointAddress );
            if (!b->BreakPointHandle) {
                m->ReturnStatus = STATUS_UNSUCCESSFUL;
            }
        }
    }

    //
    // send back our response
    //

    BdMoveMemory(AdditionalData->Buffer,
                 (PUCHAR)BpBuf,
                 a->BreakPointCount*sizeof(DBGKD_WRITE_BREAKPOINT64));

    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &MessageHeader,
                 AdditionalData);

    //
    // return the caller's continue status value.  if this is a non-zero
    // value the system is continued using this value as the continuestatus.
    //

    return a->ContinueStatus;
}

VOID
BdRestoreBreakPointEx(
    IN PDBGKD_MANIPULATE_STATE64 m,
    IN PSTRING AdditionalData,
    IN PCONTEXT Context
    )

/*++

Routine Description:

    This function is called in response of a restore breakpoint state 'ex'
    manipulation message.  Its function is to clear a list of breakpoints.

Arguments:

    m - Supplies the state manipulation message.

    AdditionalData - Supplies any additional data for the message.

    Context - Supplies the current context.

Return Value:

    None.

--*/

{

    PDBGKD_BREAKPOINTEX       a = &m->u.BreakPointEx;
    PDBGKD_RESTORE_BREAKPOINT b;
    STRING                    MessageHeader;
    ULONG                     i;
    DBGKD_RESTORE_BREAKPOINT  BpBuf[BREAKPOINT_TABLE_SIZE];


    MessageHeader.Length = sizeof(*m);
    MessageHeader.Buffer = (PCHAR)m;

    //
    // verify that the packet size is correct
    //

    if (AdditionalData->Length !=
                       a->BreakPointCount*sizeof(DBGKD_RESTORE_BREAKPOINT)) {
        m->ReturnStatus = STATUS_UNSUCCESSFUL;
        BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                     &MessageHeader,
                     AdditionalData);

        return;
    }

    BdMoveMemory((PUCHAR)BpBuf,
                  AdditionalData->Buffer,
                  a->BreakPointCount*sizeof(DBGKD_RESTORE_BREAKPOINT));

    //
    // assume success
    //

    m->ReturnStatus = STATUS_SUCCESS;

    //
    // loop thru the breakpoint handles passed in from the debugger and
    // clear any breakpoint that has a non-zero handle
    //

    b = BpBuf;
    for (i=0; i<a->BreakPointCount; i++,b++) {
        if (!BdDeleteBreakpoint(b->BreakPointHandle)) {
            m->ReturnStatus = STATUS_UNSUCCESSFUL;
        }
    }

    //
    // send back our response
    //

    BdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE,
                 &MessageHeader,
                 AdditionalData);

    return;
}