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>
14 /* PRIVATE VARIABLES **********************************************************/
16 typedef struct _MEM_HOOK
24 PVDD_MEMORY_HANDLER VddHandler
;
28 PMEMORY_READ_HANDLER FastReadHandler
;
29 PMEMORY_WRITE_HANDLER FastWriteHandler
;
32 } MEM_HOOK
, *PMEM_HOOK
;
34 static LIST_ENTRY HookList
;
35 static PMEM_HOOK PageTable
[TOTAL_PAGES
] = { NULL
};
36 static BOOLEAN A20Line
= FALSE
;
38 /* PRIVATE FUNCTIONS **********************************************************/
41 MemFastMoveMemory(OUT VOID UNALIGNED
*Destination
,
42 IN
const VOID UNALIGNED
*Source
,
47 * We use a switch here to detect small moves of memory, as these
48 * constitute the bulk of our moves.
49 * Using RtlMoveMemory for all these small moves would be slow otherwise.
57 *(PUCHAR
)Destination
= *(PUCHAR
)Source
;
61 *(PUSHORT
)Destination
= *(PUSHORT
)Source
;
65 *(PULONG
)Destination
= *(PULONG
)Source
;
68 case sizeof(ULONGLONG
):
69 *(PULONGLONG
)Destination
= *(PULONGLONG
)Source
;
74 __builtin_memmove(Destination
, Source
, Length
);
76 RtlMoveMemory(Destination
, Source
, Length
);
80 #else // defined(_MSC_VER)
82 PUCHAR Dest
= (PUCHAR
)Destination
;
83 PUCHAR Src
= (PUCHAR
)Source
;
85 SIZE_T Count
, NewSize
= Length
;
88 Count
= NewSize
>> 2; // NewSize / sizeof(ULONG);
89 NewSize
= NewSize
& 3; // NewSize % sizeof(ULONG);
90 __movsd(Dest
, Src
, Count
);
91 Dest
+= Count
<< 2; // Count * sizeof(ULONG);
95 Count
= NewSize
>> 1; // NewSize / sizeof(USHORT);
96 NewSize
= NewSize
& 1; // NewSize % sizeof(USHORT);
97 __movsw(Dest
, Src
, Count
);
98 Dest
+= Count
<< 1; // Count * sizeof(USHORT);
102 Count
= NewSize
; // NewSize / sizeof(UCHAR);
103 // NewSize = NewSize; // NewSize % sizeof(UCHAR);
104 __movsb(Dest
, Src
, Count
);
110 ReadPage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
112 if (Hook
&& !Hook
->hVdd
&& Hook
->FastReadHandler
)
114 Hook
->FastReadHandler(Address
, REAL_TO_PHYS(Address
), Size
);
117 MemFastMoveMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
121 WritePage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
125 || !Hook
->FastWriteHandler
126 || Hook
->FastWriteHandler(Address
, Buffer
, Size
))
128 MemFastMoveMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
132 /* PUBLIC FUNCTIONS ***********************************************************/
134 VOID FASTCALL
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
136 ULONG i
, Offset
, Length
;
137 ULONG FirstPage
, LastPage
;
139 UNREFERENCED_PARAMETER(State
);
141 /* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
142 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
144 /* If the A20 line is disabled, mask bit 20 */
145 if (!A20Line
) Address
&= ~(1 << 20);
147 if ((Address
+ Size
- 1) >= MAX_ADDRESS
)
149 ULONG ExtraStart
= (Address
< MAX_ADDRESS
) ? MAX_ADDRESS
- Address
: 0;
151 /* Fill the memory that was above the limit with 0xFF */
152 RtlFillMemory((PVOID
)((ULONG_PTR
)Buffer
+ ExtraStart
), Size
- ExtraStart
, 0xFF);
154 if (Address
< MAX_ADDRESS
) Size
= MAX_ADDRESS
- Address
;
158 FirstPage
= Address
>> 12;
159 LastPage
= (Address
+ Size
- 1) >> 12;
161 if (FirstPage
== LastPage
)
163 ReadPage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
167 for (i
= FirstPage
; i
<= LastPage
; i
++)
169 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
170 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
172 ReadPage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
173 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
178 VOID FASTCALL
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
180 ULONG i
, Offset
, Length
;
181 ULONG FirstPage
, LastPage
;
183 UNREFERENCED_PARAMETER(State
);
185 /* If the A20 line is disabled, mask bit 20 */
186 if (!A20Line
) Address
&= ~(1 << 20);
188 if (Address
>= MAX_ADDRESS
) return;
189 Size
= min(Size
, MAX_ADDRESS
- Address
);
191 FirstPage
= Address
>> 12;
192 LastPage
= (Address
+ Size
- 1) >> 12;
194 if (FirstPage
== LastPage
)
196 WritePage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
200 for (i
= FirstPage
; i
<= LastPage
; i
++)
202 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
203 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
205 WritePage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
206 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
211 VOID FASTCALL
EmulatorCopyMemory(PFAST486_STATE State
, ULONG DestAddress
, ULONG SrcAddress
, ULONG Size
)
214 * Guest-to-guest memory copy
217 // FIXME: This is a temporary implementation of a more useful functionality
218 // which should be a merge of EmulatorReadMemory & EmulatorWriteMemory without
219 // any local external buffer.
220 // NOTE: Process heap is by default serialized (unless one specifies it shouldn't).
221 static BYTE StaticBuffer
[8192]; // Smallest static buffer we can use.
222 static PVOID HeapBuffer
= NULL
; // Always-growing heap buffer. Use it in case StaticBuffer is too small.
223 static ULONG HeapBufferSize
= 0;
224 PVOID LocalBuffer
; // Points to either StaticBuffer or HeapBuffer
226 if (Size
<= sizeof(StaticBuffer
))
228 /* Use the static buffer */
229 LocalBuffer
= StaticBuffer
;
231 else if (/* sizeof(StaticBuffer) <= Size && */ Size
<= HeapBufferSize
)
233 /* Use the heap buffer */
234 ASSERT(HeapBufferSize
> 0 && HeapBuffer
!= NULL
);
235 LocalBuffer
= HeapBuffer
;
237 else // if (Size > HeapBufferSize)
239 /* Enlarge the heap buffer and use it */
241 if (HeapBuffer
== NULL
)
243 /* First allocation */
244 LocalBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Size
);
249 LocalBuffer
= RtlReAllocateHeap(RtlGetProcessHeap(), 0 /* HEAP_GENERATE_EXCEPTIONS */, HeapBuffer
, Size
);
251 ASSERT(LocalBuffer
!= NULL
); // We must succeed! TODO: Handle it more properly.
252 HeapBuffer
= LocalBuffer
; // HeapBuffer is now reallocated.
253 HeapBufferSize
= Size
;
256 /* Perform memory copy */
257 EmulatorReadMemory( State
, SrcAddress
, LocalBuffer
, Size
);
258 EmulatorWriteMemory(State
, DestAddress
, LocalBuffer
, Size
);
260 // if (LocalBuffer != StaticBuffer)
261 // RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
263 // Note that we don't free HeapBuffer since it's an always-growing buffer.
264 // It is freed when NTVDM termiantes.
267 VOID
EmulatorSetA20(BOOLEAN Enabled
)
272 BOOLEAN
EmulatorGetA20(VOID
)
278 MemExceptionHandler(ULONG FaultAddress
, BOOLEAN Writing
)
280 PMEM_HOOK Hook
= PageTable
[FaultAddress
>> 12];
281 DPRINT("The memory at 0x%08X could not be %s.\n", FaultAddress
, Writing
? "written" : "read");
283 /* Exceptions are only supposed to happen when using VDD-style memory hooks */
284 ASSERT(FaultAddress
< MAX_ADDRESS
&& Hook
!= NULL
&& Hook
->hVdd
!= NULL
);
286 /* Call the VDD handler */
287 Hook
->VddHandler(REAL_TO_PHYS(FaultAddress
), (ULONG
)Writing
);
291 MemInstallFastMemoryHook(PVOID Address
,
293 PMEMORY_READ_HANDLER ReadHandler
,
294 PMEMORY_WRITE_HANDLER WriteHandler
)
298 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
299 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
302 /* Make sure none of these pages are already allocated */
303 for (i
= FirstPage
; i
<= LastPage
; i
++)
305 if (PageTable
[i
] != NULL
) return FALSE
;
308 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
310 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
312 if (Hook
->hVdd
== NULL
313 && Hook
->FastReadHandler
== ReadHandler
314 && Hook
->FastWriteHandler
== WriteHandler
)
320 if (Pointer
== &HookList
)
322 /* Create and initialize a new hook entry... */
323 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
324 if (Hook
== NULL
) return FALSE
;
328 Hook
->FastReadHandler
= ReadHandler
;
329 Hook
->FastWriteHandler
= WriteHandler
;
331 /* ... and add it to the list of hooks */
332 InsertTailList(&HookList
, &Hook
->Entry
);
335 /* Increase the number of pages this hook has */
336 Hook
->Count
+= LastPage
- FirstPage
+ 1;
338 /* Add the hook entry to the page table */
339 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
345 MemRemoveFastMemoryHook(PVOID Address
, ULONG Size
)
349 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
350 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
352 if (Size
== 0) return FALSE
;
354 for (i
= FirstPage
; i
<= LastPage
; i
++)
357 if (Hook
== NULL
|| Hook
->hVdd
!= NULL
) continue;
359 if (--Hook
->Count
== 0)
361 /* This hook has no more pages */
362 RemoveEntryList(&Hook
->Entry
);
363 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
373 MemQueryMemoryZone(ULONG StartAddress
, PULONG Length
, PBOOLEAN Hooked
)
375 ULONG Page
= StartAddress
>> 12;
376 if (Page
>= TOTAL_PAGES
) return FALSE
;
379 *Hooked
= PageTable
[Page
] != NULL
;
381 while (Page
< TOTAL_PAGES
&& (PageTable
[Page
] != NULL
) == *Hooked
)
383 *Length
+= PAGE_SIZE
;
392 Sim32pGetVDMPointer(IN ULONG Address
,
393 IN BOOLEAN ProtectedMode
)
396 UNREFERENCED_PARAMETER(ProtectedMode
);
399 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
400 * or Selector (if ProtectedMode == TRUE )
401 * LOWORD(Address) == Offset
403 return (PBYTE
)FAR_POINTER(Address
);
408 MGetVdmPointer(IN ULONG Address
,
410 IN BOOLEAN ProtectedMode
)
412 UNREFERENCED_PARAMETER(Size
);
413 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
418 VdmMapFlat(IN USHORT Segment
,
423 UNREFERENCED_PARAMETER(Mode
);
425 return SEG_OFF_TO_PTR(Segment
, Offset
);
428 #ifndef VdmFlushCache
432 VdmFlushCache(IN USHORT Segment
,
448 VdmUnmapFlat(IN USHORT Segment
,
462 VDDInstallMemoryHook(IN HANDLE hVdd
,
465 IN PVDD_MEMORY_HANDLER MemoryHandler
)
470 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
471 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
472 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
473 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
476 /* Check validity of the VDD handle */
477 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
479 SetLastError(ERROR_INVALID_PARAMETER
);
483 if (dwCount
== 0) return FALSE
;
485 /* Make sure none of these pages are already allocated */
486 for (i
= FirstPage
; i
<= LastPage
; i
++)
488 if (PageTable
[i
] != NULL
) return FALSE
;
491 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
493 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
494 if (Hook
->hVdd
== hVdd
&& Hook
->VddHandler
== MemoryHandler
) break;
497 if (Pointer
== &HookList
)
499 /* Create and initialize a new hook entry... */
500 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
503 SetLastError(ERROR_OUTOFMEMORY
);
509 Hook
->VddHandler
= MemoryHandler
;
511 /* ... and add it to the list of hooks */
512 InsertTailList(&HookList
, &Hook
->Entry
);
515 /* Decommit the pages */
516 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
520 if (!NT_SUCCESS(Status
))
522 if (Pointer
== &HookList
)
524 RemoveEntryList(&Hook
->Entry
);
525 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
531 /* Increase the number of pages this hook has */
532 Hook
->Count
+= LastPage
- FirstPage
+ 1;
534 /* Add the hook entry to the page table */
535 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
542 VDDDeInstallMemoryHook(IN HANDLE hVdd
,
549 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
550 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
551 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
552 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
554 /* Check validity of the VDD handle */
555 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
557 SetLastError(ERROR_INVALID_PARAMETER
);
561 if (dwCount
== 0) return FALSE
;
563 /* Commit the pages */
564 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
570 if (!NT_SUCCESS(Status
)) return FALSE
;
572 for (i
= FirstPage
; i
<= LastPage
; i
++)
575 if (Hook
== NULL
) continue;
577 if (Hook
->hVdd
!= hVdd
)
579 DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i
);
583 if (--Hook
->Count
== 0)
585 /* This hook has no more pages */
586 RemoveEntryList(&Hook
->Entry
);
587 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
598 VDDAllocMem(IN HANDLE hVdd
,
605 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
606 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
607 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
609 /* Check validity of the VDD handle */
610 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
612 SetLastError(ERROR_INVALID_PARAMETER
);
616 if (Size
== 0) return FALSE
;
618 /* Fixup the address */
619 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
621 /* Be sure that all the region is held by the VDD */
622 for (i
= FirstPage
; i
<= LastPage
; i
++)
625 if (Hook
== NULL
) return FALSE
;
627 if (Hook
->hVdd
!= hVdd
)
629 DPRINT1("VDDAllocMem: Page %u owned by someone else.\n", i
);
634 /* OK, all the range is held by the VDD. Commit the pages. */
635 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
641 return NT_SUCCESS(Status
);
646 VDDFreeMem(IN HANDLE hVdd
,
653 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
654 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
655 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
657 /* Check validity of the VDD handle */
658 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
660 SetLastError(ERROR_INVALID_PARAMETER
);
664 if (Size
== 0) return FALSE
;
666 /* Fixup the address */
667 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
669 /* Be sure that all the region is held by the VDD */
670 for (i
= FirstPage
; i
<= LastPage
; i
++)
673 if (Hook
== NULL
) return FALSE
;
675 if (Hook
->hVdd
!= hVdd
)
677 DPRINT1("VDDFreeMem: Page %u owned by someone else.\n", i
);
682 /* OK, all the range is held by the VDD. Decommit the pages. */
683 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
687 return NT_SUCCESS(Status
);
692 VDDIncludeMem(IN HANDLE hVdd
,
703 VDDExcludeMem(IN HANDLE hVdd
,
718 SIZE_T MemorySize
= MAX_ADDRESS
; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
720 InitializeListHead(&HookList
);
725 * The reserved region starts from the very first page.
726 * We need to commit the reserved first 16 MB virtual address.
728 * NOTE: NULL has another signification for NtAllocateVirtualMemory.
730 BaseAddress
= (PVOID
)1;
733 * Since to get NULL, we allocated from 0x1, account for this.
734 * See also: kernel32/client/proc.c!CreateProcessInternalW
740 /* Allocate it anywhere */
745 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
752 MEM_RESERVE
| MEM_COMMIT
,
754 PAGE_EXECUTE_READWRITE
);
755 if (!NT_SUCCESS(Status
))
757 wprintf(L
"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status
);
762 ASSERT(BaseAddress
== NULL
);
766 * For diagnostics purposes, we fill the memory with INT 0x03 codes
767 * so that if a program wants to execute random code in memory, we can
768 * retrieve the exact CS:IP where the problem happens.
770 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
778 SIZE_T MemorySize
= MAX_ADDRESS
;
781 while (!IsListEmpty(&HookList
))
783 Pointer
= RemoveHeadList(&HookList
);
784 RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
));
787 /* Decommit the VDM memory */
788 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
797 if (!NT_SUCCESS(Status
))
799 DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status
);