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 /* 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
);
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
);
128 WritePage(PMEM_HOOK Hook
, ULONG Address
, PVOID Buffer
, ULONG Size
)
132 || !Hook
->FastWriteHandler
133 || Hook
->FastWriteHandler(Address
, Buffer
, Size
))
135 MemFastMoveMemory(REAL_TO_PHYS(Address
), Buffer
, Size
);
139 /* PUBLIC FUNCTIONS ***********************************************************/
141 VOID FASTCALL
EmulatorReadMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
143 ULONG i
, Offset
, Length
;
144 ULONG FirstPage
, LastPage
;
146 UNREFERENCED_PARAMETER(State
);
148 /* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
149 if (Address
>= 0xFFFFFFF0) Address
-= 0xFFF00000;
151 /* If the A20 line is disabled, mask bit 20 */
152 if (!A20Line
) Address
&= ~(1 << 20);
154 if ((Address
+ Size
- 1) >= MAX_ADDRESS
)
156 ULONG ExtraStart
= (Address
< MAX_ADDRESS
) ? MAX_ADDRESS
- Address
: 0;
158 /* Fill the memory that was above the limit with 0xFF */
159 RtlFillMemory((PVOID
)((ULONG_PTR
)Buffer
+ ExtraStart
), Size
- ExtraStart
, 0xFF);
161 if (Address
< MAX_ADDRESS
) Size
= MAX_ADDRESS
- Address
;
165 FirstPage
= Address
>> 12;
166 LastPage
= (Address
+ Size
- 1) >> 12;
168 if (FirstPage
== LastPage
)
170 ReadPage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
174 for (i
= FirstPage
; i
<= LastPage
; i
++)
176 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
177 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
179 ReadPage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
180 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
185 VOID FASTCALL
EmulatorWriteMemory(PFAST486_STATE State
, ULONG Address
, PVOID Buffer
, ULONG Size
)
187 ULONG i
, Offset
, Length
;
188 ULONG FirstPage
, LastPage
;
190 UNREFERENCED_PARAMETER(State
);
192 /* If the A20 line is disabled, mask bit 20 */
193 if (!A20Line
) Address
&= ~(1 << 20);
195 if (Address
>= MAX_ADDRESS
) return;
196 Size
= min(Size
, MAX_ADDRESS
- Address
);
198 FirstPage
= Address
>> 12;
199 LastPage
= (Address
+ Size
- 1) >> 12;
201 if (FirstPage
== LastPage
)
203 WritePage(PageTable
[FirstPage
], Address
, Buffer
, Size
);
207 for (i
= FirstPage
; i
<= LastPage
; i
++)
209 Offset
= (i
== FirstPage
) ? (Address
& (PAGE_SIZE
- 1)) : 0;
210 Length
= ((i
== LastPage
) ? (Address
+ Size
- (LastPage
<< 12)) : PAGE_SIZE
) - Offset
;
212 WritePage(PageTable
[i
], (i
<< 12) + Offset
, Buffer
, Length
);
213 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ Length
);
218 VOID
EmulatorSetA20(BOOLEAN Enabled
)
223 BOOLEAN
EmulatorGetA20(VOID
)
229 MemExceptionHandler(ULONG FaultAddress
, BOOLEAN Writing
)
231 PMEM_HOOK Hook
= PageTable
[FaultAddress
>> 12];
232 DPRINT("The memory at 0x%08X could not be %s.\n", FaultAddress
, Writing
? "written" : "read");
234 /* Exceptions are only supposed to happen when using VDD-style memory hooks */
235 ASSERT(FaultAddress
< MAX_ADDRESS
&& Hook
!= NULL
&& Hook
->hVdd
!= NULL
);
237 /* Call the VDD handler */
238 Hook
->VddHandler(REAL_TO_PHYS(FaultAddress
), (ULONG
)Writing
);
242 MemInstallFastMemoryHook(PVOID Address
,
244 PMEMORY_READ_HANDLER ReadHandler
,
245 PMEMORY_WRITE_HANDLER WriteHandler
)
249 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
250 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
253 /* Make sure none of these pages are already allocated */
254 for (i
= FirstPage
; i
<= LastPage
; i
++)
256 if (PageTable
[i
] != NULL
) return FALSE
;
259 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
261 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
263 if (Hook
->hVdd
== NULL
264 && Hook
->FastReadHandler
== ReadHandler
265 && Hook
->FastWriteHandler
== WriteHandler
)
271 if (Pointer
== &HookList
)
273 /* Create and initialize a new hook entry... */
274 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
275 if (Hook
== NULL
) return FALSE
;
279 Hook
->FastReadHandler
= ReadHandler
;
280 Hook
->FastWriteHandler
= WriteHandler
;
282 /* ... and add it to the list of hooks */
283 InsertTailList(&HookList
, &Hook
->Entry
);
286 /* Increase the number of pages this hook has */
287 Hook
->Count
+= LastPage
- FirstPage
+ 1;
289 /* Add the hook entry to the page table */
290 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
296 MemRemoveFastMemoryHook(PVOID Address
, ULONG Size
)
300 ULONG FirstPage
= (ULONG_PTR
)Address
>> 12;
301 ULONG LastPage
= ((ULONG_PTR
)Address
+ Size
- 1) >> 12;
303 if (Size
== 0) return FALSE
;
305 for (i
= FirstPage
; i
<= LastPage
; i
++)
308 if (Hook
== NULL
|| Hook
->hVdd
!= NULL
) continue;
310 if (--Hook
->Count
== 0)
312 /* This hook has no more pages */
313 RemoveEntryList(&Hook
->Entry
);
314 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
324 MemQueryMemoryZone(ULONG StartAddress
, PULONG Length
, PBOOLEAN Hooked
)
326 ULONG Page
= StartAddress
>> 12;
327 if (Page
>= TOTAL_PAGES
) return FALSE
;
330 *Hooked
= PageTable
[Page
] != NULL
;
332 while (Page
< TOTAL_PAGES
&& (PageTable
[Page
] != NULL
) == *Hooked
)
334 *Length
+= PAGE_SIZE
;
343 Sim32pGetVDMPointer(IN ULONG Address
,
344 IN BOOLEAN ProtectedMode
)
347 UNREFERENCED_PARAMETER(ProtectedMode
);
350 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
351 * or Selector (if ProtectedMode == TRUE )
352 * LOWORD(Address) == Offset
354 return (PBYTE
)FAR_POINTER(Address
);
359 MGetVdmPointer(IN ULONG Address
,
361 IN BOOLEAN ProtectedMode
)
363 UNREFERENCED_PARAMETER(Size
);
364 return Sim32pGetVDMPointer(Address
, ProtectedMode
);
369 VdmMapFlat(IN USHORT Segment
,
374 UNREFERENCED_PARAMETER(Mode
);
376 return SEG_OFF_TO_PTR(Segment
, Offset
);
379 #ifndef VdmFlushCache
383 VdmFlushCache(IN USHORT Segment
,
399 VdmUnmapFlat(IN USHORT Segment
,
413 VDDInstallMemoryHook(IN HANDLE hVdd
,
416 IN PVDD_MEMORY_HANDLER MemoryHandler
)
421 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
422 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
423 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
424 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
427 /* Check validity of the VDD handle */
428 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
430 SetLastError(ERROR_INVALID_PARAMETER
);
434 if (dwCount
== 0) return FALSE
;
436 /* Make sure none of these pages are already allocated */
437 for (i
= FirstPage
; i
<= LastPage
; i
++)
439 if (PageTable
[i
] != NULL
) return FALSE
;
442 for (Pointer
= HookList
.Flink
; Pointer
!= &HookList
; Pointer
= Pointer
->Flink
)
444 Hook
= CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
);
445 if (Hook
->hVdd
== hVdd
&& Hook
->VddHandler
== MemoryHandler
) break;
448 if (Pointer
== &HookList
)
450 /* Create and initialize a new hook entry... */
451 Hook
= RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook
));
454 SetLastError(ERROR_OUTOFMEMORY
);
460 Hook
->VddHandler
= MemoryHandler
;
462 /* ... and add it to the list of hooks */
463 InsertTailList(&HookList
, &Hook
->Entry
);
466 /* Decommit the pages */
467 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
471 if (!NT_SUCCESS(Status
))
473 if (Pointer
== &HookList
)
475 RemoveEntryList(&Hook
->Entry
);
476 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
482 /* Increase the number of pages this hook has */
483 Hook
->Count
+= LastPage
- FirstPage
+ 1;
485 /* Add the hook entry to the page table */
486 for (i
= FirstPage
; i
<= LastPage
; i
++) PageTable
[i
] = Hook
;
493 VDDDeInstallMemoryHook(IN HANDLE hVdd
,
500 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(pStart
) >> 12;
501 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(pStart
) + dwCount
- 1) >> 12;
502 PVOID Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
503 SIZE_T Size
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
505 /* Check validity of the VDD handle */
506 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
508 SetLastError(ERROR_INVALID_PARAMETER
);
512 if (dwCount
== 0) return FALSE
;
514 /* Commit the pages */
515 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
521 if (!NT_SUCCESS(Status
)) return FALSE
;
523 for (i
= FirstPage
; i
<= LastPage
; i
++)
526 if (Hook
== NULL
) continue;
528 if (Hook
->hVdd
!= hVdd
)
530 DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i
);
534 if (--Hook
->Count
== 0)
536 /* This hook has no more pages */
537 RemoveEntryList(&Hook
->Entry
);
538 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook
);
549 VDDAllocMem(IN HANDLE hVdd
,
556 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
557 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
558 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
560 /* Check validity of the VDD handle */
561 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
563 SetLastError(ERROR_INVALID_PARAMETER
);
567 if (Size
== 0) return FALSE
;
569 /* Fixup the address */
570 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
572 /* Be sure that all the region is held by the VDD */
573 for (i
= FirstPage
; i
<= LastPage
; i
++)
576 if (Hook
== NULL
) return FALSE
;
578 if (Hook
->hVdd
!= hVdd
)
580 DPRINT1("VDDAllocMem: Page %u owned by someone else.\n", i
);
585 /* OK, all the range is held by the VDD. Commit the pages. */
586 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
592 return NT_SUCCESS(Status
);
597 VDDFreeMem(IN HANDLE hVdd
,
604 ULONG FirstPage
= (ULONG_PTR
)PHYS_TO_REAL(Address
) >> 12;
605 ULONG LastPage
= ((ULONG_PTR
)PHYS_TO_REAL(Address
) + Size
- 1) >> 12;
606 SIZE_T RealSize
= (LastPage
- FirstPage
+ 1) * PAGE_SIZE
;
608 /* Check validity of the VDD handle */
609 if (hVdd
== NULL
|| hVdd
== INVALID_HANDLE_VALUE
)
611 SetLastError(ERROR_INVALID_PARAMETER
);
615 if (Size
== 0) return FALSE
;
617 /* Fixup the address */
618 Address
= (PVOID
)REAL_TO_PHYS(FirstPage
* PAGE_SIZE
);
620 /* Be sure that all the region is held by the VDD */
621 for (i
= FirstPage
; i
<= LastPage
; i
++)
624 if (Hook
== NULL
) return FALSE
;
626 if (Hook
->hVdd
!= hVdd
)
628 DPRINT1("VDDFreeMem: Page %u owned by someone else.\n", i
);
633 /* OK, all the range is held by the VDD. Decommit the pages. */
634 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
638 return NT_SUCCESS(Status
);
643 VDDIncludeMem(IN HANDLE hVdd
,
654 VDDExcludeMem(IN HANDLE hVdd
,
669 SIZE_T MemorySize
= MAX_ADDRESS
; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
671 InitializeListHead(&HookList
);
676 * The reserved region starts from the very first page.
677 * We need to commit the reserved first 16 MB virtual address.
679 * NOTE: NULL has another signification for NtAllocateVirtualMemory.
681 BaseAddress
= (PVOID
)1;
684 * Since to get NULL, we allocated from 0x1, account for this.
685 * See also: kernel32/client/proc.c!CreateProcessInternalW
691 /* Allocate it anywhere */
696 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
703 MEM_RESERVE
| MEM_COMMIT
,
705 PAGE_EXECUTE_READWRITE
);
706 if (!NT_SUCCESS(Status
))
708 wprintf(L
"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status
);
713 ASSERT(BaseAddress
== NULL
);
717 * For diagnostics purposes, we fill the memory with INT 0x03 codes
718 * so that if a program wants to execute random code in memory, we can
719 * retrieve the exact CS:IP where the problem happens.
721 RtlFillMemory(BaseAddress
, MAX_ADDRESS
, 0xCC);
729 SIZE_T MemorySize
= MAX_ADDRESS
;
732 while (!IsListEmpty(&HookList
))
734 Pointer
= RemoveHeadList(&HookList
);
735 RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer
, MEM_HOOK
, Entry
));
738 /* Decommit the VDM memory */
739 Status
= NtFreeVirtualMemory(NtCurrentProcess(),
748 if (!NT_SUCCESS(Status
))
750 DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status
);