xref: /reactos/subsystems/mvdm/ntvdm/memory.c (revision 6823878a)
1 /*
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>
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "ntvdm.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 #include "emulator.h"
17 #include "memory.h"
18 
19 /* Extra PSDK/NDK Headers */
20 #include <ndk/mmfuncs.h>
21 
22 /* PRIVATE VARIABLES **********************************************************/
23 
24 typedef struct _MEM_HOOK
25 {
26     LIST_ENTRY Entry;
27     HANDLE hVdd;
28     ULONG Count;
29 
30     union
31     {
32         PVDD_MEMORY_HANDLER VddHandler;
33 
34         struct
35         {
36             PMEMORY_READ_HANDLER  FastReadHandler;
37             PMEMORY_WRITE_HANDLER FastWriteHandler;
38         };
39     };
40 } MEM_HOOK, *PMEM_HOOK;
41 
42 static LIST_ENTRY HookList;
43 static PMEM_HOOK PageTable[TOTAL_PAGES] = { NULL };
44 static BOOLEAN A20Line = FALSE;
45 
46 /* PRIVATE FUNCTIONS **********************************************************/
47 
48 static inline VOID
MemFastMoveMemory(OUT VOID UNALIGNED * Destination,IN const VOID UNALIGNED * Source,IN SIZE_T Length)49 MemFastMoveMemory(OUT VOID UNALIGNED *Destination,
50                   IN const VOID UNALIGNED *Source,
51                   IN SIZE_T Length)
52 {
53 #if 1
54     /*
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.
58      */
59     switch (Length)
60     {
61         case 0:
62             return;
63 
64         case sizeof(UCHAR):
65             *(PUCHAR)Destination = *(PUCHAR)Source;
66             return;
67 
68         case sizeof(USHORT):
69             *(PUSHORT)Destination = *(PUSHORT)Source;
70             return;
71 
72         case sizeof(ULONG):
73             *(PULONG)Destination = *(PULONG)Source;
74             return;
75 
76         case sizeof(ULONGLONG):
77             *(PULONGLONG)Destination = *(PULONGLONG)Source;
78             return;
79 
80         default:
81 #if defined(__GNUC__)
82             __builtin_memmove(Destination, Source, Length);
83 #else
84             RtlMoveMemory(Destination, Source, Length);
85 #endif
86     }
87 
88 #else // defined(_MSC_VER)
89 
90     PUCHAR Dest = (PUCHAR)Destination;
91     PUCHAR Src  = (PUCHAR)Source;
92 
93     SIZE_T Count, NewSize = Length;
94 
95     /* Move dword */
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);
100     Src  += Count << 2;
101 
102     /* Move word */
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);
107     Src  += Count << 1;
108 
109     /* Move byte */
110     Count   = NewSize; // NewSize / sizeof(UCHAR);
111     // NewSize = NewSize; // NewSize % sizeof(UCHAR);
112     __movsb(Dest, Src, Count);
113 
114 #endif
115 }
116 
117 static inline VOID
ReadPage(PMEM_HOOK Hook,ULONG Address,PVOID Buffer,ULONG Size)118 ReadPage(PMEM_HOOK Hook, ULONG Address, PVOID Buffer, ULONG Size)
119 {
120     if (Hook && !Hook->hVdd && Hook->FastReadHandler)
121     {
122         Hook->FastReadHandler(Address, REAL_TO_PHYS(Address), Size);
123     }
124 
125     MemFastMoveMemory(Buffer, REAL_TO_PHYS(Address), Size);
126 }
127 
128 static inline VOID
WritePage(PMEM_HOOK Hook,ULONG Address,PVOID Buffer,ULONG Size)129 WritePage(PMEM_HOOK Hook, ULONG Address, PVOID Buffer, ULONG Size)
130 {
131     if (!Hook
132         || Hook->hVdd
133         || !Hook->FastWriteHandler
134         || Hook->FastWriteHandler(Address, Buffer, Size))
135     {
136         MemFastMoveMemory(REAL_TO_PHYS(Address), Buffer, Size);
137     }
138 }
139 
140 /* PUBLIC FUNCTIONS ***********************************************************/
141 
EmulatorReadMemory(PFAST486_STATE State,ULONG Address,PVOID Buffer,ULONG Size)142 VOID FASTCALL EmulatorReadMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
143 {
144     ULONG i, Offset, Length;
145     ULONG FirstPage, LastPage;
146 
147     UNREFERENCED_PARAMETER(State);
148 
149     /* Mirror 0x000FFFF0 at 0xFFFFFFF0 */
150     if (Address >= 0xFFFFFFF0) Address -= 0xFFF00000;
151 
152     /* If the A20 line is disabled, mask bit 20 */
153     if (!A20Line) Address &= ~(1 << 20);
154 
155     if ((Address + Size - 1) >= MAX_ADDRESS)
156     {
157         ULONG ExtraStart = (Address < MAX_ADDRESS) ? MAX_ADDRESS - Address : 0;
158 
159         /* Fill the memory that was above the limit with 0xFF */
160         RtlFillMemory((PVOID)((ULONG_PTR)Buffer + ExtraStart), Size - ExtraStart, 0xFF);
161 
162         if (Address < MAX_ADDRESS) Size = MAX_ADDRESS - Address;
163         else return;
164     }
165 
166     FirstPage = Address >> 12;
167     LastPage = (Address + Size - 1) >> 12;
168 
169     if (FirstPage == LastPage)
170     {
171         ReadPage(PageTable[FirstPage], Address, Buffer, Size);
172     }
173     else
174     {
175         for (i = FirstPage; i <= LastPage; i++)
176         {
177             Offset = (i == FirstPage) ? (Address & (PAGE_SIZE - 1)) : 0;
178             Length = ((i == LastPage) ? (Address + Size - (LastPage << 12)) : PAGE_SIZE) - Offset;
179 
180             ReadPage(PageTable[i], (i << 12) + Offset, Buffer, Length);
181             Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
182         }
183     }
184 }
185 
EmulatorWriteMemory(PFAST486_STATE State,ULONG Address,PVOID Buffer,ULONG Size)186 VOID FASTCALL EmulatorWriteMemory(PFAST486_STATE State, ULONG Address, PVOID Buffer, ULONG Size)
187 {
188     ULONG i, Offset, Length;
189     ULONG FirstPage, LastPage;
190 
191     UNREFERENCED_PARAMETER(State);
192 
193     /* If the A20 line is disabled, mask bit 20 */
194     if (!A20Line) Address &= ~(1 << 20);
195 
196     if (Address >= MAX_ADDRESS) return;
197     Size = min(Size, MAX_ADDRESS - Address);
198 
199     FirstPage = Address >> 12;
200     LastPage = (Address + Size - 1) >> 12;
201 
202     if (FirstPage == LastPage)
203     {
204         WritePage(PageTable[FirstPage], Address, Buffer, Size);
205     }
206     else
207     {
208         for (i = FirstPage; i <= LastPage; i++)
209         {
210             Offset = (i == FirstPage) ? (Address & (PAGE_SIZE - 1)) : 0;
211             Length = ((i == LastPage) ? (Address + Size - (LastPage << 12)) : PAGE_SIZE) - Offset;
212 
213             WritePage(PageTable[i], (i << 12) + Offset, Buffer, Length);
214             Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
215         }
216     }
217 }
218 
EmulatorCopyMemory(PFAST486_STATE State,ULONG DestAddress,ULONG SrcAddress,ULONG Size)219 VOID FASTCALL EmulatorCopyMemory(PFAST486_STATE State, ULONG DestAddress, ULONG SrcAddress, ULONG Size)
220 {
221     /*
222      * Guest-to-guest memory copy
223      */
224 
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
233 
234     if (Size <= sizeof(StaticBuffer))
235     {
236         /* Use the static buffer */
237         LocalBuffer = StaticBuffer;
238     }
239     else if (/* sizeof(StaticBuffer) <= Size && */ Size <= HeapBufferSize)
240     {
241         /* Use the heap buffer */
242         ASSERT(HeapBufferSize > 0 && HeapBuffer != NULL);
243         LocalBuffer = HeapBuffer;
244     }
245     else // if (Size > HeapBufferSize)
246     {
247         /* Enlarge the heap buffer and use it */
248 
249         if (HeapBuffer == NULL)
250         {
251             /* First allocation */
252             LocalBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
253         }
254         else
255         {
256             /* Reallocation */
257             LocalBuffer = RtlReAllocateHeap(RtlGetProcessHeap(), 0 /* HEAP_GENERATE_EXCEPTIONS */, HeapBuffer, Size);
258         }
259         ASSERT(LocalBuffer != NULL); // We must succeed! TODO: Handle it more properly.
260         HeapBuffer = LocalBuffer;    // HeapBuffer is now reallocated.
261         HeapBufferSize = Size;
262     }
263 
264     /* Perform memory copy */
265     EmulatorReadMemory( State, SrcAddress , LocalBuffer, Size);
266     EmulatorWriteMemory(State, DestAddress, LocalBuffer, Size);
267 
268     // if (LocalBuffer != StaticBuffer)
269     //     RtlFreeHeap(RtlGetProcessHeap(), 0, LocalBuffer);
270 
271     // Note that we don't free HeapBuffer since it's an always-growing buffer.
272     // It is freed when NTVDM termiantes.
273 }
274 
EmulatorSetA20(BOOLEAN Enabled)275 VOID EmulatorSetA20(BOOLEAN Enabled)
276 {
277     A20Line = Enabled;
278 }
279 
EmulatorGetA20(VOID)280 BOOLEAN EmulatorGetA20(VOID)
281 {
282     return A20Line;
283 }
284 
285 VOID
MemExceptionHandler(ULONG FaultAddress,BOOLEAN Writing)286 MemExceptionHandler(ULONG FaultAddress, BOOLEAN Writing)
287 {
288     PMEM_HOOK Hook = PageTable[FaultAddress >> 12];
289     DPRINT("The memory at 0x%08X could not be %s.\n", FaultAddress, Writing ? "written" : "read");
290 
291     /* Exceptions are only supposed to happen when using VDD-style memory hooks */
292     ASSERT(FaultAddress < MAX_ADDRESS && Hook != NULL && Hook->hVdd != NULL);
293 
294     /* Call the VDD handler */
295     Hook->VddHandler(REAL_TO_PHYS(FaultAddress), (ULONG)Writing);
296 }
297 
298 BOOL
MemInstallFastMemoryHook(PVOID Address,ULONG Size,PMEMORY_READ_HANDLER ReadHandler,PMEMORY_WRITE_HANDLER WriteHandler)299 MemInstallFastMemoryHook(PVOID Address,
300                          ULONG Size,
301                          PMEMORY_READ_HANDLER ReadHandler,
302                          PMEMORY_WRITE_HANDLER WriteHandler)
303 {
304     PMEM_HOOK Hook;
305     ULONG i;
306     ULONG FirstPage = (ULONG_PTR)Address >> 12;
307     ULONG LastPage = ((ULONG_PTR)Address + Size - 1) >> 12;
308     PLIST_ENTRY Pointer;
309 
310     /* Make sure none of these pages are already allocated */
311     for (i = FirstPage; i <= LastPage; i++)
312     {
313         if (PageTable[i] != NULL) return FALSE;
314     }
315 
316     for (Pointer = HookList.Flink; Pointer != &HookList; Pointer = Pointer->Flink)
317     {
318         Hook = CONTAINING_RECORD(Pointer, MEM_HOOK, Entry);
319 
320         if (Hook->hVdd == NULL
321             && Hook->FastReadHandler == ReadHandler
322             && Hook->FastWriteHandler == WriteHandler)
323         {
324             break;
325         }
326     }
327 
328     if (Pointer == &HookList)
329     {
330         /* Create and initialize a new hook entry... */
331         Hook = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook));
332         if (Hook == NULL) return FALSE;
333 
334         Hook->hVdd = NULL;
335         Hook->Count = 0;
336         Hook->FastReadHandler = ReadHandler;
337         Hook->FastWriteHandler = WriteHandler;
338 
339         /* ... and add it to the list of hooks */
340         InsertTailList(&HookList, &Hook->Entry);
341     }
342 
343     /* Increase the number of pages this hook has */
344     Hook->Count += LastPage - FirstPage + 1;
345 
346     /* Add the hook entry to the page table */
347     for (i = FirstPage; i <= LastPage; i++) PageTable[i] = Hook;
348 
349     return TRUE;
350 }
351 
352 BOOL
MemRemoveFastMemoryHook(PVOID Address,ULONG Size)353 MemRemoveFastMemoryHook(PVOID Address, ULONG Size)
354 {
355     PMEM_HOOK Hook;
356     ULONG i;
357     ULONG FirstPage = (ULONG_PTR)Address >> 12;
358     ULONG LastPage = ((ULONG_PTR)Address + Size - 1) >> 12;
359 
360     if (Size == 0) return FALSE;
361 
362     for (i = FirstPage; i <= LastPage; i++)
363     {
364         Hook = PageTable[i];
365         if (Hook == NULL || Hook->hVdd != NULL) continue;
366 
367         if (--Hook->Count == 0)
368         {
369             /* This hook has no more pages */
370             RemoveEntryList(&Hook->Entry);
371             RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
372         }
373 
374         PageTable[i] = NULL;
375     }
376 
377     return TRUE;
378 }
379 
380 BOOLEAN
MemQueryMemoryZone(ULONG StartAddress,PULONG Length,PBOOLEAN Hooked)381 MemQueryMemoryZone(ULONG StartAddress, PULONG Length, PBOOLEAN Hooked)
382 {
383     ULONG Page = StartAddress >> 12;
384     if (Page >= TOTAL_PAGES) return FALSE;
385 
386     *Length = 0;
387     *Hooked = PageTable[Page] != NULL;
388 
389     while (Page < TOTAL_PAGES && (PageTable[Page] != NULL) == *Hooked)
390     {
391         *Length += PAGE_SIZE;
392         Page++;
393     }
394 
395     return TRUE;
396 }
397 
398 PBYTE
399 WINAPI
Sim32pGetVDMPointer(IN ULONG Address,IN BOOLEAN ProtectedMode)400 Sim32pGetVDMPointer(IN ULONG   Address,
401                     IN BOOLEAN ProtectedMode)
402 {
403     // FIXME
404     UNREFERENCED_PARAMETER(ProtectedMode);
405 
406     /*
407      * HIWORD(Address) == Segment  (if ProtectedMode == FALSE)
408      *                 or Selector (if ProtectedMode == TRUE )
409      * LOWORD(Address) == Offset
410      */
411     return (PBYTE)FAR_POINTER(Address);
412 }
413 
414 PBYTE
415 WINAPI
MGetVdmPointer(IN ULONG Address,IN ULONG Size,IN BOOLEAN ProtectedMode)416 MGetVdmPointer(IN ULONG   Address,
417                IN ULONG   Size,
418                IN BOOLEAN ProtectedMode)
419 {
420     UNREFERENCED_PARAMETER(Size);
421     return Sim32pGetVDMPointer(Address, ProtectedMode);
422 }
423 
424 PVOID
425 WINAPI
VdmMapFlat(IN USHORT Segment,IN ULONG Offset,IN VDM_MODE Mode)426 VdmMapFlat(IN USHORT   Segment,
427            IN ULONG    Offset,
428            IN VDM_MODE Mode)
429 {
430     // FIXME
431     UNREFERENCED_PARAMETER(Mode);
432 
433     return SEG_OFF_TO_PTR(Segment, Offset);
434 }
435 
436 #ifndef VdmFlushCache
437 
438 BOOL
439 WINAPI
VdmFlushCache(IN USHORT Segment,IN ULONG Offset,IN ULONG Size,IN VDM_MODE Mode)440 VdmFlushCache(IN USHORT   Segment,
441               IN ULONG    Offset,
442               IN ULONG    Size,
443               IN VDM_MODE Mode)
444 {
445     // FIXME
446     UNIMPLEMENTED;
447     return TRUE;
448 }
449 
450 #endif
451 
452 #ifndef VdmUnmapFlat
453 
454 BOOL
455 WINAPI
VdmUnmapFlat(IN USHORT Segment,IN ULONG Offset,IN PVOID Buffer,IN VDM_MODE Mode)456 VdmUnmapFlat(IN USHORT   Segment,
457              IN ULONG    Offset,
458              IN PVOID    Buffer,
459              IN VDM_MODE Mode)
460 {
461     // FIXME
462     UNIMPLEMENTED;
463     return TRUE;
464 }
465 
466 #endif
467 
468 BOOL
469 WINAPI
VDDInstallMemoryHook(IN HANDLE hVdd,IN PVOID pStart,IN DWORD dwCount,IN PVDD_MEMORY_HANDLER MemoryHandler)470 VDDInstallMemoryHook(IN HANDLE hVdd,
471                      IN PVOID  pStart,
472                      IN DWORD  dwCount,
473                      IN PVDD_MEMORY_HANDLER MemoryHandler)
474 {
475     NTSTATUS Status;
476     PMEM_HOOK Hook;
477     ULONG i;
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;
482     PLIST_ENTRY Pointer;
483 
484     /* Check validity of the VDD handle */
485     if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
486     {
487         SetLastError(ERROR_INVALID_PARAMETER);
488         return FALSE;
489     }
490 
491     if (dwCount == 0) return FALSE;
492 
493     /* Make sure none of these pages are already allocated */
494     for (i = FirstPage; i <= LastPage; i++)
495     {
496         if (PageTable[i] != NULL) return FALSE;
497     }
498 
499     for (Pointer = HookList.Flink; Pointer != &HookList; Pointer = Pointer->Flink)
500     {
501         Hook = CONTAINING_RECORD(Pointer, MEM_HOOK, Entry);
502         if (Hook->hVdd == hVdd && Hook->VddHandler == MemoryHandler) break;
503     }
504 
505     if (Pointer == &HookList)
506     {
507         /* Create and initialize a new hook entry... */
508         Hook = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook));
509         if (Hook == NULL)
510         {
511             SetLastError(ERROR_OUTOFMEMORY);
512             return FALSE;
513         }
514 
515         Hook->hVdd = hVdd;
516         Hook->Count = 0;
517         Hook->VddHandler = MemoryHandler;
518 
519         /* ... and add it to the list of hooks */
520         InsertTailList(&HookList, &Hook->Entry);
521     }
522 
523     /* Decommit the pages */
524     Status = NtFreeVirtualMemory(NtCurrentProcess(),
525                                  &Address,
526                                  &Size,
527                                  MEM_DECOMMIT);
528     if (!NT_SUCCESS(Status))
529     {
530         if (Pointer == &HookList)
531         {
532             RemoveEntryList(&Hook->Entry);
533             RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
534         }
535 
536         return FALSE;
537     }
538 
539     /* Increase the number of pages this hook has */
540     Hook->Count += LastPage - FirstPage + 1;
541 
542     /* Add the hook entry to the page table */
543     for (i = FirstPage; i <= LastPage; i++) PageTable[i] = Hook;
544 
545     return TRUE;
546 }
547 
548 BOOL
549 WINAPI
VDDDeInstallMemoryHook(IN HANDLE hVdd,IN PVOID pStart,IN DWORD dwCount)550 VDDDeInstallMemoryHook(IN HANDLE hVdd,
551                        IN PVOID  pStart,
552                        IN DWORD  dwCount)
553 {
554     NTSTATUS Status;
555     PMEM_HOOK Hook;
556     ULONG i;
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;
561 
562     /* Check validity of the VDD handle */
563     if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
564     {
565         SetLastError(ERROR_INVALID_PARAMETER);
566         return FALSE;
567     }
568 
569     if (dwCount == 0) return FALSE;
570 
571     /* Commit the pages */
572     Status = NtAllocateVirtualMemory(NtCurrentProcess(),
573                                      &Address,
574                                      0,
575                                      &Size,
576                                      MEM_COMMIT,
577                                      PAGE_READWRITE);
578     if (!NT_SUCCESS(Status)) return FALSE;
579 
580     for (i = FirstPage; i <= LastPage; i++)
581     {
582         Hook = PageTable[i];
583         if (Hook == NULL) continue;
584 
585         if (Hook->hVdd != hVdd)
586         {
587             DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i);
588             continue;
589         }
590 
591         if (--Hook->Count == 0)
592         {
593             /* This hook has no more pages */
594             RemoveEntryList(&Hook->Entry);
595             RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
596         }
597 
598         PageTable[i] = NULL;
599     }
600 
601     return TRUE;
602 }
603 
604 BOOL
605 WINAPI
VDDAllocMem(IN HANDLE hVdd,IN PVOID Address,IN ULONG Size)606 VDDAllocMem(IN HANDLE hVdd,
607             IN PVOID  Address,
608             IN ULONG  Size)
609 {
610     NTSTATUS Status;
611     PMEM_HOOK Hook;
612     ULONG i;
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;
616 
617     /* Check validity of the VDD handle */
618     if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
619     {
620         SetLastError(ERROR_INVALID_PARAMETER);
621         return FALSE;
622     }
623 
624     if (Size == 0) return FALSE;
625 
626     /* Fixup the address */
627     Address = (PVOID)REAL_TO_PHYS(FirstPage * PAGE_SIZE);
628 
629     /* Be sure that all the region is held by the VDD */
630     for (i = FirstPage; i <= LastPage; i++)
631     {
632         Hook = PageTable[i];
633         if (Hook == NULL) return FALSE;
634 
635         if (Hook->hVdd != hVdd)
636         {
637             DPRINT1("VDDAllocMem: Page %u owned by someone else.\n", i);
638             return FALSE;
639         }
640     }
641 
642     /* OK, all the range is held by the VDD. Commit the pages. */
643     Status = NtAllocateVirtualMemory(NtCurrentProcess(),
644                                      &Address,
645                                      0,
646                                      &RealSize,
647                                      MEM_COMMIT,
648                                      PAGE_READWRITE);
649     return NT_SUCCESS(Status);
650 }
651 
652 BOOL
653 WINAPI
VDDFreeMem(IN HANDLE hVdd,IN PVOID Address,IN ULONG Size)654 VDDFreeMem(IN HANDLE hVdd,
655            IN PVOID  Address,
656            IN ULONG  Size)
657 {
658     NTSTATUS Status;
659     PMEM_HOOK Hook;
660     ULONG i;
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;
664 
665     /* Check validity of the VDD handle */
666     if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE)
667     {
668         SetLastError(ERROR_INVALID_PARAMETER);
669         return FALSE;
670     }
671 
672     if (Size == 0) return FALSE;
673 
674     /* Fixup the address */
675     Address = (PVOID)REAL_TO_PHYS(FirstPage * PAGE_SIZE);
676 
677     /* Be sure that all the region is held by the VDD */
678     for (i = FirstPage; i <= LastPage; i++)
679     {
680         Hook = PageTable[i];
681         if (Hook == NULL) return FALSE;
682 
683         if (Hook->hVdd != hVdd)
684         {
685             DPRINT1("VDDFreeMem: Page %u owned by someone else.\n", i);
686             return FALSE;
687         }
688     }
689 
690     /* OK, all the range is held by the VDD. Decommit the pages. */
691     Status = NtFreeVirtualMemory(NtCurrentProcess(),
692                                  &Address,
693                                  &RealSize,
694                                  MEM_DECOMMIT);
695     return NT_SUCCESS(Status);
696 }
697 
698 BOOL
699 WINAPI
VDDIncludeMem(IN HANDLE hVdd,IN PVOID Address,IN ULONG Size)700 VDDIncludeMem(IN HANDLE hVdd,
701               IN PVOID  Address,
702               IN ULONG  Size)
703 {
704     // FIXME
705     UNIMPLEMENTED;
706     return FALSE;
707 }
708 
709 BOOL
710 WINAPI
VDDExcludeMem(IN HANDLE hVdd,IN PVOID Address,IN ULONG Size)711 VDDExcludeMem(IN HANDLE hVdd,
712               IN PVOID  Address,
713               IN ULONG  Size)
714 {
715     // FIXME
716     UNIMPLEMENTED;
717     return FALSE;
718 }
719 
720 
721 
722 BOOLEAN
MemInitialize(VOID)723 MemInitialize(VOID)
724 {
725     NTSTATUS Status;
726     SIZE_T MemorySize = MAX_ADDRESS; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
727 
728     InitializeListHead(&HookList);
729 
730 #ifndef STANDALONE
731 
732     /*
733      * The reserved region starts from the very first page.
734      * We need to commit the reserved first 16 MB virtual address.
735      *
736      * NOTE: NULL has another signification for NtAllocateVirtualMemory.
737      */
738     BaseAddress = (PVOID)1;
739 
740     /*
741      * Since to get NULL, we allocated from 0x1, account for this.
742      * See also: kernel32/client/proc.c!CreateProcessInternalW
743      */
744     MemorySize -= 1;
745 
746 #else
747 
748     /* Allocate it anywhere */
749     BaseAddress = NULL;
750 
751 #endif
752 
753     Status = NtAllocateVirtualMemory(NtCurrentProcess(),
754                                      &BaseAddress,
755                                      0,
756                                      &MemorySize,
757 #ifndef STANDALONE
758                                      MEM_COMMIT,
759 #else
760                                      MEM_RESERVE | MEM_COMMIT,
761 #endif
762                                      PAGE_EXECUTE_READWRITE);
763     if (!NT_SUCCESS(Status))
764     {
765         wprintf(L"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status);
766         return FALSE;
767     }
768 
769 #ifndef STANDALONE
770     ASSERT(BaseAddress == NULL);
771 #endif
772 
773     /*
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.
777      */
778     RtlFillMemory(BaseAddress, MAX_ADDRESS, 0xCC);
779     return TRUE;
780 }
781 
782 VOID
MemCleanup(VOID)783 MemCleanup(VOID)
784 {
785     NTSTATUS Status;
786     SIZE_T MemorySize = MAX_ADDRESS;
787     PLIST_ENTRY Pointer;
788 
789     while (!IsListEmpty(&HookList))
790     {
791         Pointer = RemoveHeadList(&HookList);
792         RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer, MEM_HOOK, Entry));
793     }
794 
795     /* Decommit the VDM memory */
796     Status = NtFreeVirtualMemory(NtCurrentProcess(),
797                                  &BaseAddress,
798                                  &MemorySize,
799 #ifndef STANDALONE
800                                  MEM_DECOMMIT
801 #else
802                                  MEM_RELEASE
803 #endif
804                                  );
805     if (!NT_SUCCESS(Status))
806     {
807         DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status);
808     }
809 }
810 
811 /* EOF */
812