/*++

Copyright (c) 1992-2000  Microsoft Corporation

Module Name:
    physical.cpp

Abstract:
    Extensions to read/display physocal memory

Environment:

    User Mode.

Revision History:

    Kshitiz K. Sharma (kksharma) 5/9/2001

--*/

#include "precomp.h"
#pragma hdrstop

ULONG64 g_LastAddress = 0;

/*++

Routine Description:

    Dumps specified range of physical memory in given format

Arguments:

    Address - Address to start
    
    NumEntries - Number of entries to dump 
    
    EntrySize - Size of each entry

    ShowAsAscii - print corresponding ascii chars
        
Return Value:

    None.


--*/
BOOL
DumpPhysicalMemory(
    ULONG64 Address,
    ULONG NumEntries,
    ULONG EntrySize,
    BOOL ShowAsAscii
    )
{
#define NumberBytesToRead 32*4

    UCHAR Buffer[NumberBytesToRead];
    ULONG ActualRead=0;
    
    if ((EntrySize != 1) && (EntrySize != 2) && (EntrySize != 4) && (EntrySize != 8)) {
        EntrySize=4;
    }
    while (1) {
        if (CheckControlC()) {
            break;
        }
        ReadPhysical(Address,Buffer,sizeof(Buffer),&ActualRead);
        if (ActualRead != sizeof(Buffer)) {
            dprintf("Physical memory read at %I64lx failed\n", Address);
            return FALSE;
        } else {
            PCHAR DumpByte = (PCHAR)&Buffer[0], pRow;
            ULONG cnt;
            pRow = DumpByte;
            for(cnt=0;cnt<NumberBytesToRead;DumpByte+=EntrySize) {
                if (!(cnt & 0xf)) {
                    dprintf("#%8I64lx", Address+cnt);
                }
                switch (EntrySize) {
                case 1:
                    dprintf("%c%02lx", ((cnt&0xf) == 8 ? '-' : ' '),*((PUCHAR)DumpByte));
                    break;
                case 2:
                    dprintf(" %04lx", *((PUSHORT) DumpByte));
                    break;
                case 4:
                    dprintf(" %08lx", *((PULONG) DumpByte));
                    break;
                case 8:
                    dprintf(" %08lx'%08lx", *((PULONG) DumpByte), *((PULONG) (DumpByte+4)));
                    break;
                }

                cnt+=EntrySize, NumEntries--;
                if ((cnt && !(cnt & 0xf)) || !NumEntries) {
                    if (ShowAsAscii) {
                        char ch;
                        dprintf(" ");
                        for (ULONG d=0; d < 16; d++) {
                            ch = pRow[d];
                            if (ch < 0x20 || ch > 0x7e) {
                                ch = '.';
                            }
                            dprintf("%c", ch);
                        }
                    }
                    dprintf("\n");
                    pRow = DumpByte;

                }
                if (!NumEntries) {
                    break;
                }
            }
            Address += cnt;
            if (!NumEntries) {
                break;
            }
        }
    }
    g_LastAddress = Address;

    return TRUE;
}

/*++

Routine Description:

    Reverse sign extension of the value returned by GetExpression()
    based on the assumption that no physical address may be bigger 
    than 0xfffffff00000000.

Arguments:

    Val - points to the value to reverse sign extension

Return Value:

    None.

--*/

void
ReverseSignExtension(ULONG64* Val)
{
    if ((*Val & 0xffffffff00000000) == 0xffffffff00000000) 
    {
        *Val &= 0x00000000ffffffff;
    }
}


void
GetPhyDumpArgs(
    PCSTR Args,
    PULONG64 Address,
    PULONG Range
    )
{
    CHAR Buffer[100]={0};

    if(*Args == '\0') {
        *Address=g_LastAddress;
    } else {
        sscanf(Args, "%s", Buffer);
        *Address = GetExpression((PCSTR) &Buffer[0]);
        ReverseSignExtension(Address);
        *Address &= (~0x3);      // Truncate to dword boundary
        g_LastAddress=*Address;
        Args += strlen(&Buffer[0]);
        while (*Args && (*Args == ' ' || *Args == '\t')) {
            ++Args;
        }
        if (*Args == 'l' || *Args == 'L') {
            ++Args;
            *Range = (ULONG) GetExpression(Args);
        }
    }
    return;
}

DECLARE_API( db )

/*++

Routine Description:

    Does a read of 16 ULONGS from the physical memory of the target machine

Arguments:

    args - Supplies physical address

Return Value:

    None.

--*/

{

    ULONG64 Address = 0;
    ULONG Range = 0;

    GetPhyDumpArgs(args, &Address, &Range);
    if (!Range) {
        Range = 128;
    }
    DumpPhysicalMemory(Address, Range, 1, TRUE);
    return S_OK;
}

DECLARE_API( dd )

/*++

Routine Description:

    Does a read of 16 ULONGS from the physical memory of the target machine

Arguments:

    args - Supplies physical address

Return Value:

    None.

--*/

{

    ULONG64 Address = 0;
    ULONG Range = 0;

    GetPhyDumpArgs(args, &Address, &Range);
    if (!Range) {
        Range = 32;
    }
    DumpPhysicalMemory(Address, Range, 4, FALSE);
    return S_OK;
}

DECLARE_API( dw )

/*++

Routine Description:

    Does a read of 16 ULONGS from the physical memory of the target machine

Arguments:

    args - Supplies physical address

Return Value:

    None.

--*/

{

    ULONG64 Address = 0;
    ULONG Range = 0;

    GetPhyDumpArgs(args, &Address, &Range);
    if (!Range) {
        Range = 64;
    }
    DumpPhysicalMemory(Address, Range, 2, FALSE);
    return S_OK;
}

DECLARE_API( dp )

/*++

Routine Description:

    Does a read of 16 ULONGS from the physical memory of the target machine

Arguments:

    args - Supplies physical address

Return Value:

    None.

--*/

{

    ULONG64 Address = 0;
    ULONG Range = 0;

    GetPhyDumpArgs(args, &Address, &Range);
    if (!Range) {
        Range = IsPtr64() ? 16 : 32;
    }
    DumpPhysicalMemory(Address, Range, IsPtr64() ? 8 : 4, FALSE);
    return S_OK;
}
DECLARE_API( dc )

/*++

Routine Description:

    Does a read of N ULONGS from the physical memory of the target machine,
    dumping both hex and ASCII.

Arguments:

    args - Supplies physical address

Return Value:

    None.

--*/

{

    ULONG64 Address = 0;
    ULONG Range = 0;

    GetPhyDumpArgs(args, &Address, &Range);
    if (!Range) {
        Range = 32;
    }
    DumpPhysicalMemory(Address, Range, 4, TRUE);
    return S_OK;
}

DECLARE_API( du )

/*++

Routine Description:

    Does a read of 16 ULONGS from the physical memory of the target machine

Arguments:

    args - Supplies physical address

Return Value:

    None.

--*/

{

    ULONG64 Address = 0;
    ULONG Range = 0, ActualRead;
    WCHAR Buffer[MAX_PATH]={0};
    GetPhyDumpArgs(args, &Address, &Range);
    if (Range>MAX_PATH) {
        Range = MAX_PATH;
    }
    if (!Range) {
        Range = 16;
    }
    ReadPhysical(Address,Buffer,Range,&ActualRead);
    if (ActualRead != Range) {
        dprintf("Physical memory read at %I64lx failed\n", Address);
        return FALSE;
    } else {
        ULONG cnt;
        
        dprintf("#%8I64lx \"", Address);
        for (ULONG d=0; d < Range; d++) {
            WCHAR ch = Buffer[d];
            if (ch < 0x20 || ch > 0x7e) {
                ch = '.';
            }
            dprintf("%wc", ch);
        }
        dprintf("\"\n", Address);
    }
    return S_OK;
}


DECLARE_API( ed )

/*++

Routine Description:

    Writes a sequence of ULONGs into a given physical address on the
    target machine.

Arguments:

    arg - Supplies both the target address and the data in the form of
          "PHYSICAL_ADDRESS ULONG [ULONG, ULONG,...]"

Return Value:

    None.

--*/

{
    ULONG64 Address = 0;
    ULONG Buffer;
    ULONG ActualWritten=0;
    PCHAR NextToken;

    Address = GetExpression(args);

    strtok((PSTR)args," \t,");      // The first token is the address

    // Since we're picking off one ULONG at a time, we'll make
    // one DbgKdWritePhysicalMemoryAddress call per ULONG.  This
    // is slow, but easy to code.
    while((NextToken=strtok(NULL," \t,")) != NULL) {
        if (!sscanf(NextToken,"%lx",&Buffer)) {
            break;
        }
        WritePhysical(Address,&Buffer,sizeof(Buffer),&ActualWritten);
        Address+=sizeof(Buffer);
    }
    return S_OK;
}


DECLARE_API( eb )

/*++

Routine Description:

    Writes a sequence of BYTEs into a given physical address on the
    target machine.

Arguments:

    arg - Supplies both the target address and the data in the form of
          "PHYSICAL_ADDRESS ULONG [ULONG, ULONG,...]"

Return Value:

    None.

--*/

{
    ULONG64 Address = 0;
    ULONG Buffer;
    UCHAR c;
    ULONG ActualWritten;
    PCHAR NextToken;

    UNREFERENCED_PARAMETER (Client);

    Address = GetExpression(args);

    strtok((PSTR)args," \t,");      // The first token is the address

    // Since we're picking off one BYTE at a time, we'll make
    // one DbgKdWritePhysicalMemoryAddress call per BYTE.  This
    // is slow, but easy to code.
    while((NextToken=strtok(NULL," \t,")) != NULL) {
        if (!sscanf(NextToken,"%lx",&Buffer)) {
            break;
        }
        c = (UCHAR)Buffer;
        WritePhysical(Address,&c,sizeof(UCHAR),&ActualWritten);
        Address+=sizeof(UCHAR);
    }

    return S_OK;
}