/*++

Copyright (c) 1998  Digital Equipment Corporation

Module Name:

   debugsup.c

Abstract:

    This module contains routines which provide support for the
    kernel debugger on 64-bit Alpha systems.

Author:

    David N. Cutler (davec) 24-Feb-1998

Revision History:

--*/

#include "mi.h"

PHARDWARE_PTE
MiCheckAddress (
    IN PVOID VirtualAddress,
    OUT PVOID *AccessAddress
    )

/*++

Routine Description:


    This function checks the specified virtual address for accessibility
    and returns a pointer to the hardware PTE that maps the virtual address
    and a 43-bit superpage address at which the address can be accessed.

Arguments:

    VirtualAddress - Supplies the virtual address to check.

    AccessAddress - Supplies a pointer to a variable that receives the
         43-bit superpage address at which the address can be accessed.

Return Value:

    A value of NULL is returned if the address is invalid or not accessible.
    Otherwise, a pointer to the PTE that maps the specified virtual address
    is returned as the function value.

--*/

{

    ULONG Index;
    PHARDWARE_PTE Pdr1;
    PHARDWARE_PTE Pdr2;
    PHARDWARE_PTE Ptp;

    //
    // If the high order 21 bits of the virtual address are not all 1's or
    // 0's, then the address is invalid. Otherwise, attempt to tranverse
    // the page directory hierarchy.
    //

    if ((ULONG_PTR)(((LONG_PTR)VirtualAddress >> 43) + 1) > 1) {
        return NULL;
    }

    //
    // Compute the 43-bit superpage address of the level one page
    // directory page and check to see if the level two page directory
    // page is valid.
    //

    Pdr1 = (PHARDWARE_PTE)(KSEG43_BASE | (((PHARDWARE_PTE)PDE_SELFMAP)->PageFrameNumber << PAGE_SHIFT));
    Index = (ULONG)(((ULONG_PTR)VirtualAddress >> PDI1_SHIFT) & PDI_MASK);
    if (Pdr1[Index].Valid == 0) {
        return NULL;
    }

    //
    // Compute the 43-bit superpage address of the level two page
    // directory page and check to see if the page table page is
    // valid.
    //

    Pdr2 = (PHARDWARE_PTE)(KSEG43_BASE | (Pdr1[Index].PageFrameNumber << PAGE_SHIFT));
    Index = (ULONG)(((ULONG_PTR)VirtualAddress >> PDI2_SHIFT) & PDI_MASK);
    if (Pdr2[Index].Valid == 0) {
        return NULL;
    }

    //
    // Compute the 43-bit superpage address of the page table page and
    // check to see if the data page is valid.

    Ptp = (PHARDWARE_PTE)(KSEG43_BASE | (Pdr2[Index].PageFrameNumber << PAGE_SHIFT));
    Index = (ULONG)(((ULONG_PTR)VirtualAddress >> PTI_SHIFT) & PDI_MASK);
    if (Ptp[Index].Valid == 0) {
        return NULL;
    }

    //
    // Compute the 43-bit superpage address of the data page and return the
    // address of the PTE that maps the data page as the function value.
    //

    *AccessAddress = (PVOID)(KSEG43_BASE |
                                (Ptp[Index].PageFrameNumber << PAGE_SHIFT) |
                                (ULONG_PTR)VirtualAddress & (PAGE_SIZE - 1));

    return &Ptp[Index];
}

PVOID
MmDbgReadCheck (
    IN PVOID VirtualAddress
    )

/*++

Routine Description:

    This routine returns the 43-bit superpage address which corresponds
    to the specified virtual address if the specified virtual address
    is readable.

    N.B. This function should only be called at IRQL DISPATCH_LEVEL or
         above.

Arguments:

    VirtualAddress - Supplies the virtual address to check.

Return Value:

    If the specified virtual address is invalid or not readable, then a
    value of NULL is returned. Otherwise, the 43-bit superpage address
    which corresponds to the specified virtual address is returned.

--*/

{

    PVOID AccessAddress;

    //
    // If the address is within the 32- or 43-bit superpage regions, then
    // the address is returned as the function value.
    //

    if (MI_IS_PHYSICAL_ADDRESS(VirtualAddress)) {
        return VirtualAddress;
    }

    //
    // Check if the specified virtual address if accessible. If the virtual
    // address is accessible, then return the 43-bit superpage address which
    // corresponds to the virtual address. Otherwise, return NULL.
    //

    if (MiCheckAddress(VirtualAddress, &AccessAddress) == NULL) {
        return NULL;

    } else {
        return AccessAddress;
    }
}

PVOID
MmDbgWriteCheck (
    IN PVOID VirtualAddress,
    IN PHARDWARE_PTE Opaque
    )

/*++

Routine Description:

    This routine returns the 43-bit superpage address which corresponds
    to the specified virtual address if the specified virtual address is
    writable.

    N.B. This function should only be called at IRQL DISPATCH_LEVEL or
         above.

Arguments:

    VirtualAddress - Supplies the virtual address to check.

    Opaque - Supplies a pointer to fill with an opaque value.

Return Value:

    If the specified virtual address is invalid or not writable, then a
    value of NULL is returned. Otherwise, the 43-bit superpage address
    which corresponds to the specified virtual address is returned.

--*/

{
    MMPTE PteContents;
    PVOID AccessAddress;
    PHARDWARE_PTE Pte;
    PMMPTE InputPte;

    InputPte = (PMMPTE)Opaque;

    InputPte->u.Long = 0;

    //
    // If the address is within the 32- or 43-bit superpage regions, then
    // the address is returned as the function value.
    //

    if (MI_IS_PHYSICAL_ADDRESS(VirtualAddress)) {
        return VirtualAddress;
    }

    //
    // Check if the specified virtual address if accessible. If the virtual
    // address is accessible, then return the 43-bit superpage address which
    // corresponds to the virtual address. Otherwise, return NULL.
    //
    //

    Pte = MiCheckAddress(VirtualAddress, &AccessAddress);
    if (Pte == NULL) {
        return NULL;
    }

    if (Pte->Write == 0) {

        //
        // PTE is not writable, make it so.
        //

        PteContents = *(PMMPTE)Pte;
    
        *InputPte = PteContents;
    
        //
        // Modify the PTE to ensure write permissions :
        // turn on write and turn off fault on write.
        //
    
        PteContents.u.Hard.Write = 1;
        PteContents.u.Hard.FaultOnWrite = 0;

        *(PMMPTE)Pte = PteContents;
    
        //
        // KeFillEntryTb is liable to IPI the other processors. This is
        // definitely NOT what we want as the other processors are frozen
        // in the debugger and we will deadlock if we try and IPI them.
        // Just flush the current processor instead.
        //
        // KeFillEntryTb ((PHARDWARE_PTE)PointerPte, VirtualAddress, TRUE);
        //

        KiFlushSingleTb(TRUE, VirtualAddress);
    }
    else {
        return AccessAddress;
    }

    return VirtualAddress;
}

VOID
MmDbgReleaseAddress (
    IN PVOID VirtualAddress,
    IN PHARDWARE_PTE Opaque
    )

/*++

Routine Description:

    i386/486 implementation specific:

    This routine resets the specified virtual address access permissions
    to its original state.

Arguments:

    VirtualAddress - Supplies the virtual address to check.

    Opaque - Supplies an opaque pointer.

Return Value:

    None.

Environment:

    Kernel mode IRQL at DISPATCH_LEVEL or greater.

--*/

{
    MMPTE TempPte;
    PMMPTE PointerPte;
    PMMPTE InputPte;

    InputPte = (PMMPTE)Opaque;

    ASSERT (MmIsAddressValid (VirtualAddress));

    if (InputPte->u.Long != 0) {

        PointerPte = MiGetPteAddress (VirtualAddress);
    
        TempPte = *InputPte;

        *PointerPte = TempPte;
    
        //
        // KeFillEntryTb is liable to IPI the other processors. This is
        // definitely NOT what we want as the other processors are frozen
        // in the debugger and we will deadlock if we try and IPI them.
        // Just flush the current processor instead.
        //
        // KeFillEntryTb ((PHARDWARE_PTE)PointerPte, VirtualAddress, TRUE);
        //

        KiFlushSingleTb(TRUE, VirtualAddress);
    }

    return;
}

PVOID64
MmDbgReadCheck64 (
    IN PVOID64 VirtualAddress
    )

/*++

Routine Description:

    This routine returns the 43-bit superpage address which corresponds
    to the specified virtual address if the specified virtual address
    is readable.

    N.B. This function should only be called at IRQL DISPATCH_LEVEL or
         above.

Arguments:

    VirtualAddress - Supplies the virtual address to check.

Return Value:

    If the specified virtual address is invalid or not readable, then a
    value of NULL is returned. Otherwise, the 43-bit superpage address
    which corresponds to the specified virtual address is returned.

--*/

{

    return MmDbgReadCheck(VirtualAddress);
}

PVOID64
MmDbgWriteCheck64 (
    IN PVOID64 VirtualAddress
    )

/*++

Routine Description:

    This routine returns the 43-bit superpage address which corresponds
    to the specified virtual address if the specified virtual address is
    writable.

    N.B. This function should only be called at IRQL DISPATCH_LEVEL or
         above.

Arguments:

    VirtualAddress - Supplies the virtual address to check.

Return Value:

    If the specified virtual address is invalid or not writable, then a
    value of NULL is returned. Otherwise, the 43-bit superpage address
    which corresponds to the specified virtual address is returned.

--*/

{
    HARDWARE_PTE Opaque;

    return MmDbgWriteCheck(VirtualAddress, &Opaque);
}

PVOID64
MmDbgTranslatePhysicalAddress64 (
    IN PHYSICAL_ADDRESS PhysicalAddress
    )

/*++

Routine Description:

    ALPHA implementation specific:

    The Alpha processor provides a direct-mapped address space called
    the superpage.  The entire physical address space can be
    addressed via the superpage.  This routine translates a physical
    address to its corresponding superpage address.  Unfortunately,
    the base superpage address is processor-dependent.  Therefore, we
    have to compute it based on the processor level.  As new processors are
    released, this routine will need to be updated.

    This routine does not use PTEs.

Arguments:

    PhysicalAddress - Supplies the physical address to translate.

Return Value:

    The virtual (superpage) address which corresponds to the physical address.

Environment:

    Kernel mode IRQL at DISPATCH_LEVEL or greater.

--*/

{
    switch (KeProcessorLevel) {

    case PROCESSOR_ALPHA_21064:
    case PROCESSOR_ALPHA_21066:
    case PROCESSOR_ALPHA_21068:
        PhysicalAddress.QuadPart &= 0x00000003ffffffff;
        PhysicalAddress.QuadPart |= 0xfffffc0000000000;
        break;

    case PROCESSOR_ALPHA_21164:
    case PROCESSOR_ALPHA_21164PC:
        PhysicalAddress.QuadPart &= 0x000000ffffffffff;
        PhysicalAddress.QuadPart |= 0xfffffc0000000000;
        break;

    case PROCESSOR_ALPHA_21264:
        PhysicalAddress.QuadPart &= 0x00000fffffffffff;
        PhysicalAddress.QuadPart |= 0xffff800000000000;
        break;

    default:
        return NULL64;

    }

    return (PVOID64)PhysicalAddress.QuadPart;
}
