- Remove KD APIs from stub HAL, they've been in kdcom for a while (merge from kd...
[reactos.git] / reactos / ntoskrnl / mm / rmap.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/mm/rmap.c
6 * PURPOSE: Kernel memory managment functions
7 *
8 * PROGRAMMERS: David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 #if defined (ALLOC_PRAGMA)
18 #pragma alloc_text(INIT, MmInitializeRmapList)
19 #endif
20
21 /* TYPES ********************************************************************/
22
23 typedef struct _MM_RMAP_ENTRY
24 {
25 struct _MM_RMAP_ENTRY* Next;
26 PEPROCESS Process;
27 PVOID Address;
28 #ifdef DBG
29 PVOID Caller;
30 #endif
31 }
32 MM_RMAP_ENTRY, *PMM_RMAP_ENTRY;
33
34 /* GLOBALS ******************************************************************/
35
36 static FAST_MUTEX RmapListLock;
37 static NPAGED_LOOKASIDE_LIST RmapLookasideList;
38
39 /* FUNCTIONS ****************************************************************/
40
41 VOID
42 INIT_FUNCTION
43 NTAPI
44 MmInitializeRmapList(VOID)
45 {
46 ExInitializeFastMutex(&RmapListLock);
47 ExInitializeNPagedLookasideList (&RmapLookasideList,
48 NULL,
49 NULL,
50 0,
51 sizeof(MM_RMAP_ENTRY),
52 TAG_RMAP,
53 50);
54 }
55
56 NTSTATUS
57 NTAPI
58 MmWritePagePhysicalAddress(PFN_TYPE Page)
59 {
60 PMM_RMAP_ENTRY entry;
61 PMEMORY_AREA MemoryArea;
62 PMADDRESS_SPACE AddressSpace;
63 ULONG Type;
64 PVOID Address;
65 PEPROCESS Process;
66 PMM_PAGEOP PageOp;
67 ULONG Offset;
68 NTSTATUS Status = STATUS_SUCCESS;
69
70 /*
71 * Check that the address still has a valid rmap; then reference the
72 * process so it isn't freed while we are working.
73 */
74 ExAcquireFastMutex(&RmapListLock);
75 entry = MmGetRmapListHeadPage(Page);
76 if (entry == NULL)
77 {
78 ExReleaseFastMutex(&RmapListLock);
79 return(STATUS_UNSUCCESSFUL);
80 }
81 Process = entry->Process;
82 Address = entry->Address;
83 if ((((ULONG_PTR)Address) & 0xFFF) != 0)
84 {
85 KEBUGCHECK(0);
86 }
87 if (Address < MmSystemRangeStart)
88 {
89 Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
90 ExReleaseFastMutex(&RmapListLock);
91 if (!NT_SUCCESS(Status))
92 {
93 return Status;
94 }
95 AddressSpace = (PMADDRESS_SPACE)&Process->VadRoot;
96 }
97 else
98 {
99 ExReleaseFastMutex(&RmapListLock);
100 AddressSpace = MmGetKernelAddressSpace();
101 }
102
103 /*
104 * Lock the address space; then check that the address we are using
105 * still corresponds to a valid memory area (the page might have been
106 * freed or paged out after we read the rmap entry.)
107 */
108 MmLockAddressSpace(AddressSpace);
109 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
110 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
111 {
112 MmUnlockAddressSpace(AddressSpace);
113 if (Address < MmSystemRangeStart)
114 {
115 ObDereferenceObject(Process);
116 }
117 return(STATUS_UNSUCCESSFUL);
118 }
119
120 Type = MemoryArea->Type;
121 if (Type == MEMORY_AREA_SECTION_VIEW)
122 {
123 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
124 + MemoryArea->Data.SectionData.ViewOffset;
125 /*
126 * Get or create a pageop
127 */
128 PageOp = MmGetPageOp(MemoryArea, NULL, 0,
129 MemoryArea->Data.SectionData.Segment,
130 Offset, MM_PAGEOP_PAGEOUT, TRUE);
131
132 if (PageOp == NULL)
133 {
134 MmUnlockAddressSpace(AddressSpace);
135 if (Address < MmSystemRangeStart)
136 {
137 ObDereferenceObject(Process);
138 }
139 return(STATUS_UNSUCCESSFUL);
140 }
141
142 /*
143 * Release locks now we have a page op.
144 */
145 MmUnlockAddressSpace(AddressSpace);
146
147 /*
148 * Do the actual page out work.
149 */
150 Status = MmWritePageSectionView(AddressSpace, MemoryArea,
151 Address, PageOp);
152 }
153 else if ((Type == MEMORY_AREA_VIRTUAL_MEMORY) || (Type == MEMORY_AREA_PEB_OR_TEB))
154 {
155 PageOp = MmGetPageOp(MemoryArea, Address < MmSystemRangeStart ? Process->UniqueProcessId : NULL,
156 Address, NULL, 0, MM_PAGEOP_PAGEOUT, TRUE);
157
158 if (PageOp == NULL)
159 {
160 MmUnlockAddressSpace(AddressSpace);
161 if (Address < MmSystemRangeStart)
162 {
163 ObDereferenceObject(Process);
164 }
165 return(STATUS_UNSUCCESSFUL);
166 }
167
168 /*
169 * Release locks now we have a page op.
170 */
171 MmUnlockAddressSpace(AddressSpace);
172
173 /*
174 * Do the actual page out work.
175 */
176 Status = MmWritePageVirtualMemory(AddressSpace, MemoryArea,
177 Address, PageOp);
178 }
179 else
180 {
181 KEBUGCHECK(0);
182 }
183 if (Address < MmSystemRangeStart)
184 {
185 ObDereferenceObject(Process);
186 }
187 return(Status);
188 }
189
190 NTSTATUS
191 NTAPI
192 MmPageOutPhysicalAddress(PFN_TYPE Page)
193 {
194 PMM_RMAP_ENTRY entry;
195 PMEMORY_AREA MemoryArea;
196 PMADDRESS_SPACE AddressSpace;
197 ULONG Type;
198 PVOID Address;
199 PEPROCESS Process;
200 PMM_PAGEOP PageOp;
201 ULONG Offset;
202 NTSTATUS Status = STATUS_SUCCESS;
203
204 ExAcquireFastMutex(&RmapListLock);
205 entry = MmGetRmapListHeadPage(Page);
206 if (entry == NULL || MmGetLockCountPage(Page) != 0)
207 {
208 ExReleaseFastMutex(&RmapListLock);
209 return(STATUS_UNSUCCESSFUL);
210 }
211 Process = entry->Process;
212 Address = entry->Address;
213 if ((((ULONG_PTR)Address) & 0xFFF) != 0)
214 {
215 KEBUGCHECK(0);
216 }
217
218 if (Address < MmSystemRangeStart)
219 {
220 Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
221 ExReleaseFastMutex(&RmapListLock);
222 if (!NT_SUCCESS(Status))
223 {
224 return Status;
225 }
226 AddressSpace = (PMADDRESS_SPACE)&Process->VadRoot;
227 }
228 else
229 {
230 ExReleaseFastMutex(&RmapListLock);
231 AddressSpace = MmGetKernelAddressSpace();
232 }
233
234 MmLockAddressSpace(AddressSpace);
235 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
236 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
237 {
238 MmUnlockAddressSpace(AddressSpace);
239 if (Address < MmSystemRangeStart)
240 {
241 ObDereferenceObject(Process);
242 }
243 return(STATUS_UNSUCCESSFUL);
244 }
245 Type = MemoryArea->Type;
246 if (Type == MEMORY_AREA_SECTION_VIEW)
247 {
248 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
249 + MemoryArea->Data.SectionData.ViewOffset;;
250
251 /*
252 * Get or create a pageop
253 */
254 PageOp = MmGetPageOp(MemoryArea, NULL, 0,
255 MemoryArea->Data.SectionData.Segment,
256 Offset, MM_PAGEOP_PAGEOUT, TRUE);
257 if (PageOp == NULL)
258 {
259 MmUnlockAddressSpace(AddressSpace);
260 if (Address < MmSystemRangeStart)
261 {
262 ObDereferenceObject(Process);
263 }
264 return(STATUS_UNSUCCESSFUL);
265 }
266
267 /*
268 * Release locks now we have a page op.
269 */
270 MmUnlockAddressSpace(AddressSpace);
271
272 /*
273 * Do the actual page out work.
274 */
275 Status = MmPageOutSectionView(AddressSpace, MemoryArea,
276 Address, PageOp);
277 }
278 else if ((Type == MEMORY_AREA_VIRTUAL_MEMORY) || (Type == MEMORY_AREA_PEB_OR_TEB))
279 {
280 PageOp = MmGetPageOp(MemoryArea, Address < MmSystemRangeStart ? Process->UniqueProcessId : NULL,
281 Address, NULL, 0, MM_PAGEOP_PAGEOUT, TRUE);
282 if (PageOp == NULL)
283 {
284 MmUnlockAddressSpace(AddressSpace);
285 if (Address < MmSystemRangeStart)
286 {
287 ObDereferenceObject(Process);
288 }
289 return(STATUS_UNSUCCESSFUL);
290 }
291
292 /*
293 * Release locks now we have a page op.
294 */
295 MmUnlockAddressSpace(AddressSpace);
296
297 /*
298 * Do the actual page out work.
299 */
300 Status = MmPageOutVirtualMemory(AddressSpace, MemoryArea,
301 Address, PageOp);
302 }
303 else
304 {
305 KEBUGCHECK(0);
306 }
307 if (Address < MmSystemRangeStart)
308 {
309 ObDereferenceObject(Process);
310 }
311 return(Status);
312 }
313
314 VOID
315 NTAPI
316 MmSetCleanAllRmaps(PFN_TYPE Page)
317 {
318 PMM_RMAP_ENTRY current_entry;
319
320 ExAcquireFastMutex(&RmapListLock);
321 current_entry = MmGetRmapListHeadPage(Page);
322 if (current_entry == NULL)
323 {
324 DPRINT1("MmIsDirtyRmap: No rmaps.\n");
325 KEBUGCHECK(0);
326 }
327 while (current_entry != NULL)
328 {
329 MmSetCleanPage(current_entry->Process, current_entry->Address);
330 current_entry = current_entry->Next;
331 }
332 ExReleaseFastMutex(&RmapListLock);
333 }
334
335 VOID
336 NTAPI
337 MmSetDirtyAllRmaps(PFN_TYPE Page)
338 {
339 PMM_RMAP_ENTRY current_entry;
340
341 ExAcquireFastMutex(&RmapListLock);
342 current_entry = MmGetRmapListHeadPage(Page);
343 if (current_entry == NULL)
344 {
345 DPRINT1("MmIsDirtyRmap: No rmaps.\n");
346 KEBUGCHECK(0);
347 }
348 while (current_entry != NULL)
349 {
350 MmSetDirtyPage(current_entry->Process, current_entry->Address);
351 current_entry = current_entry->Next;
352 }
353 ExReleaseFastMutex(&RmapListLock);
354 }
355
356 BOOLEAN
357 NTAPI
358 MmIsDirtyPageRmap(PFN_TYPE Page)
359 {
360 PMM_RMAP_ENTRY current_entry;
361
362 ExAcquireFastMutex(&RmapListLock);
363 current_entry = MmGetRmapListHeadPage(Page);
364 if (current_entry == NULL)
365 {
366 ExReleaseFastMutex(&RmapListLock);
367 return(FALSE);
368 }
369 while (current_entry != NULL)
370 {
371 if (MmIsDirtyPage(current_entry->Process, current_entry->Address))
372 {
373 ExReleaseFastMutex(&RmapListLock);
374 return(TRUE);
375 }
376 current_entry = current_entry->Next;
377 }
378 ExReleaseFastMutex(&RmapListLock);
379 return(FALSE);
380 }
381
382 extern BOOLEAN RmapReady, PageOpReady, SectionsReady, PagingReady;
383
384 VOID
385 NTAPI
386 MmInsertRmap(PFN_TYPE Page, PEPROCESS Process,
387 PVOID Address)
388 {
389 PMM_RMAP_ENTRY current_entry;
390 PMM_RMAP_ENTRY new_entry;
391 ULONG PrevSize;
392
393 if (!RmapReady)
394 {
395 DPRINT1("RMAPS USED TOO SOON!!!\n");
396 while (TRUE);
397 }
398
399 Address = (PVOID)PAGE_ROUND_DOWN(Address);
400
401 new_entry = ExAllocateFromNPagedLookasideList(&RmapLookasideList);
402 if (new_entry == NULL)
403 {
404 KEBUGCHECK(0);
405 }
406 new_entry->Address = Address;
407 new_entry->Process = (PEPROCESS)Process;
408 #ifdef DBG
409 #ifdef _GNUC_
410 new_entry->Caller = __builtin_return_address(0);
411 #else
412 new_entry->Caller = _ReturnAddress();
413 #endif
414 #endif
415
416 if (MmGetPfnForProcess(Process, Address) != Page)
417 {
418 DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical "
419 "address 0x%.8X\n", Process->UniqueProcessId, Address,
420 MmGetPfnForProcess(Process, Address) << PAGE_SHIFT,
421 Page << PAGE_SHIFT);
422 KEBUGCHECK(0);
423 }
424
425 ExAcquireFastMutex(&RmapListLock);
426 current_entry = MmGetRmapListHeadPage(Page);
427 new_entry->Next = current_entry;
428 #ifdef DBG
429 while (current_entry)
430 {
431 if (current_entry->Address == new_entry->Address && current_entry->Process == new_entry->Process)
432 {
433 DbgPrint("MmInsertRmap tries to add a second rmap entry for address %p\n current caller ",
434 current_entry->Address);
435 DbgPrint("%p", new_entry->Caller);
436 DbgPrint("\n previous caller ");
437 DbgPrint("%p", current_entry->Caller);
438 DbgPrint("\n");
439 KeBugCheck(0);
440 }
441 current_entry = current_entry->Next;
442 }
443 #endif
444 MmSetRmapListHeadPage(Page, new_entry);
445 ExReleaseFastMutex(&RmapListLock);
446 if (Process == NULL)
447 {
448 Process = PsInitialSystemProcess;
449 }
450 if (Process)
451 {
452 PrevSize = InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, PAGE_SIZE);
453 if (PrevSize >= Process->Vm.PeakWorkingSetSize)
454 {
455 Process->Vm.PeakWorkingSetSize = PrevSize + PAGE_SIZE;
456 }
457 }
458 }
459
460 VOID
461 NTAPI
462 MmDeleteAllRmaps(PFN_TYPE Page, PVOID Context,
463 VOID (*DeleteMapping)(PVOID Context, PEPROCESS Process,
464 PVOID Address))
465 {
466 PMM_RMAP_ENTRY current_entry;
467 PMM_RMAP_ENTRY previous_entry;
468 PEPROCESS Process;
469
470 if (!RmapReady)
471 {
472 DPRINT1("RMAPS USED TOO SOON!!!\n");
473 while (TRUE);
474 }
475
476 ExAcquireFastMutex(&RmapListLock);
477 current_entry = MmGetRmapListHeadPage(Page);
478 if (current_entry == NULL)
479 {
480 DPRINT1("MmDeleteAllRmaps: No rmaps.\n");
481 KEBUGCHECK(0);
482 }
483 MmSetRmapListHeadPage(Page, NULL);
484 ExReleaseFastMutex(&RmapListLock);
485 while (current_entry != NULL)
486 {
487 previous_entry = current_entry;
488 current_entry = current_entry->Next;
489 if (DeleteMapping)
490 {
491 DeleteMapping(Context, previous_entry->Process,
492 previous_entry->Address);
493 }
494 Process = previous_entry->Process;
495 ExFreeToNPagedLookasideList(&RmapLookasideList, previous_entry);
496 if (Process == NULL)
497 {
498 Process = PsInitialSystemProcess;
499 }
500 if (Process)
501 {
502 (void)InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, -PAGE_SIZE);
503 }
504 }
505 }
506
507 VOID
508 NTAPI
509 MmDeleteRmap(PFN_TYPE Page, PEPROCESS Process,
510 PVOID Address)
511 {
512 PMM_RMAP_ENTRY current_entry, previous_entry;
513
514 if (!RmapReady)
515 {
516 DPRINT1("RMAPS USED TOO SOON!!!\n");
517 while (TRUE);
518 }
519
520 ExAcquireFastMutex(&RmapListLock);
521 previous_entry = NULL;
522 current_entry = MmGetRmapListHeadPage(Page);
523 while (current_entry != NULL)
524 {
525 if (current_entry->Process == (PEPROCESS)Process &&
526 current_entry->Address == Address)
527 {
528 if (previous_entry == NULL)
529 {
530 MmSetRmapListHeadPage(Page, current_entry->Next);
531 }
532 else
533 {
534 previous_entry->Next = current_entry->Next;
535 }
536 ExReleaseFastMutex(&RmapListLock);
537 ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry);
538 if (Process == NULL)
539 {
540 Process = PsInitialSystemProcess;
541 }
542 if (Process)
543 {
544 (void)InterlockedExchangeAddUL(&Process->Vm.WorkingSetSize, -PAGE_SIZE);
545 }
546 return;
547 }
548 previous_entry = current_entry;
549 current_entry = current_entry->Next;
550 }
551 KEBUGCHECK(0);
552 }