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
)
393 SetLastError(ERROR_INVALID_PARAMETER
);
397 if (dwCount
== 0) return FALSE
;
399 /* Make sure none of these pages are already allocated */
400 for (i
= FirstPage
; i
<= LastPage
; i
++)
402 if (PageTable
[i
] != NULL
) return FALSE
;
405 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
407 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
408 if (Hook
->hVdd
== hVdd
&& Hook
->VddHandler
== MemoryHandler
) break;
411 if (Pointer
== &HookList
)
413 /* Create and initialize a new hook entry... */
414 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
417 SetLastError(ERROR_OUTOFMEMORY
);
423 Hook
->VddHandler
= MemoryHandler
;
425 /* ... and add it to the list of hooks */
426 InsertTailList(&HookList
, &Hook
->Entry
);
429 /* Decommit the pages */
430 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
434 if (!NT_SUCCESS(Status
))
436 if (Pointer
== &HookList
)
438 RemoveEntryList(&Hook
->Entry
);
439 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
445 /* Increase the number of pages this hook has */
446 Hook
->Count
+= LastPage
- FirstPage
+ 1;
448 /* Add the hook entry to the page table */
449 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
456 VDDDeInstallMemoryHook(IN HANDLE hVdd
,
463 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
464 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
465 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
466 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
468 /* Check validity of the VDD handle */
469 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
471 SetLastError(ERROR_INVALID_PARAMETER
);
475 if (dwCount
== 0) return FALSE
;
477 /* Commit the pages */
478 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
484 if (!NT_SUCCESS(Status
)) return FALSE
;
486 for (i
= FirstPage
; i
<= LastPage
; i
++)
489 if (Hook
== NULL
) continue;
491 if (Hook
->hVdd
!= hVdd
)
493 DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i
);
497 if (--Hook
->Count
== 0)
499 /* This hook has no more pages */
500 RemoveEntryList(&Hook
->Entry
);
501 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
512 VDDAllocMem(IN HANDLE hVdd
,
519 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
520 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
521 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
523 /* Check validity of the VDD handle */
524 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
526 SetLastError(ERROR_INVALID_PARAMETER
);
530 if (Size
== 0) return FALSE
;
532 /* Fixup the address */
533 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
535 /* Be sure that all the region is held by the VDD */
536 for (i
= FirstPage
; i
<= LastPage
; i
++)
539 if (Hook
== NULL
) return FALSE
;
541 if (Hook
->hVdd
!= hVdd
)
543 DPRINT1("VDDAllocMem: Page %u owned by someone else.\n", i
);
548 /* OK, all the range is held by the VDD. Commit the pages. */
549 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
555 return NT_SUCCESS(Status
);
560 VDDFreeMem(IN HANDLE hVdd
,
567 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
568 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
569 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
571 /* Check validity of the VDD handle */
572 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
574 SetLastError(ERROR_INVALID_PARAMETER
);
578 if (Size
== 0) return FALSE
;
580 /* Fixup the address */
581 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
583 /* Be sure that all the region is held by the VDD */
584 for (i
= FirstPage
; i
<= LastPage
; i
++)
587 if (Hook
== NULL
) return FALSE
;
589 if (Hook
->hVdd
!= hVdd
)
591 DPRINT1("VDDFreeMem: Page %u owned by someone else.\n", i
);
596 /* OK, all the range is held by the VDD. Decommit the pages. */
597 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
601 return NT_SUCCESS(Status
);
606 VDDIncludeMem(IN HANDLE hVdd
,
617 VDDExcludeMem(IN HANDLE hVdd
,
632 SIZE_T MemorySize
= MAX_ADDRESS
; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
634 InitializeListHead(&HookList
);
639 * The reserved region starts from the very first page.
640 * We need to commit the reserved first 16 MB virtual address.
642 * NOTE: NULL has another signification for NtAllocateVirtualMemory.
644 BaseAddress
= (PVOID
)1;
647 * Since to get NULL, we allocated from 0x1, account for this.
648 * See also: kernel32/client/proc.c!CreateProcessInternalW
654 /* Allocate it anywhere */
659 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
666 MEM_RESERVE
| MEM_COMMIT
,
668 PAGE_EXECUTE_READWRITE
);
669 if (!NT_SUCCESS(Status
))
671 wprintf(L
"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status
);
676 ASSERT(BaseAddress
== NULL
);
680 * For diagnostics purposes, we fill the memory with INT 0x03 codes
681 * so that if a program wants to execute random code in memory, we can
682 * retrieve the exact CS:IP where the problem happens.
684 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
692 SIZE_T MemorySize
= MAX_ADDRESS
;
695 while (!IsListEmpty(&HookList
))
697 Pointer
= RemoveHeadList(&HookList
);
698 RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
));
701 /* Decommit the VDM memory */
702 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
711 if (!NT_SUCCESS(Status
))
713 DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status
);