451c5c252e1c0ba37cab0389d1fc260ba00a056d
[reactos.git] / ntoskrnl / mm / anonmem.c
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001, 2002 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  * PROJECT:     ReactOS kernel
22  * FILE:        ntoskrnl/mm/anonmem.c
23  * PURPOSE:     Implementing anonymous memory.
24  * PROGRAMMER:  David Welch
25  */
26  
27 /* INCLUDE *****************************************************************/
28
29 #include <ddk/ntddk.h>
30 #include <internal/mm.h>
31 #include <internal/ob.h>
32 #include <internal/io.h>
33 #include <internal/ps.h>
34 #include <internal/pool.h>
35
36 #define NDEBUG
37 #include <internal/debug.h>
38
39 /* FUNCTIONS *****************************************************************/
40
41 NTSTATUS 
42 MmWritePageVirtualMemory(PMADDRESS_SPACE AddressSpace,
43                          PMEMORY_AREA MemoryArea,
44                          PVOID Address,
45                          PMM_PAGEOP PageOp)
46 {
47   SWAPENTRY SwapEntry;
48   LARGE_INTEGER PhysicalAddress;
49   PMDL Mdl;
50   NTSTATUS Status;
51
52   /*
53    * Check for paging out from a deleted virtual memory area.
54    */
55   if (MemoryArea->DeleteInProgress)
56     {
57       PageOp->Status = STATUS_UNSUCCESSFUL;
58       KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
59       MmReleasePageOp(PageOp);
60       return(STATUS_UNSUCCESSFUL);
61     }
62
63   PhysicalAddress = 
64     MmGetPhysicalAddressForProcess(AddressSpace->Process, Address);
65
66   /*
67    * Get that the page actually is dirty.
68    */
69   if (!MmIsDirtyPage(MemoryArea->Process, Address))
70     {
71       PageOp->Status = STATUS_SUCCESS;
72       KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
73       MmReleasePageOp(PageOp);
74       return(STATUS_SUCCESS);
75     }
76
77   /*
78    * Speculatively set the mapping to clean.
79    */
80   MmSetCleanPage(MemoryArea->Process, Address);
81   
82   /*
83    * If necessary, allocate an entry in the paging file for this page
84    */
85   SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress);
86   if (SwapEntry == 0)
87     {
88       SwapEntry = MmAllocSwapPage();
89       if (SwapEntry == 0)
90         {
91           MmSetDirtyPage(MemoryArea->Process, Address);
92           PageOp->Status = STATUS_PAGEFILE_QUOTA_EXCEEDED;
93           KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
94           MmReleasePageOp(PageOp);
95           return(STATUS_PAGEFILE_QUOTA_EXCEEDED);
96         }
97     }
98
99   /*
100    * Write the page to the pagefile
101    */
102   Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE);
103   MmBuildMdlFromPages(Mdl, (PULONG)&PhysicalAddress);
104   Status = MmWriteToSwapPage(SwapEntry, Mdl);
105   if (!NT_SUCCESS(Status))
106     {
107       DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", 
108               Status);
109       MmSetDirtyPage(MemoryArea->Process, Address);
110       PageOp->Status = STATUS_UNSUCCESSFUL;
111       KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
112       MmReleasePageOp(PageOp);
113       return(STATUS_UNSUCCESSFUL);
114     }
115   
116   /*
117    * Otherwise we have succeeded.
118    */
119   MmSetSavedSwapEntryPage(PhysicalAddress, SwapEntry);
120   PageOp->Status = STATUS_SUCCESS;
121   KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
122   MmReleasePageOp(PageOp);
123   return(STATUS_SUCCESS);
124 }
125
126 NTSTATUS 
127 MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace,
128                        PMEMORY_AREA MemoryArea,
129                        PVOID Address,
130                        PMM_PAGEOP PageOp)
131 {
132    PHYSICAL_ADDRESS PhysicalAddress;
133    BOOL WasDirty;
134    SWAPENTRY SwapEntry;
135    NTSTATUS Status;
136    PMDL Mdl;
137
138    DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
139            Address, MemoryArea->Process->UniqueProcessId);
140
141    /*
142     * Check for paging out from a deleted virtual memory area.
143     */
144    if (MemoryArea->DeleteInProgress)
145      {
146        PageOp->Status = STATUS_UNSUCCESSFUL;
147        KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
148        MmReleasePageOp(PageOp);
149        return(STATUS_UNSUCCESSFUL);
150      }
151
152    /*
153     * Disable the virtual mapping.
154     */
155    MmDisableVirtualMapping(MemoryArea->Process, Address,
156                            &WasDirty, &PhysicalAddress);
157
158    if (PhysicalAddress.QuadPart == 0)
159      {
160        KeBugCheck(0);
161      }
162
163    /*
164     * Paging out non-dirty data is easy. 
165     */
166    if (!WasDirty)
167      {
168        MmDeleteVirtualMapping(MemoryArea->Process, Address, FALSE, NULL, NULL);
169        MmDeleteAllRmaps(PhysicalAddress, NULL, NULL);
170        if ((SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress)) != 0)
171          {
172            MmCreatePageFileMapping(MemoryArea->Process, Address, SwapEntry);
173            MmSetSavedSwapEntryPage(PhysicalAddress, 0);
174          }
175        MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress);
176        PageOp->Status = STATUS_SUCCESS;
177        KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
178        MmReleasePageOp(PageOp);
179        return(STATUS_SUCCESS);
180      }
181
182    /*
183     * If necessary, allocate an entry in the paging file for this page
184     */
185    SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress);
186    if (SwapEntry == 0)
187      {
188        SwapEntry = MmAllocSwapPage();
189        if (SwapEntry == 0)
190          {
191            MmShowOutOfSpaceMessagePagingFile();
192            MmEnableVirtualMapping(MemoryArea->Process, Address);
193            PageOp->Status = STATUS_UNSUCCESSFUL;
194            KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
195            MmReleasePageOp(PageOp);
196            return(STATUS_UNSUCCESSFUL);
197          }
198      }
199    
200    /*
201     * Write the page to the pagefile
202     */
203    Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE);
204    MmBuildMdlFromPages(Mdl, &PhysicalAddress.u.LowPart);
205    Status = MmWriteToSwapPage(SwapEntry, Mdl);
206    if (!NT_SUCCESS(Status))
207      {
208        DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", 
209                Status);
210        MmEnableVirtualMapping(MemoryArea->Process, Address);
211        PageOp->Status = STATUS_UNSUCCESSFUL;
212        KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
213        MmReleasePageOp(PageOp);
214        return(STATUS_UNSUCCESSFUL);
215      }
216
217    /*
218     * Otherwise we have succeeded, free the page
219     */
220    DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", PhysicalAddress);
221    MmDeleteVirtualMapping(MemoryArea->Process, Address, FALSE, NULL, NULL);
222    MmCreatePageFileMapping(MemoryArea->Process, Address, SwapEntry);
223    MmDeleteAllRmaps(PhysicalAddress, NULL, NULL);
224    MmSetSavedSwapEntryPage(PhysicalAddress, 0);
225    MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress);
226    PageOp->Status = STATUS_SUCCESS;
227    KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
228    MmReleasePageOp(PageOp);
229    return(STATUS_SUCCESS);
230 }
231
232 NTSTATUS
233 MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace,
234                                MEMORY_AREA* MemoryArea, 
235                                PVOID Address,
236                                BOOLEAN Locked)
237 /*
238  * FUNCTION: Move data into memory to satisfy a page not present fault
239  * ARGUMENTS:
240  *      AddressSpace = Address space within which the fault occurred
241  *      MemoryArea = The memory area within which the fault occurred
242  *      Address = The absolute address of fault
243  * RETURNS: Status
244  * NOTES: This function is called with the address space lock held.
245  */
246 {
247    PHYSICAL_ADDRESS Page;
248    NTSTATUS Status;
249    PMM_REGION Region;
250    PMM_PAGEOP PageOp;
251    
252    /*
253     * There is a window between taking the page fault and locking the
254     * address space when another thread could load the page so we check
255     * that.
256     */
257    if (MmIsPagePresent(NULL, Address))
258      {
259         if (Locked)
260           {
261             MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
262           }  
263         return(STATUS_SUCCESS);
264      }
265
266    /*
267     * Check for the virtual memory area being deleted.
268     */
269    if (MemoryArea->DeleteInProgress)
270      {
271        return(STATUS_UNSUCCESSFUL);
272      }
273
274    /*
275     * Get the segment corresponding to the virtual address
276     */
277    Region = MmFindRegion(MemoryArea->BaseAddress, 
278                          &MemoryArea->Data.VirtualMemoryData.RegionListHead,
279                          Address, NULL);
280    if (Region->Type == MEM_RESERVE)
281      {
282         return(STATUS_UNSUCCESSFUL);
283      }
284
285    /*
286     * Get or create a page operation
287     */
288    PageOp = MmGetPageOp(MemoryArea, (ULONG)PsGetCurrentProcessId(), 
289                         (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
290                         MM_PAGEOP_PAGEIN);
291    if (PageOp == NULL)
292      {
293        DPRINT1("MmGetPageOp failed");
294        KeBugCheck(0);
295      }
296
297    /*
298     * Check if someone else is already handling this fault, if so wait
299     * for them
300     */
301    if (PageOp->Thread != PsGetCurrentThread())
302      {
303        MmUnlockAddressSpace(AddressSpace);
304        Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
305                                       0,
306                                       KernelMode,
307                                       FALSE,
308                                       NULL);
309        /*
310         * Check for various strange conditions
311         */
312        if (Status != STATUS_SUCCESS)
313          {
314            DPRINT1("Failed to wait for page op\n");
315            KeBugCheck(0);
316          }
317        if (PageOp->Status == STATUS_PENDING)
318          {
319            DPRINT1("Woke for page op before completion\n");
320            KeBugCheck(0);
321          }
322        /*
323         * If this wasn't a pagein then we need to restart the handling
324         */
325        if (PageOp->OpType != MM_PAGEOP_PAGEIN)
326          {
327            MmLockAddressSpace(AddressSpace);
328            MmReleasePageOp(PageOp);
329            return(STATUS_MM_RESTART_OPERATION);
330          }
331        /*
332         * If the thread handling this fault has failed then we don't retry
333         */
334        if (!NT_SUCCESS(PageOp->Status))
335          {
336            MmLockAddressSpace(AddressSpace);
337            MmReleasePageOp(PageOp);
338            return(Status);
339          }
340        MmLockAddressSpace(AddressSpace);
341        if (Locked)
342          {
343            MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
344          }
345        MmReleasePageOp(PageOp);
346        return(STATUS_SUCCESS);
347      }
348    
349    /*
350     * Try to allocate a page
351     */
352    Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
353    if (Status == STATUS_NO_MEMORY)
354      {
355        MmUnlockAddressSpace(AddressSpace);
356        Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
357        MmLockAddressSpace(AddressSpace);
358      }
359
360    /*
361     * Handle swapped out pages.
362     */
363    if (MmIsPageSwapEntry(NULL, Address))
364      {
365        SWAPENTRY SwapEntry;
366        PMDL Mdl;
367
368        MmDeletePageFileMapping(NULL, Address, &SwapEntry);
369        Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE);
370        MmBuildMdlFromPages(Mdl, (PULONG)&Page);
371        Status = MmReadFromSwapPage(SwapEntry, Mdl);
372        if (!NT_SUCCESS(Status))
373          {
374            KeBugCheck(0);
375          }
376        MmSetSavedSwapEntryPage(Page, SwapEntry);
377      }
378    
379    /*
380     * Set the page. If we fail because we are out of memory then
381     * try again
382     */
383    Status = MmCreateVirtualMapping(PsGetCurrentProcess(),                     
384                                    Address,
385                                    MemoryArea->Attributes,
386                                    Page,
387                                    FALSE);
388    while (Status == STATUS_NO_MEMORY)
389      {
390         MmUnlockAddressSpace(AddressSpace);
391         Status = MmCreateVirtualMapping(PsGetCurrentProcess(),                
392                                         Address,
393                                         MemoryArea->Attributes,
394                                         Page,
395                                         TRUE);
396         MmLockAddressSpace(AddressSpace);
397      }  
398    if (!NT_SUCCESS(Status))
399      {
400        DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
401        KeBugCheck(0);
402        return(Status);
403      }
404
405    /*
406     * Add the page to the process's working set
407     */
408    MmInsertRmap(Page, PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address));
409
410    /*
411     * Finish the operation
412     */
413    if (Locked)
414      {
415        MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
416      }  
417    PageOp->Status = STATUS_SUCCESS;
418    KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
419    MmReleasePageOp(PageOp);
420    return(STATUS_SUCCESS);
421 }
422
423 VOID STATIC
424 MmModifyAttributes(PMADDRESS_SPACE AddressSpace,
425                    PVOID BaseAddress,
426                    ULONG RegionSize,
427                    ULONG OldType,
428                    ULONG OldProtect,
429                    ULONG NewType,
430                    ULONG NewProtect)
431 /*
432  * FUNCTION: Modify the attributes of a memory region
433  */
434 {      
435   /*
436    * If we are switching a previously committed region to reserved then
437    * free any allocated pages within the region
438    */
439   if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
440     {
441       ULONG i;
442       
443       for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
444         {
445           LARGE_INTEGER PhysicalAddr;
446
447           if (MmIsPageSwapEntry(AddressSpace->Process,
448                                 BaseAddress + (i * PAGE_SIZE)))
449             {
450               SWAPENTRY SwapEntry;
451               
452               MmDeletePageFileMapping(AddressSpace->Process,
453                                       BaseAddress + (i * PAGE_SIZE),
454                                       &SwapEntry);
455               MmFreeSwapPage(SwapEntry);
456             }
457           else
458             {
459               PhysicalAddr = MmGetPhysicalAddress(BaseAddress + (i*PAGE_SIZE));
460               MmDeleteVirtualMapping(AddressSpace->Process,
461                                      BaseAddress + (i*PAGE_SIZE),
462                                      FALSE, NULL, NULL);
463               if (PhysicalAddr.QuadPart != 0)
464                 {
465                   SWAPENTRY SavedSwapEntry;
466                   SavedSwapEntry = MmGetSavedSwapEntryPage(PhysicalAddr);
467                   if (SavedSwapEntry != 0)
468                     {
469                       MmFreeSwapPage(SavedSwapEntry);
470                       MmSetSavedSwapEntryPage(PhysicalAddr, 0);
471                     }
472                   MmDeleteRmap(PhysicalAddr, AddressSpace->Process,
473                                BaseAddress + (i * PAGE_SIZE));
474                   MmReleasePageMemoryConsumer(MC_USER, PhysicalAddr);
475                 }
476             }
477         }
478     }
479
480   /*
481    * If we are changing the protection attributes of a committed region then
482    * alter the attributes for any allocated pages within the region
483    */
484   if (NewType == MEM_COMMIT && OldType == MEM_COMMIT && 
485       OldProtect != NewProtect)
486     {
487       ULONG i;
488    
489       for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
490         {
491           if (MmIsPagePresent(AddressSpace->Process, 
492                               BaseAddress + (i*PAGE_SIZE)))
493             {
494               MmSetPageProtect(AddressSpace->Process,
495                                BaseAddress + (i*PAGE_SIZE), 
496                                NewProtect);
497             }
498         }
499     }
500 }
501
502 NTSTATUS STDCALL
503 NtAllocateVirtualMemory(IN      HANDLE  ProcessHandle,
504                         IN OUT  PVOID*  UBaseAddress,
505                         IN      ULONG   ZeroBits,
506                         IN OUT  PULONG  URegionSize,
507                         IN      ULONG   AllocationType,
508                         IN      ULONG   Protect)
509 /*
510  * FUNCTION: Allocates a block of virtual memory in the process address space
511  * ARGUMENTS:
512  *      ProcessHandle = The handle of the process which owns the virtual memory
513  *      BaseAddress   = A pointer to the virtual memory allocated. If you 
514  *                      supply a non zero value the system will try to 
515  *                      allocate the memory at the address supplied. It round 
516  *                      it down to a multiple  of the page size.
517  *      ZeroBits  = (OPTIONAL) You can specify the number of high order bits 
518  *                      that must be zero, ensuring that the memory will be 
519  *                      allocated at a address below a certain value.
520  *      RegionSize = The number of bytes to allocate
521  *      AllocationType = Indicates the type of virtual memory you like to 
522  *                       allocated, can be a combination of MEM_COMMIT, 
523  *                       MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
524  *      Protect = Indicates the protection type of the pages allocated, can be
525  *                a combination of PAGE_READONLY, PAGE_READWRITE, 
526  *                PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD, 
527  *                PAGE_NOACCESS
528  * RETURNS: Status
529  */
530 {
531    PEPROCESS Process;
532    MEMORY_AREA* MemoryArea;
533    ULONG Type;
534    NTSTATUS Status;
535    PMADDRESS_SPACE AddressSpace;
536    PVOID BaseAddress;
537    ULONG RegionSize;
538    PVOID PBaseAddress;
539    ULONG PRegionSize;
540
541    DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
542           "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
543           *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
544           Protect);
545    
546    /*
547     * Check the validity of the parameters
548     */
549    if ((Protect & PAGE_FLAGS_VALID_FROM_USER_MODE) != Protect)
550      {
551        return(STATUS_INVALID_PAGE_PROTECTION);
552      }
553    if ((AllocationType & (MEM_COMMIT | MEM_RESERVE)) == 0)
554      {
555        return(STATUS_INVALID_PARAMETER);
556      }
557    
558    PBaseAddress = *UBaseAddress;
559    PRegionSize = *URegionSize;
560    
561    BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
562    RegionSize = PAGE_ROUND_UP(PBaseAddress + PRegionSize) -
563      PAGE_ROUND_DOWN(PBaseAddress);
564    
565    Status = ObReferenceObjectByHandle(ProcessHandle,
566                                       PROCESS_VM_OPERATION,
567                                       NULL,
568                                       UserMode,
569                                       (PVOID*)(&Process),
570                                       NULL);
571    if (!NT_SUCCESS(Status))
572      {
573         DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
574         return(Status);
575      }
576    
577    Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
578    DPRINT("Type %x\n", Type);
579    
580    AddressSpace = &Process->AddressSpace;
581    MmLockAddressSpace(AddressSpace);
582    
583    if (PBaseAddress != 0)
584      {
585         MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
586                                                BaseAddress);
587         
588         if (MemoryArea != NULL &&
589             MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
590             MemoryArea->Length >= RegionSize)
591           {
592             Status = 
593               MmAlterRegion(AddressSpace, 
594                             MemoryArea->BaseAddress, 
595                             &MemoryArea->Data.VirtualMemoryData.RegionListHead,
596                             PBaseAddress, RegionSize,
597                             Type, Protect, MmModifyAttributes);
598             MmUnlockAddressSpace(AddressSpace);
599             ObDereferenceObject(Process);
600             DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
601             return(Status);
602           }
603         else if (MemoryArea != NULL)
604           {
605             MmUnlockAddressSpace(AddressSpace);
606             ObDereferenceObject(Process);
607             return(STATUS_UNSUCCESSFUL);
608           }
609      }
610    
611    Status = MmCreateMemoryArea(Process,
612                                &Process->AddressSpace,
613                                MEMORY_AREA_VIRTUAL_MEMORY,
614                                &BaseAddress,
615                                RegionSize,
616                                Protect,
617                                &MemoryArea,
618                                PBaseAddress != 0);
619    
620    if (!NT_SUCCESS(Status))
621      {
622         MmUnlockAddressSpace(AddressSpace);
623         ObDereferenceObject(Process);
624         DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
625         return(Status);
626      }
627    MmInitialiseRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
628                       RegionSize, Type, Protect);
629    
630    if ((AllocationType & MEM_COMMIT) &&
631        ((Protect & PAGE_READWRITE) ||
632         (Protect & PAGE_EXECUTE_READWRITE)))
633      {
634         MmReserveSwapPages(RegionSize);
635      }
636    
637    *UBaseAddress = BaseAddress;
638    *URegionSize = RegionSize;
639    DPRINT("*UBaseAddress %x  *URegionSize %x\n", BaseAddress, RegionSize);
640    
641    MmUnlockAddressSpace(AddressSpace);
642    ObDereferenceObject(Process);
643    return(STATUS_SUCCESS);
644 }
645
646 VOID STATIC
647 MmFreeVirtualMemoryPage(PVOID Context,
648                         MEMORY_AREA* MemoryArea,
649                         PVOID Address,
650                         PHYSICAL_ADDRESS PhysicalAddr,
651                         SWAPENTRY SwapEntry,
652                         BOOLEAN Dirty)
653 {
654   PEPROCESS Process = (PEPROCESS)Context;
655   
656   if (PhysicalAddr.QuadPart != 0)
657     {
658       SWAPENTRY SavedSwapEntry;
659       SavedSwapEntry = MmGetSavedSwapEntryPage(PhysicalAddr);
660       if (SavedSwapEntry != 0)
661         {
662           MmFreeSwapPage(SavedSwapEntry);
663           MmSetSavedSwapEntryPage(PhysicalAddr, 0);
664         }
665       MmDeleteRmap(PhysicalAddr, Process, Address);
666       MmReleasePageMemoryConsumer(MC_USER, PhysicalAddr);
667     }
668   else if (SwapEntry != 0)
669     {
670       MmFreeSwapPage(SwapEntry);
671     }
672 }
673
674 VOID
675 MmFreeVirtualMemory(PEPROCESS Process,
676                     PMEMORY_AREA MemoryArea)
677 {
678   PLIST_ENTRY current_entry;
679   PMM_REGION current;
680   ULONG i;
681   
682   DPRINT("MmFreeVirtualMemory(Process %p  MemoryArea %p)\n", Process, 
683          MemoryArea);
684   
685   /* Mark this memory area as about to be deleted. */
686   MemoryArea->DeleteInProgress = TRUE;
687
688   /* 
689    * Wait for any ongoing paging operations. Notice that since we have
690    * flagged this memory area as deleted no more page ops will be added.
691    */
692   if (MemoryArea->PageOpCount > 0)
693     {
694       for (i = 0; i < PAGE_ROUND_UP(MemoryArea->Length) / PAGE_SIZE; i++)
695         {
696           PMM_PAGEOP PageOp;
697
698           if (MemoryArea->PageOpCount == 0)
699             {
700               break;
701             }
702           
703           PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
704                                     MemoryArea->BaseAddress + (i * PAGE_SIZE),
705                                     NULL, 0);
706           if (PageOp != NULL)
707             {
708               NTSTATUS Status;
709               MmUnlockAddressSpace(&Process->AddressSpace);
710               Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
711                                              0,
712                                              KernelMode,
713                                              FALSE,
714                                              NULL);
715               if (Status != STATUS_SUCCESS)
716                 {
717                   DPRINT1("Failed to wait for page op\n");
718                   KeBugCheck(0);
719                 }
720               MmLockAddressSpace(&Process->AddressSpace);
721               MmReleasePageOp(PageOp);
722             }
723         }
724     }
725
726   /* Free all the individual segments. */
727   current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
728   while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
729     {
730       current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
731       current_entry = current_entry->Flink;
732       ExFreePool(current);
733     }
734
735   /* Actually free the memory area. */
736   MmFreeMemoryArea(&Process->AddressSpace,
737                    MemoryArea->BaseAddress,
738                    0,
739                    MmFreeVirtualMemoryPage,
740                    (PVOID)Process);
741 }
742
743 NTSTATUS STDCALL 
744 NtFreeVirtualMemory(IN  HANDLE  ProcessHandle,
745                     IN  PVOID*  PBaseAddress,   
746                     IN  PULONG  PRegionSize,    
747                     IN  ULONG   FreeType)
748 /*
749  * FUNCTION: Frees a range of virtual memory
750  * ARGUMENTS:
751  *        ProcessHandle = Points to the process that allocated the virtual 
752  *                        memory
753  *        BaseAddress = Points to the memory address, rounded down to a 
754  *                      multiple of the pagesize
755  *        RegionSize = Limits the range to free, rounded up to a multiple of 
756  *                     the paging size
757  *        FreeType = Can be one of the values:  MEM_DECOMMIT, or MEM_RELEASE
758  * RETURNS: Status 
759  */
760 {
761    MEMORY_AREA* MemoryArea;
762    NTSTATUS Status;
763    PEPROCESS Process;
764    PMADDRESS_SPACE AddressSpace;
765    PVOID BaseAddress;
766    ULONG RegionSize;
767    
768    DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
769           "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
770           *PRegionSize,FreeType);
771                                  
772    BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
773    RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
774      PAGE_ROUND_DOWN((*PBaseAddress));
775    
776    Status = ObReferenceObjectByHandle(ProcessHandle,
777                                       PROCESS_VM_OPERATION,
778                                       PsProcessType,
779                                       UserMode,
780                                       (PVOID*)(&Process),
781                                       NULL);
782    if (!NT_SUCCESS(Status))
783      {
784         return(Status);
785      }
786    
787    AddressSpace = &Process->AddressSpace;
788    
789    MmLockAddressSpace(AddressSpace);
790    MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
791                                           BaseAddress);
792    if (MemoryArea == NULL)
793      {
794         MmUnlockAddressSpace(AddressSpace);
795         ObDereferenceObject(Process);
796         return(STATUS_UNSUCCESSFUL);
797      }
798    
799    switch (FreeType)
800      {
801       case MEM_RELEASE:
802         /* We can only free a memory area in one step. */
803         if (MemoryArea->BaseAddress != BaseAddress)
804           {
805              MmUnlockAddressSpace(AddressSpace);
806              ObDereferenceObject(Process);
807              return(STATUS_UNSUCCESSFUL);
808           }     
809         MmFreeVirtualMemory(Process, MemoryArea);
810         MmUnlockAddressSpace(AddressSpace);
811         ObDereferenceObject(Process);
812         return(STATUS_SUCCESS);
813         
814       case MEM_DECOMMIT:
815         Status = 
816           MmAlterRegion(AddressSpace,
817                         MemoryArea->BaseAddress,
818                         &MemoryArea->Data.VirtualMemoryData.RegionListHead,
819                         BaseAddress,
820                         RegionSize,
821                         MEM_RESERVE,
822                         PAGE_NOACCESS,
823                         MmModifyAttributes);
824         MmUnlockAddressSpace(AddressSpace);     
825         ObDereferenceObject(Process);
826         return(Status);
827      }
828    MmUnlockAddressSpace(AddressSpace);
829    ObDereferenceObject(Process);
830    return(STATUS_NOT_IMPLEMENTED);
831 }
832
833 NTSTATUS
834 MmProtectAnonMem(PMADDRESS_SPACE AddressSpace,
835                  PMEMORY_AREA MemoryArea,
836                  PVOID BaseAddress,
837                  ULONG Length,
838                  ULONG Protect,
839                  PULONG OldProtect)
840 {
841   PMM_REGION Region;
842   NTSTATUS Status;
843
844   Region = MmFindRegion(MemoryArea->BaseAddress, 
845                         &MemoryArea->Data.VirtualMemoryData.RegionListHead,
846                         BaseAddress, NULL);
847   *OldProtect = Region->Protect;
848   Status = MmAlterRegion(AddressSpace, MemoryArea->BaseAddress,
849                          &MemoryArea->Data.VirtualMemoryData.RegionListHead,
850                          BaseAddress, Length, Region->Type, Protect, 
851                          MmModifyAttributes);
852   return(Status);
853 }
854
855 NTSTATUS STDCALL
856 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
857                PVOID Address,
858                PMEMORY_BASIC_INFORMATION Info,
859                PULONG ResultLength)
860 {
861   PMM_REGION Region;
862   PVOID RegionBase;
863
864   Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
865
866   Region = MmFindRegion(MemoryArea->BaseAddress,
867                         &MemoryArea->Data.VirtualMemoryData.RegionListHead,
868                         Address, &RegionBase);
869   Info->AllocationBase = RegionBase;
870   Info->AllocationProtect = Region->Protect;  /* FIXME */
871   Info->RegionSize = Region->Length;
872   Info->State = Region->Type;
873   Info->Protect = Region->Protect;
874   Info->Type = MEM_PRIVATE;
875   return(STATUS_SUCCESS);
876 }
877
878 /* EOF */