2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: Memory Management
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
14 #include <ndk/mmfuncs.h>
15 #include <ndk/rtlfuncs.h>
18 /* PRIVATE VARIABLES **********************************************************/
20 typedef struct _MEM_HOOK
28 PVDD_MEMORY_HANDLER VddHandler
;
32 PMEMORY_READ_HANDLER FastReadHandler
;
33 PMEMORY_WRITE_HANDLER FastWriteHandler
;
36 } MEM_HOOK
, *PMEM_HOOK
;
38 static LIST_ENTRY HookList
;
39 static PMEM_HOOK PageTable
[TOTAL_PAGES
];
41 /* PRIVATE FUNCTIONS **********************************************************/
44 MemFastMoveMemory(OUT VOID UNALIGNED
*Destination
,
45 IN
const VOID UNALIGNED
*Source
,
50 * We use a switch here to detect small moves of memory, as these
51 * constitute the bulk of our moves.
52 * Using RtlMoveMemory for all these small moves would be slow otherwise.
60 *(PUCHAR
)Destination
= *(PUCHAR
)Source
;
64 *(PUSHORT
)Destination
= *(PUSHORT
)Source
;
68 *(PULONG
)Destination
= *(PULONG
)Source
;
71 case sizeof(ULONGLONG
):
72 *(PULONGLONG
)Destination
= *(PULONGLONG
)Source
;
77 __builtin_memmove(Destination
, Source
, Length
);
79 RtlMoveMemory(Destination
, Source
, Length
);
83 #else // defined(_MSC_VER)
85 PUCHAR Dest
= (PUCHAR
)Destination
;
86 PUCHAR Src
= (PUCHAR
)Source
;
88 SIZE_T Count
, NewSize
= Length
;
91 Count
= NewSize
>> 2; // NewSize / sizeof(ULONG);
92 NewSize
= NewSize
& 3; // NewSize % sizeof(ULONG);
93 __movsd(Dest
, Src
, Count
);
94 Dest
+= Count
<< 2; // Count * sizeof(ULONG);
98 Count
= NewSize
>> 1; // NewSize / sizeof(USHORT);
99 NewSize
= NewSize
& 1; // NewSize % sizeof(USHORT);
100 __movsw(Dest
, Src
, Count
);
101 Dest
+= Count
<< 1; // Count * sizeof(USHORT);
105 Count
= NewSize
; // NewSize / sizeof(UCHAR);
106 // NewSize = NewSize; // NewSize % sizeof(UCHAR);
107 __movsb(Dest
, Src
, Count
);
115 ReadPage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
117 if (Hook
&& !Hook
->hVdd
&& Hook
->FastReadHandler
)
119 Hook
->FastReadHandler(Address
, REAL_TO_PHYS(Address
), Size
);
122 MemFastMoveMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
128 WritePage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
132 || !Hook
->FastWriteHandler
133 || Hook
->FastWriteHandler(Address
, Buffer
, Size
))
135 MemFastMoveMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
139 /* PUBLIC FUNCTIONS ***********************************************************/
142 MemRead(ULONG Address
, PVOID Buffer
, ULONG Size
)
144 ULONG i
, Offset
, Length
;
145 ULONG FirstPage
= Address
>> 12;
146 ULONG LastPage
= (Address
+ Size
- 1) >> 12;
148 MemFastMoveMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
150 if (FirstPage
== LastPage
)
152 ReadPage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
156 for (i
= FirstPage
; i
<= LastPage
; i
++)
158 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
159 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
161 ReadPage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
162 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
168 MemWrite(ULONG Address
, PVOID Buffer
, ULONG Size
)
170 ULONG i
, Offset
, Length
;
171 ULONG FirstPage
= Address
>> 12;
172 ULONG LastPage
= (Address
+ Size
- 1) >> 12;
174 if (FirstPage
== LastPage
)
176 WritePage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
180 for (i
= FirstPage
; i
<= LastPage
; i
++)
182 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
183 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
185 WritePage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
186 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
192 MemExceptionHandler(ULONG FaultAddress
, BOOLEAN Writing
)
194 PMEM_HOOK Hook
= PageTable
[FaultAddress
>> 12];
195 DPRINT("The memory at 0x%08X could not be %s.\n", FaultAddress
, Writing
? "written" : "read");
197 /* Exceptions are only supposed to happen when using VDD-style memory hooks */
198 ASSERT(FaultAddress
< MAX_ADDRESS
&& Hook
!= NULL
&& Hook
->hVdd
!= NULL
);
200 /* Call the VDD handler */
201 Hook
->VddHandler((PVOID
)FaultAddress
, (ULONG
)Writing
);
205 MemInstallFastMemoryHook(PVOID Address
,
207 PMEMORY_READ_HANDLER ReadHandler
,
208 PMEMORY_WRITE_HANDLER WriteHandler
)
212 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
213 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
215 /* Make sure none of these pages are already allocated */
216 for (i
= FirstPage
; i
<= LastPage
; i
++)
218 if (PageTable
[i
] != NULL
) return FALSE
;
221 /* Create and initialize a new hook entry */
222 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
223 if (Hook
== NULL
) return FALSE
;
226 Hook
->Count
= LastPage
- FirstPage
+ 1;
227 Hook
->FastReadHandler
= ReadHandler
;
228 Hook
->FastWriteHandler
= WriteHandler
;
230 /* Add the hook entry to the page table... */
231 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
233 /* ... and to the list of hooks */
234 InsertTailList(&HookList
, &Hook
->Entry
);
239 MemRemoveFastMemoryHook(PVOID Address
, ULONG Size
)
243 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
244 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
246 if (Size
== 0) return FALSE
;
248 for (i
= FirstPage
; i
<= LastPage
; i
++)
251 if (Hook
== NULL
|| Hook
->hVdd
!= NULL
) continue;
253 if (--Hook
->Count
== 0)
255 /* This hook has no more pages */
256 RemoveEntryList(&Hook
->Entry
);
257 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
270 Sim32pGetVDMPointer(IN ULONG Address
,
271 IN BOOLEAN ProtectedMode
)
274 UNREFERENCED_PARAMETER(ProtectedMode
);
277 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
278 * or Selector (if ProtectedMode == TRUE )
279 * LOWORD(Address) == Offset
281 return (PBYTE
)FAR_POINTER(Address
);
286 MGetVdmPointer(IN ULONG Address
,
288 IN BOOLEAN ProtectedMode
)
290 UNREFERENCED_PARAMETER(Size
);
291 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
296 VdmMapFlat(IN USHORT Segment
,
301 UNREFERENCED_PARAMETER(Mode
);
303 return SEG_OFF_TO_PTR(Segment
, Offset
);
308 VdmFlushCache(IN USHORT Segment
,
320 VdmUnmapFlat(IN USHORT Segment
,
332 VDDInstallMemoryHook(IN HANDLE hVdd
,
335 IN PVDD_MEMORY_HANDLER MemoryHandler
)
340 ULONG FirstPage
= (ULONG_PTR
)pStart
>> 12;
341 ULONG LastPage
= ((ULONG_PTR
)pStart
+ dwCount
- 1) >> 12;
342 PVOID Address
= (PVOID
)(FirstPage
* PAGE_SIZE
);
343 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
345 /* Check validity of the VDD handle */
346 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
) return FALSE
;
347 if (dwCount
== 0) return FALSE
;
349 /* Make sure none of these pages are already allocated */
350 for (i
= FirstPage
; i
<= LastPage
; i
++)
352 if (PageTable
[i
] != NULL
) return FALSE
;
355 /* Create and initialize a new hook entry */
356 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
357 if (Hook
== NULL
) return FALSE
;
360 Hook
->Count
= LastPage
- FirstPage
+ 1;
361 Hook
->VddHandler
= MemoryHandler
;
363 /* Decommit the pages */
364 Status
= NtFreeVirtualMemory(NtCurrentProcess(), &Address
, &Size
, MEM_DECOMMIT
);
365 if (!NT_SUCCESS(Status
))
367 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
371 /* Add the hook entry to the page table... */
372 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
374 /* ... and to the list of hooks */
375 InsertTailList(&HookList
, &Hook
->Entry
);
381 VDDDeInstallMemoryHook(IN HANDLE hVdd
,
388 ULONG FirstPage
= (ULONG_PTR
)pStart
>> 12;
389 ULONG LastPage
= ((ULONG_PTR
)pStart
+ dwCount
- 1) >> 12;
390 PVOID Address
= (PVOID
)(FirstPage
* PAGE_SIZE
);
391 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
393 if (dwCount
== 0) return FALSE
;
395 /* Commit the pages */
396 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
402 if (!NT_SUCCESS(Status
)) return FALSE
;
404 for (i
= FirstPage
; i
<= LastPage
; i
++)
407 if (Hook
== NULL
) continue;
409 if (Hook
->hVdd
!= hVdd
)
411 DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i
);
415 if (--Hook
->Count
== 0)
417 /* This hook has no more pages */
418 RemoveEntryList(&Hook
->Entry
);
419 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
434 SIZE_T MemorySize
= MAX_ADDRESS
; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
439 * The reserved region starts from the very first page.
440 * We need to commit the reserved first 16 MB virtual address.
442 * NOTE: NULL has another signification for NtAllocateVirtualMemory.
444 BaseAddress
= (PVOID
)1;
447 * Since to get NULL, we allocated from 0x1, account for this.
448 * See also: kernel32/client/proc.c!CreateProcessInternalW
454 /* Allocate it anywhere */
459 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
466 MEM_RESERVE
| MEM_COMMIT
,
468 PAGE_EXECUTE_READWRITE
);
469 if (!NT_SUCCESS(Status
))
471 wprintf(L
"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status
);
476 ASSERT(BaseAddress
== NULL
);
479 InitializeListHead(&HookList
);
482 * For diagnostics purposes, we fill the memory with INT 0x03 codes
483 * so that if a program wants to execute random code in memory, we can
484 * retrieve the exact CS:IP where the problem happens.
486 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
494 SIZE_T MemorySize
= MAX_ADDRESS
;
497 while (!IsListEmpty(&HookList
))
499 Pointer
= RemoveHeadList(&HookList
);
500 RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
));
503 /* Decommit the VDM memory */
504 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
513 if (!NT_SUCCESS(Status
))
515 DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status
);