/*++

Copyright (c) 2000-2001  Microsoft Corporation

Module Name:

    kdcpuapi.c

Abstract:

    This module implements CPU specific remote debug APIs.

Author:

    David N. Cutler (davec) 14-May-2000

Revision History:

--*/

#include "kdp.h"
#include <stdio.h>

#pragma alloc_text(PAGEKD, KdpSetContextState)
#pragma alloc_text(PAGEKD, KdpSetStateChange)
#pragma alloc_text(PAGEKD, KdpGetStateChange)
#pragma alloc_text(PAGEKD, KdpSysReadControlSpace)
#pragma alloc_text(PAGEKD, KdpSysWriteControlSpace)
#pragma alloc_text(PAGEKD, KdpReadIoSpace)
#pragma alloc_text(PAGEKD, KdpWriteIoSpace)
#pragma alloc_text(PAGEKD, KdpReadMachineSpecificRegister)
#pragma alloc_text(PAGEKD, KdpWriteMachineSpecificRegister)

VOID
KdpSetContextState (
    IN OUT PDBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange,
    IN PCONTEXT ContextRecord
    )
{

    PKPRCB Prcb;

    //
    // Copy special registers for the AMD64.
    //

    Prcb = KeGetCurrentPrcb();
    WaitStateChange->ControlReport.Dr6 =
                            Prcb->ProcessorState.SpecialRegisters.KernelDr6;

    WaitStateChange->ControlReport.Dr7 =
                            Prcb->ProcessorState.SpecialRegisters.KernelDr7;

    WaitStateChange->ControlReport.SegCs = ContextRecord->SegCs;
    WaitStateChange->ControlReport.SegDs = ContextRecord->SegDs;
    WaitStateChange->ControlReport.SegEs = ContextRecord->SegEs;
    WaitStateChange->ControlReport.SegFs = ContextRecord->SegFs;
    WaitStateChange->ControlReport.EFlags = ContextRecord->EFlags;
    WaitStateChange->ControlReport.ReportFlags = AMD64_REPORT_INCLUDES_SEGS;

    // If the current code segment is a known flat code
    // segment let the debugger know so that it doesn't
    // have to retrieve the descriptor.
    if (ContextRecord->SegCs == KGDT64_R0_CODE ||
        ContextRecord->SegCs == KGDT64_R3_CODE + 3) {
        WaitStateChange->ControlReport.ReportFlags |= AMD64_REPORT_STANDARD_CS;
    }
}

VOID
KdpSetStateChange (
    IN OUT PDBGKD_ANY_WAIT_STATE_CHANGE WaitStateChange,
    IN PEXCEPTION_RECORD ExceptionRecord,
    IN PCONTEXT ContextRecord,
    IN BOOLEAN SecondChance
    )

/*++

Routine Description:

    Fill in the Wait_State_Change message record.

Arguments:

    WaitStateChange - Supplies pointer to record to fill in

    ExceptionRecord - Supplies a pointer to an exception record.

    ContextRecord - Supplies a pointer to a context record.

    SecondChance - Supplies a boolean value that determines whether this is
        the first or second chance for the exception.

Return Value:

    None.

--*/

{
    KdpSetContextState(WaitStateChange, ContextRecord);
}

VOID
KdpGetStateChange (
    IN PDBGKD_MANIPULATE_STATE64 ManipulateState,
    IN PCONTEXT ContextRecord
    )

/*++

Routine Description:

    Extract continuation control data from manipulate state message.

Arguments:

    ManipulateState - Supplies pointer to manipulate state packet.

    ContextRecord - Supplies a pointer to a context record.

Return Value:

    None.

--*/

{

    ULONG Number;
    PKPRCB Prcb;

    //
    // If the status of the manipulate state message was successful, then
    // extract the continuation control information.
    //

    if (NT_SUCCESS(ManipulateState->u.Continue2.ContinueStatus) != FALSE) {

        //
        // Set or clear the TF flag in the EFLAGS field of the context record.
        //

        if (ManipulateState->u.Continue2.ControlSet.TraceFlag != FALSE) {
            ContextRecord->EFlags |= EFLAGS_TF_MASK;

        } else {
            ContextRecord->EFlags &= ~EFLAGS_TF_MASK;

        }

        //
        // Clear DR6 and set the specified DR7 value for each of the processors.
        //

        for (Number = 0; Number < (ULONG)KeNumberProcessors; Number += 1) {
            Prcb = KiProcessorBlock[Number];
            Prcb->ProcessorState.SpecialRegisters.KernelDr6 = 0;
            Prcb->ProcessorState.SpecialRegisters.KernelDr7 =
                                ManipulateState->u.Continue2.ControlSet.Dr7;
        }
    }

    return;
}

NTSTATUS
KdpSysReadControlSpace (
    ULONG Processor,
    ULONG64 Address,
    PVOID Buffer,
    ULONG Request,
    PULONG Actual
    )

/*++

Routine Description:

    This function reads implementation specific system data for the specified
    processor.

Arguments:

    Processor - Supplies the source processor number.

    Address - Supplies the type of data to read.

    Buffer - Supplies the address of the output buffer.

    Request - Supplies the requested number of bytes of data.

    Actual - Supplies a point to a variable that receives the actual number
        of bytes of data returned.

Return Value:

    NTSTATUS.

--*/

{

    PVOID Data;
    ULONG Length;
    PKPRCB Prcb;
    PVOID Source;

    //
    // If the specified processor number is greater than the number of
    // processors in the system or the specified processor is not in the
    // host configuration, then return an unsuccessful status.
    //

    *Actual = 0;
    if ((Processor >= (ULONG)KeNumberProcessors) ||
        (KiProcessorBlock[Processor] == NULL)) {
        return STATUS_UNSUCCESSFUL;
    }

    //
    // Case on address to determine what part of Control space is being read.
    //

    Prcb = KiProcessorBlock[Processor];
    switch (Address) {

        //
        // Read the address of the PCR for the specified processor.
        //

    case DEBUG_CONTROL_SPACE_PCR:
        Data = CONTAINING_RECORD(Prcb, KPCR, Prcb);
        Source = &Data;
        Length = sizeof(PVOID);
        break;

        //
        // Read the address of the PRCB for the specified processor.
        //

    case DEBUG_CONTROL_SPACE_PRCB:
        Source = &Prcb;
        Length = sizeof(PVOID);
        break;

        //
        // Read the address of the current thread for the specified
        // processor.
        //

    case DEBUG_CONTROL_SPACE_THREAD:
        Source = &Prcb->CurrentThread;
        Length = sizeof(PVOID);
        break;

        //
        // Read the special processor registers structure for the specified
        // processor.
        //

    case DEBUG_CONTROL_SPACE_KSPECIAL:
        Source = &Prcb->ProcessorState.SpecialRegisters;
        Length = sizeof(KSPECIAL_REGISTERS);
        break;

        //
        // Invalid information type.
        //

    default:
        return STATUS_UNSUCCESSFUL;

    }

    //
    // If the length of the data is greater than the request length, then
    // reduce the length to the requested length.
    //

    if (Length > Request) {
        Length = Request;
    }

    //
    // Move the data to the supplied buffer and return status dependent on
    // whether the entire data item can be moved.
    //

    return KdpCopyToPtr(Buffer, Source, Length, Actual);
}

NTSTATUS
KdpSysWriteControlSpace (
    ULONG Processor,
    ULONG64 Address,
    PVOID Buffer,
    ULONG Request,
    PULONG Actual
    )

/*++

Routine Description:

    This function write implementation specific system data for the specified
    processor.

Arguments:

    Processor - Supplies the source processor number.

    Address - Supplies the type of data to write.

    Buffer - Supplies the address of the input buffer.

    Request - Supplies the requested number of bytes of data.

    Actual - Supplies a point to a variable that receives the actual number
        of bytes of data written.

Return Value:

    NTSTATUS.

--*/

{

    PKPRCB Prcb;

    //
    // If the specified processor number is greater than the number of
    // processors in the system or the specified processor is not in the
    // host configuration, then return an unsuccessful status.
    //

    *Actual = 0;
    if ((Processor >= (ULONG)KeNumberProcessors) ||
        (KiProcessorBlock[Processor] == NULL)) {
        return STATUS_UNSUCCESSFUL;
    }

    //
    // Case on address to determine what part of control space is being writen.
    //

    Prcb = KiProcessorBlock[Processor];
    switch (Address) {
    
        //
        // Write the special processor registers structure for the specified
        // processor.
        //

    case DEBUG_CONTROL_SPACE_KSPECIAL:

        //
        // If the length of the data is greater than the request length, then
        // reduce the requested length to the length of the data.
        //

        if (Request > sizeof(KSPECIAL_REGISTERS)) {
            Request = sizeof(KSPECIAL_REGISTERS);
        }
    
        //
        // Move the data to the supplied buffer and return status dependent on
        // whether the entire data item can be moved.
        //

        return KdpCopyFromPtr(&Prcb->ProcessorState.SpecialRegisters,
                              Buffer,
                              Request,
                              Actual);

        //
        // Invalid information type.
        //

    default:
        return STATUS_UNSUCCESSFUL;
    }
}

NTSTATUS
KdpSysReadIoSpace(
    INTERFACE_TYPE InterfaceType,
    ULONG BusNumber,
    ULONG AddressSpace,
    ULONG64 Address,
    PVOID Buffer,
    ULONG Request,
    PULONG Actual
    )

/*++

Routine Description:

    Reads system I/O locations.

Arguments:

    InterfaceType - I/O interface type.

    BusNumber - Bus number.

    AddressSpace - Address space.

    Address - I/O address.

    Buffer - Data buffer.

    Request - Amount of data to move.

    Actual - Amount of data actually moved.

Return Value:

    NTSTATUS.

--*/

{

    NTSTATUS Status = STATUS_SUCCESS;

    if ((InterfaceType != Isa) || (BusNumber != 0) || (AddressSpace != 1)) {
        *Actual = 0;
        return STATUS_UNSUCCESSFUL;
    }

    //
    // Check Size and Alignment
    //

    switch (Request) {
        case 1:
            *(PUCHAR)Buffer = READ_PORT_UCHAR((PUCHAR)Address);
            *Actual = 1;
            break;

        case 2:
            if (Address & 1) {
                Status = STATUS_DATATYPE_MISALIGNMENT;

            } else {
                *(PUSHORT)Buffer = READ_PORT_USHORT((PUSHORT)Address);
                *Actual = 2;
            }

            break;

        case 4:
            if (Address & 3) {
                Status = STATUS_DATATYPE_MISALIGNMENT;

            } else {
                *(PULONG)Buffer = READ_PORT_ULONG((PULONG)Address);
                *Actual = 4;
            }

            break;

        default:
            Status = STATUS_INVALID_PARAMETER;
            *Actual = 0;
            break;
    }

    return Status;
}

NTSTATUS
KdpSysWriteIoSpace(
    INTERFACE_TYPE InterfaceType,
    ULONG BusNumber,
    ULONG AddressSpace,
    ULONG64 Address,
    PVOID Buffer,
    ULONG Request,
    PULONG Actual
    )

/*++

Routine Description:

    Writes system I/O locations.

Arguments:

    InterfaceType - I/O interface type.

    BusNumber - Bus number.

    AddressSpace - Address space.

    Address - I/O address.

    Buffer - Data buffer.

    Request - Amount of data to move.

    Actual - Amount of data actually moved.

Return Value:

    NTSTATUS.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    if ((InterfaceType != Isa) || (BusNumber != 0) || (AddressSpace != 1)) {
        *Actual = 0;
        return STATUS_UNSUCCESSFUL;
    }

    //
    // Check Size and Alignment
    //

    switch (Request) {
        case 1:
            WRITE_PORT_UCHAR((PUCHAR)Address, *(PUCHAR)Buffer);
            *Actual = 1;
            break;

        case 2:
            if (Address & 1) {
                Status = STATUS_DATATYPE_MISALIGNMENT;

            } else {
                WRITE_PORT_USHORT((PUSHORT)Address, *(PUSHORT)Buffer);
                *Actual = 2;
            }

            break;

        case 4:
            if (Address & 3) {
                Status = STATUS_DATATYPE_MISALIGNMENT;

            } else {
                WRITE_PORT_ULONG((PULONG)Address, *(PULONG)Buffer);
                *Actual = 4;
            }

            break;

        default:
            Status = STATUS_INVALID_PARAMETER;
            *Actual = 0;
            break;
    }

    return Status;
}

NTSTATUS
KdpSysReadMsr(
    ULONG Msr,
    PULONG64 Data
    )

/*++

Routine Description:

    Reads an MSR.

Arguments:

    Msr - MSR index.

    Data - Data buffer.

Return Value:

    NTSTATUS.

--*/

{

    NTSTATUS Status = STATUS_SUCCESS;

    try {
        *Data = ReadMSR(Msr);

    } except (EXCEPTION_EXECUTE_HANDLER) {
        *Data = 0;
        Status = STATUS_NO_SUCH_DEVICE;
    }

    return Status;
}

NTSTATUS
KdpSysWriteMsr(
    ULONG Msr,
    PULONG64 Data
    )

/*++

Routine Description:

    Writes an MSR.

Arguments:

    Msr - MSR index.

    Data - Data buffer.

Return Value:

    NTSTATUS.

--*/

{

    NTSTATUS Status = STATUS_SUCCESS;

    try {
        WriteMSR(Msr, *Data);

    } except (EXCEPTION_EXECUTE_HANDLER) {
        Status = STATUS_NO_SUCH_DEVICE;
    }

    return Status;
}