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