Windows2000/private/windbg64/debugger/dm/cache.c
2020-09-30 17:12:32 +02:00

1298 lines
29 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
cache.c
Abstract:
Author:
Wesley Witt (wesw) 8-Mar-1992
Environment:
NT 3.1
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
typedef unsigned long DWORD;
typedef int BOOL;
#define NTINCLUDES
#define MIN_READ_SIZE 0x1
#define MIN_CACHE_MISSES 3
ULONG KdMaxCacheSize = 100*1024;
ULONG KdCacheMisses = 0;
ULONG KdCachePurges = 0;
ULONG KdCacheSize = 0;
ULONG KdNodeCount = 0;
BOOL KdPurgeOverride = FALSE;
BOOL KdCacheDecodePTEs = TRUE;
typedef struct {
RTL_SPLAY_LINKS SplayLinks;
ULONG64 Offset;
USHORT Length;
USHORT Flags;
union {
PUCHAR Data;
NTSTATUS Status;
} u;
} CACHE, *PCACHE;
#define C_ERROR 0x0001 // Cache of error code
#define C_DONTEXTEND 0x0002 // Don't try to extend
#define C_NONDISCARDABLE 0x0004 // never purge this node
#define LARGECACHENODE 1024 // Size of large cache node
PCACHE VirtCacheRoot; // Root of cache node tree
// Prototypes...
extern void DebugPrint(char *, ...);
BOOL
KdConvertToPhysicalAddr (
ULONG64 addr,
PPHYSICAL_ADDRESS pa
);
PCACHE
CacheLookup (
ULONG64 Offset,
ULONG Length,
PULONG LengthUsed
);
VOID
InsertCacheNode (
IN PCACHE node
);
PUCHAR vcmalloc (
IN ULONG Length
);
VOID
vcfree (
IN PUCHAR Memory,
IN ULONG Length
);
NTSTATUS
VCReadTranslatePTEAndReadMemory (
IN ULONG64 TargetBaseAddress,
OUT PVOID UserInterfaceBuffer,
IN ULONG TransferCount,
OUT PULONG ActualBytesRead OPTIONAL
);
VOID
DmKdSetMaxCacheSize(
IN ULONG MaxCacheSize
)
{
KdMaxCacheSize = MaxCacheSize;
}
ULONG
DmKdReadCachedVirtualMemory (
IN ULONG64 BaseAddress,
IN ULONG TransferCount,
IN PUCHAR UserBuffer,
IN PULONG BytesRead,
IN ULONG NonDiscardable
)
/*++
This function returns the specified data from the system being debugged
using the current mapping of the processor. If the data is not
in the cache, it will then be read from the target system.
Arguments:
TargetBaseAddress - Supplies the base address of the memory to be
copied into the UserBuffer.
TransferCount - Amount of data to be copied to the UserBuffer.
UserBuffer - Address to copy the requested data.
BytesRead - Number of bytes which could actually be copied
Return Value:
STATUS_SUCCESS - The specified read occured.
other (see DmKdReadVirtualMemoryNow).
--*/
{
NTSTATUS st;
PCACHE node, node2;
ULONG nextlength;
ULONG i, br;
PUCHAR p;
*BytesRead = 0;
if (KdMaxCacheSize == 0) {
// Cache is off
goto ReadDirect;
}
// DebugPrint( "readcache %I64x %d\n", BaseAddress, TransferCount );
node = CacheLookup(BaseAddress, TransferCount, &nextlength);
st = STATUS_SUCCESS;
while( TRUE ) {
// Check if current command has been canceled. If yes, go back to
// kd prompt.
if (node == NULL || node->Offset > BaseAddress) {
// We are missing the leading data, read it into the cache
if (!node) {
nextlength = max( nextlength, MIN_READ_SIZE );
} else {
// Only get (exactly) enough data to reach neighboring cache
// node. If an overlapped read occurs between the two nodes,
// the data will be concatenated then.
assert((ULONG64)(node->Offset - BaseAddress) <= 0xffffffffUI64);
nextlength = (ULONG)(node->Offset - BaseAddress);
}
p = vcmalloc (nextlength);
node = (PCACHE) vcmalloc (sizeof (CACHE));
if (p == NULL || node == NULL) {
// Out of memory - just read directly to UserBuffer
if (p) {
vcfree (p, nextlength);
}
if (node) {
vcfree ((PUCHAR)node, sizeof (CACHE));
}
goto ReadDirect;
}
// Read missing data into cache node
node->Offset = BaseAddress;
node->u.Data = p;
node->Flags = 0;
if (NonDiscardable) {
node->Flags |= C_NONDISCARDABLE;
}
KdCacheMisses++;
while( TRUE ) {
st = DmKdReadVirtualMemoryNow(
BaseAddress,
node->u.Data,
nextlength,
&br
);
if (NT_SUCCESS(st)) {
break;
}
// Before accepting the error, make sure request
// didn't fail because it was enlarged for caching.
i = nextlength;
//nextlength = TransferCount;
// If length crosses possible page boundary, shrink request
// even further.
if ((BaseAddress & ~0xfff) != ((BaseAddress+nextlength) & ~0xfff)) {
nextlength = (ULONG)((BaseAddress | 0xfff) - BaseAddress + 1);
}
// If nextlength is shorter then failed request, then loop
// and try again
if (nextlength >= i) {
// If implicit decode of the pte is requested, go
// try getting this memory by it's physical address
if (st == STATUS_UNSUCCESSFUL && KdCacheDecodePTEs) {
st = VCReadTranslatePTEAndReadMemory (
BaseAddress,
node->u.Data,
nextlength,
&br
);
}
break;
}
}
if (!NT_SUCCESS(st)) {
// There was an error, cache the error for the starting
// byte of this range
vcfree (p, nextlength);
if (st != STATUS_UNSUCCESSFUL) {
// For now be safe, don't cache this error
vcfree ((PUCHAR)node, sizeof (CACHE));
return *BytesRead ? STATUS_SUCCESS : st;
}
node->Length = 1;
node->Flags |= C_ERROR;
node->u.Status = st;
} else {
node->Length = (USHORT) br;
if (br != nextlength) {
// Some data was not transfered, cache what was returned
node->Flags |= C_DONTEXTEND;
KdCacheSize -= (nextlength - br);
}
}
// Insert cache node into splay tree
InsertCacheNode (node);
}
if (node->Flags & C_ERROR) {
// Hit an error range, we're done
return *BytesRead ? STATUS_SUCCESS : node->u.Status;
}
// Move available data to UserBuffer
i = (ULONG)(BaseAddress - node->Offset);
p = node->u.Data + i;
i = (ULONG) node->Length - i;
if (TransferCount < i) {
i = TransferCount;
}
memcpy (UserBuffer, p, i);
TransferCount -= i;
BaseAddress += i;
UserBuffer += i;
*BytesRead += i;
if (!TransferCount) {
// All of the users data has been transfered
return STATUS_SUCCESS;
}
// Look for another cache node with more data
node2 = CacheLookup (BaseAddress, TransferCount, &nextlength);
if (node2) {
if ((node2->Flags & C_ERROR) == 0 &&
node2->Offset == BaseAddress &&
node2->Length + node->Length < LARGECACHENODE) {
// Data is continued in node2, adjoin the neigboring
// cached data in node & node2 together.
p = vcmalloc (node->Length + node2->Length);
if (p != NULL) {
memcpy (p, node->u.Data, node->Length);
memcpy (p+node->Length, node2->u.Data, node2->Length);
vcfree (node->u.Data, node->Length);
node->u.Data = p;
node->Length += node2->Length;
VirtCacheRoot = (PCACHE) RtlDelete ((PRTL_SPLAY_LINKS)node2);
vcfree ((PUCHAR)node2->u.Data, node2->Length);
vcfree ((PUCHAR)node2, sizeof (CACHE));
KdNodeCount--;
continue;
}
}
// Only get enough data to reach the neighboring cache node2
nextlength = (ULONG)(node2->Offset - BaseAddress);
if (nextlength == 0) {
// Data is continued in node2, go get it.
node = node2;
continue;
}
} else {
if (node->Length > LARGECACHENODE) {
// Current cache node is already big enough. Don't extend
// it, add another cache node.
node = NULL;
continue;
}
}
// Extend the current node to include missing data
if (node->Flags & C_DONTEXTEND) {
node = NULL;
continue;
}
//nextlength = max( nextlength, MIN_READ_SIZE );
p = vcmalloc (node->Length + nextlength);
if (!p) {
node = NULL;
continue;
}
memcpy (p, node->u.Data, node->Length);
vcfree (node->u.Data, node->Length);
node->u.Data = p;
// Add new data to end of this node
KdCacheMisses++;
while( TRUE ) {
st = DmKdReadVirtualMemoryNow (
BaseAddress,
(node->u.Data + node->Length),
nextlength,
&br
);
if (NT_SUCCESS(st)) {
break;
}
// Before accepting the error, make sure request
// didn't fail because it was enlarged for caching.
node->Flags |= C_DONTEXTEND;
i = TransferCount;
// If length crosses possible page boundry, shrink request
// even furture.
if ((BaseAddress & ~0xfff) != ((BaseAddress + i) & ~0xfff)) {
i = (ULONG)((BaseAddress | 0xfff) - BaseAddress + 1);
}
// If nextlength is shorter, then loop (try the read again)
if (i >= nextlength) {
// If implicit decode of the pte is requested, go
// try getting this memory by it's physical address
if (st == STATUS_UNSUCCESSFUL && KdCacheDecodePTEs) {
st = VCReadTranslatePTEAndReadMemory (
BaseAddress,
(node->u.Data + node->Length),
nextlength,
&br
);
}
break;
}
// Adjust counts for new transfer size
KdCacheSize -= (nextlength - i);
nextlength = i;
}
if (!NT_SUCCESS(st)) {
// Return to error to the caller
node->Flags |= C_DONTEXTEND;
KdCacheSize -= nextlength;
return *BytesRead ? STATUS_SUCCESS : st;
}
if (br != nextlength) {
node->Flags |= C_DONTEXTEND;
KdCacheSize -= (nextlength - br);
}
node->Length += (USHORT) br;
// Loop, and move data to user's buffer
}
ReadDirect:
while (TransferCount) {
nextlength = TransferCount;
while( TRUE ) {
st = DmKdReadVirtualMemoryNow (
BaseAddress,
UserBuffer,
nextlength,
&br
);
if (NT_SUCCESS(st)) {
break;
}
if ((BaseAddress & ~0xfff) != ((BaseAddress+nextlength) & ~0xfff)) {
// Before accepting the error, make sure request
// didn't fail because it crossed multiple pages
nextlength = (ULONG)((BaseAddress | 0xfff) - BaseAddress + 1);
} else {
if (st == STATUS_UNSUCCESSFUL && KdCacheDecodePTEs) {
// Try getting the memory by looking up the physical
// location of the page
st = VCReadTranslatePTEAndReadMemory (
BaseAddress,
UserBuffer,
nextlength,
&br
);
if (NT_SUCCESS(st)) {
break;
}
}
// Return to error to the caller
return *BytesRead ? STATUS_SUCCESS : st;
}
}
TransferCount -= br;
BaseAddress += br;
UserBuffer += br;
*BytesRead += br;
}
return STATUS_SUCCESS;
}
PCACHE
CacheLookup (
ULONG64 Offset,
ULONG Length,
PULONG LengthUsed
)
/*++
Routine Description:
Walks the cache tree looking for a matching range closest to
the supplied Offset. The length of the range searched is based on
the past length, but may be adjusted slightly.
This function will always search for the starting byte.
Arguments:
Offset - Starting byte being looked for in cache
Length - Length of range being looked for in cache
LengthUsed - Length of range which was really search for
Return Value:
NULL - data for returned range was not found
PCACHE - leftmost cachenode which has data for returned range
--*/
{
PCACHE node, node2;
ULONG64 SumOffsetLength;
// DebugPrint( "CacheLookup\n" );
if (Length < MIN_READ_SIZE && KdCacheMisses > MIN_CACHE_MISSES) {
// Try to cache more then tiny amount
Length = MIN_READ_SIZE;
}
SumOffsetLength = Offset + Length;
if (SumOffsetLength < Length) {
// Offset + Length wrapped. Adjust Length to be only
// enough bytes before wrapping.
Length = (ULONG)(0 - Offset);
SumOffsetLength = (ULONG64) -1;
}
*LengthUsed = Length;
// Find leftmost cache node for BaseAddress thru BaseAddress+Length
node2 = NULL;
node = VirtCacheRoot;
while (node != NULL) {
if (SumOffsetLength <= node->Offset) {
node = (PCACHE) RtlLeftChild(&node->SplayLinks);
} else if (node->Offset + node->Length <= Offset) {
node = (PCACHE) RtlRightChild(&node->SplayLinks);
} else {
if (node->Offset <= Offset) {
// Found starting byte
return node;
}
// Check to see if there's a node which has a match closer
// to the start of the requested range
node2 = node;
Length = (ULONG)(node->Offset - Offset);
node = (PCACHE) RtlLeftChild(&node->SplayLinks);
}
}
return node2;
}
VOID
InsertCacheNode (
IN PCACHE node
)
{
PCACHE node2;
ULONG64 BaseAddress;
// Insert cache node into splay tree
// DebugPrint( "insertcache\n" );
RtlInitializeSplayLinks(&node->SplayLinks);
KdNodeCount++;
if (VirtCacheRoot == NULL) {
VirtCacheRoot = node;
return;
}
node2 = VirtCacheRoot;
BaseAddress = node->Offset;
while( TRUE ) {
if (BaseAddress < node2->Offset) {
if (RtlLeftChild(&node2->SplayLinks)) {
node2 = (PCACHE) RtlLeftChild(&node2->SplayLinks);
continue;
}
RtlInsertAsLeftChild(node2, node);
break;
} else {
if (RtlRightChild(&node2->SplayLinks)) {
node2 = (PCACHE) RtlRightChild(&node2->SplayLinks);
continue;
}
RtlInsertAsRightChild(node2, node);
break;
}
}
VirtCacheRoot = (PCACHE) RtlSplay((PRTL_SPLAY_LINKS)node2);
}
VOID
DmKdInitVirtualCacheEntry (
IN ULONG64 BaseAddress,
IN ULONG Length,
IN PUCHAR UserBuffer,
IN ULONG NonDiscardable
)
/*++
Routine Description:
Insert some data into the virtual cache.
Arguments:
BaseAddress - Virtual address
Length - length to cache
UserBuffer - data to put into cache
NonDiscardable -
Return Value:
--*/
{
PCACHE node;
PUCHAR p;
ULONG LengthUsed;
if (KdMaxCacheSize == 0) {
// Cache is off
return ;
}
// DebugPrint( "DmKdInitVirtualCacheEntry\n" );
node = CacheLookup( BaseAddress, Length, &LengthUsed );
if (node) {
return;
}
// Delete any cached info which hits range
DmKdWriteCachedVirtualMemory (BaseAddress, Length, UserBuffer);
p = vcmalloc (Length);
node = (PCACHE) vcmalloc (sizeof (CACHE));
if (p == NULL || node == NULL) {
// Out of memory - don't bother
if (p) {
vcfree (p, Length);
}
if (node) {
vcfree ((PUCHAR)node, sizeof (CACHE));
}
return ;
}
// Put data into cache node
node->Offset = BaseAddress;
node->Length = (USHORT) Length;
node->u.Data = p;
node->Flags = 0;
if (NonDiscardable) {
node->Flags |= C_NONDISCARDABLE;
}
memcpy (p, UserBuffer, Length);
InsertCacheNode (node);
}
PUCHAR
vcmalloc (
IN ULONG Length
)
/*++
Routine Description:
Allocates memory for virtual cache, and tracks total memory
usage.
Arguments:
Length - Amount of memory to allocate
Return Value:
NULL - too much memory is in use, or memory could not
be allocated
Otherwise, returns to address of the allocated memory
--*/
{
PUCHAR p;
if (KdCacheSize + Length > KdMaxCacheSize) {
return NULL;
}
if (!(p = (PUCHAR) malloc (Length))) {
// Out of memory - don't get any larger
KdCacheSize = KdMaxCacheSize+1;
return NULL;
}
KdCacheSize += Length;
return p;
}
VOID
vcfree (
IN PUCHAR Memory,
IN ULONG Length
)
/*++
Routine Description:
Free memory allocated with vcmalloc. Adjusts cache is use totals.
Arguments:
Memory - Address of allocated memory
Length - Length of allocated memory
Return Value:
NONE
--*/
{
KdCacheSize -= Length;
free (Memory);
}
NTSTATUS
VCReadTranslatePTEAndReadMemory (
IN ULONG64 TargetBaseAddress,
OUT PVOID UserInterfaceBuffer,
IN ULONG TransferCount,
OUT PULONG ActualBytesRead OPTIONAL
)
/*++
--*/
{
static BOOL ConvertingAnAddress;
NTSTATUS status;
BOOL converted;
PHYSICAL_ADDRESS TargetPhysicalAddress;
if (ConvertingAnAddress) {
return STATUS_UNSUCCESSFUL;
}
// Memory could not be read, try its physical address.
ConvertingAnAddress = TRUE;
converted = KdConvertToPhysicalAddr (
TargetBaseAddress,
&TargetPhysicalAddress
);
if (converted) {
status = DmKdReadPhysicalMemory (
TargetPhysicalAddress.QuadPart,
UserInterfaceBuffer,
TransferCount,
ActualBytesRead
);
} else {
status = STATUS_UNSUCCESSFUL;
}
ConvertingAnAddress = FALSE;
return NT_SUCCESS(status) ? status : STATUS_UNSUCCESSFUL;
}
VOID
DmKdWriteCachedVirtualMemory (
IN ULONG64 BaseAddress,
IN ULONG TransferCount,
IN PUCHAR UserBuffer
)
/*++
Routine Description:
Invalidates range from the cache.
Arguments:
BaseAddress - Starting address to purge
TransferCount - Length of area to purge
UserBuffer - not used
Return Value:
NONE
--*/
{
PCACHE node;
ULONG bogus;
// Invalidate any data in the cache which covers this range
while (node = CacheLookup(BaseAddress, TransferCount, &bogus)) {
// For now just delete the entire cache node which hits the range
VirtCacheRoot = (PCACHE) RtlDelete (&node->SplayLinks);
if (!(node->Flags & C_ERROR)) {
vcfree (node->u.Data, node->Length);
}
vcfree ((PUCHAR)node, sizeof (CACHE));
KdNodeCount--;
}
}
PCACHE
WalkForDelete(
PCACHE node
)
{
PCACHE node2;
if (!(node->Flags & C_NONDISCARDABLE)) {
return node;
}
node2 = (PCACHE)RtlRightChild(&node->SplayLinks);
if (node2) {
if (!(node2->Flags & C_NONDISCARDABLE)) {
return node2;
} else {
node2 = WalkForDelete( node2 );
if (node2) {
return node2;
}
}
}
node2 = (PCACHE)RtlLeftChild(&node->SplayLinks);
if (node2) {
if (!(node2->Flags & C_NONDISCARDABLE)) {
return node2;
} else {
node2 = WalkForDelete( node2 );
if (node2) {
return node2;
}
}
}
return NULL;
}
VOID
DmKdPurgeCachedVirtualMemory (
BOOL fPurgeNonDiscardable
)
/*++
Routine Description:
Purges to entire virtual memory cache
Arguments:
NONE
Return Value:
NONE
--*/
{
PCACHE node;
PCACHE node2;
if (KdPurgeOverride) {
DMPrintShellMsg("** Warning: cache being held\n");
return;
}
node = VirtCacheRoot;
KdCacheMisses = 0;
KdCachePurges++;
if (fPurgeNonDiscardable) {
while( node ) {
if (!(node->Flags & C_ERROR)) {
vcfree (node->u.Data, node->Length);
}
KdNodeCount--;
node = VirtCacheRoot = (PCACHE)RtlDelete((PRTL_SPLAY_LINKS)node);
}
KdCacheSize = 0;
return;
}
while( node ) {
node2 = WalkForDelete( node );
if (node2) {
if (!(node2->Flags & C_ERROR)) {
vcfree (node2->u.Data, node->Length);
}
KdNodeCount--;
node = VirtCacheRoot = (PCACHE)RtlDelete((PRTL_SPLAY_LINKS)node2);
} else {
node = node2;
}
}
return;
}
void
WalkForDump(
PCACHE node
)
{
PCACHE l,r;
r = (PCACHE)RtlRightChild(&node->SplayLinks);
l = (PCACHE)RtlLeftChild(&node->SplayLinks);
DMPrintShellMsg( "%08I64x %8d (%c)\n", node->Offset, node->Length, (node->Flags&C_NONDISCARDABLE) ? 'Y' : 'N' );
if (r) {
WalkForDump( r );
}
if (l) {
WalkForDump( l );
}
}
BOOL
KdConvertToPhysicalAddr64 (
IN ULONG64 uAddress,
OUT PPHYSICAL_ADDRESS PhysicalAddress
)
{
return FALSE;
}
BOOL
KdConvertToPhysicalAddr32 (
IN ULONG uAddress,
OUT PPHYSICAL_ADDRESS PhysicalAddress
)
/*++
Routine Description:
Convert a virtual address to a physical one.
Note: that this function is called from within the virtual memory
cache code. This function can read from the virtual memory cache
so long as it only read's PDE's and PTE's and so long as it fails
to convert a PDE or PTE virtual address.
Arguments:
uAddress - address to convert
PhysicalAddress - returned physical address
Return Value:
TRUE - physical address was returned
otherwise, FALSE
--*/
{
#if defined( TARGET_IA64 )
#define PDE_TOP 0xC07FFFFF
#else
#define PDE_TOP 0xC03FFFFF
#endif // IA64
#if defined( TARGET_i386 )
#define MM_PTE_PROTOTYPE_MASK 0x400
#define MM_PTE_TRANSITION_MASK 0x800
#define PTE_CONT 0x1
#elif defined( TARGET_ALPHA ) || defined( TARGET_AXP64 )
#define MM_PTE_PROTOTYPE_MASK 0x2
#define MM_PTE_TRANSITION_MASK 0x4
#define PTE_CONT 0x1
#elif defined( TARGET_IA64 )
#define MM_PTE_PROTOTYPE_MASK 0x2
#define MM_PTE_TRANSITION_MASK 0x4
#define PTE_CONT 0x1
#endif
#if defined(TARGET_IA64)
#define PDE_SHIFT (21)
#define PMMPTE ULONG
#define MiGetPdeAddress(va) ((PMMPTE)(((((ULONG)(va)) >> PDE_SHIFT) << PDE_SHIFT) + PDE_BASE))
#define MiGetPteAddress(va) ((PMMPTE)(((((ULONG)(va)) >> PAGE_SHIFT) << PDE_SHIFT) + PTE_BASE))
#else
#define MiGetPdeAddress(va) ((ULONG)(((((ULONG)(va)) >> 22) << 2) + PDE_BASE))
#define MiGetPteAddress(va) ((ULONG)(((((ULONG)(va)) >> 12) << 2) + PTE_BASE))
#endif
ULONG Address;
ULONG Pte;
ULONG Pde;
ULONG PdeContents;
ULONG PteContents;
NTSTATUS status;
ULONG result;
Address = (ULONG) uAddress;
if (Address >= PTE_BASE && Address < PDE_TOP) {
// The address is the address of a PTE, rather than
// a virtual address. DO NOT CONVERT IT.
return FALSE;
}
Pde = MiGetPdeAddress (Address);
Pte = MiGetPteAddress (Address);
status = DmKdReadVirtualMemoryNow(SE32To64(Pde),
&PdeContents,
sizeof(ULONG),
&result);
if ((status != STATUS_SUCCESS) || (result < sizeof(ULONG))) {
return FALSE;
}
if (!(PdeContents & PTE_CONT)) {
return FALSE;
}
status = DmKdReadVirtualMemoryNow(SE32To64(Pte),
&PteContents,
sizeof(ULONG),
&result);
if ((status != STATUS_SUCCESS) || (result < sizeof(ULONG))) {
return FALSE;
}
if (!(PteContents & PTE_CONT)) {
if ( (PteContents & MM_PTE_PROTOTYPE_MASK) ||
!(PteContents & MM_PTE_TRANSITION_MASK)) {
return FALSE;
}
}
// This is a page which is either present or in transition.
// Return the physical address for the request virtual address.
PhysicalAddress->LowPart = (PteContents & ~(0xFFF)) | (Address & 0xFFF);
PhysicalAddress->QuadPart = SE32To64(PhysicalAddress->LowPart);
return TRUE;
}
BOOL
KdConvertToPhysicalAddr (
IN ULONG64 uAddress,
OUT PPHYSICAL_ADDRESS PhysicalAddress
)
{
if (DmKdPtr64) {
return KdConvertToPhysicalAddr64(uAddress, PhysicalAddress);
} else {
return KdConvertToPhysicalAddr32((ULONG)uAddress, PhysicalAddress);
}
}
VOID
ProcessCacheCmd(
LPSTR pchCommand
)
{
ULONG CacheSize;
ULONG64 Address;
while (*pchCommand == ' ') {
pchCommand++;
}
_strlwr (pchCommand);
if (strcmp (pchCommand, "?") == 0) {
usage:
DMPrintShellMsg("\n.cache [{cachesize} | dump | hold | unhold | decodeptes | nodecodeptes]\n");
DMPrintShellMsg(".cache [flushall | flushu | flush addr]\n\n");
return;
} else
if (strcmp (pchCommand, "dump") == 0) {
if (VirtCacheRoot) {
DMPrintShellMsg("\n Address Length Discardable\n");
WalkForDump( VirtCacheRoot );
}
} else
if (strcmp (pchCommand, "hold") == 0) {
KdPurgeOverride = TRUE;
} else
if (strcmp (pchCommand, "unhold") == 0) {
KdPurgeOverride = FALSE;
} else
if (strcmp (pchCommand, "decodeptes") == 0) {
DmKdPurgeCachedVirtualMemory(TRUE);
KdCacheDecodePTEs = TRUE;
} else
if (strcmp (pchCommand, "nodecodeptes") == 0) {
KdCacheDecodePTEs = FALSE;
} else
if (strcmp (pchCommand, "flushall") == 0) {
DmKdPurgeCachedVirtualMemory(TRUE);
} else
if (strcmp (pchCommand, "flushu") == 0) {
DmKdPurgeCachedVirtualMemory(TRUE);
} else
if (*pchCommand == 'f') {
while (*pchCommand >= 'a' && *pchCommand <= 'z') {
pchCommand++;
}
Address = _tcstoul(pchCommand,NULL,0);
DmKdWriteCachedVirtualMemory (Address, 4096, NULL); // this is a flush
DMPrintShellMsg("Cached info for address %I64x for 4096 bytes was flushed\n", Address);
} else if (*pchCommand) {
if (*pchCommand < '0' || *pchCommand > '9') {
goto usage;
} else {
CacheSize = _tcstoul(pchCommand,NULL,0);
KdMaxCacheSize = CacheSize * 1024;
if (CacheSize == 0) {
DmKdPurgeCachedVirtualMemory(TRUE);
KdCachePurges = 0;
}
}
}
DMPrintShellMsg("\n");
DMPrintShellMsg("Max cache size is....: %ld %s\n", KdMaxCacheSize,
KdMaxCacheSize ? "" : "(cache is off)");
DMPrintShellMsg("Total memory in cache: %ld\n", KdCacheSize - KdNodeCount * sizeof (CACHE) );
DMPrintShellMsg("No of regions cached.: %ld\n", KdNodeCount);
DMPrintShellMsg("Cache misses.........: %ld\n", KdCacheMisses);
DMPrintShellMsg("Cache purges.........: %ld\n", KdCachePurges);
if (KdCacheDecodePTEs) {
DMPrintShellMsg("** Transition ptes are implicity decoded\n");
}
if (KdPurgeOverride) {
DMPrintShellMsg("** Implicit cache flushing disabled **\n");
}
DMPrintShellMsg("\n");
}