81df3e416298351584a7ce2eb3494956ebf10c89
[reactos.git] / 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$
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 /* GLOBALS ******************************************************************/
49
50 static FAST_MUTEX RmapListLock;
51
52 /* FUNCTIONS ****************************************************************/
53
54 VOID
55 MmInitializeRmapList(VOID)
56 {
57   ExInitializeFastMutex(&RmapListLock);
58 }
59
60 NTSTATUS
61 MmWritePagePhysicalAddress(PHYSICAL_ADDRESS PhysicalAddress)
62 {
63   PMM_RMAP_ENTRY entry;
64   PMEMORY_AREA MemoryArea;
65   ULONG Type;
66   PVOID Address;
67   PEPROCESS Process;
68   PMM_PAGEOP PageOp;
69   LARGE_INTEGER Offset;
70   NTSTATUS Status;
71
72   /*
73    * Check that the address still has a valid rmap; then reference the
74    * process so it isn't freed while we are working.
75    */
76   ExAcquireFastMutex(&RmapListLock);
77   entry = MmGetRmapListHeadPage(PhysicalAddress);
78   if (entry == NULL)
79     {
80       ExReleaseFastMutex(&RmapListLock);
81       return(STATUS_UNSUCCESSFUL);
82     }
83   Process = entry->Process;
84   Address = entry->Address;
85   if ((((ULONG)Address) & 0xFFF) != 0)
86     {
87       KeBugCheck(0);
88     }
89   Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
90   ExReleaseFastMutex(&RmapListLock);
91   if (!NT_SUCCESS(Status))
92   {
93      return Status;
94   }
95
96   /*
97    * Lock the address space; then check that the address we are using
98    * still corresponds to a valid memory area (the page might have been
99    * freed or paged out after we read the rmap entry.) 
100    */
101   MmLockAddressSpace(&Process->AddressSpace);
102   MemoryArea = MmOpenMemoryAreaByAddress(&Process->AddressSpace, Address);
103   if (MemoryArea == NULL)
104     {
105       ObDereferenceObject(Process);
106       return(STATUS_UNSUCCESSFUL);
107     }
108
109   Type = MemoryArea->Type;
110   if (Type == MEMORY_AREA_SECTION_VIEW)
111     {
112       Offset.QuadPart = (ULONG)((Address - (ULONG)MemoryArea->BaseAddress) +
113         MemoryArea->Data.SectionData.ViewOffset);
114
115       /*
116        * Get or create a pageop
117        */
118       PageOp = MmGetPageOp(MemoryArea, 0, 0, 
119                            MemoryArea->Data.SectionData.Segment, 
120                            Offset.u.LowPart, MM_PAGEOP_PAGEOUT);
121       if (PageOp == NULL)
122         {
123           DPRINT1("MmGetPageOp failed\n");
124           KeBugCheck(0);
125         }
126
127
128       if (PageOp->Thread != PsGetCurrentThread())
129         {
130           MmReleasePageOp(PageOp);
131           MmUnlockAddressSpace(&Process->AddressSpace);
132           ObDereferenceObject(Process);
133           return(STATUS_UNSUCCESSFUL);
134         }
135       
136       /*
137        * Release locks now we have a page op.
138        */
139       MmUnlockAddressSpace(&Process->AddressSpace);      
140
141       /*
142        * Do the actual page out work.
143        */
144       Status = MmWritePageSectionView(&Process->AddressSpace, MemoryArea, 
145                                       Address, PageOp);
146     }
147   else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
148     {
149       PageOp = MmGetPageOp(MemoryArea, Process->UniqueProcessId,
150                            Address, NULL, 0, MM_PAGEOP_PAGEOUT);
151       
152
153       if (PageOp->Thread != PsGetCurrentThread())
154         {
155           MmReleasePageOp(PageOp);
156           MmUnlockAddressSpace(&Process->AddressSpace);
157           ObDereferenceObject(Process);
158           return(STATUS_UNSUCCESSFUL);
159         }
160
161       /*
162        * Release locks now we have a page op.
163        */
164       MmUnlockAddressSpace(&Process->AddressSpace);
165
166       /*
167        * Do the actual page out work.
168        */
169       Status = MmWritePageVirtualMemory(&Process->AddressSpace, MemoryArea, 
170                                         Address, PageOp);
171     }
172   else
173     {
174       KeBugCheck(0);
175     }  
176   ObDereferenceObject(Process);
177   return(Status);
178 }
179
180 NTSTATUS
181 MmPageOutPhysicalAddress(PHYSICAL_ADDRESS PhysicalAddress)
182 {
183   PMM_RMAP_ENTRY entry;
184   PMEMORY_AREA MemoryArea;
185   ULONG Type;
186   PVOID Address;
187   PEPROCESS Process;
188   PMM_PAGEOP PageOp;
189   LARGE_INTEGER Offset;
190   NTSTATUS Status;
191
192   ExAcquireFastMutex(&RmapListLock);
193   entry = MmGetRmapListHeadPage(PhysicalAddress);
194   if (entry == NULL)
195     {
196       ExReleaseFastMutex(&RmapListLock);
197       return(STATUS_UNSUCCESSFUL);
198     }
199   Process = entry->Process;
200   Address = entry->Address;
201   if ((((ULONG)Address) & 0xFFF) != 0)
202     {
203       KeBugCheck(0);
204     }
205
206   Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
207   ExReleaseFastMutex(&RmapListLock);
208   if (!NT_SUCCESS(Status))
209   {
210       return Status;
211   }
212   MmLockAddressSpace(&Process->AddressSpace);
213   MemoryArea = MmOpenMemoryAreaByAddress(&Process->AddressSpace, Address);
214   Type = MemoryArea->Type;
215   if (Type == MEMORY_AREA_SECTION_VIEW)
216     {
217       Offset.QuadPart = (ULONG)((Address - (ULONG)MemoryArea->BaseAddress) +
218         MemoryArea->Data.SectionData.ViewOffset);
219
220       /*
221        * Get or create a pageop
222        */
223       PageOp = MmGetPageOp(MemoryArea, 0, 0, 
224                            MemoryArea->Data.SectionData.Segment, 
225                            Offset.u.LowPart, MM_PAGEOP_PAGEOUT);
226       if (PageOp == NULL)
227         {
228           DPRINT1("MmGetPageOp failed\n");
229           KeBugCheck(0);
230         }
231
232       if (PageOp->Thread != PsGetCurrentThread())
233         {
234           MmReleasePageOp(PageOp);
235           MmUnlockAddressSpace(&Process->AddressSpace);
236           ObDereferenceObject(Process);
237           return(STATUS_UNSUCCESSFUL);
238         }
239       
240       /*
241        * Release locks now we have a page op.
242        */
243       MmUnlockAddressSpace(&Process->AddressSpace);
244
245       /*
246        * Do the actual page out work.
247        */
248       Status = MmPageOutSectionView(&Process->AddressSpace, MemoryArea, 
249                                     Address, PageOp);
250     }
251   else if (Type == MEMORY_AREA_VIRTUAL_MEMORY)
252     {
253       PageOp = MmGetPageOp(MemoryArea, Process->UniqueProcessId,
254                            Address, NULL, 0, MM_PAGEOP_PAGEOUT);
255       if (PageOp->Thread != PsGetCurrentThread())
256         {
257           MmReleasePageOp(PageOp);
258           MmUnlockAddressSpace(&Process->AddressSpace);
259           ObDereferenceObject(Process);
260           return(STATUS_UNSUCCESSFUL);
261         }
262
263       /*
264        * Release locks now we have a page op.
265        */
266       MmUnlockAddressSpace(&Process->AddressSpace);
267
268       /*
269        * Do the actual page out work.
270        */
271       Status = MmPageOutVirtualMemory(&Process->AddressSpace, MemoryArea, 
272                                       Address, PageOp);
273     }
274   else
275     {
276       KeBugCheck(0);
277     }
278   ObDereferenceObject(Process);
279   return(Status);
280 }
281
282 VOID
283 MmSetCleanAllRmaps(PHYSICAL_ADDRESS PhysicalAddress)
284 {
285   PMM_RMAP_ENTRY current_entry;
286
287   ExAcquireFastMutex(&RmapListLock);
288   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
289   if (current_entry == NULL)
290     {
291       DPRINT1("MmIsDirtyRmap: No rmaps.\n");
292       KeBugCheck(0);
293     }
294   while (current_entry != NULL)
295     {      
296       MmSetCleanPage(current_entry->Process, current_entry->Address);
297       current_entry = current_entry->Next;
298     }
299   ExReleaseFastMutex(&RmapListLock);
300 }
301
302 VOID
303 MmSetDirtyAllRmaps(PHYSICAL_ADDRESS PhysicalAddress)
304 {
305   PMM_RMAP_ENTRY current_entry;
306
307   ExAcquireFastMutex(&RmapListLock);
308   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
309   if (current_entry == NULL)
310     {
311       DPRINT1("MmIsDirtyRmap: No rmaps.\n");
312       KeBugCheck(0);
313     }
314   while (current_entry != NULL)
315     {      
316       MmSetDirtyPage(current_entry->Process, current_entry->Address);
317       current_entry = current_entry->Next;
318     }
319   ExReleaseFastMutex(&RmapListLock);
320 }
321
322 BOOL
323 MmIsDirtyPageRmap(PHYSICAL_ADDRESS PhysicalAddress)
324 {
325   PMM_RMAP_ENTRY current_entry;
326
327   ExAcquireFastMutex(&RmapListLock);
328   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
329   if (current_entry == NULL)
330     {
331       ExReleaseFastMutex(&RmapListLock);
332       return(FALSE);
333     }
334   while (current_entry != NULL)
335     {      
336       if (MmIsDirtyPage(current_entry->Process, current_entry->Address))
337         {         
338           ExReleaseFastMutex(&RmapListLock);
339           return(TRUE);
340         }
341       current_entry = current_entry->Next;
342     }
343   ExReleaseFastMutex(&RmapListLock);
344   return(FALSE);
345 }
346
347 VOID
348 MmInsertRmap(PHYSICAL_ADDRESS PhysicalAddress, PEPROCESS Process, 
349              PVOID Address)
350 {
351   PMM_RMAP_ENTRY current_entry;
352   PMM_RMAP_ENTRY new_entry;
353
354   Address = (PVOID)PAGE_ROUND_DOWN(Address);
355
356   new_entry = ExAllocatePool(NonPagedPool, sizeof(MM_RMAP_ENTRY));
357   if (new_entry == NULL)
358     {
359       KeBugCheck(0);
360     }
361   new_entry->Address = Address;
362   new_entry->Process = Process;
363
364   if (MmGetPhysicalAddressForProcess(Process, Address).QuadPart != 
365       PhysicalAddress.QuadPart)
366     {
367       DPRINT1("Insert rmap (%d, 0x%.8X) 0x%.8X which doesn't match physical "
368               "address 0x%.8X\n", Process->UniqueProcessId, Address, 
369               MmGetPhysicalAddressForProcess(Process, Address), 
370               PhysicalAddress)
371       KeBugCheck(0);
372     }
373
374   ExAcquireFastMutex(&RmapListLock);
375   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
376   new_entry->Next = current_entry;
377   MmSetRmapListHeadPage(PhysicalAddress, new_entry);
378   ExReleaseFastMutex(&RmapListLock);
379 }
380
381 VOID
382 MmDeleteAllRmaps(PHYSICAL_ADDRESS PhysicalAddress, PVOID Context, 
383                  VOID (*DeleteMapping)(PVOID Context, PEPROCESS Process, 
384                                        PVOID Address))
385 {
386   PMM_RMAP_ENTRY current_entry;
387   PMM_RMAP_ENTRY previous_entry;
388
389   ExAcquireFastMutex(&RmapListLock);
390   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
391   if (current_entry == NULL)
392     {
393       DPRINT1("MmDeleteAllRmaps: No rmaps.\n");
394       KeBugCheck(0);
395     }
396   while (current_entry != NULL)
397     {
398       previous_entry = current_entry;
399       current_entry = current_entry->Next;
400       if (DeleteMapping)
401         {
402           DeleteMapping(Context, previous_entry->Process, 
403                         previous_entry->Address);
404         }
405       ExFreePool(previous_entry);
406     }
407   MmSetRmapListHeadPage(PhysicalAddress, NULL);
408   ExReleaseFastMutex(&RmapListLock);
409 }
410
411 VOID
412 MmDeleteRmap(PHYSICAL_ADDRESS PhysicalAddress, PEPROCESS Process, 
413              PVOID Address)
414 {
415   PMM_RMAP_ENTRY current_entry, previous_entry;
416
417   ExAcquireFastMutex(&RmapListLock);
418   previous_entry = NULL;
419   current_entry = MmGetRmapListHeadPage(PhysicalAddress);
420   while (current_entry != NULL)
421     {
422       if (current_entry->Process == Process && 
423           current_entry->Address == Address)
424         {
425           if (previous_entry == NULL)
426             {
427               MmSetRmapListHeadPage(PhysicalAddress, current_entry->Next);
428               ExReleaseFastMutex(&RmapListLock);
429               ExFreePool(current_entry);
430             }
431           else
432             {
433               previous_entry->Next = current_entry->Next;
434               ExReleaseFastMutex(&RmapListLock);
435               ExFreePool(current_entry);
436             }
437           return;
438         }
439       previous_entry = current_entry;
440       current_entry = current_entry->Next;
441     }
442   KeBugCheck(0);
443 }