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 /* Extra PSDK/NDK Headers */
20 #include <ndk/mmfuncs.h>
22 /* PRIVATE VARIABLES **********************************************************/
24 typedef struct _MEM_HOOK
32 PVDD_MEMORY_HANDLER VddHandler
;
36 PMEMORY_READ_HANDLER FastReadHandler
;
37 PMEMORY_WRITE_HANDLER FastWriteHandler
;
40 } MEM_HOOK
, *PMEM_HOOK
;
42 static LIST_ENTRY HookList
;
43 static PMEM_HOOK PageTable
[TOTAL_PAGES
] = { NULL
};
44 static BOOLEAN A20Line
= FALSE
;
46 /* PRIVATE FUNCTIONS **********************************************************/
49 MemFastMoveMemory(OUT VOID UNALIGNED
*Destination
,
50 IN
const VOID UNALIGNED
*Source
,
55 * We use a switch here to detect small moves of memory, as these
56 * constitute the bulk of our moves.
57 * Using RtlMoveMemory for all these small moves would be slow otherwise.
65 *(PUCHAR
)Destination
= *(PUCHAR
)Source
;
69 *(PUSHORT
)Destination
= *(PUSHORT
)Source
;
73 *(PULONG
)Destination
= *(PULONG
)Source
;
76 case sizeof(ULONGLONG
):
77 *(PULONGLONG
)Destination
= *(PULONGLONG
)Source
;
82 __builtin_memmove(Destination
, Source
, Length
);
84 RtlMoveMemory(Destination
, Source
, Length
);
88 #else // defined(_MSC_VER)
90 PUCHAR Dest
= (PUCHAR
)Destination
;
91 PUCHAR Src
= (PUCHAR
)Source
;
93 SIZE_T Count
, NewSize
= Length
;
96 Count
= NewSize
>> 2; // NewSize / sizeof(ULONG);
97 NewSize
= NewSize
& 3; // NewSize % sizeof(ULONG);
98 __movsd(Dest
, Src
, Count
);
99 Dest
+= Count
<< 2; // Count * sizeof(ULONG);
103 Count
= NewSize
>> 1; // NewSize / sizeof(USHORT);
104 NewSize
= NewSize
& 1; // NewSize % sizeof(USHORT);
105 __movsw(Dest
, Src
, Count
);
106 Dest
+= Count
<< 1; // Count * sizeof(USHORT);
110 Count
= NewSize
; // NewSize / sizeof(UCHAR);
111 // NewSize = NewSize; // NewSize % sizeof(UCHAR);
112 __movsb(Dest
, Src
, Count
);
118 ReadPage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
120 if (Hook
&& !Hook
->hVdd
&& Hook
->FastReadHandler
)
122 Hook
->FastReadHandler(Address
, REAL_TO_PHYS(Address
), Size
);
125 MemFastMoveMemory(Buffer
, REAL_TO_PHYS(Address
), Size
);
129 WritePage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
133 || !Hook
->FastWriteHandler
134 || Hook
->FastWriteHandler(Address
, Buffer
, Size
))
136 MemFastMoveMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
140 /* PUBLIC FUNCTIONS ***********************************************************/
142 VOID FASTCALL
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
144 ULONG i
, Offset
, Length
;
145 ULONG FirstPage
, LastPage
;
147 UNREFERENCED_PARAMETER(State
);
149 /* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
150 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
152 /* If the A20 line is disabled, mask bit 20 */
153 if (!A20Line
) Address
&= ~(1 << 20);
155 if ((Address
+ Size
- 1) >= MAX_ADDRESS
)
157 ULONG ExtraStart
= (Address
< MAX_ADDRESS
) ? MAX_ADDRESS
- Address
: 0;
159 /* Fill the memory that was above the limit with 0xFF */
160 RtlFillMemory((PVOID
)((ULONG_PTR
)Buffer
+ ExtraStart
), Size
- ExtraStart
, 0xFF);
162 if (Address
< MAX_ADDRESS
) Size
= MAX_ADDRESS
- Address
;
166 FirstPage
= Address
>> 12;
167 LastPage
= (Address
+ Size
- 1) >> 12;
169 if (FirstPage
== LastPage
)
171 ReadPage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
175 for (i
= FirstPage
; i
<= LastPage
; i
++)
177 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
178 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
180 ReadPage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
181 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
186 VOID FASTCALL
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
188 ULONG i
, Offset
, Length
;
189 ULONG FirstPage
, LastPage
;
191 UNREFERENCED_PARAMETER(State
);
193 /* If the A20 line is disabled, mask bit 20 */
194 if (!A20Line
) Address
&= ~(1 << 20);
196 if (Address
>= MAX_ADDRESS
) return;
197 Size
= min(Size
, MAX_ADDRESS
- Address
);
199 FirstPage
= Address
>> 12;
200 LastPage
= (Address
+ Size
- 1) >> 12;
202 if (FirstPage
== LastPage
)
204 WritePage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
208 for (i
= FirstPage
; i
<= LastPage
; i
++)
210 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
211 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
213 WritePage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
214 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
219 VOID FASTCALL
EmulatorCopyMemory(PFAST486_STATE State
, ULONG DestAddress
, ULONG SrcAddress
, ULONG Size
)
222 * Guest-to-guest memory copy
225 // FIXME: This is a temporary implementation of a more useful functionality
226 // which should be a merge of EmulatorReadMemory & EmulatorWriteMemory without
227 // any local external buffer.
228 // NOTE: Process heap is by default serialized (unless one specifies it shouldn't).
229 static BYTE StaticBuffer
[8192]; // Smallest static buffer we can use.
230 static PVOID HeapBuffer
= NULL
; // Always-growing heap buffer. Use it in case StaticBuffer is too small.
231 static ULONG HeapBufferSize
= 0;
232 PVOID LocalBuffer
; // Points to either StaticBuffer or HeapBuffer
234 if (Size
<= sizeof(StaticBuffer
))
236 /* Use the static buffer */
237 LocalBuffer
= StaticBuffer
;
239 else if (/* sizeof(StaticBuffer) <= Size && */ Size
<= HeapBufferSize
)
241 /* Use the heap buffer */
242 ASSERT(HeapBufferSize
> 0 && HeapBuffer
!= NULL
);
243 LocalBuffer
= HeapBuffer
;
245 else // if (Size > HeapBufferSize)
247 /* Enlarge the heap buffer and use it */
249 if (HeapBuffer
== NULL
)
251 /* First allocation */
252 LocalBuffer
= RtlAllocateHeap(RtlGetProcessHeap(), 0, Size
);
257 LocalBuffer
= RtlReAllocateHeap(RtlGetProcessHeap(), 0 /* HEAP_GENERATE_EXCEPTIONS */, HeapBuffer
, Size
);
259 ASSERT(LocalBuffer
!= NULL
); // We must succeed! TODO: Handle it more properly.
260 HeapBuffer
= LocalBuffer
; // HeapBuffer is now reallocated.
261 HeapBufferSize
= Size
;
264 /* Perform memory copy */
265 EmulatorReadMemory( State
, SrcAddress
, LocalBuffer
, Size
);
266 EmulatorWriteMemory(State
, DestAddress
, LocalBuffer
, Size
);
268 // if (LocalBuffer != StaticBuffer)
269 // RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
271 // Note that we don't free HeapBuffer since it's an always-growing buffer.
272 // It is freed when NTVDM termiantes.
275 VOID
EmulatorSetA20(BOOLEAN Enabled
)
280 BOOLEAN
EmulatorGetA20(VOID
)
286 MemExceptionHandler(ULONG FaultAddress
, BOOLEAN Writing
)
288 PMEM_HOOK Hook
= PageTable
[FaultAddress
>> 12];
289 DPRINT("The memory at 0x%08X could not be %s.\n", FaultAddress
, Writing
? "written" : "read");
291 /* Exceptions are only supposed to happen when using VDD-style memory hooks */
292 ASSERT(FaultAddress
< MAX_ADDRESS
&& Hook
!= NULL
&& Hook
->hVdd
!= NULL
);
294 /* Call the VDD handler */
295 Hook
->VddHandler(REAL_TO_PHYS(FaultAddress
), (ULONG
)Writing
);
299 MemInstallFastMemoryHook(PVOID Address
,
301 PMEMORY_READ_HANDLER ReadHandler
,
302 PMEMORY_WRITE_HANDLER WriteHandler
)
306 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
307 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
310 /* Make sure none of these pages are already allocated */
311 for (i
= FirstPage
; i
<= LastPage
; i
++)
313 if (PageTable
[i
] != NULL
) return FALSE
;
316 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
318 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
320 if (Hook
->hVdd
== NULL
321 && Hook
->FastReadHandler
== ReadHandler
322 && Hook
->FastWriteHandler
== WriteHandler
)
328 if (Pointer
== &HookList
)
330 /* Create and initialize a new hook entry... */
331 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
332 if (Hook
== NULL
) return FALSE
;
336 Hook
->FastReadHandler
= ReadHandler
;
337 Hook
->FastWriteHandler
= WriteHandler
;
339 /* ... and add it to the list of hooks */
340 InsertTailList(&HookList
, &Hook
->Entry
);
343 /* Increase the number of pages this hook has */
344 Hook
->Count
+= LastPage
- FirstPage
+ 1;
346 /* Add the hook entry to the page table */
347 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
353 MemRemoveFastMemoryHook(PVOID Address
, ULONG Size
)
357 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
358 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
360 if (Size
== 0) return FALSE
;
362 for (i
= FirstPage
; i
<= LastPage
; i
++)
365 if (Hook
== NULL
|| Hook
->hVdd
!= NULL
) continue;
367 if (--Hook
->Count
== 0)
369 /* This hook has no more pages */
370 RemoveEntryList(&Hook
->Entry
);
371 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
381 MemQueryMemoryZone(ULONG StartAddress
, PULONG Length
, PBOOLEAN Hooked
)
383 ULONG Page
= StartAddress
>> 12;
384 if (Page
>= TOTAL_PAGES
) return FALSE
;
387 *Hooked
= PageTable
[Page
] != NULL
;
389 while (Page
< TOTAL_PAGES
&& (PageTable
[Page
] != NULL
) == *Hooked
)
391 *Length
+= PAGE_SIZE
;
400 Sim32pGetVDMPointer(IN ULONG Address
,
401 IN BOOLEAN ProtectedMode
)
404 UNREFERENCED_PARAMETER(ProtectedMode
);
407 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
408 * or Selector (if ProtectedMode == TRUE )
409 * LOWORD(Address) == Offset
411 return (PBYTE
)FAR_POINTER(Address
);
416 MGetVdmPointer(IN ULONG Address
,
418 IN BOOLEAN ProtectedMode
)
420 UNREFERENCED_PARAMETER(Size
);
421 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
426 VdmMapFlat(IN USHORT Segment
,
431 UNREFERENCED_PARAMETER(Mode
);
433 return SEG_OFF_TO_PTR(Segment
, Offset
);
436 #ifndef VdmFlushCache
440 VdmFlushCache(IN USHORT Segment
,
456 VdmUnmapFlat(IN USHORT Segment
,
470 VDDInstallMemoryHook(IN HANDLE hVdd
,
473 IN PVDD_MEMORY_HANDLER MemoryHandler
)
478 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
479 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
480 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
481 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
484 /* Check validity of the VDD handle */
485 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
487 SetLastError(ERROR_INVALID_PARAMETER
);
491 if (dwCount
== 0) return FALSE
;
493 /* Make sure none of these pages are already allocated */
494 for (i
= FirstPage
; i
<= LastPage
; i
++)
496 if (PageTable
[i
] != NULL
) return FALSE
;
499 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
501 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
502 if (Hook
->hVdd
== hVdd
&& Hook
->VddHandler
== MemoryHandler
) break;
505 if (Pointer
== &HookList
)
507 /* Create and initialize a new hook entry... */
508 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
511 SetLastError(ERROR_OUTOFMEMORY
);
517 Hook
->VddHandler
= MemoryHandler
;
519 /* ... and add it to the list of hooks */
520 InsertTailList(&HookList
, &Hook
->Entry
);
523 /* Decommit the pages */
524 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
528 if (!NT_SUCCESS(Status
))
530 if (Pointer
== &HookList
)
532 RemoveEntryList(&Hook
->Entry
);
533 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
539 /* Increase the number of pages this hook has */
540 Hook
->Count
+= LastPage
- FirstPage
+ 1;
542 /* Add the hook entry to the page table */
543 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
550 VDDDeInstallMemoryHook(IN HANDLE hVdd
,
557 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
558 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
559 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
560 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
562 /* Check validity of the VDD handle */
563 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
565 SetLastError(ERROR_INVALID_PARAMETER
);
569 if (dwCount
== 0) return FALSE
;
571 /* Commit the pages */
572 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
578 if (!NT_SUCCESS(Status
)) return FALSE
;
580 for (i
= FirstPage
; i
<= LastPage
; i
++)
583 if (Hook
== NULL
) continue;
585 if (Hook
->hVdd
!= hVdd
)
587 DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i
);
591 if (--Hook
->Count
== 0)
593 /* This hook has no more pages */
594 RemoveEntryList(&Hook
->Entry
);
595 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
606 VDDAllocMem(IN HANDLE hVdd
,
613 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
614 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
615 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
617 /* Check validity of the VDD handle */
618 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
620 SetLastError(ERROR_INVALID_PARAMETER
);
624 if (Size
== 0) return FALSE
;
626 /* Fixup the address */
627 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
629 /* Be sure that all the region is held by the VDD */
630 for (i
= FirstPage
; i
<= LastPage
; i
++)
633 if (Hook
== NULL
) return FALSE
;
635 if (Hook
->hVdd
!= hVdd
)
637 DPRINT1("VDDAllocMem: Page %u owned by someone else.\n", i
);
642 /* OK, all the range is held by the VDD. Commit the pages. */
643 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
649 return NT_SUCCESS(Status
);
654 VDDFreeMem(IN HANDLE hVdd
,
661 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
662 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
663 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
665 /* Check validity of the VDD handle */
666 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
668 SetLastError(ERROR_INVALID_PARAMETER
);
672 if (Size
== 0) return FALSE
;
674 /* Fixup the address */
675 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
677 /* Be sure that all the region is held by the VDD */
678 for (i
= FirstPage
; i
<= LastPage
; i
++)
681 if (Hook
== NULL
) return FALSE
;
683 if (Hook
->hVdd
!= hVdd
)
685 DPRINT1("VDDFreeMem: Page %u owned by someone else.\n", i
);
690 /* OK, all the range is held by the VDD. Decommit the pages. */
691 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
695 return NT_SUCCESS(Status
);
700 VDDIncludeMem(IN HANDLE hVdd
,
711 VDDExcludeMem(IN HANDLE hVdd
,
726 SIZE_T MemorySize
= MAX_ADDRESS
; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
728 InitializeListHead(&HookList
);
733 * The reserved region starts from the very first page.
734 * We need to commit the reserved first 16 MB virtual address.
736 * NOTE: NULL has another signification for NtAllocateVirtualMemory.
738 BaseAddress
= (PVOID
)1;
741 * Since to get NULL, we allocated from 0x1, account for this.
742 * See also: kernel32/client/proc.c!CreateProcessInternalW
748 /* Allocate it anywhere */
753 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
760 MEM_RESERVE
| MEM_COMMIT
,
762 PAGE_EXECUTE_READWRITE
);
763 if (!NT_SUCCESS(Status
))
765 wprintf(L
"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status
);
770 ASSERT(BaseAddress
== NULL
);
774 * For diagnostics purposes, we fill the memory with INT 0x03 codes
775 * so that if a program wants to execute random code in memory, we can
776 * retrieve the exact CS:IP where the problem happens.
778 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
786 SIZE_T MemorySize
= MAX_ADDRESS
;
789 while (!IsListEmpty(&HookList
))
791 Pointer
= RemoveHeadList(&HookList
);
792 RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
));
795 /* Decommit the VDM memory */
796 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
805 if (!NT_SUCCESS(Status
))
807 DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status
);