7b3074c9e784176785bb9cd029a4fb9a7ae805ee
[reactos.git] / reactos / subsystems / mvdm / ntvdm / memory.c
1 /*
2 * COPYRIGHT: GPLv2+ - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: memory.c
5 * PURPOSE: Memory Management
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "emulator.h"
14 #include <ndk/mmfuncs.h>
15 #include <ndk/rtlfuncs.h>
16 #include "memory.h"
17
18 /* PRIVATE VARIABLES **********************************************************/
19
20 typedef struct _MEM_HOOK
21 {
22 LIST_ENTRY Entry;
23 HANDLE hVdd;
24 ULONG Count;
25
26 union
27 {
28 PVDD_MEMORY_HANDLER VddHandler;
29
30 struct
31 {
32 PMEMORY_READ_HANDLER FastReadHandler;
33 PMEMORY_WRITE_HANDLER FastWriteHandler;
34 };
35 };
36 } MEM_HOOK, *PMEM_HOOK;
37
38 static LIST_ENTRY HookList;
39 static PMEM_HOOK PageTable[TOTAL_PAGES];
40
41 /* PRIVATE FUNCTIONS **********************************************************/
42
43 static inline VOID
44 MemFastMoveMemory(OUT VOID UNALIGNED *Destination,
45 IN const VOID UNALIGNED *Source,
46 IN SIZE_T Length)
47 {
48 #if 1
49 /*
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.
53 */
54 switch (Length)
55 {
56 case 0:
57 return;
58
59 case sizeof(UCHAR):
60 *(PUCHAR)Destination = *(PUCHAR)Source;
61 return;
62
63 case sizeof(USHORT):
64 *(PUSHORT)Destination = *(PUSHORT)Source;
65 return;
66
67 case sizeof(ULONG):
68 *(PULONG)Destination = *(PULONG)Source;
69 return;
70
71 case sizeof(ULONGLONG):
72 *(PULONGLONG)Destination = *(PULONGLONG)Source;
73 return;
74
75 default:
76 #if defined(__GNUC__)
77 __builtin_memmove(Destination, Source, Length);
78 #else
79 RtlMoveMemory(Destination, Source, Length);
80 #endif
81 }
82
83 #else // defined(_MSC_VER)
84
85 PUCHAR Dest = (PUCHAR)Destination;
86 PUCHAR Src = (PUCHAR)Source;
87
88 SIZE_T Count, NewSize = Length;
89
90 /* Move dword */
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);
95 Src += Count << 2;
96
97 /* Move word */
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);
102 Src += Count << 1;
103
104 /* Move byte */
105 Count = NewSize; // NewSize / sizeof(UCHAR);
106 // NewSize = NewSize; // NewSize % sizeof(UCHAR);
107 __movsb(Dest, Src, Count);
108
109 #endif
110 }
111
112 static
113 inline
114 VOID
115 ReadPage(PMEM_HOOK Hook, ULONG Address, PVOID Buffer, ULONG Size)
116 {
117 if (Hook && !Hook->hVdd && Hook->FastReadHandler)
118 {
119 Hook->FastReadHandler(Address, REAL_TO_PHYS(Address), Size);
120 }
121
122 MemFastMoveMemory(Buffer, REAL_TO_PHYS(Address), Size);
123 }
124
125 static
126 inline
127 VOID
128 WritePage(PMEM_HOOK Hook, ULONG Address, PVOID Buffer, ULONG Size)
129 {
130 if (!Hook
131 || Hook->hVdd
132 || !Hook->FastWriteHandler
133 || Hook->FastWriteHandler(Address, Buffer, Size))
134 {
135 MemFastMoveMemory(REAL_TO_PHYS(Address), Buffer, Size);
136 }
137 }
138
139 /* PUBLIC FUNCTIONS ***********************************************************/
140
141 VOID
142 MemRead(ULONG Address, PVOID Buffer, ULONG Size)
143 {
144 ULONG i, Offset, Length;
145 ULONG FirstPage = Address >> 12;
146 ULONG LastPage = (Address + Size - 1) >> 12;
147
148 MemFastMoveMemory(Buffer, REAL_TO_PHYS(Address), Size);
149
150 if (FirstPage == LastPage)
151 {
152 ReadPage(PageTable[FirstPage], Address, Buffer, Size);
153 }
154 else
155 {
156 for (i = FirstPage; i <= LastPage; i++)
157 {
158 Offset = (i == FirstPage) ? (Address & (PAGE_SIZE - 1)) : 0;
159 Length = ((i == LastPage) ? (Address + Size - (LastPage << 12)) : PAGE_SIZE) - Offset;
160
161 ReadPage(PageTable[i], (i << 12) + Offset, Buffer, Length);
162 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
163 }
164 }
165 }
166
167 VOID
168 MemWrite(ULONG Address, PVOID Buffer, ULONG Size)
169 {
170 ULONG i, Offset, Length;
171 ULONG FirstPage = Address >> 12;
172 ULONG LastPage = (Address + Size - 1) >> 12;
173
174 if (FirstPage == LastPage)
175 {
176 WritePage(PageTable[FirstPage], Address, Buffer, Size);
177 }
178 else
179 {
180 for (i = FirstPage; i <= LastPage; i++)
181 {
182 Offset = (i == FirstPage) ? (Address & (PAGE_SIZE - 1)) : 0;
183 Length = ((i == LastPage) ? (Address + Size - (LastPage << 12)) : PAGE_SIZE) - Offset;
184
185 WritePage(PageTable[i], (i << 12) + Offset, Buffer, Length);
186 Buffer = (PVOID)((ULONG_PTR)Buffer + Length);
187 }
188 }
189 }
190
191 VOID
192 MemExceptionHandler(ULONG FaultAddress, BOOLEAN Writing)
193 {
194 PMEM_HOOK Hook = PageTable[FaultAddress >> 12];
195 DPRINT("The memory at 0x%08X could not be %s.\n", FaultAddress, Writing ? "written" : "read");
196
197 /* Exceptions are only supposed to happen when using VDD-style memory hooks */
198 ASSERT(FaultAddress < MAX_ADDRESS && Hook != NULL && Hook->hVdd != NULL);
199
200 /* Call the VDD handler */
201 Hook->VddHandler((PVOID)FaultAddress, (ULONG)Writing);
202 }
203
204 BOOL
205 MemInstallFastMemoryHook(PVOID Address,
206 ULONG Size,
207 PMEMORY_READ_HANDLER ReadHandler,
208 PMEMORY_WRITE_HANDLER WriteHandler)
209 {
210 PMEM_HOOK Hook;
211 ULONG i;
212 ULONG FirstPage = (ULONG_PTR)Address >> 12;
213 ULONG LastPage = ((ULONG_PTR)Address + Size - 1) >> 12;
214
215 /* Make sure none of these pages are already allocated */
216 for (i = FirstPage; i <= LastPage; i++)
217 {
218 if (PageTable[i] != NULL) return FALSE;
219 }
220
221 /* Create and initialize a new hook entry */
222 Hook = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook));
223 if (Hook == NULL) return FALSE;
224
225 Hook->hVdd = NULL;
226 Hook->Count = LastPage - FirstPage + 1;
227 Hook->FastReadHandler = ReadHandler;
228 Hook->FastWriteHandler = WriteHandler;
229
230 /* Add the hook entry to the page table... */
231 for (i = FirstPage; i <= LastPage; i++) PageTable[i] = Hook;
232
233 /* ... and to the list of hooks */
234 InsertTailList(&HookList, &Hook->Entry);
235 return TRUE;
236 }
237
238 BOOL
239 MemRemoveFastMemoryHook(PVOID Address, ULONG Size)
240 {
241 PMEM_HOOK Hook;
242 ULONG i;
243 ULONG FirstPage = (ULONG_PTR)Address >> 12;
244 ULONG LastPage = ((ULONG_PTR)Address + Size - 1) >> 12;
245
246 if (Size == 0) return FALSE;
247
248 for (i = FirstPage; i <= LastPage; i++)
249 {
250 Hook = PageTable[i];
251 if (Hook == NULL || Hook->hVdd != NULL) continue;
252
253 if (--Hook->Count == 0)
254 {
255 /* This hook has no more pages */
256 RemoveEntryList(&Hook->Entry);
257 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
258 }
259
260 PageTable[i] = NULL;
261 }
262
263 return TRUE;
264 }
265
266
267
268 PBYTE
269 WINAPI
270 Sim32pGetVDMPointer(IN ULONG Address,
271 IN BOOLEAN ProtectedMode)
272 {
273 // FIXME
274 UNREFERENCED_PARAMETER(ProtectedMode);
275
276 /*
277 * HIWORD(Address) == Segment (if ProtectedMode == FALSE)
278 * or Selector (if ProtectedMode == TRUE )
279 * LOWORD(Address) == Offset
280 */
281 return (PBYTE)FAR_POINTER(Address);
282 }
283
284 PBYTE
285 WINAPI
286 MGetVdmPointer(IN ULONG Address,
287 IN ULONG Size,
288 IN BOOLEAN ProtectedMode)
289 {
290 UNREFERENCED_PARAMETER(Size);
291 return Sim32pGetVDMPointer(Address, ProtectedMode);
292 }
293
294 PVOID
295 WINAPI
296 VdmMapFlat(IN USHORT Segment,
297 IN ULONG Offset,
298 IN VDM_MODE Mode)
299 {
300 // FIXME
301 UNREFERENCED_PARAMETER(Mode);
302
303 return SEG_OFF_TO_PTR(Segment, Offset);
304 }
305
306 BOOL
307 WINAPI
308 VdmFlushCache(IN USHORT Segment,
309 IN ULONG Offset,
310 IN ULONG Size,
311 IN VDM_MODE Mode)
312 {
313 // FIXME
314 UNIMPLEMENTED;
315 return TRUE;
316 }
317
318 BOOL
319 WINAPI
320 VdmUnmapFlat(IN USHORT Segment,
321 IN ULONG Offset,
322 IN PVOID Buffer,
323 IN VDM_MODE Mode)
324 {
325 // FIXME
326 UNIMPLEMENTED;
327 return TRUE;
328 }
329
330 BOOL
331 WINAPI
332 VDDInstallMemoryHook(IN HANDLE hVdd,
333 IN PVOID pStart,
334 IN DWORD dwCount,
335 IN PVDD_MEMORY_HANDLER MemoryHandler)
336 {
337 NTSTATUS Status;
338 PMEM_HOOK Hook;
339 ULONG i;
340 ULONG FirstPage = (ULONG_PTR)pStart >> 12;
341 ULONG LastPage = ((ULONG_PTR)pStart + dwCount - 1) >> 12;
342 PVOID Address = (PVOID)(FirstPage * PAGE_SIZE);
343 SIZE_T Size = (LastPage - FirstPage + 1) * PAGE_SIZE;
344
345 /* Check validity of the VDD handle */
346 if (hVdd == NULL || hVdd == INVALID_HANDLE_VALUE) return FALSE;
347 if (dwCount == 0) return FALSE;
348
349 /* Make sure none of these pages are already allocated */
350 for (i = FirstPage; i <= LastPage; i++)
351 {
352 if (PageTable[i] != NULL) return FALSE;
353 }
354
355 /* Create and initialize a new hook entry */
356 Hook = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(*Hook));
357 if (Hook == NULL) return FALSE;
358
359 Hook->hVdd = hVdd;
360 Hook->Count = LastPage - FirstPage + 1;
361 Hook->VddHandler = MemoryHandler;
362
363 /* Decommit the pages */
364 Status = NtFreeVirtualMemory(NtCurrentProcess(), &Address, &Size, MEM_DECOMMIT);
365 if (!NT_SUCCESS(Status))
366 {
367 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
368 return FALSE;
369 }
370
371 /* Add the hook entry to the page table... */
372 for (i = FirstPage; i <= LastPage; i++) PageTable[i] = Hook;
373
374 /* ... and to the list of hooks */
375 InsertTailList(&HookList, &Hook->Entry);
376 return TRUE;
377 }
378
379 BOOL
380 WINAPI
381 VDDDeInstallMemoryHook(IN HANDLE hVdd,
382 IN PVOID pStart,
383 IN DWORD dwCount)
384 {
385 NTSTATUS Status;
386 PMEM_HOOK Hook;
387 ULONG i;
388 ULONG FirstPage = (ULONG_PTR)pStart >> 12;
389 ULONG LastPage = ((ULONG_PTR)pStart + dwCount - 1) >> 12;
390 PVOID Address = (PVOID)(FirstPage * PAGE_SIZE);
391 SIZE_T Size = (LastPage - FirstPage + 1) * PAGE_SIZE;
392
393 if (dwCount == 0) return FALSE;
394
395 /* Commit the pages */
396 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
397 &Address,
398 0,
399 &Size,
400 MEM_COMMIT,
401 PAGE_READWRITE);
402 if (!NT_SUCCESS(Status)) return FALSE;
403
404 for (i = FirstPage; i <= LastPage; i++)
405 {
406 Hook = PageTable[i];
407 if (Hook == NULL) continue;
408
409 if (Hook->hVdd != hVdd)
410 {
411 DPRINT1("VDDDeInstallMemoryHook: Page %u owned by someone else.\n", i);
412 continue;
413 }
414
415 if (--Hook->Count == 0)
416 {
417 /* This hook has no more pages */
418 RemoveEntryList(&Hook->Entry);
419 RtlFreeHeap(RtlGetProcessHeap(), 0, Hook);
420 }
421
422 PageTable[i] = NULL;
423 }
424
425 return TRUE;
426 }
427
428
429
430 BOOLEAN
431 MemInitialize(VOID)
432 {
433 NTSTATUS Status;
434 SIZE_T MemorySize = MAX_ADDRESS; // See: kernel32/client/vdm.c!BaseGetVdmConfigInfo
435
436 #ifndef STANDALONE
437
438 /*
439 * The reserved region starts from the very first page.
440 * We need to commit the reserved first 16 MB virtual address.
441 *
442 * NOTE: NULL has another signification for NtAllocateVirtualMemory.
443 */
444 BaseAddress = (PVOID)1;
445
446 /*
447 * Since to get NULL, we allocated from 0x1, account for this.
448 * See also: kernel32/client/proc.c!CreateProcessInternalW
449 */
450 MemorySize -= 1;
451
452 #else
453
454 /* Allocate it anywhere */
455 BaseAddress = NULL;
456
457 #endif
458
459 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
460 &BaseAddress,
461 0,
462 &MemorySize,
463 #ifndef STANDALONE
464 MEM_COMMIT,
465 #else
466 MEM_RESERVE | MEM_COMMIT,
467 #endif
468 PAGE_EXECUTE_READWRITE);
469 if (!NT_SUCCESS(Status))
470 {
471 wprintf(L"FATAL: Failed to commit VDM memory, Status 0x%08lx\n", Status);
472 return FALSE;
473 }
474
475 #ifndef STANDALONE
476 ASSERT(BaseAddress == NULL);
477 #endif
478
479 InitializeListHead(&HookList);
480
481 /*
482 * For diagnostics purposes, we fill the memory with INT 0x03 codes
483 * so that if a program wants to execute random code in memory, we can
484 * retrieve the exact CS:IP where the problem happens.
485 */
486 RtlFillMemory(BaseAddress, MAX_ADDRESS, 0xCC);
487 return TRUE;
488 }
489
490 VOID
491 MemCleanup(VOID)
492 {
493 NTSTATUS Status;
494 SIZE_T MemorySize = MAX_ADDRESS;
495 PLIST_ENTRY Pointer;
496
497 while (!IsListEmpty(&HookList))
498 {
499 Pointer = RemoveHeadList(&HookList);
500 RtlFreeHeap(RtlGetProcessHeap(), 0, CONTAINING_RECORD(Pointer, MEM_HOOK, Entry));
501 }
502
503 /* Decommit the VDM memory */
504 Status = NtFreeVirtualMemory(NtCurrentProcess(),
505 &BaseAddress,
506 &MemorySize,
507 #ifndef STANDALONE
508 MEM_DECOMMIT
509 #else
510 MEM_RELEASE
511 #endif
512 );
513 if (!NT_SUCCESS(Status))
514 {
515 DPRINT1("NTVDM: Failed to decommit VDM memory, Status 0x%08lx\n", Status);
516 }
517 }
518
519 /* EOF */