[NTVDM][FAST486]

- Implement VDDInstallMemoryHook and VDDDeInstallMemoryHook using page guards.
- Implement another API for memory hooks that should be faster than page guards
(for NTVDM only).
- Adjust the VGA and EMS memory handlers to use this method.
- In Fast486, implement a method that will allow us to "rewind" the current instruction,
in case it was interrupted by a memory hook page fault.
- Use a memory hook to protect the BIOS ROM from being written to.


svn path=/trunk/; revision=66666
This commit is contained in:
Aleksandar Andrejevic 2015-03-13 17:57:51 +00:00
parent 027720cd57
commit 888e1c450b
12 changed files with 668 additions and 233 deletions

View file

@ -576,6 +576,10 @@ Fast486SetSegment
USHORT Selector
);
VOID
NTAPI
Fast486Rewind(PFAST486_STATE State);
#endif // _FAST486_H_
/* EOF */

View file

@ -333,4 +333,14 @@ Fast486SetSegment(PFAST486_STATE State,
Fast486LoadSegment(State, Segment, Selector);
}
VOID
NTAPI
Fast486Rewind(PFAST486_STATE State)
{
/* This function is used when an instruction has been interrupted remotely */
State->PrefixFlags = 0;
State->InstPtr.Long = State->SavedInstPtr.Long;
State->PrefetchValid = FALSE;
}
/* EOF */

View file

@ -37,6 +37,7 @@ list(APPEND SOURCE
emulator.c
int32.c
io.c
memory.c
utils.c
vddsup.c
ntvdm.c
@ -45,6 +46,6 @@ list(APPEND SOURCE
add_executable(ntvdm ${SOURCE})
set_module_type(ntvdm win32cui UNICODE IMAGEBASE 0x0F000000)
target_link_libraries(ntvdm fast486)
target_link_libraries(ntvdm fast486 ${PSEH_LIB})
add_importlibs(ntvdm user32 gdi32 advapi32 msvcrt kernel32 ntdll)
add_cd_file(TARGET ntvdm DESTINATION reactos/system32 FOR all)

View file

@ -11,6 +11,7 @@
#define NDEBUG
#include "emulator.h"
#include "memory.h"
#include "cpu/callback.h"
#include "cpu/bop.h"
@ -40,6 +41,12 @@ PBIOS_CONFIG_TABLE Bct;
/* PRIVATE FUNCTIONS **********************************************************/
static BOOLEAN NTAPI BiosRomWrite(ULONG Address, PVOID Buffer, ULONG Size)
{
/* Prevent writing to ROM */
return FALSE;
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID WINAPI BiosEquipmentService(LPWORD Stack)
@ -74,6 +81,11 @@ BiosInitialize(IN LPCSTR BiosFileName)
// RegisterBop(BOP_EQUIPLIST , BiosEquipmentService);
// RegisterBop(BOP_GETMEMSIZE, BiosGetMemorySize);
MemInstallFastMemoryHook((PVOID)ROM_AREA_START,
ROM_AREA_END - ROM_AREA_START + 1,
NULL,
BiosRomWrite);
if (BiosFileName && BiosFileName[0] != '\0')
{
PVOID BiosLocation = NULL;

View file

@ -13,9 +13,11 @@
#include "cpu.h"
#include "emulator.h"
#include "memory.h"
#include "callback.h"
#include "bop.h"
#include <isvbop.h>
#include <pseh/pseh2.h>
#include "clock.h"
#include "bios/rom.h"
@ -112,6 +114,8 @@ VOID CpuStep(VOID)
VOID CpuSimulate(VOID)
{
EXCEPTION_RECORD LocalExceptionRecord;
if (CpuCallLevel > MaxCpuCallLevel)
{
DisplayMessage(L"Too many CPU levels of recursion (%d, expected maximum %d)",
@ -125,7 +129,29 @@ VOID CpuSimulate(VOID)
DPRINT("CpuSimulate --> Level %d\n", CpuCallLevel);
CpuRunning = TRUE;
while (VdmRunning && CpuRunning) ClockUpdate();
while (VdmRunning && CpuRunning)
{
_SEH2_TRY
{
while (VdmRunning && CpuRunning) ClockUpdate();
}
_SEH2_EXCEPT(LocalExceptionRecord = *_SEH2_GetExceptionInformation()->ExceptionRecord,
EXCEPTION_EXECUTE_HANDLER)
{
BOOLEAN Writing = (LocalExceptionRecord.ExceptionInformation[0] == 1);
DWORD FaultingAddress = (DWORD)LocalExceptionRecord.ExceptionInformation[1];
/* Make sure this was an access violation */
ASSERT(LocalExceptionRecord.ExceptionCode == EXCEPTION_ACCESS_VIOLATION);
/* Fix the CPU state */
Fast486Rewind(&EmulatorContext);
/* Call the handler */
MemExceptionHandler(FaultingAddress, Writing);
}
_SEH2_END;
}
DPRINT("CpuSimulate <-- Level %d\n", CpuCallLevel);
CpuCallLevel--;

View file

@ -15,6 +15,7 @@
#include <ndk/rtltypes.h>
#include <ndk/rtlfuncs.h>
#include "ems.h"
#include "memory.h"
/* PRIVATE VARIABLES **********************************************************/
@ -294,23 +295,26 @@ static VOID WINAPI EmsIntHandler(LPWORD Stack)
}
}
static VOID NTAPI EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
}
static BOOLEAN NTAPI EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
return TRUE;
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
}
VOID EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
}
VOID EmsInitialize(VOID)
{
ULONG i;
RtlZeroMemory(BitmapBuffer, sizeof(BitmapBuffer));
RtlInitializeBitMap(&AllocBitmap, BitmapBuffer, EMS_TOTAL_PAGES);
for (i = 0; i < EMS_MAX_HANDLES; i++)
@ -320,5 +324,16 @@ VOID EmsInitialize(VOID)
InitializeListHead(&HandleTable[i].PageList);
}
MemInstallFastMemoryHook(SEG_OFF_TO_PTR(EMS_SEGMENT, 0),
EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE,
EmsReadMemory,
EmsWriteMemory);
RegisterBiosInt32(EMS_INTERRUPT_NUM, EmsIntHandler);
}
VOID EmsCleanup(VOID)
{
MemRemoveFastMemoryHook(SEG_OFF_TO_PTR(EMS_SEGMENT, 0),
EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE);
}

View file

@ -12,15 +12,13 @@
/* DEFINITIONS ****************************************************************/
#define EMS_INTERRUPT_NUM 0x67
#define EMS_SEGMENT 0xE000
#define EMS_SEGMENT 0xD000
#define EMS_MAX_HANDLES 16
#define EMS_TOTAL_PAGES 256
#define EMS_PAGE_BITS 14
#define EMS_PAGE_SIZE (1 << EMS_PAGE_BITS)
#define EMS_ADDRESS 0xA00000
#define EMS_PHYSICAL_PAGES 4
#define EMS_START_ADDRESS (EMS_SEGMENT << 4)
#define EMS_END_ADDRESS (EMS_START_ADDRESS + EMS_PHYSICAL_PAGES * EMS_PAGE_SIZE)
#define EMS_STATUS_OK 0x00
#define EMS_STATUS_INTERNAL_ERROR 0x80
@ -66,9 +64,8 @@ typedef struct _EMS_COPY_DATA
/* FUNCTIONS ******************************************************************/
VOID EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size);
VOID EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size);
VOID EmsInitialize(VOID);
VOID EmsCleanup(VOID);
#endif // _EMS_H_

View file

@ -11,6 +11,7 @@
#define NDEBUG
#include "emulator.h"
#include "memory.h"
#include "cpu/callback.h"
#include "cpu/cpu.h"
@ -66,143 +67,35 @@ LPCWSTR ExceptionName[] =
/* PRIVATE FUNCTIONS **********************************************************/
static inline VOID
EmulatorMoveMemory(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
}
VOID WINAPI EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
{
UNREFERENCED_PARAMETER(State);
// BIG HACK!!!! To make BIOS images working correctly,
// until Aleksander rewrites memory management!!
/* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
/* If the A20 line is disabled, mask bit 20 */
if (!A20Line) Address &= ~(1 << 20);
if (!A20Line) Address &= ~(1 << 20);
/* Make sure the requested address is valid */
if ((Address + Size) >= MAX_ADDRESS) return;
if (Address >= MAX_ADDRESS) return;
Size = min(Size, MAX_ADDRESS - Address);
/*
* Check if we are going to read the VGA memory and
* copy it into the virtual address space if needed.
*/
if (((Address + Size) >= VgaGetVideoBaseAddress())
&& (Address < VgaGetVideoLimitAddress()))
{
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
- VgaAddress + 1;
LPBYTE DestBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
/* Read from the VGA memory */
VgaReadMemory(VgaAddress, DestBuffer, ActualSize);
}
/* Read the data from the virtual address space and store it in the buffer */
EmulatorMoveMemory(Buffer, REAL_TO_PHYS(Address), Size);
/* Read while calling fast memory hooks */
MemRead(Address, Buffer, Size);
}
VOID WINAPI EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
{
UNREFERENCED_PARAMETER(State);
// BIG HACK!!!! To make BIOS images working correctly,
// until Aleksander rewrites memory management!!
if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
/* If the A20 line is disabled, mask bit 20 */
if (!A20Line) Address &= ~(1 << 20);
if (!A20Line) Address &= ~(1 << 20);
/* Make sure the requested address is valid */
if ((Address + Size) >= MAX_ADDRESS) return;
if (Address >= MAX_ADDRESS) return;
Size = min(Size, MAX_ADDRESS - Address);
/* Make sure we don't write to the ROM area */
if ((Address + Size) >= ROM_AREA_START && (Address < ROM_AREA_END)) return;
/* Read the data from the buffer and store it in the virtual address space */
EmulatorMoveMemory(REAL_TO_PHYS(Address), Buffer, Size);
/*
* Check if we modified the VGA memory.
*/
if (((Address + Size) >= VgaGetVideoBaseAddress())
&& (Address < VgaGetVideoLimitAddress()))
{
DWORD VgaAddress = max(Address, VgaGetVideoBaseAddress());
DWORD ActualSize = min(Address + Size - 1, VgaGetVideoLimitAddress())
- VgaAddress + 1;
LPBYTE SrcBuffer = (LPBYTE)REAL_TO_PHYS(VgaAddress);
/* Write to the VGA memory */
VgaWriteMemory(VgaAddress, SrcBuffer, ActualSize);
}
/* Write while calling fast memory hooks */
MemWrite(Address, Buffer, Size);
}
UCHAR WINAPI EmulatorIntAcknowledge(PFAST486_STATE State)
@ -566,57 +459,13 @@ VOID DumpMemory(BOOLEAN TextFormat)
BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
{
#ifdef STANDALONE
/* Allocate 16 MB memory for the 16-bit address space */
BaseAddress = HeapAlloc(GetProcessHeap(), /*HEAP_ZERO_MEMORY*/ 0, MAX_ADDRESS);
if (BaseAddress == NULL)
/* Initialize memory */
if (!MemInitialize())
{
wprintf(L"FATAL: Failed to allocate VDM memory.\n");
wprintf(L"Memory initialization failed.\n");
return FALSE;
}
#else
NTSTATUS Status;
SIZE_T MemorySize = MAX_ADDRESS; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
/*
* The reserved region starts from the very first page.
* We need to commit the reserved first 16 MB virtual address.
*/
BaseAddress = (PVOID)1; // NULL has another signification for NtAllocateVirtualMemory
/*
* Since to get NULL, we allocated from 0x1, account for this.
* See also: kernel32/client/proc.c!CreateProcessInternalW
*/
MemorySize -= 1;
/* Commit the reserved memory */
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
&BaseAddress,
0,
&MemorySize,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(Status))
{
wprintf(L"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status);
return FALSE;
}
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);
/* Initialize I/O ports */
/* Initialize RAM */
@ -700,11 +549,6 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
VOID EmulatorCleanup(VOID)
{
#ifndef STANDALONE
NTSTATUS Status;
SIZE_T MemorySize = MAX_ADDRESS;
#endif
VgaCleanup();
/* Close the input thread handle */
@ -713,6 +557,7 @@ VOID EmulatorCleanup(VOID)
PS2Cleanup();
EmsCleanup();
SpeakerCleanup();
CmosCleanup();
// PitCleanup();
@ -721,29 +566,7 @@ VOID EmulatorCleanup(VOID)
// DmaCleanup();
CpuCleanup();
#ifdef STANDALONE
/* Free the memory allocated for the 16-bit address space */
if (BaseAddress != NULL) HeapFree(GetProcessHeap(), 0, BaseAddress);
#else
/* The reserved region starts from the very first page */
// BaseAddress = (PVOID)1;
/* Since to get NULL, we allocated from 0x1, account for this */
MemorySize -= 1;
Status = NtFreeVirtualMemory(NtCurrentProcess(),
&BaseAddress,
&MemorySize,
MEM_DECOMMIT);
if (!NT_SUCCESS(Status))
{
DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status);
}
#endif
MemCleanup();
}

View file

@ -14,6 +14,7 @@
#include "vga.h"
#include <bios/vidbios.h>
#include "memory.h"
#include "io.h"
/* PRIVATE VARIABLES **********************************************************/
@ -418,6 +419,16 @@ __InvalidateConsoleDIBits(IN HANDLE hConsoleOutput,
static inline DWORD VgaGetAddressSize(VOID);
static VOID VgaUpdateTextCursor(VOID);
static inline DWORD VgaGetVideoBaseAddress(VOID)
{
return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
}
static inline DWORD VgaGetVideoLimitAddress(VOID)
{
return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
}
static VOID VgaUpdateCursorPosition(VOID)
{
/*
@ -1745,16 +1756,6 @@ static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
/* PUBLIC FUNCTIONS ***********************************************************/
DWORD VgaGetVideoBaseAddress(VOID)
{
return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
}
DWORD VgaGetVideoLimitAddress(VOID)
{
return MemoryLimit[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
}
COORD VgaGetDisplayResolution(VOID)
{
COORD Resolution;
@ -1881,12 +1882,15 @@ VOID VgaHorizontalRetrace(VOID)
InHorizontalRetrace = TRUE;
}
VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
VOID NTAPI VgaReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
{
DWORD i;
DWORD VideoAddress;
PUCHAR BufPtr = (PUCHAR)Buffer;
DPRINT("VgaReadMemory: Address 0x%08X, Size %lu\n", Address, Size);
Address = max(min(Address, VgaGetVideoLimitAddress() - 1), VgaGetVideoBaseAddress());
Size = min(Size, VgaGetVideoLimitAddress() - Address + 1);
/* Ignore if video RAM access is disabled */
if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return;
@ -1897,7 +1901,7 @@ VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
VideoAddress = VgaTranslateReadAddress(Address + i);
/* Copy the value to the buffer */
Buffer[i] = VgaMemory[VideoAddress];
BufPtr[i] = VgaMemory[VideoAddress];
}
/* Load the latch registers */
@ -1907,18 +1911,21 @@ VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
VgaLatchRegisters[3] = VgaMemory[(3 * VGA_BANK_SIZE) + LOWORD(VideoAddress)];
}
VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
BOOLEAN NTAPI VgaWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
{
DWORD i, j;
DWORD VideoAddress;
PUCHAR BufPtr = (PUCHAR)Buffer;
DPRINT("VgaWriteMemory: Address 0x%08X, Size %lu\n", Address, Size);
Address = max(min(Address, VgaGetVideoLimitAddress() - 1), VgaGetVideoBaseAddress());
Size = min(Size, VgaGetVideoLimitAddress() - Address + 1);
/* Ignore if video RAM access is disabled */
if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return;
if ((VgaMiscRegister & VGA_MISC_RAM_ENABLED) == 0) return TRUE;
/* Also ignore if write access to all planes is disabled */
if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return;
if ((VgaSeqRegisters[VGA_SEQ_MASK_REG] & 0x0F) == 0x00) return TRUE;
/* Loop through each byte */
for (i = 0; i < Size; i++)
@ -1951,9 +1958,11 @@ VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size)
}
/* Copy the value to the VGA memory */
VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(Buffer[i], j);
VgaMemory[VideoAddress + j * VGA_BANK_SIZE] = VgaTranslateByteForWriting(BufPtr[i], j);
}
}
return TRUE;
}
VOID VgaClearMemory(VOID)
@ -2074,6 +2083,9 @@ BOOLEAN VgaInitialize(HANDLE TextHandle)
/* Clear the VGA memory */
VgaClearMemory();
/* Register the memory hook */
MemInstallFastMemoryHook((PVOID)0xA0000, 0x20000, VgaReadMemory, VgaWriteMemory);
/* Register the I/O Ports */
RegisterIoPort(0x3CC, VgaReadPort, NULL); // VGA_MISC_READ
@ -2112,6 +2124,7 @@ VOID VgaCleanup(VOID)
}
VgaDetachFromConsole(FALSE);
MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
CloseHandle(AnotherEvent);
CloseHandle(EndEvent);

View file

@ -258,14 +258,10 @@ VOID ScreenEventHandler(PWINDOW_BUFFER_SIZE_RECORD ScreenEvent);
BOOL VgaAttachToConsole(VOID);
VOID VgaDetachFromConsole(BOOL ChangeMode);
DWORD VgaGetVideoBaseAddress(VOID);
DWORD VgaGetVideoLimitAddress(VOID);
COORD VgaGetDisplayResolution(VOID);
VOID VgaRefreshDisplay(VOID);
VOID VgaHorizontalRetrace(VOID);
VOID VgaWriteFont(UINT FontNumber, CONST UCHAR *FontData, UINT Height);
VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size);
VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size);
VOID VgaClearMemory(VOID);
BOOLEAN VgaInitialize(HANDLE TextHandle);

View file

@ -0,0 +1,422 @@
/*
* COPYRIGHT: GPLv2+ - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: memory.c
* PURPOSE: Expanded Memory Support
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
/* INCLUDES *******************************************************************/
#define NDEBUG
#include "emulator.h"
#include <ndk/mmfuncs.h>
#include <ndk/rtlfuncs.h>
#include "memory.h"
/* PRIVATE VARIABLES **********************************************************/
static LIST_ENTRY HookList;
static PMEM_HOOK PageTable[TOTAL_PAGES];
/* 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
MemRead(ULONG Address, PVOID Buffer, ULONG Size)
{
ULONG i, Offset, Length;
ULONG FirstPage = Address >> 12;
ULONG LastPage = (Address + Size - 1) >> 12;
MemFastMoveMemory(Buffer, REAL_TO_PHYS(Address), Size);
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
MemWrite(ULONG Address, PVOID Buffer, ULONG Size)
{
ULONG i, Offset, Length;
ULONG FirstPage = Address >> 12;
ULONG 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
MemExceptionHandler(DWORD Address, BOOLEAN Writing)
{
PMEM_HOOK Hook = PageTable[Address >> 12];
DPRINT("The memory at 0x%08X could not be %s.\n", Address, Writing ? "written" : "read");
/* Exceptions are only supposed to happen when using VDD-style memory hooks */
ASSERT(Address < MAX_ADDRESS && Hook != NULL && Hook->hVdd != NULL);
/* Call the VDD handler */
Hook->VddHandler(Address, Writing);
}
BOOL
MemInstallFastMemoryHook(PVOID Address,
ULONG Size,
PMEMORY_READ_HANDLER ReadHandler,
PMEMORY_WRITE_HANDLER WriteHandler)
{
PMEM_HOOK Hook;
ULONG i;
ULONG FirstPage = (ULONG)Address >> 12;
ULONG LastPage = ((ULONG)Address + Size - 1) >> 12;
/* Make sure none of these pages are already allocated */
for (i = FirstPage; i <= LastPage; i++)
{
if (PageTable[i] != NULL) return FALSE;
}
Hook = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MEM_HOOK));
if (Hook == NULL) return FALSE;
Hook->hVdd = NULL;
Hook->Count = LastPage - FirstPage + 1;
Hook->FastReadHandler = ReadHandler;
Hook->FastWriteHandler = WriteHandler;
for (i = FirstPage; i <= LastPage; i++) PageTable[i] = Hook;
InsertTailList(&HookList, &Hook->Entry);
return TRUE;
}
BOOL
MemRemoveFastMemoryHook(PVOID Address, ULONG Size)
{
ULONG i;
ULONG FirstPage = (ULONG)Address >> 12;
ULONG LastPage = ((ULONG)Address + Size - 1) >> 12;
if (Size == 0) return FALSE;
for (i = FirstPage; i <= LastPage; i++)
{
PMEM_HOOK 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;
}
BOOL
WINAPI
VDDInstallMemoryHook(HANDLE hVdd,
PVOID pStart,
DWORD dwCount,
PVDD_MEMORY_HANDLER pHandler)
{
NTSTATUS Status;
PMEM_HOOK Hook;
ULONG i;
ULONG FirstPage = (ULONG)pStart >> 12;
ULONG LastPage = ((ULONG)pStart + dwCount - 1) >> 12;
PVOID Address = (PVOID)(FirstPage * PAGE_SIZE);
SIZE_T Size = (LastPage - FirstPage + 1) * PAGE_SIZE;
/* Check validity of the VDD handle */
if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE) 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;
}
Hook = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(MEM_HOOK));
if (Hook == NULL) return FALSE;
Hook->hVdd = hVdd;
Hook->Count = LastPage - FirstPage + 1;
Hook->VddHandler = pHandler;
/* Decommit the pages */
Status = NtFreeVirtualMemory(NtCurrentProcess(), &Address, &Size, MEM_DECOMMIT);
if (!NT_SUCCESS(Status))
{
RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
return FALSE;
}
for (i = FirstPage; i <= LastPage; i++) PageTable[i] = Hook;
InsertTailList(&HookList, &Hook->Entry);
return TRUE;
}
BOOL
WINAPI
VDDDeInstallMemoryHook(HANDLE hVdd,
PVOID pStart,
DWORD dwCount)
{
NTSTATUS Status;
ULONG i;
ULONG FirstPage = (ULONG)pStart >> 12;
ULONG LastPage = ((ULONG)pStart + dwCount - 1) >> 12;
PVOID Address = (PVOID)(FirstPage * PAGE_SIZE);
SIZE_T Size = (LastPage - FirstPage + 1) * PAGE_SIZE;
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++)
{
PMEM_HOOK 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;
}
BOOLEAN
MemInitialize(VOID)
{
NTSTATUS Status;
SIZE_T MemorySize = MAX_ADDRESS; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
#ifndef STANDALONE
/*
* The reserved region starts from the very first page.
* We need to commit the reserved first 16 MB virtual address.
*/
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
InitializeListHead(&HookList);
/*
* 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;
while (HookList.Flink != &HookList)
{
PLIST_ENTRY 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);
}
}

View file

@ -0,0 +1,116 @@
/*
* COPYRIGHT: GPLv2+ - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: memory.h
* PURPOSE: Expanded Memory Support
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
#ifndef _MEMORY_H_
#define _MEMORY_H_
/* DEFINITIONS ****************************************************************/
#define TOTAL_PAGES (MAX_ADDRESS / PAGE_SIZE)
typedef VOID
(WINAPI *PVDD_MEMORY_HANDLER)
(
DWORD FaultingAddress,
BOOLEAN Writing
);
typedef VOID
(WINAPI *PMEMORY_READ_HANDLER)
(
ULONG Address,
PVOID Buffer,
ULONG Size
);
typedef BOOLEAN
(WINAPI *PMEMORY_WRITE_HANDLER)
(
ULONG Address,
PVOID Buffer,
ULONG Size
);
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;
/* FUNCTIONS ******************************************************************/
BOOLEAN MemInitialize(VOID);
VOID MemCleanup(VOID);
VOID MemExceptionHandler(DWORD Address, BOOLEAN Writing);
VOID
MemRead
(
ULONG Address,
PVOID Buffer,
ULONG Size
);
VOID
MemWrite
(
ULONG Address,
PVOID Buffer,
ULONG Size
);
BOOL
MemInstallFastMemoryHook
(
PVOID Address,
ULONG Size,
PMEMORY_READ_HANDLER ReadHandler,
PMEMORY_WRITE_HANDLER WriteHandler
);
BOOL
MemRemoveFastMemoryHook
(
PVOID Address,
ULONG Size
);
BOOL
WINAPI
VDDInstallMemoryHook
(
HANDLE hVdd,
PVOID pStart,
DWORD dwCount,
PVDD_MEMORY_HANDLER pHandler
);
BOOL
WINAPI
VDDDeInstallMemoryHook
(
HANDLE hVdd,
PVOID pStart,
DWORD dwCount
);
#endif // _MEMORY_H_
/* EOF */