branch update for HEAD-2003021201
[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)MemoryArea->Process->UniqueProcessId, 
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            KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
329            MmReleasePageOp(PageOp);
330            return(STATUS_MM_RESTART_OPERATION);
331          }
332        /*
333         * If the thread handling this fault has failed then we don't retry
334         */
335        if (!NT_SUCCESS(PageOp->Status))
336          {
337            MmLockAddressSpace(AddressSpace);
338            KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
339            Status = PageOp->Status;
340            MmReleasePageOp(PageOp);
341            return(Status);
342          }
343        MmLockAddressSpace(AddressSpace);
344        if (Locked)
345          {
346            MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
347          }
348        KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
349        MmReleasePageOp(PageOp);
350        return(STATUS_SUCCESS);
351      }
352    
353    /*
354     * Try to allocate a page
355     */
356    Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
357    if (Status == STATUS_NO_MEMORY)
358      {
359        MmUnlockAddressSpace(AddressSpace);
360        Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
361        MmLockAddressSpace(AddressSpace);
362      }
363    if (!NT_SUCCESS(Status))
364    {
365       DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
366       KeBugCheck(0);
367    }
368
369    /*
370     * Handle swapped out pages.
371     */
372    if (MmIsPageSwapEntry(NULL, Address))
373      {
374        SWAPENTRY SwapEntry;
375        PMDL Mdl;
376
377        MmDeletePageFileMapping(NULL, Address, &SwapEntry);
378        Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE);
379        MmBuildMdlFromPages(Mdl, (PULONG)&Page);
380        Status = MmReadFromSwapPage(SwapEntry, Mdl);
381        if (!NT_SUCCESS(Status))
382          {
383            KeBugCheck(0);
384          }
385        MmSetSavedSwapEntryPage(Page, SwapEntry);
386      }
387    
388    /*
389     * Set the page. If we fail because we are out of memory then
390     * try again
391     */
392    Status = MmCreateVirtualMapping(MemoryArea->Process,               
393                                    (PVOID)PAGE_ROUND_DOWN(Address),
394                                    MemoryArea->Attributes,
395                                    Page,
396                                    FALSE);
397    while (Status == STATUS_NO_MEMORY)
398      {
399         MmUnlockAddressSpace(AddressSpace);
400         Status = MmCreateVirtualMapping(MemoryArea->Process,          
401                                         Address,
402                                         MemoryArea->Attributes,
403                                         Page,
404                                         TRUE);
405         MmLockAddressSpace(AddressSpace);
406      }  
407    if (!NT_SUCCESS(Status))
408      {
409        DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
410        KeBugCheck(0);
411        return(Status);
412      }
413
414    /*
415     * Add the page to the process's working set
416     */
417    MmInsertRmap(Page, MemoryArea->Process, (PVOID)PAGE_ROUND_DOWN(Address));
418
419    /*
420     * Finish the operation
421     */
422    if (Locked)
423      {
424        MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
425      }  
426    PageOp->Status = STATUS_SUCCESS;
427    KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
428    MmReleasePageOp(PageOp);
429    return(STATUS_SUCCESS);
430 }
431
432 VOID STATIC
433 MmModifyAttributes(PMADDRESS_SPACE AddressSpace,
434                    PVOID BaseAddress,
435                    ULONG RegionSize,
436                    ULONG OldType,
437                    ULONG OldProtect,
438                    ULONG NewType,
439                    ULONG NewProtect)
440 /*
441  * FUNCTION: Modify the attributes of a memory region
442  */
443 {      
444   /*
445    * If we are switching a previously committed region to reserved then
446    * free any allocated pages within the region
447    */
448   if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
449     {
450       ULONG i;
451       
452       for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
453         {
454           LARGE_INTEGER PhysicalAddr;
455
456           if (MmIsPageSwapEntry(AddressSpace->Process,
457                                 BaseAddress + (i * PAGE_SIZE)))
458             {
459               SWAPENTRY SwapEntry;
460               
461               MmDeletePageFileMapping(AddressSpace->Process,
462                                       BaseAddress + (i * PAGE_SIZE),
463                                       &SwapEntry);
464               MmFreeSwapPage(SwapEntry);
465             }
466           else
467             {
468               PhysicalAddr = MmGetPhysicalAddress(BaseAddress + (i*PAGE_SIZE));
469               MmDeleteVirtualMapping(AddressSpace->Process,
470                                      BaseAddress + (i*PAGE_SIZE),
471                                      FALSE, NULL, NULL);
472               if (PhysicalAddr.QuadPart != 0)
473                 {
474                   SWAPENTRY SavedSwapEntry;
475                   SavedSwapEntry = MmGetSavedSwapEntryPage(PhysicalAddr);
476                   if (SavedSwapEntry != 0)
477                     {
478                       MmFreeSwapPage(SavedSwapEntry);
479                       MmSetSavedSwapEntryPage(PhysicalAddr, 0);
480                     }
481                   MmDeleteRmap(PhysicalAddr, AddressSpace->Process,
482                                BaseAddress + (i * PAGE_SIZE));
483                   MmReleasePageMemoryConsumer(MC_USER, PhysicalAddr);
484                 }
485             }
486         }
487     }
488
489   /*
490    * If we are changing the protection attributes of a committed region then
491    * alter the attributes for any allocated pages within the region
492    */
493   if (NewType == MEM_COMMIT && OldType == MEM_COMMIT && 
494       OldProtect != NewProtect)
495     {
496       ULONG i;
497    
498       for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
499         {
500           if (MmIsPagePresent(AddressSpace->Process, 
501                               BaseAddress + (i*PAGE_SIZE)))
502             {
503               MmSetPageProtect(AddressSpace->Process,
504                                BaseAddress + (i*PAGE_SIZE), 
505                                NewProtect);
506             }
507         }
508     }
509 }
510
511 NTSTATUS STDCALL
512 NtAllocateVirtualMemory(IN      HANDLE  ProcessHandle,
513                         IN OUT  PVOID*  UBaseAddress,
514                         IN      ULONG   ZeroBits,
515                         IN OUT  PULONG  URegionSize,
516                         IN      ULONG   AllocationType,
517                         IN      ULONG   Protect)
518 /*
519  * FUNCTION: Allocates a block of virtual memory in the process address space
520  * ARGUMENTS:
521  *      ProcessHandle = The handle of the process which owns the virtual memory
522  *      BaseAddress   = A pointer to the virtual memory allocated. If you 
523  *                      supply a non zero value the system will try to 
524  *                      allocate the memory at the address supplied. It round 
525  *                      it down to a multiple  of the page size.
526  *      ZeroBits  = (OPTIONAL) You can specify the number of high order bits 
527  *                      that must be zero, ensuring that the memory will be 
528  *                      allocated at a address below a certain value.
529  *      RegionSize = The number of bytes to allocate
530  *      AllocationType = Indicates the type of virtual memory you like to 
531  *                       allocated, can be a combination of MEM_COMMIT, 
532  *                       MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
533  *      Protect = Indicates the protection type of the pages allocated, can be
534  *                a combination of PAGE_READONLY, PAGE_READWRITE, 
535  *                PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD, 
536  *                PAGE_NOACCESS
537  * RETURNS: Status
538  */
539 {
540    PEPROCESS Process;
541    MEMORY_AREA* MemoryArea;
542    ULONG Type;
543    NTSTATUS Status;
544    PMADDRESS_SPACE AddressSpace;
545    PVOID BaseAddress;
546    ULONG RegionSize;
547    PVOID PBaseAddress;
548    ULONG PRegionSize;
549
550    DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
551           "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
552           *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
553           Protect);
554    
555    /*
556     * Check the validity of the parameters
557     */
558    if ((Protect & PAGE_FLAGS_VALID_FROM_USER_MODE) != Protect)
559      {
560        return(STATUS_INVALID_PAGE_PROTECTION);
561      }
562    if ((AllocationType & (MEM_COMMIT | MEM_RESERVE)) == 0)
563      {
564        return(STATUS_INVALID_PARAMETER);
565      }
566    
567    PBaseAddress = *UBaseAddress;
568    PRegionSize = *URegionSize;
569    
570    BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
571    RegionSize = PAGE_ROUND_UP(PBaseAddress + PRegionSize) -
572      PAGE_ROUND_DOWN(PBaseAddress);
573    
574    Status = ObReferenceObjectByHandle(ProcessHandle,
575                                       PROCESS_VM_OPERATION,
576                                       NULL,
577                                       UserMode,
578                                       (PVOID*)(&Process),
579                                       NULL);
580    if (!NT_SUCCESS(Status))
581      {
582         DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
583         return(Status);
584      }
585    
586    Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
587    DPRINT("Type %x\n", Type);
588    
589    AddressSpace = &Process->AddressSpace;
590    MmLockAddressSpace(AddressSpace);
591    
592    if (PBaseAddress != 0)
593      {
594         MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
595                                                BaseAddress);
596         
597         if (MemoryArea != NULL &&
598             MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
599             MemoryArea->Length >= RegionSize)
600           {
601             Status = 
602               MmAlterRegion(AddressSpace, 
603                             MemoryArea->BaseAddress, 
604                             &MemoryArea->Data.VirtualMemoryData.RegionListHead,
605                             BaseAddress, RegionSize,
606                             Type, Protect, MmModifyAttributes);
607             MmUnlockAddressSpace(AddressSpace);
608             ObDereferenceObject(Process);
609             DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
610             return(Status);
611           }
612         else if (MemoryArea != NULL)
613           {
614             MmUnlockAddressSpace(AddressSpace);
615             ObDereferenceObject(Process);
616             return(STATUS_UNSUCCESSFUL);
617           }
618      }
619    
620    Status = MmCreateMemoryArea(Process,
621                                AddressSpace,
622                                MEMORY_AREA_VIRTUAL_MEMORY,
623                                &BaseAddress,
624                                RegionSize,
625                                Protect,
626                                &MemoryArea,
627                                PBaseAddress != 0);
628    
629    if (!NT_SUCCESS(Status))
630      {
631         MmUnlockAddressSpace(AddressSpace);
632         ObDereferenceObject(Process);
633         DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
634         return(Status);
635      }
636    MmInitialiseRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
637                       RegionSize, Type, Protect);
638    
639    if ((AllocationType & MEM_COMMIT) &&
640        ((Protect & PAGE_READWRITE) ||
641         (Protect & PAGE_EXECUTE_READWRITE)))
642      {
643         MmReserveSwapPages(RegionSize);
644      }
645    
646    *UBaseAddress = BaseAddress;
647    *URegionSize = RegionSize;
648    DPRINT("*UBaseAddress %x  *URegionSize %x\n", BaseAddress, RegionSize);
649    
650    MmUnlockAddressSpace(AddressSpace);
651    ObDereferenceObject(Process);
652    return(STATUS_SUCCESS);
653 }
654
655 VOID STATIC
656 MmFreeVirtualMemoryPage(PVOID Context,
657                         MEMORY_AREA* MemoryArea,
658                         PVOID Address,
659                         PHYSICAL_ADDRESS PhysicalAddr,
660                         SWAPENTRY SwapEntry,
661                         BOOLEAN Dirty)
662 {
663   PEPROCESS Process = (PEPROCESS)Context;
664   
665   if (PhysicalAddr.QuadPart != 0)
666     {
667       SWAPENTRY SavedSwapEntry;
668       SavedSwapEntry = MmGetSavedSwapEntryPage(PhysicalAddr);
669       if (SavedSwapEntry != 0)
670         {
671           MmFreeSwapPage(SavedSwapEntry);
672           MmSetSavedSwapEntryPage(PhysicalAddr, 0);
673         }
674       MmDeleteRmap(PhysicalAddr, Process, Address);
675       MmReleasePageMemoryConsumer(MC_USER, PhysicalAddr);
676     }
677   else if (SwapEntry != 0)
678     {
679       MmFreeSwapPage(SwapEntry);
680     }
681 }
682
683 VOID
684 MmFreeVirtualMemory(PEPROCESS Process,
685                     PMEMORY_AREA MemoryArea)
686 {
687   PLIST_ENTRY current_entry;
688   PMM_REGION current;
689   ULONG i;
690   
691   DPRINT("MmFreeVirtualMemory(Process %p  MemoryArea %p)\n", Process, 
692          MemoryArea);
693   
694   /* Mark this memory area as about to be deleted. */
695   MemoryArea->DeleteInProgress = TRUE;
696
697   /* 
698    * Wait for any ongoing paging operations. Notice that since we have
699    * flagged this memory area as deleted no more page ops will be added.
700    */
701   if (MemoryArea->PageOpCount > 0)
702     {
703       for (i = 0; i < PAGE_ROUND_UP(MemoryArea->Length) / PAGE_SIZE; i++)
704         {
705           PMM_PAGEOP PageOp;
706
707           if (MemoryArea->PageOpCount == 0)
708             {
709               break;
710             }
711           
712           PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
713                                     MemoryArea->BaseAddress + (i * PAGE_SIZE),
714                                     NULL, 0);
715           if (PageOp != NULL)
716             {
717               NTSTATUS Status;
718               MmUnlockAddressSpace(&Process->AddressSpace);
719               Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
720                                              0,
721                                              KernelMode,
722                                              FALSE,
723                                              NULL);
724               if (Status != STATUS_SUCCESS)
725                 {
726                   DPRINT1("Failed to wait for page op\n");
727                   KeBugCheck(0);
728                 }
729               MmLockAddressSpace(&Process->AddressSpace);
730               MmReleasePageOp(PageOp);
731             }
732         }
733     }
734
735   /* Free all the individual segments. */
736   current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
737   while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
738     {
739       current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
740       current_entry = current_entry->Flink;
741       ExFreePool(current);
742     }
743
744   /* Actually free the memory area. */
745   MmFreeMemoryArea(&Process->AddressSpace,
746                    MemoryArea->BaseAddress,
747                    0,
748                    MmFreeVirtualMemoryPage,
749                    (PVOID)Process);
750 }
751
752 NTSTATUS STDCALL 
753 NtFreeVirtualMemory(IN  HANDLE  ProcessHandle,
754                     IN  PVOID*  PBaseAddress,   
755                     IN  PULONG  PRegionSize,    
756                     IN  ULONG   FreeType)
757 /*
758  * FUNCTION: Frees a range of virtual memory
759  * ARGUMENTS:
760  *        ProcessHandle = Points to the process that allocated the virtual 
761  *                        memory
762  *        BaseAddress = Points to the memory address, rounded down to a 
763  *                      multiple of the pagesize
764  *        RegionSize = Limits the range to free, rounded up to a multiple of 
765  *                     the paging size
766  *        FreeType = Can be one of the values:  MEM_DECOMMIT, or MEM_RELEASE
767  * RETURNS: Status 
768  */
769 {
770    MEMORY_AREA* MemoryArea;
771    NTSTATUS Status;
772    PEPROCESS Process;
773    PMADDRESS_SPACE AddressSpace;
774    PVOID BaseAddress;
775    ULONG RegionSize;
776    
777    DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
778           "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
779           *PRegionSize,FreeType);
780                                  
781    BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
782    RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
783      PAGE_ROUND_DOWN((*PBaseAddress));
784    
785    Status = ObReferenceObjectByHandle(ProcessHandle,
786                                       PROCESS_VM_OPERATION,
787                                       PsProcessType,
788                                       UserMode,
789                                       (PVOID*)(&Process),
790                                       NULL);
791    if (!NT_SUCCESS(Status))
792      {
793         return(Status);
794      }
795    
796    AddressSpace = &Process->AddressSpace;
797    
798    MmLockAddressSpace(AddressSpace);
799    MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
800                                           BaseAddress);
801    if (MemoryArea == NULL)
802      {
803         MmUnlockAddressSpace(AddressSpace);
804         ObDereferenceObject(Process);
805         return(STATUS_UNSUCCESSFUL);
806      }
807    
808    switch (FreeType)
809      {
810       case MEM_RELEASE:
811         /* We can only free a memory area in one step. */
812         if (MemoryArea->BaseAddress != BaseAddress)
813           {
814              MmUnlockAddressSpace(AddressSpace);
815              ObDereferenceObject(Process);
816              return(STATUS_UNSUCCESSFUL);
817           }     
818         MmFreeVirtualMemory(Process, MemoryArea);
819         MmUnlockAddressSpace(AddressSpace);
820         ObDereferenceObject(Process);
821         return(STATUS_SUCCESS);
822         
823       case MEM_DECOMMIT:
824         Status = 
825           MmAlterRegion(AddressSpace,
826                         MemoryArea->BaseAddress,
827                         &MemoryArea->Data.VirtualMemoryData.RegionListHead,
828                         BaseAddress,
829                         RegionSize,
830                         MEM_RESERVE,
831                         PAGE_NOACCESS,
832                         MmModifyAttributes);
833         MmUnlockAddressSpace(AddressSpace);     
834         ObDereferenceObject(Process);
835         return(Status);
836      }
837    MmUnlockAddressSpace(AddressSpace);
838    ObDereferenceObject(Process);
839    return(STATUS_NOT_IMPLEMENTED);
840 }
841
842 NTSTATUS
843 MmProtectAnonMem(PMADDRESS_SPACE AddressSpace,
844                  PMEMORY_AREA MemoryArea,
845                  PVOID BaseAddress,
846                  ULONG Length,
847                  ULONG Protect,
848                  PULONG OldProtect)
849 {
850   PMM_REGION Region;
851   NTSTATUS Status;
852
853   Region = MmFindRegion(MemoryArea->BaseAddress, 
854                         &MemoryArea->Data.VirtualMemoryData.RegionListHead,
855                         BaseAddress, NULL);
856   *OldProtect = Region->Protect;
857   Status = MmAlterRegion(AddressSpace, MemoryArea->BaseAddress,
858                          &MemoryArea->Data.VirtualMemoryData.RegionListHead,
859                          BaseAddress, Length, Region->Type, Protect, 
860                          MmModifyAttributes);
861   return(Status);
862 }
863
864 NTSTATUS STDCALL
865 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
866                PVOID Address,
867                PMEMORY_BASIC_INFORMATION Info,
868                PULONG ResultLength)
869 {
870   PMM_REGION Region;
871   PVOID RegionBase;
872
873   Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
874
875   Region = MmFindRegion(MemoryArea->BaseAddress,
876                         &MemoryArea->Data.VirtualMemoryData.RegionListHead,
877                         Address, &RegionBase);
878   Info->AllocationBase = RegionBase;
879   Info->AllocationProtect = Region->Protect;  /* FIXME */
880   Info->RegionSize = Region->Length;
881   Info->State = Region->Type;
882   Info->Protect = Region->Protect;
883   Info->Type = MEM_PRIVATE;
884   return(STATUS_SUCCESS);
885 }
886
887 /* EOF */