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