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 *******************************************************************/
17 /* Extra PSDK/NDK Headers */
18 #include <ndk/mmfuncs.h>
20 /* PRIVATE VARIABLES **********************************************************/
22 typedef struct _MEM_HOOK
30 PVDD_MEMORY_HANDLER VddHandler
;
34 PMEMORY_READ_HANDLER FastReadHandler
;
35 PMEMORY_WRITE_HANDLER FastWriteHandler
;
38 } MEM_HOOK
, *PMEM_HOOK
;
40 static LIST_ENTRY HookList
;
41 static PMEM_HOOK PageTable
[TOTAL_PAGES
] = { NULL
};
43 /* PRIVATE FUNCTIONS **********************************************************/
46 MemFastMoveMemory(OUT VOID UNALIGNED
*Destination
,
47 IN
const VOID UNALIGNED
*Source
,
52 * We use a switch here to detect small moves of memory, as these
53 * constitute the bulk of our moves.
54 * Using RtlMoveMemory for all these small moves would be slow otherwise.
62 *(PUCHAR
)Destination
= *(PUCHAR
)Source
;
66 *(PUSHORT
)Destination
= *(PUSHORT
)Source
;
70 *(PULONG
)Destination
= *(PULONG
)Source
;
73 case sizeof(ULONGLONG
):
74 *(PULONGLONG
)Destination
= *(PULONGLONG
)Source
;
79 __builtin_memmove(Destination
, Source
, Length
);
81 RtlMoveMemory(Destination
, Source
, Length
);
85 #else // defined(_MSC_VER)
87 PUCHAR Dest
= (PUCHAR
)Destination
;
88 PUCHAR Src
= (PUCHAR
)Source
;
90 SIZE_T Count
, NewSize
= Length
;
93 Count
= NewSize
>> 2; // NewSize / sizeof(ULONG);
94 NewSize
= NewSize
& 3; // NewSize % sizeof(ULONG);
95 __movsd(Dest
, Src
, Count
);
96 Dest
+= Count
<< 2; // Count * sizeof(ULONG);
100 Count
= NewSize
>> 1; // NewSize / sizeof(USHORT);
101 NewSize
= NewSize
& 1; // NewSize % sizeof(USHORT);
102 __movsw(Dest
, Src
, Count
);
103 Dest
+= Count
<< 1; // Count * sizeof(USHORT);
107 Count
= NewSize
; // NewSize / sizeof(UCHAR);
108 // NewSize = NewSize; // NewSize % sizeof(UCHAR);
109 __movsb(Dest
, Src
, Count
);
117 ReadPage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
119 if (Hook
&& !Hook
->hVdd
&& Hook
->FastReadHandler
)
121 Hook
->FastReadHandler(Address
, REAL_TO_PHYS(Address
), Size
);
124 MemFastMoveMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
130 WritePage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
134 || !Hook
->FastWriteHandler
135 || Hook
->FastWriteHandler(Address
, Buffer
, Size
))
137 MemFastMoveMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
141 /* PUBLIC FUNCTIONS ***********************************************************/
144 MemRead(ULONG Address
, PVOID Buffer
, ULONG Size
)
146 ULONG i
, Offset
, Length
;
147 ULONG FirstPage
= Address
>> 12;
148 ULONG LastPage
= (Address
+ Size
- 1) >> 12;
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(REAL_TO_PHYS(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;
216 /* Make sure none of these pages are already allocated */
217 for (i
= FirstPage
; i
<= LastPage
; i
++)
219 if (PageTable
[i
] != NULL
) return FALSE
;
222 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
224 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
226 if (Hook
->hVdd
== NULL
227 && Hook
->FastReadHandler
== ReadHandler
228 && Hook
->FastWriteHandler
== WriteHandler
)
234 if (Pointer
== &HookList
)
236 /* Create and initialize a new hook entry... */
237 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
238 if (Hook
== NULL
) return FALSE
;
242 Hook
->FastReadHandler
= ReadHandler
;
243 Hook
->FastWriteHandler
= WriteHandler
;
245 /* ... and add it to the list of hooks */
246 InsertTailList(&HookList
, &Hook
->Entry
);
249 /* Increase the number of pages this hook has */
250 Hook
->Count
+= LastPage
- FirstPage
+ 1;
252 /* Add the hook entry to the page table */
253 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
259 MemRemoveFastMemoryHook(PVOID Address
, ULONG Size
)
263 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
264 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
266 if (Size
== 0) return FALSE
;
268 for (i
= FirstPage
; i
<= LastPage
; i
++)
271 if (Hook
== NULL
|| Hook
->hVdd
!= NULL
) continue;
273 if (--Hook
->Count
== 0)
275 /* This hook has no more pages */
276 RemoveEntryList(&Hook
->Entry
);
277 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
287 MemQueryMemoryZone(ULONG StartAddress
, PULONG Length
, PBOOLEAN Hooked
)
289 ULONG Page
= StartAddress
>> 12;
290 if (Page
>= TOTAL_PAGES
) return FALSE
;
293 *Hooked
= PageTable
[Page
] != NULL
;
295 while (Page
< TOTAL_PAGES
&& (PageTable
[Page
] != NULL
) == *Hooked
)
297 *Length
+= PAGE_SIZE
;
306 Sim32pGetVDMPointer(IN ULONG Address
,
307 IN BOOLEAN ProtectedMode
)
310 UNREFERENCED_PARAMETER(ProtectedMode
);
313 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
314 * or Selector (if ProtectedMode == TRUE )
315 * LOWORD(Address) == Offset
317 return (PBYTE
)FAR_POINTER(Address
);
322 MGetVdmPointer(IN ULONG Address
,
324 IN BOOLEAN ProtectedMode
)
326 UNREFERENCED_PARAMETER(Size
);
327 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
332 VdmMapFlat(IN USHORT Segment
,
337 UNREFERENCED_PARAMETER(Mode
);
339 return SEG_OFF_TO_PTR(Segment
, Offset
);
342 #ifndef VdmFlushCache
346 VdmFlushCache(IN USHORT Segment
,
362 VdmUnmapFlat(IN USHORT Segment
,
376 VDDInstallMemoryHook(IN HANDLE hVdd
,
379 IN PVDD_MEMORY_HANDLER MemoryHandler
)
384 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
385 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
386 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
387 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
390 /* Check validity of the VDD handle */
391 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
) return FALSE
;
392 if (dwCount
== 0) return FALSE
;
394 /* Make sure none of these pages are already allocated */
395 for (i
= FirstPage
; i
<= LastPage
; i
++)
397 if (PageTable
[i
] != NULL
) return FALSE
;
400 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
402 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
403 if (Hook
->hVdd
== hVdd
&& Hook
->VddHandler
== MemoryHandler
) break;
406 if (Pointer
== &HookList
)
408 /* Create and initialize a new hook entry... */
409 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
410 if (Hook
== NULL
) return FALSE
;
414 Hook
->VddHandler
= MemoryHandler
;
416 /* ... and add it to the list of hooks */
417 InsertTailList(&HookList
, &Hook
->Entry
);
420 /* Decommit the pages */
421 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
425 if (!NT_SUCCESS(Status
))
427 if (Pointer
== &HookList
)
429 RemoveEntryList(&Hook
->Entry
);
430 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
436 /* Increase the number of pages this hook has */
437 Hook
->Count
+= LastPage
- FirstPage
+ 1;
439 /* Add the hook entry to the page table */
440 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
447 VDDDeInstallMemoryHook(IN HANDLE hVdd
,
454 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
455 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
456 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
457 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
459 if (dwCount
== 0) return FALSE
;
461 /* Commit the pages */
462 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
468 if (!NT_SUCCESS(Status
)) return FALSE
;
470 for (i
= FirstPage
; i
<= LastPage
; i
++)
473 if (Hook
== NULL
) continue;
475 if (Hook
->hVdd
!= hVdd
)
477 DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i
);
481 if (--Hook
->Count
== 0)
483 /* This hook has no more pages */
484 RemoveEntryList(&Hook
->Entry
);
485 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
496 VDDAllocMem(IN HANDLE hVdd
,
503 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
504 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
505 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
507 if (Size
== 0) return FALSE
;
509 /* Fixup the address */
510 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
512 /* Be sure that all the region is held by the VDD */
513 for (i
= FirstPage
; i
<= LastPage
; i
++)
516 if (Hook
== NULL
) return FALSE
;
518 if (Hook
->hVdd
!= hVdd
)
520 DPRINT1("VDDAllocMem: Page %u owned by someone else.\n", i
);
525 /* OK, all the range is held by the VDD. Commit the pages. */
526 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
532 return NT_SUCCESS(Status
);
537 VDDFreeMem(IN HANDLE hVdd
,
544 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
545 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
546 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
548 if (Size
== 0) return FALSE
;
550 /* Fixup the address */
551 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
553 /* Be sure that all the region is held by the VDD */
554 for (i
= FirstPage
; i
<= LastPage
; i
++)
557 if (Hook
== NULL
) return FALSE
;
559 if (Hook
->hVdd
!= hVdd
)
561 DPRINT1("VDDFreeMem: Page %u owned by someone else.\n", i
);
566 /* OK, all the range is held by the VDD. Decommit the pages. */
567 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
571 return NT_SUCCESS(Status
);
576 VDDIncludeMem(IN HANDLE hVdd
,
587 VDDExcludeMem(IN HANDLE hVdd
,
602 SIZE_T MemorySize
= MAX_ADDRESS
; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
604 InitializeListHead(&HookList
);
609 * The reserved region starts from the very first page.
610 * We need to commit the reserved first 16 MB virtual address.
612 * NOTE: NULL has another signification for NtAllocateVirtualMemory.
614 BaseAddress
= (PVOID
)1;
617 * Since to get NULL, we allocated from 0x1, account for this.
618 * See also: kernel32/client/proc.c!CreateProcessInternalW
624 /* Allocate it anywhere */
629 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
636 MEM_RESERVE
| MEM_COMMIT
,
638 PAGE_EXECUTE_READWRITE
);
639 if (!NT_SUCCESS(Status
))
641 wprintf(L
"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status
);
646 ASSERT(BaseAddress
== NULL
);
650 * For diagnostics purposes, we fill the memory with INT 0x03 codes
651 * so that if a program wants to execute random code in memory, we can
652 * retrieve the exact CS:IP where the problem happens.
654 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
662 SIZE_T MemorySize
= MAX_ADDRESS
;
665 while (!IsListEmpty(&HookList
))
667 Pointer
= RemoveHeadList(&HookList
);
668 RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
));
671 /* Decommit the VDM memory */
672 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
681 if (!NT_SUCCESS(Status
))
683 DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status
);