update for HEAD-2003091401
[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_PAGEFILE_QUOTA);
197          }
198      }
199    
200    /*
201     * Write the page to the pagefile
202     */
203    Mdl = MmCreateMdl(NULL, NULL, PAGE_SIZE);
204    MmBuildMdlFromPages(Mdl, (ULONG *)&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 || Region->Protect == PAGE_NOACCESS)
281      {
282         return(STATUS_ACCESS_VIOLATION);
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                                    Region->Protect,
395                                    Page,
396                                    FALSE);
397    while (Status == STATUS_NO_MEMORY)
398      {
399         MmUnlockAddressSpace(AddressSpace);
400         Status = MmCreateVirtualMapping(MemoryArea->Process,          
401                                         Address,
402                                         Region->Protect,
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 /*
512  * @implemented
513  */
514 NTSTATUS STDCALL
515 NtAllocateVirtualMemory(IN      HANDLE  ProcessHandle,
516                         IN OUT  PVOID*  UBaseAddress,
517                         IN      ULONG   ZeroBits,
518                         IN OUT  PULONG  URegionSize,
519                         IN      ULONG   AllocationType,
520                         IN      ULONG   Protect)
521 /*
522  * FUNCTION: Allocates a block of virtual memory in the process address space
523  * ARGUMENTS:
524  *      ProcessHandle = The handle of the process which owns the virtual memory
525  *      BaseAddress   = A pointer to the virtual memory allocated. If you 
526  *                      supply a non zero value the system will try to 
527  *                      allocate the memory at the address supplied. It round 
528  *                      it down to a multiple  of the page size.
529  *      ZeroBits  = (OPTIONAL) You can specify the number of high order bits 
530  *                      that must be zero, ensuring that the memory will be 
531  *                      allocated at a address below a certain value.
532  *      RegionSize = The number of bytes to allocate
533  *      AllocationType = Indicates the type of virtual memory you like to 
534  *                       allocated, can be a combination of MEM_COMMIT, 
535  *                       MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
536  *      Protect = Indicates the protection type of the pages allocated, can be
537  *                a combination of PAGE_READONLY, PAGE_READWRITE, 
538  *                PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD, 
539  *                PAGE_NOACCESS
540  * RETURNS: Status
541  */
542 {
543    PEPROCESS Process;
544    MEMORY_AREA* MemoryArea;
545    ULONG Type;
546    NTSTATUS Status;
547    PMADDRESS_SPACE AddressSpace;
548    PVOID BaseAddress;
549    ULONG RegionSize;
550    PVOID PBaseAddress;
551    ULONG PRegionSize;
552
553    DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
554           "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
555           *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
556           Protect);
557    
558    /*
559     * Check the validity of the parameters
560     */
561    if ((Protect & PAGE_FLAGS_VALID_FROM_USER_MODE) != Protect)
562      {
563        return(STATUS_INVALID_PAGE_PROTECTION);
564      }
565    if ((AllocationType & (MEM_COMMIT | MEM_RESERVE)) == 0)
566      {
567        return(STATUS_INVALID_PARAMETER);
568      }
569    
570    PBaseAddress = *UBaseAddress;
571    PRegionSize = *URegionSize;
572    
573    BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
574    RegionSize = PAGE_ROUND_UP(PBaseAddress + PRegionSize) -
575      PAGE_ROUND_DOWN(PBaseAddress);
576    
577    Status = ObReferenceObjectByHandle(ProcessHandle,
578                                       PROCESS_VM_OPERATION,
579                                       NULL,
580                                       UserMode,
581                                       (PVOID*)(&Process),
582                                       NULL);
583    if (!NT_SUCCESS(Status))
584      {
585         DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
586         return(Status);
587      }
588    
589    Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
590    DPRINT("Type %x\n", Type);
591    
592    AddressSpace = &Process->AddressSpace;
593    MmLockAddressSpace(AddressSpace);
594    
595    if (PBaseAddress != 0)
596      {
597         MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
598                                                BaseAddress);
599         
600         if (MemoryArea != NULL &&
601             MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
602             MemoryArea->Length >= RegionSize)
603           {
604             Status = 
605               MmAlterRegion(AddressSpace, 
606                             MemoryArea->BaseAddress, 
607                             &MemoryArea->Data.VirtualMemoryData.RegionListHead,
608                             BaseAddress, RegionSize,
609                             Type, Protect, MmModifyAttributes);
610             MmUnlockAddressSpace(AddressSpace);
611             ObDereferenceObject(Process);
612             DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
613             return(Status);
614           }
615         else if (MemoryArea != NULL)
616           {
617             MmUnlockAddressSpace(AddressSpace);
618             ObDereferenceObject(Process);
619             return(STATUS_UNSUCCESSFUL);
620           }
621      }
622    
623    Status = MmCreateMemoryArea(Process,
624                                AddressSpace,
625                                MEMORY_AREA_VIRTUAL_MEMORY,
626                                &BaseAddress,
627                                RegionSize,
628                                Protect,
629                                &MemoryArea,
630                                PBaseAddress != 0,
631                                (AllocationType & MEM_TOP_DOWN));
632    if (!NT_SUCCESS(Status))
633      {
634         MmUnlockAddressSpace(AddressSpace);
635         ObDereferenceObject(Process);
636         DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
637         return(Status);
638      }
639    MmInitialiseRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
640                       RegionSize, Type, Protect);
641    
642    if ((AllocationType & MEM_COMMIT) &&
643        ((Protect & PAGE_READWRITE) ||
644         (Protect & PAGE_EXECUTE_READWRITE)))
645      {
646         MmReserveSwapPages(RegionSize);
647      }
648    
649    *UBaseAddress = BaseAddress;
650    *URegionSize = RegionSize;
651    DPRINT("*UBaseAddress %x  *URegionSize %x\n", BaseAddress, RegionSize);
652    
653    MmUnlockAddressSpace(AddressSpace);
654    ObDereferenceObject(Process);
655    return(STATUS_SUCCESS);
656 }
657
658 VOID STATIC
659 MmFreeVirtualMemoryPage(PVOID Context,
660                         MEMORY_AREA* MemoryArea,
661                         PVOID Address,
662                         PHYSICAL_ADDRESS PhysicalAddr,
663                         SWAPENTRY SwapEntry,
664                         BOOLEAN Dirty)
665 {
666   PEPROCESS Process = (PEPROCESS)Context;
667   
668   if (PhysicalAddr.QuadPart != 0)
669     {
670       SWAPENTRY SavedSwapEntry;
671       SavedSwapEntry = MmGetSavedSwapEntryPage(PhysicalAddr);
672       if (SavedSwapEntry != 0)
673         {
674           MmFreeSwapPage(SavedSwapEntry);
675           MmSetSavedSwapEntryPage(PhysicalAddr, 0);
676         }
677       MmDeleteRmap(PhysicalAddr, Process, Address);
678       MmReleasePageMemoryConsumer(MC_USER, PhysicalAddr);
679     }
680   else if (SwapEntry != 0)
681     {
682       MmFreeSwapPage(SwapEntry);
683     }
684 }
685
686 VOID
687 MmFreeVirtualMemory(PEPROCESS Process,
688                     PMEMORY_AREA MemoryArea)
689 {
690   PLIST_ENTRY current_entry;
691   PMM_REGION current;
692   ULONG i;
693   
694   DPRINT("MmFreeVirtualMemory(Process %p  MemoryArea %p)\n", Process, 
695          MemoryArea);
696   
697   /* Mark this memory area as about to be deleted. */
698   MemoryArea->DeleteInProgress = TRUE;
699
700   /* 
701    * Wait for any ongoing paging operations. Notice that since we have
702    * flagged this memory area as deleted no more page ops will be added.
703    */
704   if (MemoryArea->PageOpCount > 0)
705     {
706       for (i = 0; i < PAGE_ROUND_UP(MemoryArea->Length) / PAGE_SIZE; i++)
707         {
708           PMM_PAGEOP PageOp;
709
710           if (MemoryArea->PageOpCount == 0)
711             {
712               break;
713             }
714           
715           PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
716                                     MemoryArea->BaseAddress + (i * PAGE_SIZE),
717                                     NULL, 0);
718           if (PageOp != NULL)
719             {
720               NTSTATUS Status;
721               MmUnlockAddressSpace(&Process->AddressSpace);
722               Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
723                                              0,
724                                              KernelMode,
725                                              FALSE,
726                                              NULL);
727               if (Status != STATUS_SUCCESS)
728                 {
729                   DPRINT1("Failed to wait for page op\n");
730                   KEBUGCHECK(0);
731                 }
732               MmLockAddressSpace(&Process->AddressSpace);
733               MmReleasePageOp(PageOp);
734             }
735         }
736     }
737
738   /* Free all the individual segments. */
739   current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
740   while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
741     {
742       current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
743       current_entry = current_entry->Flink;
744       ExFreePool(current);
745     }
746
747   /* Actually free the memory area. */
748   MmFreeMemoryArea(&Process->AddressSpace,
749                    MemoryArea->BaseAddress,
750                    0,
751                    MmFreeVirtualMemoryPage,
752                    (PVOID)Process);
753 }
754
755 /*
756  * @unimplemented
757  */
758 NTSTATUS STDCALL 
759 NtFreeVirtualMemory(IN  HANDLE  ProcessHandle,
760                     IN  PVOID*  PBaseAddress,   
761                     IN  PULONG  PRegionSize,    
762                     IN  ULONG   FreeType)
763 /*
764  * FUNCTION: Frees a range of virtual memory
765  * ARGUMENTS:
766  *        ProcessHandle = Points to the process that allocated the virtual 
767  *                        memory
768  *        BaseAddress = Points to the memory address, rounded down to a 
769  *                      multiple of the pagesize
770  *        RegionSize = Limits the range to free, rounded up to a multiple of 
771  *                     the paging size
772  *        FreeType = Can be one of the values:  MEM_DECOMMIT, or MEM_RELEASE
773  * RETURNS: Status 
774  */
775 {
776    MEMORY_AREA* MemoryArea;
777    NTSTATUS Status;
778    PEPROCESS Process;
779    PMADDRESS_SPACE AddressSpace;
780    PVOID BaseAddress;
781    ULONG RegionSize;
782    
783    DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
784           "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
785           *PRegionSize,FreeType);
786                                  
787    BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
788    RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
789      PAGE_ROUND_DOWN((*PBaseAddress));
790    
791    Status = ObReferenceObjectByHandle(ProcessHandle,
792                                       PROCESS_VM_OPERATION,
793                                       PsProcessType,
794                                       UserMode,
795                                       (PVOID*)(&Process),
796                                       NULL);
797    if (!NT_SUCCESS(Status))
798      {
799         return(Status);
800      }
801    
802    AddressSpace = &Process->AddressSpace;
803    
804    MmLockAddressSpace(AddressSpace);
805    MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
806                                           BaseAddress);
807    if (MemoryArea == NULL)
808      {
809         MmUnlockAddressSpace(AddressSpace);
810         ObDereferenceObject(Process);
811         return(STATUS_UNSUCCESSFUL);
812      }
813    
814    switch (FreeType)
815      {
816       case MEM_RELEASE:
817         /* We can only free a memory area in one step. */
818         if (MemoryArea->BaseAddress != BaseAddress)
819           {
820              MmUnlockAddressSpace(AddressSpace);
821              ObDereferenceObject(Process);
822              return(STATUS_UNSUCCESSFUL);
823           }     
824         MmFreeVirtualMemory(Process, MemoryArea);
825         MmUnlockAddressSpace(AddressSpace);
826         ObDereferenceObject(Process);
827         return(STATUS_SUCCESS);
828         
829       case MEM_DECOMMIT:
830         Status = 
831           MmAlterRegion(AddressSpace,
832                         MemoryArea->BaseAddress,
833                         &MemoryArea->Data.VirtualMemoryData.RegionListHead,
834                         BaseAddress,
835                         RegionSize,
836                         MEM_RESERVE,
837                         PAGE_NOACCESS,
838                         MmModifyAttributes);
839         MmUnlockAddressSpace(AddressSpace);     
840         ObDereferenceObject(Process);
841         return(Status);
842      }
843    MmUnlockAddressSpace(AddressSpace);
844    ObDereferenceObject(Process);
845    return(STATUS_NOT_IMPLEMENTED);
846 }
847
848 NTSTATUS
849 MmProtectAnonMem(PMADDRESS_SPACE AddressSpace,
850                  PMEMORY_AREA MemoryArea,
851                  PVOID BaseAddress,
852                  ULONG Length,
853                  ULONG Protect,
854                  PULONG OldProtect)
855 {
856   PMM_REGION Region;
857   NTSTATUS Status;
858
859   Region = MmFindRegion(MemoryArea->BaseAddress, 
860                         &MemoryArea->Data.VirtualMemoryData.RegionListHead,
861                         BaseAddress, NULL);
862   *OldProtect = Region->Protect;
863   Status = MmAlterRegion(AddressSpace, MemoryArea->BaseAddress,
864                          &MemoryArea->Data.VirtualMemoryData.RegionListHead,
865                          BaseAddress, Length, Region->Type, Protect, 
866                          MmModifyAttributes);
867   return(Status);
868 }
869
870 NTSTATUS STDCALL
871 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
872                PVOID Address,
873                PMEMORY_BASIC_INFORMATION Info,
874                PULONG ResultLength)
875 {
876   PMM_REGION Region;
877   PVOID RegionBase;
878
879   Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
880
881   Region = MmFindRegion(MemoryArea->BaseAddress,
882                         &MemoryArea->Data.VirtualMemoryData.RegionListHead,
883                         Address, &RegionBase);
884   Info->AllocationBase = RegionBase;
885   Info->AllocationProtect = Region->Protect;  /* FIXME */
886   Info->RegionSize = RegionBase + Region->Length - Info->BaseAddress;
887   Info->State = Region->Type;
888   Info->Protect = Region->Protect;
889   Info->Type = MEM_PRIVATE;
890
891   *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
892   return(STATUS_SUCCESS);
893 }
894
895 /* EOF */