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 *******************************************************************/
19 /* PRIVATE VARIABLES **********************************************************/
21 typedef struct _MEM_HOOK
29 PVDD_MEMORY_HANDLER VddHandler
;
33 PMEMORY_READ_HANDLER FastReadHandler
;
34 PMEMORY_WRITE_HANDLER FastWriteHandler
;
37 } MEM_HOOK
, *PMEM_HOOK
;
39 static LIST_ENTRY HookList
;
40 static PMEM_HOOK PageTable
[TOTAL_PAGES
] = { NULL
};
41 static BOOLEAN A20Line
= FALSE
;
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
);
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
);
126 WritePage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
130 || !Hook
->FastWriteHandler
131 || Hook
->FastWriteHandler(Address
, Buffer
, Size
))
133 MemFastMoveMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
137 /* PUBLIC FUNCTIONS ***********************************************************/
139 VOID FASTCALL
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
141 ULONG i
, Offset
, Length
;
142 ULONG FirstPage
, LastPage
;
144 UNREFERENCED_PARAMETER(State
);
146 /* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
147 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
149 /* If the A20 line is disabled, mask bit 20 */
150 if (!A20Line
) Address
&= ~(1 << 20);
152 if ((Address
+ Size
- 1) >= MAX_ADDRESS
)
154 ULONG ExtraStart
= (Address
< MAX_ADDRESS
) ? MAX_ADDRESS
- Address
: 0;
156 /* Fill the memory that was above the limit with 0xFF */
157 RtlFillMemory((PVOID
)((ULONG_PTR
)Buffer
+ ExtraStart
), Size
- ExtraStart
, 0xFF);
159 if (Address
< MAX_ADDRESS
) Size
= MAX_ADDRESS
- Address
;
163 FirstPage
= Address
>> 12;
164 LastPage
= (Address
+ Size
- 1) >> 12;
166 if (FirstPage
== LastPage
)
168 ReadPage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
172 for (i
= FirstPage
; i
<= LastPage
; i
++)
174 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
175 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
177 ReadPage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
178 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
183 VOID FASTCALL
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
185 ULONG i
, Offset
, Length
;
186 ULONG FirstPage
, LastPage
;
188 UNREFERENCED_PARAMETER(State
);
190 /* If the A20 line is disabled, mask bit 20 */
191 if (!A20Line
) Address
&= ~(1 << 20);
193 if (Address
>= MAX_ADDRESS
) return;
194 Size
= min(Size
, MAX_ADDRESS
- Address
);
196 FirstPage
= Address
>> 12;
197 LastPage
= (Address
+ Size
- 1) >> 12;
199 if (FirstPage
== LastPage
)
201 WritePage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
205 for (i
= FirstPage
; i
<= LastPage
; i
++)
207 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
208 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
210 WritePage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
211 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
216 VOID FASTCALL
EmulatorCopyMemory(PFAST486_STATE State
, ULONG DestAddress
, ULONG SrcAddress
, ULONG Size
)
219 * Guest-to-guest memory copy
222 // FIXME: This is a temporary implementation of a more useful functionality
223 // which should be a merge of EmulatorReadMemory & EmulatorWriteMemory without
224 // any local external buffer.
225 // NOTE: Process heap is by default serialized (unless one specifies it shouldn't).
226 static BYTE StaticBuffer
[8192]; // Smallest static buffer we can use.
227 static PVOID HeapBuffer
= NULL
; // Always-growing heap buffer. Use it in case StaticBuffer is too small.
228 static ULONG HeapBufferSize
= 0;
229 PVOID LocalBuffer
; // Points to either StaticBuffer or HeapBuffer
231 if (Size
<= sizeof(StaticBuffer
))
233 /* Use the static buffer */
234 LocalBuffer
= StaticBuffer
;
236 else if (/* sizeof(StaticBuffer) <= Size && */ Size
<= HeapBufferSize
)
238 /* Use the heap buffer */
239 ASSERT(HeapBufferSize
> 0 && HeapBuffer
!= NULL
);
240 LocalBuffer
= HeapBuffer
;
242 else // if (Size > HeapBufferSize)
244 /* Enlarge the heap buffer and use it */
246 if (HeapBuffer
== NULL
)
248 /* First allocation */
249 LocalBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Size
);
254 LocalBuffer
= RtlReAllocateHeap(RtlGetProcessHeap(), 0 /* HEAP_GENERATE_EXCEPTIONS */, HeapBuffer
, Size
);
256 ASSERT(LocalBuffer
!= NULL
); // We must succeed! TODO: Handle it more properly.
257 HeapBuffer
= LocalBuffer
; // HeapBuffer is now reallocated.
258 HeapBufferSize
= Size
;
261 /* Perform memory copy */
262 EmulatorReadMemory( State
, SrcAddress
, LocalBuffer
, Size
);
263 EmulatorWriteMemory(State
, DestAddress
, LocalBuffer
, Size
);
265 // if (LocalBuffer != StaticBuffer)
266 // RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
268 // Note that we don't free HeapBuffer since it's an always-growing buffer.
269 // It is freed when NTVDM termiantes.
272 VOID
EmulatorSetA20(BOOLEAN Enabled
)
277 BOOLEAN
EmulatorGetA20(VOID
)
283 MemExceptionHandler(ULONG FaultAddress
, BOOLEAN Writing
)
285 PMEM_HOOK Hook
= PageTable
[FaultAddress
>> 12];
286 DPRINT("The memory at 0x%08X could not be %s.\n", FaultAddress
, Writing
? "written" : "read");
288 /* Exceptions are only supposed to happen when using VDD-style memory hooks */
289 ASSERT(FaultAddress
< MAX_ADDRESS
&& Hook
!= NULL
&& Hook
->hVdd
!= NULL
);
291 /* Call the VDD handler */
292 Hook
->VddHandler(REAL_TO_PHYS(FaultAddress
), (ULONG
)Writing
);
296 MemInstallFastMemoryHook(PVOID Address
,
298 PMEMORY_READ_HANDLER ReadHandler
,
299 PMEMORY_WRITE_HANDLER WriteHandler
)
303 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
304 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
307 /* Make sure none of these pages are already allocated */
308 for (i
= FirstPage
; i
<= LastPage
; i
++)
310 if (PageTable
[i
] != NULL
) return FALSE
;
313 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
315 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
317 if (Hook
->hVdd
== NULL
318 && Hook
->FastReadHandler
== ReadHandler
319 && Hook
->FastWriteHandler
== WriteHandler
)
325 if (Pointer
== &HookList
)
327 /* Create and initialize a new hook entry... */
328 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
329 if (Hook
== NULL
) return FALSE
;
333 Hook
->FastReadHandler
= ReadHandler
;
334 Hook
->FastWriteHandler
= WriteHandler
;
336 /* ... and add it to the list of hooks */
337 InsertTailList(&HookList
, &Hook
->Entry
);
340 /* Increase the number of pages this hook has */
341 Hook
->Count
+= LastPage
- FirstPage
+ 1;
343 /* Add the hook entry to the page table */
344 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
350 MemRemoveFastMemoryHook(PVOID Address
, ULONG Size
)
354 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
355 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
357 if (Size
== 0) return FALSE
;
359 for (i
= FirstPage
; i
<= LastPage
; i
++)
362 if (Hook
== NULL
|| Hook
->hVdd
!= NULL
) continue;
364 if (--Hook
->Count
== 0)
366 /* This hook has no more pages */
367 RemoveEntryList(&Hook
->Entry
);
368 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
378 MemQueryMemoryZone(ULONG StartAddress
, PULONG Length
, PBOOLEAN Hooked
)
380 ULONG Page
= StartAddress
>> 12;
381 if (Page
>= TOTAL_PAGES
) return FALSE
;
384 *Hooked
= PageTable
[Page
] != NULL
;
386 while (Page
< TOTAL_PAGES
&& (PageTable
[Page
] != NULL
) == *Hooked
)
388 *Length
+= PAGE_SIZE
;
397 Sim32pGetVDMPointer(IN ULONG Address
,
398 IN BOOLEAN ProtectedMode
)
401 UNREFERENCED_PARAMETER(ProtectedMode
);
404 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
405 * or Selector (if ProtectedMode == TRUE )
406 * LOWORD(Address) == Offset
408 return (PBYTE
)FAR_POINTER(Address
);
413 MGetVdmPointer(IN ULONG Address
,
415 IN BOOLEAN ProtectedMode
)
417 UNREFERENCED_PARAMETER(Size
);
418 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
423 VdmMapFlat(IN USHORT Segment
,
428 UNREFERENCED_PARAMETER(Mode
);
430 return SEG_OFF_TO_PTR(Segment
, Offset
);
433 #ifndef VdmFlushCache
437 VdmFlushCache(IN USHORT Segment
,
453 VdmUnmapFlat(IN USHORT Segment
,
467 VDDInstallMemoryHook(IN HANDLE hVdd
,
470 IN PVDD_MEMORY_HANDLER MemoryHandler
)
475 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
476 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
477 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
478 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
481 /* Check validity of the VDD handle */
482 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
484 SetLastError(ERROR_INVALID_PARAMETER
);
488 if (dwCount
== 0) return FALSE
;
490 /* Make sure none of these pages are already allocated */
491 for (i
= FirstPage
; i
<= LastPage
; i
++)
493 if (PageTable
[i
] != NULL
) return FALSE
;
496 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
498 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
499 if (Hook
->hVdd
== hVdd
&& Hook
->VddHandler
== MemoryHandler
) break;
502 if (Pointer
== &HookList
)
504 /* Create and initialize a new hook entry... */
505 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
508 SetLastError(ERROR_OUTOFMEMORY
);
514 Hook
->VddHandler
= MemoryHandler
;
516 /* ... and add it to the list of hooks */
517 InsertTailList(&HookList
, &Hook
->Entry
);
520 /* Decommit the pages */
521 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
525 if (!NT_SUCCESS(Status
))
527 if (Pointer
== &HookList
)
529 RemoveEntryList(&Hook
->Entry
);
530 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
536 /* Increase the number of pages this hook has */
537 Hook
->Count
+= LastPage
- FirstPage
+ 1;
539 /* Add the hook entry to the page table */
540 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
547 VDDDeInstallMemoryHook(IN HANDLE hVdd
,
554 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
555 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
556 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
557 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
559 /* Check validity of the VDD handle */
560 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
562 SetLastError(ERROR_INVALID_PARAMETER
);
566 if (dwCount
== 0) return FALSE
;
568 /* Commit the pages */
569 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
575 if (!NT_SUCCESS(Status
)) return FALSE
;
577 for (i
= FirstPage
; i
<= LastPage
; i
++)
580 if (Hook
== NULL
) continue;
582 if (Hook
->hVdd
!= hVdd
)
584 DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i
);
588 if (--Hook
->Count
== 0)
590 /* This hook has no more pages */
591 RemoveEntryList(&Hook
->Entry
);
592 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
603 VDDAllocMem(IN HANDLE hVdd
,
610 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
611 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
612 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
614 /* Check validity of the VDD handle */
615 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
617 SetLastError(ERROR_INVALID_PARAMETER
);
621 if (Size
== 0) return FALSE
;
623 /* Fixup the address */
624 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
626 /* Be sure that all the region is held by the VDD */
627 for (i
= FirstPage
; i
<= LastPage
; i
++)
630 if (Hook
== NULL
) return FALSE
;
632 if (Hook
->hVdd
!= hVdd
)
634 DPRINT1("VDDAllocMem: Page %u owned by someone else.\n", i
);
639 /* OK, all the range is held by the VDD. Commit the pages. */
640 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
646 return NT_SUCCESS(Status
);
651 VDDFreeMem(IN HANDLE hVdd
,
658 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
659 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
660 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
662 /* Check validity of the VDD handle */
663 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
665 SetLastError(ERROR_INVALID_PARAMETER
);
669 if (Size
== 0) return FALSE
;
671 /* Fixup the address */
672 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
674 /* Be sure that all the region is held by the VDD */
675 for (i
= FirstPage
; i
<= LastPage
; i
++)
678 if (Hook
== NULL
) return FALSE
;
680 if (Hook
->hVdd
!= hVdd
)
682 DPRINT1("VDDFreeMem: Page %u owned by someone else.\n", i
);
687 /* OK, all the range is held by the VDD. Decommit the pages. */
688 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
692 return NT_SUCCESS(Status
);
697 VDDIncludeMem(IN HANDLE hVdd
,
708 VDDExcludeMem(IN HANDLE hVdd
,
723 SIZE_T MemorySize
= MAX_ADDRESS
; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
725 InitializeListHead(&HookList
);
730 * The reserved region starts from the very first page.
731 * We need to commit the reserved first 16 MB virtual address.
733 * NOTE: NULL has another signification for NtAllocateVirtualMemory.
735 BaseAddress
= (PVOID
)1;
738 * Since to get NULL, we allocated from 0x1, account for this.
739 * See also: kernel32/client/proc.c!CreateProcessInternalW
745 /* Allocate it anywhere */
750 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
757 MEM_RESERVE
| MEM_COMMIT
,
759 PAGE_EXECUTE_READWRITE
);
760 if (!NT_SUCCESS(Status
))
762 wprintf(L
"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status
);
767 ASSERT(BaseAddress
== NULL
);
771 * For diagnostics purposes, we fill the memory with INT 0x03 codes
772 * so that if a program wants to execute random code in memory, we can
773 * retrieve the exact CS:IP where the problem happens.
775 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
783 SIZE_T MemorySize
= MAX_ADDRESS
;
786 while (!IsListEmpty(&HookList
))
788 Pointer
= RemoveHeadList(&HookList
);
789 RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
));
792 /* Decommit the VDM memory */
793 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
802 if (!NT_SUCCESS(Status
))
804 DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status
);