- Fixed the used offset for page table/directory entries and pageops.
[reactos.git] / reactos / ntoskrnl / mm / rmap.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: rmap.c,v 1.18 2003/06/06 21:01:36 hbirr Exp $
20 *
21 * COPYRIGHT: See COPYING in the top directory
22 * PROJECT: ReactOS kernel
23 * FILE: ntoskrnl/mm/rmap.c
24 * PURPOSE: kernel memory managment functions
25 * PROGRAMMER: David Welch (welch@cwcom.net)
26 * UPDATE HISTORY:
27 * Created 27/12/01
28 */
29
30 /* INCLUDES *****************************************************************/
31
32 #include <ddk/ntddk.h>
33 #include <internal/mm.h>
34 #include <internal/ps.h>
35
36 #define NDEBUG
37 #include <internal/debug.h>
38
39 /* TYPES ********************************************************************/
40
41 typedef struct _MM_RMAP_ENTRY
42 {
43 struct _MM_RMAP_ENTRY* Next;
44 PEPROCESS Process;
45 PVOID Address;
46 } MM_RMAP_ENTRY, *PMM_RMAP_ENTRY;
47
48 #define TAG_RMAP TAG('R', 'M', 'A', 'P')
49
50 /* GLOBALS ******************************************************************/
51
52 static FAST_MUTEX RmapListLock;
53 static NPAGED_LOOKASIDE_LIST RmapLookasideList;
54
55 /* FUNCTIONS ****************************************************************/
56
57 VOID
58 MmInitializeRmapList(VOID)
59 {
60 ExInitializeFastMutex(&RmapListLock);
61 ExInitializeNPagedLookasideList (&RmapLookasideList,
62 NULL,
63 NULL,
64 0,
65 sizeof(MM_RMAP_ENTRY),
66 TAG_RMAP,
67 50);
68 }
69
70 NTSTATUS
71 MmWritePagePhysicalAddress(PHYSICAL_ADDRESS PhysicalAddress)
72 {
73 PMM_RMAP_ENTRY entry;
74 PMEMORY_AREA MemoryArea;
75 PMADDRESS_SPACE AddressSpace;
76 ULONG Type;
77 PVOID Address;
78 PEPROCESS Process;
79 PMM_PAGEOP PageOp;
80 ULONG Offset;
81 NTSTATUS Status = STATUS_SUCCESS;
82
83 /*
84 * Check that the address still has a valid rmap; then reference the
85 * process so it isn't freed while we are working.
86 */
87 ExAcquireFastMutex(&RmapListLock);
88 entry = MmGetRmapListHeadPage(PhysicalAddress);
89 if (entry == NULL)
90 {
91 ExReleaseFastMutex(&RmapListLock);
92 return(STATUS_UNSUCCESSFUL);
93 }
94 Process = entry->Process;
95 Address = entry->Address;
96 if ((((ULONG)Address) & 0xFFF) != 0)
97 {
98 KeBugCheck(0);
99 }
100 if (Address < (PVOID)KERNEL_BASE)
101 {
102 Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
103 ExReleaseFastMutex(&RmapListLock);
104 if (!NT_SUCCESS(Status))
105 {
106 return Status;
107 }
108 AddressSpace = &Process->AddressSpace;
109 }
110 else
111 {
112 ExReleaseFastMutex(&RmapListLock);
113 AddressSpace = MmGetKernelAddressSpace();
114 }
115
116 /*
117 * Lock the address space; then check that the address we are using
118 * still corresponds to a valid memory area (the page might have been
119 * freed or paged out after we read the rmap entry.)
120 */
121 MmLockAddressSpace(AddressSpace);
122 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace, Address);
123 if (MemoryArea == NULL)
124 {
125 MmUnlockAddressSpace(AddressSpace);
126 if (Address < (PVOID)KERNEL_BASE)
127 {
128 ObDereferenceObject(Process);
129 }
130 return(STATUS_UNSUCCESSFUL);
131 }
132
133 Type = MemoryArea->Type;
134 if (Type == MEMORY_AREA_SECTION_VIEW)
135 {
136 Offset = (ULONG)(Address - (ULONG)MemoryArea->BaseAddress);
137
138 /*
139 * Get or create a pageop
140 */
141 PageOp = MmGetPageOp(MemoryArea, 0, 0,
142 MemoryArea->Data.SectionData.Segment,
143 Offset, MM_PAGEOP_PAGEOUT);
144 if (PageOp == NULL)
145 {
146 DPRINT1("MmGetPageOp failed\n");
147 KeBugCheck(0);
148 }
149
150
151 if (PageOp->Thread != PsGetCurrentThread())
152 {
153 MmReleasePageOp(PageOp);
154 MmUnlockAddressSpace(AddressSpace);
155 if (Address < (PVOID)KERNEL_BASE)
156 {
157 ObDereferenceObject(Process);
158 }
159 return(STATUS_UNSUCCESSFUL);
160 }
161
162 /*
163 * Release locks now we have a page op.
164 */
165 MmUnlockAddressSpace(AddressSpace);
166
167 /*
168 * Do the actual page out work.
169 */
170 Status = MmWritePageSectionView(AddressSpace, MemoryArea,
171 Address, PageOp);
172 }
173 else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
174 {
175 PageOp = MmGetPageOp(MemoryArea, Address < (PVOID)KERNEL_BASE ? Process->UniqueProcessId : 0,
176 Address, NULL, 0, MM_PAGEOP_PAGEOUT);
177
178 if (PageOp->Thread != PsGetCurrentThread())
179 {
180 MmReleasePageOp(PageOp);
181 MmUnlockAddressSpace(AddressSpace);
182 if (Address < (PVOID)KERNEL_BASE)
183 {
184 ObDereferenceObject(Process);
185 }
186 return(STATUS_UNSUCCESSFUL);
187 }
188
189 /*
190 * Release locks now we have a page op.
191 */
192 MmUnlockAddressSpace(AddressSpace);
193
194 /*
195 * Do the actual page out work.
196 */
197 Status = MmWritePageVirtualMemory(AddressSpace, MemoryArea,
198 Address, PageOp);
199 }
200 else
201 {
202 KeBugCheck(0);
203 }
204 if (Address < (PVOID)KERNEL_BASE)
205 {
206 ObDereferenceObject(Process);
207 }
208 return(Status);
209 }
210
211 NTSTATUS
212 MmPageOutPhysicalAddress(PHYSICAL_ADDRESS PhysicalAddress)
213 {
214 PMM_RMAP_ENTRY entry;
215 PMEMORY_AREA MemoryArea;
216 PMADDRESS_SPACE AddressSpace;
217 ULONG Type;
218 PVOID Address;
219 PEPROCESS Process;
220 PMM_PAGEOP PageOp;
221 ULONG Offset;
222 NTSTATUS Status = STATUS_SUCCESS;
223
224 ExAcquireFastMutex(&RmapListLock);
225 entry = MmGetRmapListHeadPage(PhysicalAddress);
226 if (entry == NULL)
227 {
228 ExReleaseFastMutex(&RmapListLock);
229 return(STATUS_UNSUCCESSFUL);
230 }
231 Process = entry->Process;
232 Address = entry->Address;
233 if ((((ULONG)Address) & 0xFFF) != 0)
234 {
235 KeBugCheck(0);
236 }
237
238 if (Address < (PVOID)KERNEL_BASE)
239 {
240 Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
241 ExReleaseFastMutex(&RmapListLock);
242 if (!NT_SUCCESS(Status))
243 {
244 return Status;
245 }
246 AddressSpace = &Process->AddressSpace;
247 }
248 else
249 {
250 ExReleaseFastMutex(&RmapListLock);
251 AddressSpace = MmGetKernelAddressSpace();
252 }
253
254 MmLockAddressSpace(AddressSpace);
255 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace, Address);
256 Type = MemoryArea->Type;
257 if (Type == MEMORY_AREA_SECTION_VIEW)
258 {
259 Offset = (ULONG)(Address - (ULONG)MemoryArea->BaseAddress);
260
261 /*
262 * Get or create a pageop
263 */
264 PageOp = MmGetPageOp(MemoryArea, 0, 0,
265 MemoryArea->Data.SectionData.Segment,
266 Offset, MM_PAGEOP_PAGEOUT);
267 if (PageOp == NULL)
268 {
269 DPRINT1("MmGetPageOp failed\n");
270 KeBugCheck(0);
271 }
272
273 if (PageOp->Thread != PsGetCurrentThread())
274 {
275 MmReleasePageOp(PageOp);
276 MmUnlockAddressSpace(AddressSpace);
277 if (Address < (PVOID)KERNEL_BASE)
278 {
279 ObDereferenceObject(Process);
280 }
281 return(STATUS_UNSUCCESSFUL);
282 }
283
284 /*
285 * Release locks now we have a page op.
286 */
287 MmUnlockAddressSpace(AddressSpace);
288
289 /*
290 * Do the actual page out work.
291 */
292 Status = MmPageOutSectionView(AddressSpace, MemoryArea,
293 Address, PageOp);
294 }
295 else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
296 {
297 PageOp = MmGetPageOp(MemoryArea, Address < (PVOID)KERNEL_BASE ? Process->UniqueProcessId : 0,
298 Address, NULL, 0, MM_PAGEOP_PAGEOUT);
299 if (PageOp->Thread != PsGetCurrentThread())
300 {
301 MmReleasePageOp(PageOp);
302 MmUnlockAddressSpace(AddressSpace);
303 if (Address < (PVOID)KERNEL_BASE)
304 {
305 ObDereferenceObject(Process);
306 }
307 return(STATUS_UNSUCCESSFUL);
308 }
309
310 /*
311 * Release locks now we have a page op.
312 */
313 MmUnlockAddressSpace(AddressSpace);
314
315 /*
316 * Do the actual page out work.
317 */
318 Status = MmPageOutVirtualMemory(AddressSpace, MemoryArea,
319 Address, PageOp);
320 }
321 else
322 {
323 KeBugCheck(0);
324 }
325 if (Address < (PVOID)KERNEL_BASE)
326 {
327 ObDereferenceObject(Process);
328 }
329 return(Status);
330 }
331
332 VOID
333 MmSetCleanAllRmaps(PHYSICAL_ADDRESS PhysicalAddress)
334 {
335 PMM_RMAP_ENTRY current_entry;
336
337 ExAcquireFastMutex(&RmapListLock);
338 current_entry = MmGetRmapListHeadPage(PhysicalAddress);
339 if (current_entry == NULL)
340 {
341 DPRINT1("MmIsDirtyRmap: No rmaps.\n");
342 KeBugCheck(0);
343 }
344 while (current_entry != NULL)
345 {
346 MmSetCleanPage(current_entry->Process, current_entry->Address);
347 current_entry = current_entry->Next;
348 }
349 ExReleaseFastMutex(&RmapListLock);
350 }
351
352 VOID
353 MmSetDirtyAllRmaps(PHYSICAL_ADDRESS PhysicalAddress)
354 {
355 PMM_RMAP_ENTRY current_entry;
356
357 ExAcquireFastMutex(&RmapListLock);
358 current_entry = MmGetRmapListHeadPage(PhysicalAddress);
359 if (current_entry == NULL)
360 {
361 DPRINT1("MmIsDirtyRmap: No rmaps.\n");
362 KeBugCheck(0);
363 }
364 while (current_entry != NULL)
365 {
366 MmSetDirtyPage(current_entry->Process, current_entry->Address);
367 current_entry = current_entry->Next;
368 }
369 ExReleaseFastMutex(&RmapListLock);
370 }
371
372 BOOL
373 MmIsDirtyPageRmap(PHYSICAL_ADDRESS PhysicalAddress)
374 {
375 PMM_RMAP_ENTRY current_entry;
376
377 ExAcquireFastMutex(&RmapListLock);
378 current_entry = MmGetRmapListHeadPage(PhysicalAddress);
379 if (current_entry == NULL)
380 {
381 ExReleaseFastMutex(&RmapListLock);
382 return(FALSE);
383 }
384 while (current_entry != NULL)
385 {
386 if (MmIsDirtyPage(current_entry->Process, current_entry->Address))
387 {
388 ExReleaseFastMutex(&RmapListLock);
389 return(TRUE);
390 }
391 current_entry = current_entry->Next;
392 }
393 ExReleaseFastMutex(&RmapListLock);
394 return(FALSE);
395 }
396
397 VOID
398 MmInsertRmap(PHYSICAL_ADDRESS PhysicalAddress, PEPROCESS Process,
399 PVOID Address)
400 {
401 PMM_RMAP_ENTRY current_entry;
402 PMM_RMAP_ENTRY new_entry;
403
404 Address = (PVOID)PAGE_ROUND_DOWN(Address);
405
406 new_entry = ExAllocateFromNPagedLookasideList(&RmapLookasideList);
407 if (new_entry == NULL)
408 {
409 KeBugCheck(0);
410 }
411 new_entry->Address = Address;
412 new_entry->Process = Process;
413
414 if (MmGetPhysicalAddressForProcess(Process, Address).QuadPart !=
415 PhysicalAddress.QuadPart)
416 {
417 DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical "
418 "address 0x%.8X\n", Process->UniqueProcessId, Address,
419 MmGetPhysicalAddressForProcess(Process, Address),
420 PhysicalAddress)
421 KeBugCheck(0);
422 }
423
424 ExAcquireFastMutex(&RmapListLock);
425 current_entry = MmGetRmapListHeadPage(PhysicalAddress);
426 new_entry->Next = current_entry;
427 MmSetRmapListHeadPage(PhysicalAddress, new_entry);
428 ExReleaseFastMutex(&RmapListLock);
429 }
430
431 VOID
432 MmDeleteAllRmaps(PHYSICAL_ADDRESS PhysicalAddress, PVOID Context,
433 VOID (*DeleteMapping)(PVOID Context, PEPROCESS Process,
434 PVOID Address))
435 {
436 PMM_RMAP_ENTRY current_entry;
437 PMM_RMAP_ENTRY previous_entry;
438
439 ExAcquireFastMutex(&RmapListLock);
440 current_entry = MmGetRmapListHeadPage(PhysicalAddress);
441 if (current_entry == NULL)
442 {
443 DPRINT1("MmDeleteAllRmaps: No rmaps.\n");
444 KeBugCheck(0);
445 }
446 while (current_entry != NULL)
447 {
448 previous_entry = current_entry;
449 current_entry = current_entry->Next;
450 if (DeleteMapping)
451 {
452 DeleteMapping(Context, previous_entry->Process,
453 previous_entry->Address);
454 }
455 ExFreeToNPagedLookasideList(&RmapLookasideList, previous_entry);
456 }
457 MmSetRmapListHeadPage(PhysicalAddress, NULL);
458 ExReleaseFastMutex(&RmapListLock);
459 }
460
461 VOID
462 MmDeleteRmap(PHYSICAL_ADDRESS PhysicalAddress, PEPROCESS Process,
463 PVOID Address)
464 {
465 PMM_RMAP_ENTRY current_entry, previous_entry;
466
467 ExAcquireFastMutex(&RmapListLock);
468 previous_entry = NULL;
469 current_entry = MmGetRmapListHeadPage(PhysicalAddress);
470 while (current_entry != NULL)
471 {
472 if (current_entry->Process == Process &&
473 current_entry->Address == Address)
474 {
475 if (previous_entry == NULL)
476 {
477 MmSetRmapListHeadPage(PhysicalAddress, current_entry->Next);
478 }
479 else
480 {
481 previous_entry->Next = current_entry->Next;
482 }
483 ExReleaseFastMutex(&RmapListLock);
484 ExFreeToNPagedLookasideList(&RmapLookasideList, current_entry);
485 return;
486 }
487 previous_entry = current_entry;
488 current_entry = current_entry->Next;
489 }
490 KeBugCheck(0);
491 }