3 * Copyright (C) 2002-2005 ReactOS Team (and the authors from the programmers section)
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/anonmem.c
22 * PURPOSE: Implementing anonymous memory.
24 * PROGRAMMERS: David Welch
34 * Gunnar Andre' Dalsnes
36 * Thomas Weidenmueller
42 /* INCLUDE *****************************************************************/
46 #include <internal/debug.h>
48 /* FUNCTIONS *****************************************************************/
52 MmWritePageVirtualMemory(PMADDRESS_SPACE AddressSpace
,
53 PMEMORY_AREA MemoryArea
,
62 * Check for paging out from a deleted virtual memory area.
64 if (MemoryArea
->DeleteInProgress
)
66 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
67 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
68 MmReleasePageOp(PageOp
);
69 return(STATUS_UNSUCCESSFUL
);
72 Page
= MmGetPfnForProcess(AddressSpace
->Process
, Address
);
75 * Get that the page actually is dirty.
77 if (!MmIsDirtyPage(AddressSpace
->Process
, Address
))
79 PageOp
->Status
= STATUS_SUCCESS
;
80 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
81 MmReleasePageOp(PageOp
);
82 return(STATUS_SUCCESS
);
86 * Speculatively set the mapping to clean.
88 MmSetCleanPage(AddressSpace
->Process
, Address
);
91 * If necessary, allocate an entry in the paging file for this page
93 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
96 SwapEntry
= MmAllocSwapPage();
99 MmSetDirtyPage(AddressSpace
->Process
, Address
);
100 PageOp
->Status
= STATUS_PAGEFILE_QUOTA_EXCEEDED
;
101 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
102 MmReleasePageOp(PageOp
);
103 return(STATUS_PAGEFILE_QUOTA_EXCEEDED
);
108 * Write the page to the pagefile
110 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
111 if (!NT_SUCCESS(Status
))
113 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
115 MmSetDirtyPage(AddressSpace
->Process
, Address
);
116 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
117 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
118 MmReleasePageOp(PageOp
);
119 return(STATUS_UNSUCCESSFUL
);
123 * Otherwise we have succeeded.
125 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
126 PageOp
->Status
= STATUS_SUCCESS
;
127 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
128 MmReleasePageOp(PageOp
);
129 return(STATUS_SUCCESS
);
134 MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace
,
135 PMEMORY_AREA MemoryArea
,
144 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
145 Address
, AddressSpace
->Process
->UniqueProcessId
);
148 * Check for paging out from a deleted virtual memory area.
150 if (MemoryArea
->DeleteInProgress
)
152 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
153 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
154 MmReleasePageOp(PageOp
);
155 return(STATUS_UNSUCCESSFUL
);
159 * Disable the virtual mapping.
161 MmDisableVirtualMapping(AddressSpace
->Process
, Address
,
170 * Paging out non-dirty data is easy.
174 MmLockAddressSpace(AddressSpace
);
175 MmDeleteVirtualMapping(AddressSpace
->Process
, Address
, FALSE
, NULL
, NULL
);
176 MmDeleteAllRmaps(Page
, NULL
, NULL
);
177 if ((SwapEntry
= MmGetSavedSwapEntryPage(Page
)) != 0)
179 MmCreatePageFileMapping(AddressSpace
->Process
, Address
, SwapEntry
);
180 MmSetSavedSwapEntryPage(Page
, 0);
182 MmUnlockAddressSpace(AddressSpace
);
183 MmReleasePageMemoryConsumer(MC_USER
, Page
);
184 PageOp
->Status
= STATUS_SUCCESS
;
185 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
186 MmReleasePageOp(PageOp
);
187 return(STATUS_SUCCESS
);
191 * If necessary, allocate an entry in the paging file for this page
193 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
196 SwapEntry
= MmAllocSwapPage();
199 MmShowOutOfSpaceMessagePagingFile();
200 MmEnableVirtualMapping(AddressSpace
->Process
, Address
);
201 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
202 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
203 MmReleasePageOp(PageOp
);
204 return(STATUS_PAGEFILE_QUOTA
);
209 * Write the page to the pagefile
211 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
212 if (!NT_SUCCESS(Status
))
214 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
216 MmEnableVirtualMapping(AddressSpace
->Process
, Address
);
217 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
218 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
219 MmReleasePageOp(PageOp
);
220 return(STATUS_UNSUCCESSFUL
);
224 * Otherwise we have succeeded, free the page
226 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page
<< PAGE_SHIFT
);
227 MmLockAddressSpace(AddressSpace
);
228 MmDeleteVirtualMapping(AddressSpace
->Process
, Address
, FALSE
, NULL
, NULL
);
229 MmCreatePageFileMapping(AddressSpace
->Process
, Address
, SwapEntry
);
230 MmUnlockAddressSpace(AddressSpace
);
231 MmDeleteAllRmaps(Page
, NULL
, NULL
);
232 MmSetSavedSwapEntryPage(Page
, 0);
233 MmReleasePageMemoryConsumer(MC_USER
, Page
);
234 PageOp
->Status
= STATUS_SUCCESS
;
235 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
236 MmReleasePageOp(PageOp
);
237 return(STATUS_SUCCESS
);
242 MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace
,
243 MEMORY_AREA
* MemoryArea
,
247 * FUNCTION: Move data into memory to satisfy a page not present fault
249 * AddressSpace = Address space within which the fault occurred
250 * MemoryArea = The memory area within which the fault occurred
251 * Address = The absolute address of fault
253 * NOTES: This function is called with the address space lock held.
262 * There is a window between taking the page fault and locking the
263 * address space when another thread could load the page so we check
266 if (MmIsPagePresent(NULL
, Address
))
270 MmLockPage(MmGetPfnForProcess(NULL
, Address
));
272 return(STATUS_SUCCESS
);
276 * Check for the virtual memory area being deleted.
278 if (MemoryArea
->DeleteInProgress
)
280 return(STATUS_UNSUCCESSFUL
);
284 * Get the segment corresponding to the virtual address
286 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
287 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
289 if (Region
->Type
== MEM_RESERVE
|| Region
->Protect
== PAGE_NOACCESS
)
291 return(STATUS_ACCESS_VIOLATION
);
295 * Get or create a page operation
297 PageOp
= MmGetPageOp(MemoryArea
, AddressSpace
->Process
->UniqueProcessId
,
298 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
299 MM_PAGEOP_PAGEIN
, FALSE
);
302 DPRINT1("MmGetPageOp failed");
307 * Check if someone else is already handling this fault, if so wait
310 if (PageOp
->Thread
!= PsGetCurrentThread())
312 MmUnlockAddressSpace(AddressSpace
);
313 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
319 * Check for various strange conditions
321 if (Status
!= STATUS_SUCCESS
)
323 DPRINT1("Failed to wait for page op\n");
326 if (PageOp
->Status
== STATUS_PENDING
)
328 DPRINT1("Woke for page op before completion\n");
332 * If this wasn't a pagein then we need to restart the handling
334 if (PageOp
->OpType
!= MM_PAGEOP_PAGEIN
)
336 MmLockAddressSpace(AddressSpace
);
337 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
338 MmReleasePageOp(PageOp
);
339 return(STATUS_MM_RESTART_OPERATION
);
342 * If the thread handling this fault has failed then we don't retry
344 if (!NT_SUCCESS(PageOp
->Status
))
346 MmLockAddressSpace(AddressSpace
);
347 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
348 Status
= PageOp
->Status
;
349 MmReleasePageOp(PageOp
);
352 MmLockAddressSpace(AddressSpace
);
355 MmLockPage(MmGetPfnForProcess(NULL
, Address
));
357 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
358 MmReleasePageOp(PageOp
);
359 return(STATUS_SUCCESS
);
363 * Try to allocate a page
365 Status
= MmRequestPageMemoryConsumer(MC_USER
, FALSE
, &Page
);
366 if (Status
== STATUS_NO_MEMORY
)
368 MmUnlockAddressSpace(AddressSpace
);
369 Status
= MmRequestPageMemoryConsumer(MC_USER
, TRUE
, &Page
);
370 MmLockAddressSpace(AddressSpace
);
372 if (!NT_SUCCESS(Status
))
374 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status
);
379 * Handle swapped out pages.
381 if (MmIsPageSwapEntry(NULL
, Address
))
385 MmDeletePageFileMapping(AddressSpace
->Process
, Address
, &SwapEntry
);
386 Status
= MmReadFromSwapPage(SwapEntry
, Page
);
387 if (!NT_SUCCESS(Status
))
391 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
395 * Set the page. If we fail because we are out of memory then
398 Status
= MmCreateVirtualMapping(AddressSpace
->Process
,
399 (PVOID
)PAGE_ROUND_DOWN(Address
),
403 while (Status
== STATUS_NO_MEMORY
)
405 MmUnlockAddressSpace(AddressSpace
);
406 Status
= MmCreateVirtualMapping(AddressSpace
->Process
,
411 MmLockAddressSpace(AddressSpace
);
413 if (!NT_SUCCESS(Status
))
415 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
421 * Add the page to the process's working set
423 MmInsertRmap(Page
, AddressSpace
->Process
, (PVOID
)PAGE_ROUND_DOWN(Address
));
426 * Finish the operation
432 PageOp
->Status
= STATUS_SUCCESS
;
433 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
434 MmReleasePageOp(PageOp
);
435 return(STATUS_SUCCESS
);
439 MmModifyAttributes(PMADDRESS_SPACE AddressSpace
,
447 * FUNCTION: Modify the attributes of a memory region
451 * If we are switching a previously committed region to reserved then
452 * free any allocated pages within the region
454 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
458 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
462 if (MmIsPageSwapEntry(AddressSpace
->Process
,
463 (char*)BaseAddress
+ (i
* PAGE_SIZE
)))
467 MmDeletePageFileMapping(AddressSpace
->Process
,
468 (char*)BaseAddress
+ (i
* PAGE_SIZE
),
470 MmFreeSwapPage(SwapEntry
);
474 MmDeleteVirtualMapping(AddressSpace
->Process
,
475 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
479 SWAPENTRY SavedSwapEntry
;
480 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
481 if (SavedSwapEntry
!= 0)
483 MmFreeSwapPage(SavedSwapEntry
);
484 MmSetSavedSwapEntryPage(Page
, 0);
486 MmDeleteRmap(Page
, AddressSpace
->Process
,
487 (char*)BaseAddress
+ (i
* PAGE_SIZE
));
488 MmReleasePageMemoryConsumer(MC_USER
, Page
);
495 * If we are changing the protection attributes of a committed region then
496 * alter the attributes for any allocated pages within the region
498 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
499 OldProtect
!= NewProtect
)
503 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
505 if (MmIsPagePresent(AddressSpace
->Process
,
506 (char*)BaseAddress
+ (i
*PAGE_SIZE
)))
508 MmSetPageProtect(AddressSpace
->Process
,
509 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
520 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
521 IN OUT PVOID
* UBaseAddress
,
523 IN OUT PULONG URegionSize
,
524 IN ULONG AllocationType
,
527 * FUNCTION: Allocates a block of virtual memory in the process address space
529 * ProcessHandle = The handle of the process which owns the virtual memory
530 * BaseAddress = A pointer to the virtual memory allocated. If you
531 * supply a non zero value the system will try to
532 * allocate the memory at the address supplied. It round
533 * it down to a multiple of the page size.
534 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
535 * that must be zero, ensuring that the memory will be
536 * allocated at a address below a certain value.
537 * RegionSize = The number of bytes to allocate
538 * AllocationType = Indicates the type of virtual memory you like to
539 * allocated, can be a combination of MEM_COMMIT,
540 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
541 * Protect = Indicates the protection type of the pages allocated, can be
542 * a combination of PAGE_READONLY, PAGE_READWRITE,
543 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
549 MEMORY_AREA
* MemoryArea
;
550 ULONG_PTR MemoryAreaLength
;
553 PMADDRESS_SPACE AddressSpace
;
558 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
560 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
561 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
562 *UBaseAddress
,ZeroBits
,*URegionSize
,AllocationType
,
565 /* Check for valid protection flags */
566 if ((Protect
& PAGE_FLAGS_VALID_FROM_USER_MODE
) != Protect
)
568 DPRINT1("Invalid page protection\n");
569 return STATUS_INVALID_PAGE_PROTECTION
;
572 /* Check for valid Zero bits */
575 DPRINT1("Too many zero bits\n");
576 return STATUS_INVALID_PARAMETER_3
;
579 /* Check for valid Allocation Types */
580 if ((AllocationType
&~ (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
581 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
583 DPRINT1("Invalid Allocation Type\n");
584 return STATUS_INVALID_PARAMETER_5
;
587 /* Check for at least one of these Allocation Types to be set */
588 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
590 DPRINT1("No memory allocation base type\n");
591 return STATUS_INVALID_PARAMETER_5
;
594 /* MEM_RESET is an exclusive flag, make sure that is valid too */
595 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
597 DPRINT1("MEM_RESET used illegaly\n");
598 return STATUS_INVALID_PARAMETER_5
;
601 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
602 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
604 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
605 return STATUS_INVALID_PARAMETER_5
;
608 /* MEM_PHYSICAL can only be used with MEM_RESERVE, and can only be R/W */
609 if (AllocationType
& MEM_PHYSICAL
)
611 /* First check for MEM_RESERVE exclusivity */
612 if (AllocationType
!= (MEM_RESERVE
| MEM_PHYSICAL
))
614 DPRINT1("MEM_PHYSICAL used with other flags then MEM_RESERVE or"
615 "MEM_RESERVE was not present at all\n");
616 return STATUS_INVALID_PARAMETER_5
;
619 /* Then make sure PAGE_READWRITE is used */
620 if (Protect
!= PAGE_READWRITE
)
622 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
623 return STATUS_INVALID_PAGE_PROTECTION
;
627 PBaseAddress
= *UBaseAddress
;
628 PRegionSize
= *URegionSize
;
629 BoundaryAddressMultiple
.QuadPart
= 0;
631 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
632 RegionSize
= PAGE_ROUND_UP(PBaseAddress
+ PRegionSize
) -
633 PAGE_ROUND_DOWN(PBaseAddress
);
636 * We've captured and calculated the data, now do more checks
637 * Yes, MmCreateMemoryArea does similar checks, but they don't return
638 * the right status codes that a caller of this routine would expect.
640 if (BaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
642 DPRINT1("Virtual allocation above User Space\n");
643 return STATUS_INVALID_PARAMETER_2
;
647 DPRINT1("Region size is invalid\n");
648 return STATUS_INVALID_PARAMETER_4
;
650 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
652 DPRINT1("Region size would overflow into kernel-memory\n");
653 return STATUS_INVALID_PARAMETER_4
;
657 * Copy on Write is reserved for system use. This case is a certain failure
658 * but there may be other cases...needs more testing
660 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
661 ((Protect
& PAGE_WRITECOPY
) || (Protect
& PAGE_EXECUTE_WRITECOPY
)))
663 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
664 return STATUS_INVALID_PAGE_PROTECTION
;
668 Status
= ObReferenceObjectByHandle(ProcessHandle
,
669 PROCESS_VM_OPERATION
,
674 if (!NT_SUCCESS(Status
))
676 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
680 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
681 DPRINT("Type %x\n", Type
);
683 AddressSpace
= &Process
->AddressSpace
;
684 MmLockAddressSpace(AddressSpace
);
686 if (PBaseAddress
!= 0)
688 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
690 if (MemoryArea
!= NULL
)
692 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
693 (ULONG_PTR
)MemoryArea
->StartingAddress
;
694 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
695 MemoryAreaLength
>= RegionSize
)
698 MmAlterRegion(AddressSpace
,
699 MemoryArea
->StartingAddress
,
700 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
701 BaseAddress
, RegionSize
,
702 Type
, Protect
, MmModifyAttributes
);
703 MmUnlockAddressSpace(AddressSpace
);
704 ObDereferenceObject(Process
);
705 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
708 else if (MemoryAreaLength
>= RegionSize
)
711 MmAlterRegion(AddressSpace
,
712 MemoryArea
->StartingAddress
,
713 &MemoryArea
->Data
.SectionData
.RegionListHead
,
714 BaseAddress
, RegionSize
,
715 Type
, Protect
, MmModifyAttributes
);
716 MmUnlockAddressSpace(AddressSpace
);
717 ObDereferenceObject(Process
);
718 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
723 MmUnlockAddressSpace(AddressSpace
);
724 ObDereferenceObject(Process
);
725 return(STATUS_UNSUCCESSFUL
);
730 Status
= MmCreateMemoryArea(AddressSpace
,
731 MEMORY_AREA_VIRTUAL_MEMORY
,
737 AllocationType
& MEM_TOP_DOWN
,
738 BoundaryAddressMultiple
);
739 if (!NT_SUCCESS(Status
))
741 MmUnlockAddressSpace(AddressSpace
);
742 ObDereferenceObject(Process
);
743 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
747 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
748 (ULONG_PTR
)MemoryArea
->StartingAddress
;
750 MmInitializeRegion(&MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
751 MemoryAreaLength
, Type
, Protect
);
753 if ((AllocationType
& MEM_COMMIT
) &&
754 ((Protect
& PAGE_READWRITE
) ||
755 (Protect
& PAGE_EXECUTE_READWRITE
)))
757 MmReserveSwapPages(MemoryAreaLength
);
760 *UBaseAddress
= BaseAddress
;
761 *URegionSize
= MemoryAreaLength
;
762 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
764 MmUnlockAddressSpace(AddressSpace
);
765 ObDereferenceObject(Process
);
766 return(STATUS_SUCCESS
);
770 MmFreeVirtualMemoryPage(PVOID Context
,
771 MEMORY_AREA
* MemoryArea
,
777 PEPROCESS Process
= (PEPROCESS
)Context
;
781 SWAPENTRY SavedSwapEntry
;
782 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
783 if (SavedSwapEntry
!= 0)
785 MmFreeSwapPage(SavedSwapEntry
);
786 MmSetSavedSwapEntryPage(Page
, 0);
788 MmDeleteRmap(Page
, Process
, Address
);
789 MmReleasePageMemoryConsumer(MC_USER
, Page
);
791 else if (SwapEntry
!= 0)
793 MmFreeSwapPage(SwapEntry
);
799 MmFreeVirtualMemory(PEPROCESS Process
,
800 PMEMORY_AREA MemoryArea
)
802 PLIST_ENTRY current_entry
;
806 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process
,
809 /* Mark this memory area as about to be deleted. */
810 MemoryArea
->DeleteInProgress
= TRUE
;
813 * Wait for any ongoing paging operations. Notice that since we have
814 * flagged this memory area as deleted no more page ops will be added.
816 if (MemoryArea
->PageOpCount
> 0)
818 ULONG_PTR MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
819 (ULONG_PTR
)MemoryArea
->StartingAddress
;
821 /* FiN TODO: Optimize loop counter! */
822 for (i
= 0; i
< PAGE_ROUND_UP(MemoryAreaLength
) / PAGE_SIZE
; i
++)
826 if (MemoryArea
->PageOpCount
== 0)
831 PageOp
= MmCheckForPageOp(MemoryArea
, Process
->UniqueProcessId
,
832 (PVOID
)((ULONG_PTR
)MemoryArea
->StartingAddress
+ (i
* PAGE_SIZE
)),
837 MmUnlockAddressSpace(&Process
->AddressSpace
);
838 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
843 if (Status
!= STATUS_SUCCESS
)
845 DPRINT1("Failed to wait for page op\n");
848 MmLockAddressSpace(&Process
->AddressSpace
);
849 MmReleasePageOp(PageOp
);
854 /* Free all the individual segments. */
855 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
.Flink
;
856 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
)
858 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
859 current_entry
= current_entry
->Flink
;
863 /* Actually free the memory area. */
864 MmFreeMemoryArea(&Process
->AddressSpace
,
866 MmFreeVirtualMemoryPage
,
874 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
875 IN PVOID
* PBaseAddress
,
876 IN PULONG PRegionSize
,
879 * FUNCTION: Frees a range of virtual memory
881 * ProcessHandle = Points to the process that allocated the virtual
883 * BaseAddress = Points to the memory address, rounded down to a
884 * multiple of the pagesize
885 * RegionSize = Limits the range to free, rounded up to a multiple of
887 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
891 MEMORY_AREA
* MemoryArea
;
894 PMADDRESS_SPACE AddressSpace
;
898 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
899 "*PRegionSize %x, FreeType %x)\n",ProcessHandle
,*PBaseAddress
,
900 *PRegionSize
,FreeType
);
902 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((*PBaseAddress
));
903 RegionSize
= PAGE_ROUND_UP((*PBaseAddress
) + (*PRegionSize
)) -
904 PAGE_ROUND_DOWN((*PBaseAddress
));
906 Status
= ObReferenceObjectByHandle(ProcessHandle
,
907 PROCESS_VM_OPERATION
,
912 if (!NT_SUCCESS(Status
))
917 AddressSpace
= &Process
->AddressSpace
;
919 MmLockAddressSpace(AddressSpace
);
920 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
921 if (MemoryArea
== NULL
)
923 MmUnlockAddressSpace(AddressSpace
);
924 ObDereferenceObject(Process
);
925 return(STATUS_UNSUCCESSFUL
);
931 /* We can only free a memory area in one step. */
932 if (MemoryArea
->StartingAddress
!= BaseAddress
||
933 MemoryArea
->Type
!= MEMORY_AREA_VIRTUAL_MEMORY
)
935 MmUnlockAddressSpace(AddressSpace
);
936 ObDereferenceObject(Process
);
937 return(STATUS_UNSUCCESSFUL
);
939 MmFreeVirtualMemory(Process
, MemoryArea
);
940 MmUnlockAddressSpace(AddressSpace
);
941 ObDereferenceObject(Process
);
942 return(STATUS_SUCCESS
);
946 MmAlterRegion(AddressSpace
,
947 MemoryArea
->StartingAddress
,
948 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
954 MmUnlockAddressSpace(AddressSpace
);
955 ObDereferenceObject(Process
);
958 MmUnlockAddressSpace(AddressSpace
);
959 ObDereferenceObject(Process
);
960 return(STATUS_NOT_IMPLEMENTED
);
965 MmProtectAnonMem(PMADDRESS_SPACE AddressSpace
,
966 PMEMORY_AREA MemoryArea
,
975 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
976 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
978 *OldProtect
= Region
->Protect
;
979 Status
= MmAlterRegion(AddressSpace
, MemoryArea
->StartingAddress
,
980 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
981 BaseAddress
, Length
, Region
->Type
, Protect
,
987 MmQueryAnonMem(PMEMORY_AREA MemoryArea
,
989 PMEMORY_BASIC_INFORMATION Info
,
993 PVOID RegionBase
= NULL
;
995 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
997 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
998 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
999 Address
, &RegionBase
);
1000 Info
->BaseAddress
= RegionBase
;
1001 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
1002 Info
->AllocationProtect
= MemoryArea
->Protect
;
1003 Info
->RegionSize
= Region
->Length
;
1004 Info
->State
= Region
->Type
;
1005 Info
->Protect
= Region
->Protect
;
1006 Info
->Type
= MEM_PRIVATE
;
1008 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1009 return(STATUS_SUCCESS
);