mirror of
https://github.com/reactos/reactos.git
synced 2024-09-02 08:38:28 +00:00
809 lines
21 KiB
C
809 lines
21 KiB
C
/*
|
|
* COPYRIGHT: GPLv2+ - See COPYING in the top level directory
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
* FILE: subsystems/mvdm/ntvdm/memory.c
|
|
* PURPOSE: Memory Management
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "ntvdm.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include "emulator.h"
|
|
#include "memory.h"
|
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
typedef struct _MEM_HOOK
|
|
{
|
|
LIST_ENTRY Entry;
|
|
HANDLE hVdd;
|
|
ULONG Count;
|
|
|
|
union
|
|
{
|
|
PVDD_MEMORY_HANDLER VddHandler;
|
|
|
|
struct
|
|
{
|
|
PMEMORY_READ_HANDLER FastReadHandler;
|
|
PMEMORY_WRITE_HANDLER FastWriteHandler;
|
|
};
|
|
};
|
|
} MEM_HOOK, *PMEM_HOOK;
|
|
|
|
static LIST_ENTRY HookList;
|
|
static PMEM_HOOK PageTable[TOTAL_PAGES] = { NULL };
|
|
static BOOLEAN A20Line = FALSE;
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
static inline VOID
|
|
MemFastMoveMemory(OUT VOID UNALIGNED *Destination,
|
|
IN const VOID UNALIGNED *Source,
|
|
IN SIZE_T Length)
|
|
{
|
|
#if 1
|
|
/*
|
|
* We use a switch here to detect small moves of memory, as these
|
|
* constitute the bulk of our moves.
|
|
* Using RtlMoveMemory for all these small moves would be slow otherwise.
|
|
*/
|
|
switch (Length)
|
|
{
|
|
case 0:
|
|
return;
|
|
|
|
case sizeof(UCHAR):
|
|
*(PUCHAR)Destination = *(PUCHAR)Source;
|
|
return;
|
|
|
|
case sizeof(USHORT):
|
|
*(PUSHORT)Destination = *(PUSHORT)Source;
|
|
return;
|
|
|
|
case sizeof(ULONG):
|
|
*(PULONG)Destination = *(PULONG)Source;
|
|
return;
|
|
|
|
case sizeof(ULONGLONG):
|
|
*(PULONGLONG)Destination = *(PULONGLONG)Source;
|
|
return;
|
|
|
|
default:
|
|
#if defined(__GNUC__)
|
|
__builtin_memmove(Destination, Source, Length);
|
|
#else
|
|
RtlMoveMemory(Destination, Source, Length);
|
|
#endif
|
|
}
|
|
|
|
#else // defined(_MSC_VER)
|
|
|
|
PUCHAR Dest = (PUCHAR)Destination;
|
|
PUCHAR Src = (PUCHAR)Source;
|
|
|
|
SIZE_T Count, NewSize = Length;
|
|
|
|
/* Move dword */
|
|
Count = NewSize >> 2; // NewSize / sizeof(ULONG);
|
|
NewSize = NewSize & 3; // NewSize % sizeof(ULONG);
|
|
__movsd(Dest, Src, Count);
|
|
Dest += Count << 2; // Count * sizeof(ULONG);
|
|
Src += Count << 2;
|
|
|
|
/* Move word */
|
|
Count = NewSize >> 1; // NewSize / sizeof(USHORT);
|
|
NewSize = NewSize & 1; // NewSize % sizeof(USHORT);
|
|
__movsw(Dest, Src, Count);
|
|
Dest += Count << 1; // Count * sizeof(USHORT);
|
|
Src += Count << 1;
|
|
|
|
/* Move byte */
|
|
Count = NewSize; // NewSize / sizeof(UCHAR);
|
|
// NewSize = NewSize; // NewSize % sizeof(UCHAR);
|
|
__movsb(Dest, Src, Count);
|
|
|
|
#endif
|
|
}
|
|
|
|
static inline VOID
|
|
ReadPage(PMEM_HOOK Hook, ULONG Address, PVOID Buffer, ULONG Size)
|
|
{
|
|
if (Hook && !Hook->hVdd && Hook->FastReadHandler)
|
|
{
|
|
Hook->FastReadHandler(Address, REAL_TO_PHYS(Address), Size);
|
|
}
|
|
|
|
MemFastMoveMemory(Buffer, REAL_TO_PHYS(Address), Size);
|
|
}
|
|
|
|
static inline VOID
|
|
WritePage(PMEM_HOOK Hook, ULONG Address, PVOID Buffer, ULONG Size)
|
|
{
|
|
if (!Hook
|
|
|| Hook->hVdd
|
|
|| !Hook->FastWriteHandler
|
|
|| Hook->FastWriteHandler(Address, Buffer, Size))
|
|
{
|
|
MemFastMoveMemory(REAL_TO_PHYS(Address), Buffer, Size);
|
|
}
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
VOID FASTCALL EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
|
|
{
|
|
ULONG i, Offset, Length;
|
|
ULONG FirstPage, LastPage;
|
|
|
|
UNREFERENCED_PARAMETER(State);
|
|
|
|
/* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
|
|
if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
|
|
|
|
/* If the A20 line is disabled, mask bit 20 */
|
|
if (!A20Line) Address &= ~(1 << 20);
|
|
|
|
if ((Address + Size - 1) >= MAX_ADDRESS)
|
|
{
|
|
ULONG ExtraStart = (Address < MAX_ADDRESS) ? MAX_ADDRESS - Address : 0;
|
|
|
|
/* Fill the memory that was above the limit with 0xFF */
|
|
RtlFillMemory((PVOID)((ULONG_PTR)Buffer + ExtraStart), Size - ExtraStart, 0xFF);
|
|
|
|
if (Address < MAX_ADDRESS) Size = MAX_ADDRESS - Address;
|
|
else return;
|
|
}
|
|
|
|
FirstPage = Address >> 12;
|
|
LastPage = (Address + Size - 1) >> 12;
|
|
|
|
if (FirstPage == LastPage)
|
|
{
|
|
ReadPage(PageTable[FirstPage], Address, Buffer, Size);
|
|
}
|
|
else
|
|
{
|
|
for (i = FirstPage; i <= LastPage; i++)
|
|
{
|
|
Offset = (i == FirstPage) ? (Address & (PAGE_SIZE - 1)) : 0;
|
|
Length = ((i == LastPage) ? (Address + Size - (LastPage << 12)) : PAGE_SIZE) - Offset;
|
|
|
|
ReadPage(PageTable[i], (i << 12) + Offset, Buffer, Length);
|
|
Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID FASTCALL EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
|
|
{
|
|
ULONG i, Offset, Length;
|
|
ULONG FirstPage, LastPage;
|
|
|
|
UNREFERENCED_PARAMETER(State);
|
|
|
|
/* If the A20 line is disabled, mask bit 20 */
|
|
if (!A20Line) Address &= ~(1 << 20);
|
|
|
|
if (Address >= MAX_ADDRESS) return;
|
|
Size = min(Size, MAX_ADDRESS - Address);
|
|
|
|
FirstPage = Address >> 12;
|
|
LastPage = (Address + Size - 1) >> 12;
|
|
|
|
if (FirstPage == LastPage)
|
|
{
|
|
WritePage(PageTable[FirstPage], Address, Buffer, Size);
|
|
}
|
|
else
|
|
{
|
|
for (i = FirstPage; i <= LastPage; i++)
|
|
{
|
|
Offset = (i == FirstPage) ? (Address & (PAGE_SIZE - 1)) : 0;
|
|
Length = ((i == LastPage) ? (Address + Size - (LastPage << 12)) : PAGE_SIZE) - Offset;
|
|
|
|
WritePage(PageTable[i], (i << 12) + Offset, Buffer, Length);
|
|
Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID FASTCALL EmulatorCopyMemory(PFAST486_STATE State, ULONG DestAddress, ULONG SrcAddress, ULONG Size)
|
|
{
|
|
/*
|
|
* Guest-to-guest memory copy
|
|
*/
|
|
|
|
// FIXME: This is a temporary implementation of a more useful functionality
|
|
// which should be a merge of EmulatorReadMemory & EmulatorWriteMemory without
|
|
// any local external buffer.
|
|
// NOTE: Process heap is by default serialized (unless one specifies it shouldn't).
|
|
static BYTE StaticBuffer[8192]; // Smallest static buffer we can use.
|
|
static PVOID HeapBuffer = NULL; // Always-growing heap buffer. Use it in case StaticBuffer is too small.
|
|
static ULONG HeapBufferSize = 0;
|
|
PVOID LocalBuffer; // Points to either StaticBuffer or HeapBuffer
|
|
|
|
if (Size <= sizeof(StaticBuffer))
|
|
{
|
|
/* Use the static buffer */
|
|
LocalBuffer = StaticBuffer;
|
|
}
|
|
else if (/* sizeof(StaticBuffer) <= Size && */ Size <= HeapBufferSize)
|
|
{
|
|
/* Use the heap buffer */
|
|
ASSERT(HeapBufferSize > 0 && HeapBuffer != NULL);
|
|
LocalBuffer = HeapBuffer;
|
|
}
|
|
else // if (Size > HeapBufferSize)
|
|
{
|
|
/* Enlarge the heap buffer and use it */
|
|
|
|
if (HeapBuffer == NULL)
|
|
{
|
|
/* First allocation */
|
|
LocalBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
|
|
}
|
|
else
|
|
{
|
|
/* Reallocation */
|
|
LocalBuffer = RtlReAllocateHeap(RtlGetProcessHeap(), 0 /* HEAP_GENERATE_EXCEPTIONS */, HeapBuffer, Size);
|
|
}
|
|
ASSERT(LocalBuffer != NULL); // We must succeed! TODO: Handle it more properly.
|
|
HeapBuffer = LocalBuffer; // HeapBuffer is now reallocated.
|
|
HeapBufferSize = Size;
|
|
}
|
|
|
|
/* Perform memory copy */
|
|
EmulatorReadMemory( State, SrcAddress , LocalBuffer, Size);
|
|
EmulatorWriteMemory(State, DestAddress, LocalBuffer, Size);
|
|
|
|
// if (LocalBuffer != StaticBuffer)
|
|
// RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
|
|
|
|
// Note that we don't free HeapBuffer since it's an always-growing buffer.
|
|
// It is freed when NTVDM termiantes.
|
|
}
|
|
|
|
VOID EmulatorSetA20(BOOLEAN Enabled)
|
|
{
|
|
A20Line = Enabled;
|
|
}
|
|
|
|
BOOLEAN EmulatorGetA20(VOID)
|
|
{
|
|
return A20Line;
|
|
}
|
|
|
|
VOID
|
|
MemExceptionHandler(ULONG FaultAddress, BOOLEAN Writing)
|
|
{
|
|
PMEM_HOOK Hook = PageTable[FaultAddress >> 12];
|
|
DPRINT("The memory at 0x%08X could not be %s.\n", FaultAddress, Writing ? "written" : "read");
|
|
|
|
/* Exceptions are only supposed to happen when using VDD-style memory hooks */
|
|
ASSERT(FaultAddress < MAX_ADDRESS && Hook != NULL && Hook->hVdd != NULL);
|
|
|
|
/* Call the VDD handler */
|
|
Hook->VddHandler(REAL_TO_PHYS(FaultAddress), (ULONG)Writing);
|
|
}
|
|
|
|
BOOL
|
|
MemInstallFastMemoryHook(PVOID Address,
|
|
ULONG Size,
|
|
PMEMORY_READ_HANDLER ReadHandler,
|
|
PMEMORY_WRITE_HANDLER WriteHandler)
|
|
{
|
|
PMEM_HOOK Hook;
|
|
ULONG i;
|
|
ULONG FirstPage = (ULONG_PTR)Address >> 12;
|
|
ULONG LastPage = ((ULONG_PTR)Address + Size - 1) >> 12;
|
|
PLIST_ENTRY Pointer;
|
|
|
|
/* Make sure none of these pages are already allocated */
|
|
for (i = FirstPage; i <= LastPage; i++)
|
|
{
|
|
if (PageTable[i] != NULL) return FALSE;
|
|
}
|
|
|
|
for (Pointer = HookList.Flink; Pointer != &HookList; Pointer = Pointer->Flink)
|
|
{
|
|
Hook = CONTAINING_RECORD(Pointer, MEM_HOOK, Entry);
|
|
|
|
if (Hook->hVdd == NULL
|
|
&& Hook->FastReadHandler == ReadHandler
|
|
&& Hook->FastWriteHandler == WriteHandler)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Pointer == &HookList)
|
|
{
|
|
/* Create and initialize a new hook entry... */
|
|
Hook = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook));
|
|
if (Hook == NULL) return FALSE;
|
|
|
|
Hook->hVdd = NULL;
|
|
Hook->Count = 0;
|
|
Hook->FastReadHandler = ReadHandler;
|
|
Hook->FastWriteHandler = WriteHandler;
|
|
|
|
/* ... and add it to the list of hooks */
|
|
InsertTailList(&HookList, &Hook->Entry);
|
|
}
|
|
|
|
/* Increase the number of pages this hook has */
|
|
Hook->Count += LastPage - FirstPage + 1;
|
|
|
|
/* Add the hook entry to the page table */
|
|
for (i = FirstPage; i <= LastPage; i++) PageTable[i] = Hook;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
MemRemoveFastMemoryHook(PVOID Address, ULONG Size)
|
|
{
|
|
PMEM_HOOK Hook;
|
|
ULONG i;
|
|
ULONG FirstPage = (ULONG_PTR)Address >> 12;
|
|
ULONG LastPage = ((ULONG_PTR)Address + Size - 1) >> 12;
|
|
|
|
if (Size == 0) return FALSE;
|
|
|
|
for (i = FirstPage; i <= LastPage; i++)
|
|
{
|
|
Hook = PageTable[i];
|
|
if (Hook == NULL || Hook->hVdd != NULL) continue;
|
|
|
|
if (--Hook->Count == 0)
|
|
{
|
|
/* This hook has no more pages */
|
|
RemoveEntryList(&Hook->Entry);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
|
|
}
|
|
|
|
PageTable[i] = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
MemQueryMemoryZone(ULONG StartAddress, PULONG Length, PBOOLEAN Hooked)
|
|
{
|
|
ULONG Page = StartAddress >> 12;
|
|
if (Page >= TOTAL_PAGES) return FALSE;
|
|
|
|
*Length = 0;
|
|
*Hooked = PageTable[Page] != NULL;
|
|
|
|
while (Page < TOTAL_PAGES && (PageTable[Page] != NULL) == *Hooked)
|
|
{
|
|
*Length += PAGE_SIZE;
|
|
Page++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PBYTE
|
|
WINAPI
|
|
Sim32pGetVDMPointer(IN ULONG Address,
|
|
IN BOOLEAN ProtectedMode)
|
|
{
|
|
// FIXME
|
|
UNREFERENCED_PARAMETER(ProtectedMode);
|
|
|
|
/*
|
|
* HIWORD(Address) == Segment (if ProtectedMode == FALSE)
|
|
* or Selector (if ProtectedMode == TRUE )
|
|
* LOWORD(Address) == Offset
|
|
*/
|
|
return (PBYTE)FAR_POINTER(Address);
|
|
}
|
|
|
|
PBYTE
|
|
WINAPI
|
|
MGetVdmPointer(IN ULONG Address,
|
|
IN ULONG Size,
|
|
IN BOOLEAN ProtectedMode)
|
|
{
|
|
UNREFERENCED_PARAMETER(Size);
|
|
return Sim32pGetVDMPointer(Address, ProtectedMode);
|
|
}
|
|
|
|
PVOID
|
|
WINAPI
|
|
VdmMapFlat(IN USHORT Segment,
|
|
IN ULONG Offset,
|
|
IN VDM_MODE Mode)
|
|
{
|
|
// FIXME
|
|
UNREFERENCED_PARAMETER(Mode);
|
|
|
|
return SEG_OFF_TO_PTR(Segment, Offset);
|
|
}
|
|
|
|
#ifndef VdmFlushCache
|
|
|
|
BOOL
|
|
WINAPI
|
|
VdmFlushCache(IN USHORT Segment,
|
|
IN ULONG Offset,
|
|
IN ULONG Size,
|
|
IN VDM_MODE Mode)
|
|
{
|
|
// FIXME
|
|
UNIMPLEMENTED;
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef VdmUnmapFlat
|
|
|
|
BOOL
|
|
WINAPI
|
|
VdmUnmapFlat(IN USHORT Segment,
|
|
IN ULONG Offset,
|
|
IN PVOID Buffer,
|
|
IN VDM_MODE Mode)
|
|
{
|
|
// FIXME
|
|
UNIMPLEMENTED;
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL
|
|
WINAPI
|
|
VDDInstallMemoryHook(IN HANDLE hVdd,
|
|
IN PVOID pStart,
|
|
IN DWORD dwCount,
|
|
IN PVDD_MEMORY_HANDLER MemoryHandler)
|
|
{
|
|
NTSTATUS Status;
|
|
PMEM_HOOK Hook;
|
|
ULONG i;
|
|
ULONG FirstPage = (ULONG_PTR)PHYS_TO_REAL(pStart) >> 12;
|
|
ULONG LastPage = ((ULONG_PTR)PHYS_TO_REAL(pStart) + dwCount - 1) >> 12;
|
|
PVOID Address = (PVOID)REAL_TO_PHYS(FirstPage * PAGE_SIZE);
|
|
SIZE_T Size = (LastPage - FirstPage + 1) * PAGE_SIZE;
|
|
PLIST_ENTRY Pointer;
|
|
|
|
/* Check validity of the VDD handle */
|
|
if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (dwCount == 0) return FALSE;
|
|
|
|
/* Make sure none of these pages are already allocated */
|
|
for (i = FirstPage; i <= LastPage; i++)
|
|
{
|
|
if (PageTable[i] != NULL) return FALSE;
|
|
}
|
|
|
|
for (Pointer = HookList.Flink; Pointer != &HookList; Pointer = Pointer->Flink)
|
|
{
|
|
Hook = CONTAINING_RECORD(Pointer, MEM_HOOK, Entry);
|
|
if (Hook->hVdd == hVdd && Hook->VddHandler == MemoryHandler) break;
|
|
}
|
|
|
|
if (Pointer == &HookList)
|
|
{
|
|
/* Create and initialize a new hook entry... */
|
|
Hook = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook));
|
|
if (Hook == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
Hook->hVdd = hVdd;
|
|
Hook->Count = 0;
|
|
Hook->VddHandler = MemoryHandler;
|
|
|
|
/* ... and add it to the list of hooks */
|
|
InsertTailList(&HookList, &Hook->Entry);
|
|
}
|
|
|
|
/* Decommit the pages */
|
|
Status = NtFreeVirtualMemory(NtCurrentProcess(),
|
|
&Address,
|
|
&Size,
|
|
MEM_DECOMMIT);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Pointer == &HookList)
|
|
{
|
|
RemoveEntryList(&Hook->Entry);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* Increase the number of pages this hook has */
|
|
Hook->Count += LastPage - FirstPage + 1;
|
|
|
|
/* Add the hook entry to the page table */
|
|
for (i = FirstPage; i <= LastPage; i++) PageTable[i] = Hook;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
VDDDeInstallMemoryHook(IN HANDLE hVdd,
|
|
IN PVOID pStart,
|
|
IN DWORD dwCount)
|
|
{
|
|
NTSTATUS Status;
|
|
PMEM_HOOK Hook;
|
|
ULONG i;
|
|
ULONG FirstPage = (ULONG_PTR)PHYS_TO_REAL(pStart) >> 12;
|
|
ULONG LastPage = ((ULONG_PTR)PHYS_TO_REAL(pStart) + dwCount - 1) >> 12;
|
|
PVOID Address = (PVOID)REAL_TO_PHYS(FirstPage * PAGE_SIZE);
|
|
SIZE_T Size = (LastPage - FirstPage + 1) * PAGE_SIZE;
|
|
|
|
/* Check validity of the VDD handle */
|
|
if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (dwCount == 0) return FALSE;
|
|
|
|
/* Commit the pages */
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&Address,
|
|
0,
|
|
&Size,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status)) return FALSE;
|
|
|
|
for (i = FirstPage; i <= LastPage; i++)
|
|
{
|
|
Hook = PageTable[i];
|
|
if (Hook == NULL) continue;
|
|
|
|
if (Hook->hVdd != hVdd)
|
|
{
|
|
DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i);
|
|
continue;
|
|
}
|
|
|
|
if (--Hook->Count == 0)
|
|
{
|
|
/* This hook has no more pages */
|
|
RemoveEntryList(&Hook->Entry);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
|
|
}
|
|
|
|
PageTable[i] = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
VDDAllocMem(IN HANDLE hVdd,
|
|
IN PVOID Address,
|
|
IN ULONG Size)
|
|
{
|
|
NTSTATUS Status;
|
|
PMEM_HOOK Hook;
|
|
ULONG i;
|
|
ULONG FirstPage = (ULONG_PTR)PHYS_TO_REAL(Address) >> 12;
|
|
ULONG LastPage = ((ULONG_PTR)PHYS_TO_REAL(Address) + Size - 1) >> 12;
|
|
SIZE_T RealSize = (LastPage - FirstPage + 1) * PAGE_SIZE;
|
|
|
|
/* Check validity of the VDD handle */
|
|
if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Size == 0) return FALSE;
|
|
|
|
/* Fixup the address */
|
|
Address = (PVOID)REAL_TO_PHYS(FirstPage * PAGE_SIZE);
|
|
|
|
/* Be sure that all the region is held by the VDD */
|
|
for (i = FirstPage; i <= LastPage; i++)
|
|
{
|
|
Hook = PageTable[i];
|
|
if (Hook == NULL) return FALSE;
|
|
|
|
if (Hook->hVdd != hVdd)
|
|
{
|
|
DPRINT1("VDDAllocMem: Page %u owned by someone else.\n", i);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* OK, all the range is held by the VDD. Commit the pages. */
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&Address,
|
|
0,
|
|
&RealSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
VDDFreeMem(IN HANDLE hVdd,
|
|
IN PVOID Address,
|
|
IN ULONG Size)
|
|
{
|
|
NTSTATUS Status;
|
|
PMEM_HOOK Hook;
|
|
ULONG i;
|
|
ULONG FirstPage = (ULONG_PTR)PHYS_TO_REAL(Address) >> 12;
|
|
ULONG LastPage = ((ULONG_PTR)PHYS_TO_REAL(Address) + Size - 1) >> 12;
|
|
SIZE_T RealSize = (LastPage - FirstPage + 1) * PAGE_SIZE;
|
|
|
|
/* Check validity of the VDD handle */
|
|
if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Size == 0) return FALSE;
|
|
|
|
/* Fixup the address */
|
|
Address = (PVOID)REAL_TO_PHYS(FirstPage * PAGE_SIZE);
|
|
|
|
/* Be sure that all the region is held by the VDD */
|
|
for (i = FirstPage; i <= LastPage; i++)
|
|
{
|
|
Hook = PageTable[i];
|
|
if (Hook == NULL) return FALSE;
|
|
|
|
if (Hook->hVdd != hVdd)
|
|
{
|
|
DPRINT1("VDDFreeMem: Page %u owned by someone else.\n", i);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* OK, all the range is held by the VDD. Decommit the pages. */
|
|
Status = NtFreeVirtualMemory(NtCurrentProcess(),
|
|
&Address,
|
|
&RealSize,
|
|
MEM_DECOMMIT);
|
|
return NT_SUCCESS(Status);
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
VDDIncludeMem(IN HANDLE hVdd,
|
|
IN PVOID Address,
|
|
IN ULONG Size)
|
|
{
|
|
// FIXME
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
VDDExcludeMem(IN HANDLE hVdd,
|
|
IN PVOID Address,
|
|
IN ULONG Size)
|
|
{
|
|
// FIXME
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
MemInitialize(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
SIZE_T MemorySize = MAX_ADDRESS; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
|
|
|
|
InitializeListHead(&HookList);
|
|
|
|
#ifndef STANDALONE
|
|
|
|
/*
|
|
* The reserved region starts from the very first page.
|
|
* We need to commit the reserved first 16 MB virtual address.
|
|
*
|
|
* NOTE: NULL has another signification for NtAllocateVirtualMemory.
|
|
*/
|
|
BaseAddress = (PVOID)1;
|
|
|
|
/*
|
|
* Since to get NULL, we allocated from 0x1, account for this.
|
|
* See also: kernel32/client/proc.c!CreateProcessInternalW
|
|
*/
|
|
MemorySize -= 1;
|
|
|
|
#else
|
|
|
|
/* Allocate it anywhere */
|
|
BaseAddress = NULL;
|
|
|
|
#endif
|
|
|
|
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
|
|
&BaseAddress,
|
|
0,
|
|
&MemorySize,
|
|
#ifndef STANDALONE
|
|
MEM_COMMIT,
|
|
#else
|
|
MEM_RESERVE | MEM_COMMIT,
|
|
#endif
|
|
PAGE_EXECUTE_READWRITE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
wprintf(L"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
#ifndef STANDALONE
|
|
ASSERT(BaseAddress == NULL);
|
|
#endif
|
|
|
|
/*
|
|
* For diagnostics purposes, we fill the memory with INT 0x03 codes
|
|
* so that if a program wants to execute random code in memory, we can
|
|
* retrieve the exact CS:IP where the problem happens.
|
|
*/
|
|
RtlFillMemory(BaseAddress, MAX_ADDRESS, 0xCC);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
MemCleanup(VOID)
|
|
{
|
|
NTSTATUS Status;
|
|
SIZE_T MemorySize = MAX_ADDRESS;
|
|
PLIST_ENTRY Pointer;
|
|
|
|
while (!IsListEmpty(&HookList))
|
|
{
|
|
Pointer = RemoveHeadList(&HookList);
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer, MEM_HOOK, Entry));
|
|
}
|
|
|
|
/* Decommit the VDM memory */
|
|
Status = NtFreeVirtualMemory(NtCurrentProcess(),
|
|
&BaseAddress,
|
|
&MemorySize,
|
|
#ifndef STANDALONE
|
|
MEM_DECOMMIT
|
|
#else
|
|
MEM_RELEASE
|
|
#endif
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status);
|
|
}
|
|
}
|
|
|
|
/* EOF */
|