[NTVDM][FAST486]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 13 Mar 2015 17:57:51 +0000 (17:57 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 13 Mar 2015 17:57:51 +0000 (17:57 +0000)
- 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

12 files changed:
reactos/include/reactos/libs/fast486/fast486.h
reactos/lib/fast486/fast486.c
reactos/subsystems/mvdm/ntvdm/CMakeLists.txt
reactos/subsystems/mvdm/ntvdm/bios/bios.c
reactos/subsystems/mvdm/ntvdm/cpu/cpu.c
reactos/subsystems/mvdm/ntvdm/ems.c
reactos/subsystems/mvdm/ntvdm/ems.h
reactos/subsystems/mvdm/ntvdm/emulator.c
reactos/subsystems/mvdm/ntvdm/hardware/video/vga.c
reactos/subsystems/mvdm/ntvdm/hardware/video/vga.h
reactos/subsystems/mvdm/ntvdm/memory.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/memory.h [new file with mode: 0644]

index b3a628b..413f7c7 100644 (file)
@@ -576,6 +576,10 @@ Fast486SetSegment
     USHORT Selector
 );
 
+VOID
+NTAPI
+Fast486Rewind(PFAST486_STATE State);
+
 #endif // _FAST486_H_
 
 /* EOF */
index f03a99b..86f5811 100644 (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 */
index 9dcde7f..9f80333 100644 (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)
index 7b6d038..78f78c1 100644 (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;
index b835442..54f1333 100644 (file)
 #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--;
index 72d7740..6770a43 100644 (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)
     }
 }
 
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-VOID EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
+static VOID NTAPI EmsReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
 {
     // TODO: NOT IMPLEMENTED
     UNIMPLEMENTED;
 }
 
-VOID EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
+static BOOLEAN NTAPI EmsWriteMemory(ULONG Address, PVOID Buffer, ULONG Size)
 {
     // TODO: NOT IMPLEMENTED
     UNIMPLEMENTED;
+    return TRUE;
 }
 
+/* PUBLIC FUNCTIONS ***********************************************************/
+
 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);
+}
index e89c85a..ccfdbe9 100644 (file)
 /* 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_
 
index 0d6a0d9..3d50713 100644 (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;
-
-    /*
-     * 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);
+    if (Address >= MAX_ADDRESS) return;
+    Size = min(Size, MAX_ADDRESS - Address);
 
-        /* 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;
-
-    /* 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);
+    if (Address >= MAX_ADDRESS) return;
+    Size = min(Size, MAX_ADDRESS - Address);
 
-        /* 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();
 }
 
 
index 687bb0d..ea104e9 100644 (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);
index c3aaadd..298caf0 100644 (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);
diff --git a/reactos/subsystems/mvdm/ntvdm/memory.c b/reactos/subsystems/mvdm/ntvdm/memory.c
new file mode 100644 (file)
index 0000000..fffce40
--- /dev/null
@@ -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);
+    }
+}
diff --git a/reactos/subsystems/mvdm/ntvdm/memory.h b/reactos/subsystems/mvdm/ntvdm/memory.h
new file mode 100644 (file)
index 0000000..5260f9e
--- /dev/null
@@ -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 */