2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/memory.c
5 * PURPOSE: Memory Management
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
17 /* PRIVATE VARIABLES **********************************************************/
19 typedef struct _MEM_HOOK
27 PVDD_MEMORY_HANDLER VddHandler
;
31 PMEMORY_READ_HANDLER FastReadHandler
;
32 PMEMORY_WRITE_HANDLER FastWriteHandler
;
35 } MEM_HOOK
, *PMEM_HOOK
;
37 static LIST_ENTRY HookList
;
38 static PMEM_HOOK PageTable
[TOTAL_PAGES
] = { NULL
};
39 static BOOLEAN A20Line
= FALSE
;
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
);
113 ReadPage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
115 if (Hook
&& !Hook
->hVdd
&& Hook
->FastReadHandler
)
117 Hook
->FastReadHandler(Address
, REAL_TO_PHYS(Address
), Size
);
120 MemFastMoveMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
124 WritePage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
128 || !Hook
->FastWriteHandler
129 || Hook
->FastWriteHandler(Address
, Buffer
, Size
))
131 MemFastMoveMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
135 /* PUBLIC FUNCTIONS ***********************************************************/
137 VOID FASTCALL
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
139 ULONG i
, Offset
, Length
;
140 ULONG FirstPage
, LastPage
;
142 UNREFERENCED_PARAMETER(State
);
144 /* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
145 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
147 /* If the A20 line is disabled, mask bit 20 */
148 if (!A20Line
) Address
&= ~(1 << 20);
150 if ((Address
+ Size
- 1) >= MAX_ADDRESS
)
152 ULONG ExtraStart
= (Address
< MAX_ADDRESS
) ? MAX_ADDRESS
- Address
: 0;
154 /* Fill the memory that was above the limit with 0xFF */
155 RtlFillMemory((PVOID
)((ULONG_PTR
)Buffer
+ ExtraStart
), Size
- ExtraStart
, 0xFF);
157 if (Address
< MAX_ADDRESS
) Size
= MAX_ADDRESS
- Address
;
161 FirstPage
= Address
>> 12;
162 LastPage
= (Address
+ Size
- 1) >> 12;
164 if (FirstPage
== LastPage
)
166 ReadPage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
170 for (i
= FirstPage
; i
<= LastPage
; i
++)
172 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
173 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
175 ReadPage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
176 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
181 VOID FASTCALL
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
183 ULONG i
, Offset
, Length
;
184 ULONG FirstPage
, LastPage
;
186 UNREFERENCED_PARAMETER(State
);
188 /* If the A20 line is disabled, mask bit 20 */
189 if (!A20Line
) Address
&= ~(1 << 20);
191 if (Address
>= MAX_ADDRESS
) return;
192 Size
= min(Size
, MAX_ADDRESS
- Address
);
194 FirstPage
= Address
>> 12;
195 LastPage
= (Address
+ Size
- 1) >> 12;
197 if (FirstPage
== LastPage
)
199 WritePage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
203 for (i
= FirstPage
; i
<= LastPage
; i
++)
205 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
206 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
208 WritePage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
209 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
214 VOID FASTCALL
EmulatorCopyMemory(PFAST486_STATE State
, ULONG DestAddress
, ULONG SrcAddress
, ULONG Size
)
217 * Guest-to-guest memory copy
220 // FIXME: This is a temporary implementation of a more useful functionality
221 // which should be a merge of EmulatorReadMemory & EmulatorWriteMemory without
222 // any local external buffer.
223 // NOTE: Process heap is by default serialized (unless one specifies it shouldn't).
224 static BYTE StaticBuffer
[8192]; // Smallest static buffer we can use.
225 static PVOID HeapBuffer
= NULL
; // Always-growing heap buffer. Use it in case StaticBuffer is too small.
226 static ULONG HeapBufferSize
= 0;
227 PVOID LocalBuffer
; // Points to either StaticBuffer or HeapBuffer
229 if (Size
<= sizeof(StaticBuffer
))
231 /* Use the static buffer */
232 LocalBuffer
= StaticBuffer
;
234 else if (/* sizeof(StaticBuffer) <= Size && */ Size
<= HeapBufferSize
)
236 /* Use the heap buffer */
237 ASSERT(HeapBufferSize
> 0 && HeapBuffer
!= NULL
);
238 LocalBuffer
= HeapBuffer
;
240 else // if (Size > HeapBufferSize)
242 /* Enlarge the heap buffer and use it */
244 if (HeapBuffer
== NULL
)
246 /* First allocation */
247 LocalBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Size
);
252 LocalBuffer
= RtlReAllocateHeap(RtlGetProcessHeap(), 0 /* HEAP_GENERATE_EXCEPTIONS */, HeapBuffer
, Size
);
254 ASSERT(LocalBuffer
!= NULL
); // We must succeed! TODO: Handle it more properly.
255 HeapBuffer
= LocalBuffer
; // HeapBuffer is now reallocated.
256 HeapBufferSize
= Size
;
259 /* Perform memory copy */
260 EmulatorReadMemory( State
, SrcAddress
, LocalBuffer
, Size
);
261 EmulatorWriteMemory(State
, DestAddress
, LocalBuffer
, Size
);
263 // if (LocalBuffer != StaticBuffer)
264 // RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
266 // Note that we don't free HeapBuffer since it's an always-growing buffer.
267 // It is freed when NTVDM termiantes.
270 VOID
EmulatorSetA20(BOOLEAN Enabled
)
275 BOOLEAN
EmulatorGetA20(VOID
)
281 MemExceptionHandler(ULONG FaultAddress
, BOOLEAN Writing
)
283 PMEM_HOOK Hook
= PageTable
[FaultAddress
>> 12];
284 DPRINT("The memory at 0x%08X could not be %s.\n", FaultAddress
, Writing
? "written" : "read");
286 /* Exceptions are only supposed to happen when using VDD-style memory hooks */
287 ASSERT(FaultAddress
< MAX_ADDRESS
&& Hook
!= NULL
&& Hook
->hVdd
!= NULL
);
289 /* Call the VDD handler */
290 Hook
->VddHandler(REAL_TO_PHYS(FaultAddress
), (ULONG
)Writing
);
294 MemInstallFastMemoryHook(PVOID Address
,
296 PMEMORY_READ_HANDLER ReadHandler
,
297 PMEMORY_WRITE_HANDLER WriteHandler
)
301 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
302 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
305 /* Make sure none of these pages are already allocated */
306 for (i
= FirstPage
; i
<= LastPage
; i
++)
308 if (PageTable
[i
] != NULL
) return FALSE
;
311 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
313 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
315 if (Hook
->hVdd
== NULL
316 && Hook
->FastReadHandler
== ReadHandler
317 && Hook
->FastWriteHandler
== WriteHandler
)
323 if (Pointer
== &HookList
)
325 /* Create and initialize a new hook entry... */
326 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
327 if (Hook
== NULL
) return FALSE
;
331 Hook
->FastReadHandler
= ReadHandler
;
332 Hook
->FastWriteHandler
= WriteHandler
;
334 /* ... and add it to the list of hooks */
335 InsertTailList(&HookList
, &Hook
->Entry
);
338 /* Increase the number of pages this hook has */
339 Hook
->Count
+= LastPage
- FirstPage
+ 1;
341 /* Add the hook entry to the page table */
342 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
348 MemRemoveFastMemoryHook(PVOID Address
, ULONG Size
)
352 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
353 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
355 if (Size
== 0) return FALSE
;
357 for (i
= FirstPage
; i
<= LastPage
; i
++)
360 if (Hook
== NULL
|| Hook
->hVdd
!= NULL
) continue;
362 if (--Hook
->Count
== 0)
364 /* This hook has no more pages */
365 RemoveEntryList(&Hook
->Entry
);
366 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
376 MemQueryMemoryZone(ULONG StartAddress
, PULONG Length
, PBOOLEAN Hooked
)
378 ULONG Page
= StartAddress
>> 12;
379 if (Page
>= TOTAL_PAGES
) return FALSE
;
382 *Hooked
= PageTable
[Page
] != NULL
;
384 while (Page
< TOTAL_PAGES
&& (PageTable
[Page
] != NULL
) == *Hooked
)
386 *Length
+= PAGE_SIZE
;
395 Sim32pGetVDMPointer(IN ULONG Address
,
396 IN BOOLEAN ProtectedMode
)
399 UNREFERENCED_PARAMETER(ProtectedMode
);
402 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
403 * or Selector (if ProtectedMode == TRUE )
404 * LOWORD(Address) == Offset
406 return (PBYTE
)FAR_POINTER(Address
);
411 MGetVdmPointer(IN ULONG Address
,
413 IN BOOLEAN ProtectedMode
)
415 UNREFERENCED_PARAMETER(Size
);
416 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
421 VdmMapFlat(IN USHORT Segment
,
426 UNREFERENCED_PARAMETER(Mode
);
428 return SEG_OFF_TO_PTR(Segment
, Offset
);
431 #ifndef VdmFlushCache
435 VdmFlushCache(IN USHORT Segment
,
451 VdmUnmapFlat(IN USHORT Segment
,
465 VDDInstallMemoryHook(IN HANDLE hVdd
,
468 IN PVDD_MEMORY_HANDLER MemoryHandler
)
473 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
474 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
475 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
476 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
479 /* Check validity of the VDD handle */
480 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
482 SetLastError(ERROR_INVALID_PARAMETER
);
486 if (dwCount
== 0) return FALSE
;
488 /* Make sure none of these pages are already allocated */
489 for (i
= FirstPage
; i
<= LastPage
; i
++)
491 if (PageTable
[i
] != NULL
) return FALSE
;
494 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
496 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
497 if (Hook
->hVdd
== hVdd
&& Hook
->VddHandler
== MemoryHandler
) break;
500 if (Pointer
== &HookList
)
502 /* Create and initialize a new hook entry... */
503 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
506 SetLastError(ERROR_OUTOFMEMORY
);
512 Hook
->VddHandler
= MemoryHandler
;
514 /* ... and add it to the list of hooks */
515 InsertTailList(&HookList
, &Hook
->Entry
);
518 /* Decommit the pages */
519 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
523 if (!NT_SUCCESS(Status
))
525 if (Pointer
== &HookList
)
527 RemoveEntryList(&Hook
->Entry
);
528 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
534 /* Increase the number of pages this hook has */
535 Hook
->Count
+= LastPage
- FirstPage
+ 1;
537 /* Add the hook entry to the page table */
538 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
545 VDDDeInstallMemoryHook(IN HANDLE hVdd
,
552 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
553 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
554 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
555 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
557 /* Check validity of the VDD handle */
558 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
560 SetLastError(ERROR_INVALID_PARAMETER
);
564 if (dwCount
== 0) return FALSE
;
566 /* Commit the pages */
567 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
573 if (!NT_SUCCESS(Status
)) return FALSE
;
575 for (i
= FirstPage
; i
<= LastPage
; i
++)
578 if (Hook
== NULL
) continue;
580 if (Hook
->hVdd
!= hVdd
)
582 DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i
);
586 if (--Hook
->Count
== 0)
588 /* This hook has no more pages */
589 RemoveEntryList(&Hook
->Entry
);
590 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
601 VDDAllocMem(IN HANDLE hVdd
,
608 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
609 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
610 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
612 /* Check validity of the VDD handle */
613 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
615 SetLastError(ERROR_INVALID_PARAMETER
);
619 if (Size
== 0) return FALSE
;
621 /* Fixup the address */
622 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
624 /* Be sure that all the region is held by the VDD */
625 for (i
= FirstPage
; i
<= LastPage
; i
++)
628 if (Hook
== NULL
) return FALSE
;
630 if (Hook
->hVdd
!= hVdd
)
632 DPRINT1("VDDAllocMem: Page %u owned by someone else.\n", i
);
637 /* OK, all the range is held by the VDD. Commit the pages. */
638 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
644 return NT_SUCCESS(Status
);
649 VDDFreeMem(IN HANDLE hVdd
,
656 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
657 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
658 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
660 /* Check validity of the VDD handle */
661 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
663 SetLastError(ERROR_INVALID_PARAMETER
);
667 if (Size
== 0) return FALSE
;
669 /* Fixup the address */
670 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
672 /* Be sure that all the region is held by the VDD */
673 for (i
= FirstPage
; i
<= LastPage
; i
++)
676 if (Hook
== NULL
) return FALSE
;
678 if (Hook
->hVdd
!= hVdd
)
680 DPRINT1("VDDFreeMem: Page %u owned by someone else.\n", i
);
685 /* OK, all the range is held by the VDD. Decommit the pages. */
686 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
690 return NT_SUCCESS(Status
);
695 VDDIncludeMem(IN HANDLE hVdd
,
706 VDDExcludeMem(IN HANDLE hVdd
,
721 SIZE_T MemorySize
= MAX_ADDRESS
; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
723 InitializeListHead(&HookList
);
728 * The reserved region starts from the very first page.
729 * We need to commit the reserved first 16 MB virtual address.
731 * NOTE: NULL has another signification for NtAllocateVirtualMemory.
733 BaseAddress
= (PVOID
)1;
736 * Since to get NULL, we allocated from 0x1, account for this.
737 * See also: kernel32/client/proc.c!CreateProcessInternalW
743 /* Allocate it anywhere */
748 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
755 MEM_RESERVE
| MEM_COMMIT
,
757 PAGE_EXECUTE_READWRITE
);
758 if (!NT_SUCCESS(Status
))
760 wprintf(L
"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status
);
765 ASSERT(BaseAddress
== NULL
);
769 * For diagnostics purposes, we fill the memory with INT 0x03 codes
770 * so that if a program wants to execute random code in memory, we can
771 * retrieve the exact CS:IP where the problem happens.
773 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
781 SIZE_T MemorySize
= MAX_ADDRESS
;
784 while (!IsListEmpty(&HookList
))
786 Pointer
= RemoveHeadList(&HookList
);
787 RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
));
790 /* Decommit the VDM memory */
791 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
800 if (!NT_SUCCESS(Status
))
802 DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status
);