From 888e1c450bf99ccde5d813c8e41aa0e1f8b6a670 Mon Sep 17 00:00:00 2001 From: Aleksandar Andrejevic Date: Fri, 13 Mar 2015 17:57:51 +0000 Subject: [PATCH] [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 --- .../include/reactos/libs/fast486/fast486.h | 4 + reactos/lib/fast486/fast486.c | 10 + reactos/subsystems/mvdm/ntvdm/CMakeLists.txt | 3 +- reactos/subsystems/mvdm/ntvdm/bios/bios.c | 12 + reactos/subsystems/mvdm/ntvdm/cpu/cpu.c | 28 +- reactos/subsystems/mvdm/ntvdm/ems.c | 39 +- reactos/subsystems/mvdm/ntvdm/ems.h | 7 +- reactos/subsystems/mvdm/ntvdm/emulator.c | 211 +-------- .../mvdm/ntvdm/hardware/video/vga.c | 45 +- .../mvdm/ntvdm/hardware/video/vga.h | 4 - reactos/subsystems/mvdm/ntvdm/memory.c | 422 ++++++++++++++++++ reactos/subsystems/mvdm/ntvdm/memory.h | 116 +++++ 12 files changed, 668 insertions(+), 233 deletions(-) create mode 100644 reactos/subsystems/mvdm/ntvdm/memory.c create mode 100644 reactos/subsystems/mvdm/ntvdm/memory.h diff --git a/reactos/include/reactos/libs/fast486/fast486.h b/reactos/include/reactos/libs/fast486/fast486.h index b3a628b7e42..413f7c76bde 100644 --- a/reactos/include/reactos/libs/fast486/fast486.h +++ b/reactos/include/reactos/libs/fast486/fast486.h @@ -576,6 +576,10 @@ Fast486SetSegment USHORT Selector ); +VOID +NTAPI +Fast486Rewind(PFAST486_STATE State); + #endif // _FAST486_H_ /* EOF */ diff --git a/reactos/lib/fast486/fast486.c b/reactos/lib/fast486/fast486.c index f03a99b6660..86f5811ce95 100644 --- a/reactos/lib/fast486/fast486.c +++ b/reactos/lib/fast486/fast486.c @@ -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 */ diff --git a/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt b/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt index 9dcde7fd799..9f80333e0f8 100644 --- a/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt +++ b/reactos/subsystems/mvdm/ntvdm/CMakeLists.txt @@ -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) diff --git a/reactos/subsystems/mvdm/ntvdm/bios/bios.c b/reactos/subsystems/mvdm/ntvdm/bios/bios.c index 7b6d038c309..78f78c14d91 100644 --- a/reactos/subsystems/mvdm/ntvdm/bios/bios.c +++ b/reactos/subsystems/mvdm/ntvdm/bios/bios.c @@ -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; diff --git a/reactos/subsystems/mvdm/ntvdm/cpu/cpu.c b/reactos/subsystems/mvdm/ntvdm/cpu/cpu.c index b8354426c88..54f1333f07e 100644 --- a/reactos/subsystems/mvdm/ntvdm/cpu/cpu.c +++ b/reactos/subsystems/mvdm/ntvdm/cpu/cpu.c @@ -13,9 +13,11 @@ #include "cpu.h" #include "emulator.h" +#include "memory.h" #include "callback.h" #include "bop.h" #include +#include #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--; diff --git a/reactos/subsystems/mvdm/ntvdm/ems.c b/reactos/subsystems/mvdm/ntvdm/ems.c index 72d77400ad7..6770a438204 100644 --- a/reactos/subsystems/mvdm/ntvdm/ems.c +++ b/reactos/subsystems/mvdm/ntvdm/ems.c @@ -15,6 +15,7 @@ #include #include #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); +} diff --git a/reactos/subsystems/mvdm/ntvdm/ems.h b/reactos/subsystems/mvdm/ntvdm/ems.h index e89c85af983..ccfdbe99f27 100644 --- a/reactos/subsystems/mvdm/ntvdm/ems.h +++ b/reactos/subsystems/mvdm/ntvdm/ems.h @@ -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_ diff --git a/reactos/subsystems/mvdm/ntvdm/emulator.c b/reactos/subsystems/mvdm/ntvdm/emulator.c index 0d6a0d97fb3..3d50713edc9 100644 --- a/reactos/subsystems/mvdm/ntvdm/emulator.c +++ b/reactos/subsystems/mvdm/ntvdm/emulator.c @@ -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(); } diff --git a/reactos/subsystems/mvdm/ntvdm/hardware/video/vga.c b/reactos/subsystems/mvdm/ntvdm/hardware/video/vga.c index 687bb0d1424..ea104e96ebd 100644 --- a/reactos/subsystems/mvdm/ntvdm/hardware/video/vga.c +++ b/reactos/subsystems/mvdm/ntvdm/hardware/video/vga.c @@ -14,6 +14,7 @@ #include "vga.h" #include +#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); diff --git a/reactos/subsystems/mvdm/ntvdm/hardware/video/vga.h b/reactos/subsystems/mvdm/ntvdm/hardware/video/vga.h index c3aaadd36e0..298caf089b5 100644 --- a/reactos/subsystems/mvdm/ntvdm/hardware/video/vga.h +++ b/reactos/subsystems/mvdm/ntvdm/hardware/video/vga.h @@ -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); diff --git a/reactos/subsystems/mvdm/ntvdm/memory.c b/reactos/subsystems/mvdm/ntvdm/memory.c new file mode 100644 index 00000000000..fffce40ed8e --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/memory.c @@ -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 + */ + +/* INCLUDES *******************************************************************/ + +#define NDEBUG + +#include "emulator.h" +#include +#include +#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); + } +} diff --git a/reactos/subsystems/mvdm/ntvdm/memory.h b/reactos/subsystems/mvdm/ntvdm/memory.h new file mode 100644 index 00000000000..5260f9e7eaf --- /dev/null +++ b/reactos/subsystems/mvdm/ntvdm/memory.h @@ -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 + */ + +#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 */