948 lines
25 KiB
C
948 lines
25 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
debugsup.c
|
||
|
||
Abstract:
|
||
|
||
This module contains routines which provide support for the
|
||
kernel debugger.
|
||
|
||
Author:
|
||
|
||
Lou Perazzoli (loup) 02-Aug-1990
|
||
Landy Wang (landyw) 02-June-1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "mi.h"
|
||
|
||
#include <kdp.h>
|
||
|
||
ULONG MmPoisonedTb;
|
||
|
||
|
||
PVOID
|
||
MiDbgWriteCheck (
|
||
IN PVOID VirtualAddress,
|
||
IN PHARDWARE_PTE Opaque,
|
||
IN LOGICAL ForceWritableIfPossible
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks the specified virtual address and if it is
|
||
valid and writable, it returns that virtual address, otherwise
|
||
it returns NULL.
|
||
|
||
Arguments:
|
||
|
||
VirtualAddress - Supplies the virtual address to check.
|
||
|
||
Opaque - Supplies an opaque pointer.
|
||
|
||
Return Value:
|
||
|
||
Returns NULL if the address is not valid or writable, otherwise
|
||
returns the virtual address.
|
||
|
||
Environment:
|
||
|
||
Kernel mode IRQL at DISPATCH_LEVEL or greater.
|
||
|
||
--*/
|
||
|
||
{
|
||
MMPTE PteContents;
|
||
PMMPTE InputPte;
|
||
PMMPTE PointerPte;
|
||
ULONG_PTR IsPhysical;
|
||
|
||
InputPte = (PMMPTE)Opaque;
|
||
|
||
InputPte->u.Long = 0;
|
||
|
||
if (!MmIsAddressValid (VirtualAddress)) {
|
||
return NULL;
|
||
}
|
||
|
||
#if defined(_IA64_)
|
||
|
||
//
|
||
// There are regions mapped by TRs (PALcode, PCR, etc) that are
|
||
// not part of the MI_IS_PHYSICAL_ADDRESS macro.
|
||
//
|
||
|
||
IsPhysical = MiIsVirtualAddressMappedByTr (VirtualAddress);
|
||
if (IsPhysical == FALSE) {
|
||
IsPhysical = MI_IS_PHYSICAL_ADDRESS (VirtualAddress);
|
||
}
|
||
#else
|
||
IsPhysical = MI_IS_PHYSICAL_ADDRESS (VirtualAddress);
|
||
#endif
|
||
|
||
if (IsPhysical) {
|
||
|
||
//
|
||
// All superpage mappings must be read-write and never generate
|
||
// faults so nothing needs to be done for this case.
|
||
//
|
||
|
||
return VirtualAddress;
|
||
}
|
||
|
||
PointerPte = MiGetPteAddress (VirtualAddress);
|
||
|
||
PteContents = *PointerPte;
|
||
|
||
#if defined(NT_UP) || defined(_IA64_)
|
||
if (PteContents.u.Hard.Write == 0)
|
||
#else
|
||
if (PteContents.u.Hard.Writable == 0)
|
||
#endif
|
||
{
|
||
if (ForceWritableIfPossible == FALSE) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// PTE is not writable, make it so.
|
||
//
|
||
|
||
*InputPte = PteContents;
|
||
|
||
//
|
||
// Carefully modify the PTE to ensure write permissions,
|
||
// preserving the page's cache attributes to keep the TB
|
||
// coherent.
|
||
//
|
||
|
||
#if defined(NT_UP) || defined(_IA64_)
|
||
PteContents.u.Hard.Write = 1;
|
||
#else
|
||
PteContents.u.Hard.Writable = 1;
|
||
#endif
|
||
MI_SET_PTE_DIRTY (PteContents);
|
||
MI_SET_ACCESSED_IN_PTE (&PteContents, 1);
|
||
|
||
*PointerPte = PteContents;
|
||
|
||
//
|
||
// Note KeFillEntryTb does not IPI the other processors. This is
|
||
// required 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);
|
||
}
|
||
|
||
return VirtualAddress;
|
||
}
|
||
|
||
VOID
|
||
MiDbgReleaseAddress (
|
||
IN PVOID VirtualAddress,
|
||
IN PHARDWARE_PTE Opaque
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
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;
|
||
TempPte.u.Hard.Dirty = 1;
|
||
|
||
*PointerPte = TempPte;
|
||
|
||
KeFillEntryTb ((PHARDWARE_PTE)PointerPte, VirtualAddress, TRUE);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
PVOID64
|
||
MiDbgTranslatePhysicalAddress (
|
||
IN PHYSICAL_ADDRESS PhysicalAddress,
|
||
IN ULONG Flags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine maps the specified physical address and returns
|
||
the virtual address which maps the physical address.
|
||
|
||
The next call to MiDbgTranslatePhysicalAddress removes the
|
||
previous physical address translation, hence only a single
|
||
physical address can be examined at a time (can't cross page
|
||
boundaries).
|
||
|
||
Arguments:
|
||
|
||
PhysicalAddress - Supplies the physical address to map and translate.
|
||
|
||
Flags -
|
||
|
||
MMDBG_COPY_WRITE - Ignored.
|
||
|
||
MMDBG_COPY_PHYSICAL - Ignored.
|
||
|
||
MMDBG_COPY_UNSAFE - Ignored.
|
||
|
||
MMDBG_COPY_CACHED - Use a PTE with the cached attribute for the
|
||
mapping to ensure TB coherence.
|
||
|
||
MMDBG_COPY_UNCACHED - Use a PTE with the uncached attribute for the
|
||
mapping to ensure TB coherence.
|
||
|
||
MMDBG_COPY_WRITE_COMBINED - Use a PTE with the writecombined attribute
|
||
for the mapping to ensure TB coherence.
|
||
|
||
Note the cached/uncached/write combined attribute requested by the
|
||
caller is ignored if Mm can internally determine the proper attribute.
|
||
|
||
Return Value:
|
||
|
||
The virtual address which corresponds to the physical address.
|
||
|
||
Environment:
|
||
|
||
Kernel mode IRQL at DISPATCH_LEVEL or greater.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Hint;
|
||
MMPTE TempPte;
|
||
PVOID BaseAddress;
|
||
PFN_NUMBER PageFrameIndex;
|
||
PMMPFN Pfn1;
|
||
MMPTE OriginalPte;
|
||
|
||
//
|
||
// The debugger can call this before Mm has even initialized in Phase 0 !
|
||
// MmDebugPte cannot be referenced before Mm has initialized without
|
||
// causing an infinite loop wedging the machine.
|
||
//
|
||
|
||
if (MmPhysicalMemoryBlock == NULL) {
|
||
return NULL;
|
||
}
|
||
|
||
Hint = 0;
|
||
BaseAddress = MiGetVirtualAddressMappedByPte (MmDebugPte);
|
||
|
||
TempPte = ValidKernelPte;
|
||
|
||
PageFrameIndex = (PFN_NUMBER)(PhysicalAddress.QuadPart >> PAGE_SHIFT);
|
||
|
||
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
|
||
|
||
if (MiIsPhysicalMemoryAddress (PageFrameIndex, &Hint, FALSE) == TRUE) {
|
||
|
||
Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
|
||
|
||
switch (Pfn1->u3.e1.CacheAttribute) {
|
||
|
||
case MiCached:
|
||
case MiNotMapped:
|
||
default:
|
||
break;
|
||
case MiNonCached:
|
||
MI_DISABLE_CACHING (TempPte);
|
||
break;
|
||
case MiWriteCombined:
|
||
MI_SET_PTE_WRITE_COMBINE (TempPte);
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
|
||
if (Flags & MMDBG_COPY_CACHED) {
|
||
NOTHING;
|
||
}
|
||
else if (Flags & MMDBG_COPY_UNCACHED) {
|
||
|
||
//
|
||
// Just flush the entire TB on this processor but not the others
|
||
// as an IPI may not be safe depending on when/why we broke into
|
||
// the debugger.
|
||
//
|
||
// If IPIs were safe, we would have used
|
||
// MI_PREPARE_FOR_NONCACHED (MiNonCached) instead.
|
||
//
|
||
|
||
KeFlushCurrentTb ();
|
||
|
||
MI_DISABLE_CACHING (TempPte);
|
||
}
|
||
else if (Flags & MMDBG_COPY_WRITE_COMBINED) {
|
||
|
||
//
|
||
// Just flush the entire TB on this processor but not the others
|
||
// as an IPI may not be safe depending on when/why we broke into
|
||
// the debugger.
|
||
//
|
||
// If IPIs were safe, we would have used
|
||
// MI_PREPARE_FOR_NONCACHED (MiWriteCombined) instead.
|
||
//
|
||
|
||
KeFlushCurrentTb ();
|
||
|
||
MI_SET_PTE_WRITE_COMBINE (TempPte);
|
||
}
|
||
else {
|
||
|
||
//
|
||
// This is an access to I/O space and we don't know the correct
|
||
// attribute type. Only proceed if the caller explicitly specified
|
||
// an attribute and hope he didn't get it wrong. If no attribute
|
||
// is specified then just return failure.
|
||
//
|
||
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Since we really don't know if the caller got the attribute right,
|
||
// set the flag below so (assuming the machine doesn't hard hang) we
|
||
// can at least tell in the crash that he may have whacked the TB.
|
||
//
|
||
|
||
MmPoisonedTb += 1;
|
||
}
|
||
|
||
MI_SET_ACCESSED_IN_PTE (&TempPte, 1);
|
||
|
||
OriginalPte.u.Long = 0;
|
||
|
||
OriginalPte.u.Long = InterlockedCompareExchangePte (MmDebugPte,
|
||
TempPte.u.Long,
|
||
OriginalPte.u.Long);
|
||
|
||
if (OriginalPte.u.Long != 0) {
|
||
|
||
//
|
||
// Someone else is using the debug PTE. Inform our caller it is not
|
||
// available.
|
||
//
|
||
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Just flush (no sweep) the TB entry on this processor as an IPI
|
||
// may not be safe depending on when/why we broke into the debugger.
|
||
// Note that if we are in kd, then all the processors are frozen and
|
||
// this thread can't migrate so the local TB flush is enough. For
|
||
// the localkd case, our caller has raised to DISPATCH_LEVEL thereby
|
||
// ensuring this thread can't migrate even though the other processors
|
||
// are not frozen.
|
||
//
|
||
|
||
KiFlushSingleTb (TRUE, BaseAddress);
|
||
|
||
return (PVOID64)((ULONG_PTR)BaseAddress + BYTE_OFFSET(PhysicalAddress.LowPart));
|
||
}
|
||
|
||
VOID
|
||
MiDbgUnTranslatePhysicalAddress (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine unmaps the virtual address currently mapped by the debug PTE.
|
||
|
||
This is needed so that stale PTE mappings are not left in the debug PTE
|
||
as if the page attribute subsequently changes, a stale mapping would
|
||
cause TB incoherency.
|
||
|
||
This can only be called if the previous MiDbgTranslatePhysicalAddress
|
||
succeeded.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Kernel mode IRQL at DISPATCH_LEVEL or greater.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVOID BaseAddress;
|
||
|
||
BaseAddress = MiGetVirtualAddressMappedByPte (MmDebugPte);
|
||
|
||
ASSERT (MmIsAddressValid (BaseAddress));
|
||
|
||
#if defined (_WIN64)
|
||
InterlockedExchange64 ((PLONG64)MmDebugPte, ZeroPte.u.Long);
|
||
#elif defined(_X86PAE_)
|
||
KeInterlockedSwapPte ((PHARDWARE_PTE)MmDebugPte,
|
||
(PHARDWARE_PTE)&ZeroPte.u.Long);
|
||
#else
|
||
InterlockedExchange ((PLONG)MmDebugPte, ZeroPte.u.Long);
|
||
#endif
|
||
|
||
KiFlushSingleTb (TRUE, BaseAddress);
|
||
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
MmDbgCopyMemory (
|
||
IN ULONG64 UntrustedAddress,
|
||
IN PVOID Buffer,
|
||
IN ULONG Size,
|
||
IN ULONG Flags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Transfers a single chunk of memory between a buffer and a system
|
||
address. The transfer can be a read or write with a virtual or
|
||
physical address.
|
||
|
||
The chunk size must be 1, 2, 4 or 8 bytes and the address
|
||
must be appropriately aligned for the size.
|
||
|
||
Arguments:
|
||
|
||
UntrustedAddress - Supplies the system address being read from or written
|
||
into. The address is translated appropriately and
|
||
validated before being used. This address must not
|
||
cross a page boundary.
|
||
|
||
Buffer - Supplies the buffer to read into or write from. It is the caller's
|
||
responsibility to ensure this buffer address is nonpaged and valid
|
||
(ie: will not generate any faults including access bit faults)
|
||
throughout the duration of this call. This routine (not the
|
||
caller) will handle copying into this buffer as the buffer
|
||
address may not be aligned properly for the requested transfer.
|
||
|
||
Typically this buffer points to a kd circular buffer or an
|
||
ExLockUserBuffer'd address. Note this buffer can cross page
|
||
boundaries.
|
||
|
||
Size - Supplies the size of the transfer. This may be 1, 2, 4 or 8 bytes.
|
||
|
||
Flags -
|
||
|
||
MMDBG_COPY_WRITE - Write from the buffer to the address.
|
||
If this is not set a read is done.
|
||
|
||
MMDBG_COPY_PHYSICAL - The address is a physical address and by default
|
||
a PTE with a cached attribute will be used to
|
||
map it to retrieve (or set) the specified data.
|
||
If this is not set the address is virtual.
|
||
|
||
MMDBG_COPY_UNSAFE - No locks are taken during operation. It
|
||
is the caller's responsibility to ensure
|
||
stability of the system during the call.
|
||
|
||
MMDBG_COPY_CACHED - If MMDBG_COPY_PHYSICAL is specified, then use
|
||
a PTE with the cached attribute for the mapping
|
||
to ensure TB coherence.
|
||
|
||
MMDBG_COPY_UNCACHED - If MMDBG_COPY_PHYSICAL is specified, then use
|
||
a PTE with the uncached attribute for the mapping
|
||
to ensure TB coherence.
|
||
|
||
MMDBG_COPY_WRITE_COMBINED - If MMDBG_COPY_PHYSICAL is specified, then
|
||
use a PTE with the writecombined attribute
|
||
for the mapping to ensure TB coherence.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS.
|
||
|
||
--*/
|
||
|
||
{
|
||
LOGICAL ForceWritableIfPossible;
|
||
ULONG i;
|
||
KIRQL PfnIrql;
|
||
KIRQL OldIrql;
|
||
PVOID VirtualAddress;
|
||
HARDWARE_PTE Opaque;
|
||
CHAR TempBuffer[8];
|
||
PCHAR SourceBuffer;
|
||
PCHAR TargetBuffer;
|
||
PHYSICAL_ADDRESS PhysicalAddress;
|
||
PETHREAD Thread;
|
||
LOGICAL PfnHeld;
|
||
ULONG WsHeld;
|
||
|
||
switch (Size) {
|
||
case 1:
|
||
break;
|
||
case 2:
|
||
break;
|
||
case 4:
|
||
break;
|
||
case 8:
|
||
break;
|
||
default:
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
}
|
||
|
||
if (UntrustedAddress & (Size - 1)) {
|
||
|
||
//
|
||
// The untrusted address is not properly aligned with the requested
|
||
// transfer size. This is a caller error.
|
||
//
|
||
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
}
|
||
|
||
if (((ULONG)UntrustedAddress & ~(Size - 1)) !=
|
||
(((ULONG)UntrustedAddress + Size - 1) & ~(Size - 1))) {
|
||
|
||
//
|
||
// The range spanned by the untrusted address crosses a page boundary.
|
||
// Straddling pages is not allowed. This is a caller error.
|
||
//
|
||
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
}
|
||
|
||
PfnHeld = FALSE;
|
||
WsHeld = 0;
|
||
|
||
//
|
||
// Initializing OldIrql and PhysicalAddress are not needed for
|
||
// correctness but without it the compiler cannot compile this code
|
||
// W4 to check for use of uninitialized variables.
|
||
//
|
||
|
||
PfnIrql = PASSIVE_LEVEL;
|
||
OldIrql = PASSIVE_LEVEL;
|
||
PhysicalAddress.LowPart = 0;
|
||
ForceWritableIfPossible = TRUE;
|
||
|
||
if ((Flags & MMDBG_COPY_PHYSICAL) == 0) {
|
||
|
||
//
|
||
// If the caller has not frozen the machine (ie: this is localkd or the
|
||
// equivalent), then acquire the PFN lock. This keeps the address
|
||
// valid even after the return from the MmIsAddressValid call. Note
|
||
// that for system (or session) addresses, the relevant working set
|
||
// mutex is acquired to prevent the page from getting trimmed or the
|
||
// PTE access bit from getting cleared. For user space addresses,
|
||
// no mutex is needed because the access is performed using the user
|
||
// virtual address inside an exception handler.
|
||
//
|
||
|
||
if ((Flags & MMDBG_COPY_UNSAFE) == 0) {
|
||
|
||
if (KeGetCurrentIrql () > APC_LEVEL) {
|
||
return STATUS_INVALID_PARAMETER_4;
|
||
}
|
||
|
||
//
|
||
// Note that for safe copy mode (ie: the system is live), the
|
||
// address must not be made writable if it is not already because
|
||
// other threads might concurrently access it this way and losing
|
||
// copy-on-write semantics, etc would be very bad.
|
||
//
|
||
|
||
ForceWritableIfPossible = FALSE;
|
||
|
||
if ((PVOID) (ULONG_PTR) UntrustedAddress >= MmSystemRangeStart) {
|
||
|
||
Thread = PsGetCurrentThread ();
|
||
|
||
if (MmIsSessionAddress ((PVOID)(ULONG_PTR)UntrustedAddress)) {
|
||
if (MmGetSessionId (PsGetCurrentProcess ()) == 0) {
|
||
return STATUS_INVALID_PARAMETER_1;
|
||
}
|
||
|
||
WsHeld = 1;
|
||
LOCK_SESSION_SPACE_WS (OldIrql, Thread);
|
||
}
|
||
else {
|
||
WsHeld = 2;
|
||
LOCK_SYSTEM_WS (OldIrql, Thread);
|
||
}
|
||
|
||
PfnHeld = TRUE;
|
||
LOCK_PFN (PfnIrql);
|
||
}
|
||
else {
|
||
//
|
||
// The caller specified a user address. Probe and access
|
||
// the address carefully inside an exception handler.
|
||
//
|
||
|
||
try {
|
||
if (Flags & MMDBG_COPY_WRITE) {
|
||
ProbeForWrite ((PVOID)(ULONG_PTR)UntrustedAddress, Size, Size);
|
||
}
|
||
else {
|
||
ProbeForRead ((PVOID)(ULONG_PTR)UntrustedAddress, Size, Size);
|
||
}
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
VirtualAddress = (PVOID) (ULONG_PTR) UntrustedAddress;
|
||
|
||
if (Flags & MMDBG_COPY_WRITE) {
|
||
goto WriteData;
|
||
}
|
||
else {
|
||
goto ReadData;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (MmIsAddressValid ((PVOID) (ULONG_PTR) UntrustedAddress) == FALSE) {
|
||
|
||
if (PfnHeld == TRUE) {
|
||
UNLOCK_PFN (PfnIrql);
|
||
}
|
||
if (WsHeld == 1) {
|
||
UNLOCK_SESSION_SPACE_WS (OldIrql);
|
||
}
|
||
else if (WsHeld == 2) {
|
||
UNLOCK_SYSTEM_WS (OldIrql);
|
||
}
|
||
|
||
return STATUS_INVALID_PARAMETER_1;
|
||
}
|
||
|
||
VirtualAddress = (PVOID) (ULONG_PTR) UntrustedAddress;
|
||
}
|
||
else {
|
||
|
||
PhysicalAddress.QuadPart = UntrustedAddress;
|
||
|
||
//
|
||
// If the caller has not frozen the machine (ie: this is localkd or the
|
||
// equivalent), then acquire the PFN lock. This prevents
|
||
// MmPhysicalMemoryBlock from changing inside the debug PTE routines
|
||
// and also blocks APCs so malicious callers cannot suspend us
|
||
// while we hold the debug PTE.
|
||
//
|
||
|
||
if ((Flags & MMDBG_COPY_UNSAFE) == 0) {
|
||
|
||
if (KeGetCurrentIrql () > APC_LEVEL) {
|
||
return STATUS_INVALID_PARAMETER_4;
|
||
}
|
||
|
||
PfnHeld = TRUE;
|
||
LOCK_PFN (PfnIrql);
|
||
}
|
||
|
||
VirtualAddress = (PVOID) (ULONG_PTR) MiDbgTranslatePhysicalAddress (PhysicalAddress, Flags);
|
||
|
||
if (VirtualAddress == NULL) {
|
||
if (PfnHeld == TRUE) {
|
||
UNLOCK_PFN (PfnIrql);
|
||
}
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
if (Flags & MMDBG_COPY_WRITE) {
|
||
VirtualAddress = MiDbgWriteCheck (VirtualAddress, &Opaque, ForceWritableIfPossible);
|
||
|
||
if (VirtualAddress == NULL) {
|
||
if (PfnHeld == TRUE) {
|
||
UNLOCK_PFN (PfnIrql);
|
||
}
|
||
if (WsHeld == 1) {
|
||
UNLOCK_SESSION_SPACE_WS (OldIrql);
|
||
}
|
||
else if (WsHeld == 2) {
|
||
UNLOCK_SYSTEM_WS (OldIrql);
|
||
}
|
||
return STATUS_INVALID_PARAMETER_1;
|
||
}
|
||
|
||
WriteData:
|
||
|
||
//
|
||
// Carefully capture the source buffer into a local *aligned* buffer
|
||
// as the write to the target must be done using the desired operation
|
||
// size specified by the caller. This is because the target may be
|
||
// a memory mapped device which requires specific transfer sizes.
|
||
//
|
||
|
||
SourceBuffer = (PCHAR) Buffer;
|
||
|
||
try {
|
||
for (i = 0; i < Size; i += 1) {
|
||
TempBuffer[i] = *SourceBuffer;
|
||
SourceBuffer += 1;
|
||
}
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
ASSERT (WsHeld == 0);
|
||
ASSERT (PfnHeld == FALSE);
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
switch (Size) {
|
||
case 1:
|
||
*(PCHAR) VirtualAddress = *(PCHAR) TempBuffer;
|
||
break;
|
||
case 2:
|
||
*(PSHORT) VirtualAddress = *(PSHORT) TempBuffer;
|
||
break;
|
||
case 4:
|
||
*(PULONG) VirtualAddress = *(PULONG) TempBuffer;
|
||
break;
|
||
case 8:
|
||
*(PULONGLONG) VirtualAddress = *(PULONGLONG) TempBuffer;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if ((PVOID) (ULONG_PTR) UntrustedAddress >= MmSystemRangeStart) {
|
||
MiDbgReleaseAddress (VirtualAddress, &Opaque);
|
||
}
|
||
}
|
||
else {
|
||
|
||
ReadData:
|
||
|
||
try {
|
||
switch (Size) {
|
||
case 1:
|
||
*(PCHAR) TempBuffer = *(PCHAR) VirtualAddress;
|
||
break;
|
||
case 2:
|
||
*(PSHORT) TempBuffer = *(PSHORT) VirtualAddress;
|
||
break;
|
||
case 4:
|
||
*(PULONG) TempBuffer = *(PULONG) VirtualAddress;
|
||
break;
|
||
case 8:
|
||
*(PULONGLONG) TempBuffer = *(PULONGLONG) VirtualAddress;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
ASSERT (WsHeld == 0);
|
||
ASSERT (PfnHeld == FALSE);
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// The buffer to fill may not be aligned so do it one character at
|
||
// a time.
|
||
//
|
||
|
||
TargetBuffer = (PCHAR) Buffer;
|
||
|
||
for (i = 0; i < Size; i += 1) {
|
||
*TargetBuffer = TempBuffer[i];
|
||
TargetBuffer += 1;
|
||
}
|
||
}
|
||
|
||
if (Flags & MMDBG_COPY_PHYSICAL) {
|
||
MiDbgUnTranslatePhysicalAddress ();
|
||
}
|
||
|
||
if (PfnHeld == TRUE) {
|
||
UNLOCK_PFN (PfnIrql);
|
||
}
|
||
|
||
if (WsHeld == 1) {
|
||
UNLOCK_SESSION_SPACE_WS (OldIrql);
|
||
}
|
||
else if (WsHeld == 2) {
|
||
UNLOCK_SYSTEM_WS (OldIrql);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
LOGICAL
|
||
MmDbgIsLowMemOk (
|
||
IN PFN_NUMBER PageFrameIndex,
|
||
OUT PPFN_NUMBER NextPageFrameIndex,
|
||
IN OUT PULONG CorruptionOffset
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a special function called only from the kernel debugger
|
||
to check that the physical memory below 4Gb removed with /NOLOWMEM
|
||
contains the expected fill patterns. If not, there is a high
|
||
probability that a driver which cannot handle physical addresses greater
|
||
than 32 bits corrupted the memory.
|
||
|
||
Arguments:
|
||
|
||
PageFrameIndex - Supplies the physical page number to check.
|
||
|
||
NextPageFrameIndex - Supplies the next physical page number the caller
|
||
should check or 0 if the search is complete.
|
||
|
||
CorruptionOffset - If corruption is found, the byte offset
|
||
of the corruption start is returned here.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the page was removed and the fill pattern is correct, or
|
||
if the page was never removed. FALSE if corruption was detected
|
||
in the page.
|
||
|
||
Environment:
|
||
|
||
This routine is for use of the kernel debugger ONLY, specifically
|
||
the !chklowmem command.
|
||
|
||
The debugger's PTE will be repointed.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if defined (_MI_MORE_THAN_4GB_)
|
||
|
||
PULONG Va;
|
||
ULONG Index;
|
||
PHYSICAL_ADDRESS Pa;
|
||
#if DBG
|
||
PMMPFN Pfn;
|
||
#endif
|
||
|
||
if (MiNoLowMemory == 0) {
|
||
*NextPageFrameIndex = 0;
|
||
return TRUE;
|
||
}
|
||
|
||
if (MiLowMemoryBitMap == NULL) {
|
||
*NextPageFrameIndex = 0;
|
||
return TRUE;
|
||
}
|
||
|
||
if (PageFrameIndex >= MiNoLowMemory - 1) {
|
||
*NextPageFrameIndex = 0;
|
||
}
|
||
else {
|
||
*NextPageFrameIndex = PageFrameIndex + 1;
|
||
}
|
||
|
||
//
|
||
// Verify that the page to be verified is one of the reclaimed
|
||
// pages.
|
||
//
|
||
|
||
if ((PageFrameIndex >= MiLowMemoryBitMap->SizeOfBitMap) ||
|
||
(RtlCheckBit (MiLowMemoryBitMap, PageFrameIndex) == 0)) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// At this point we have a low page that is not in active use.
|
||
// The fill pattern must match.
|
||
//
|
||
|
||
#if DBG
|
||
Pfn = MI_PFN_ELEMENT (PageFrameIndex);
|
||
ASSERT (Pfn->u4.PteFrame == MI_MAGIC_4GB_RECLAIM);
|
||
ASSERT (Pfn->u3.e1.PageLocation == ActiveAndValid);
|
||
#endif
|
||
|
||
//
|
||
// Map the physical page using the debug PTE so the
|
||
// fill pattern can be validated.
|
||
//
|
||
// The debugger cannot be using this virtual address on entry or exit.
|
||
//
|
||
|
||
Pa.QuadPart = ((ULONGLONG)PageFrameIndex) << PAGE_SHIFT;
|
||
|
||
Va = (PULONG) MiDbgTranslatePhysicalAddress (Pa, 0);
|
||
|
||
if (Va == NULL) {
|
||
return TRUE;
|
||
}
|
||
|
||
for (Index = 0; Index < PAGE_SIZE / sizeof(ULONG); Index += 1) {
|
||
|
||
if (*Va != (PageFrameIndex | MI_LOWMEM_MAGIC_BIT)) {
|
||
|
||
if (CorruptionOffset != NULL) {
|
||
*CorruptionOffset = Index * sizeof(ULONG);
|
||
}
|
||
|
||
MiDbgUnTranslatePhysicalAddress ();
|
||
return FALSE;
|
||
}
|
||
|
||
Va += 1;
|
||
}
|
||
MiDbgUnTranslatePhysicalAddress ();
|
||
#else
|
||
UNREFERENCED_PARAMETER (PageFrameIndex);
|
||
UNREFERENCED_PARAMETER (CorruptionOffset);
|
||
|
||
*NextPageFrameIndex = 0;
|
||
#endif
|
||
|
||
return TRUE;
|
||
}
|