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
33 * Gunnar Andre' Dalsnes
35 * Thomas Weidenmueller
41 /* INCLUDE *****************************************************************/
45 #include <internal/debug.h>
47 /* FUNCTIONS *****************************************************************/
51 MmWritePageVirtualMemory(PMADDRESS_SPACE AddressSpace
,
52 PMEMORY_AREA MemoryArea
,
61 * Check for paging out from a deleted virtual memory area.
63 if (MemoryArea
->DeleteInProgress
)
65 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
66 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
67 MmReleasePageOp(PageOp
);
68 return(STATUS_UNSUCCESSFUL
);
71 Page
= MmGetPfnForProcess(AddressSpace
->Process
, Address
);
74 * Get that the page actually is dirty.
76 if (!MmIsDirtyPage(AddressSpace
->Process
, Address
))
78 PageOp
->Status
= STATUS_SUCCESS
;
79 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
80 MmReleasePageOp(PageOp
);
81 return(STATUS_SUCCESS
);
85 * Speculatively set the mapping to clean.
87 MmSetCleanPage(AddressSpace
->Process
, Address
);
90 * If necessary, allocate an entry in the paging file for this page
92 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
95 SwapEntry
= MmAllocSwapPage();
98 MmSetDirtyPage(AddressSpace
->Process
, Address
);
99 PageOp
->Status
= STATUS_PAGEFILE_QUOTA_EXCEEDED
;
100 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
101 MmReleasePageOp(PageOp
);
102 return(STATUS_PAGEFILE_QUOTA_EXCEEDED
);
107 * Write the page to the pagefile
109 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
110 if (!NT_SUCCESS(Status
))
112 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
114 MmSetDirtyPage(AddressSpace
->Process
, Address
);
115 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
116 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
117 MmReleasePageOp(PageOp
);
118 return(STATUS_UNSUCCESSFUL
);
122 * Otherwise we have succeeded.
124 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
125 PageOp
->Status
= STATUS_SUCCESS
;
126 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
127 MmReleasePageOp(PageOp
);
128 return(STATUS_SUCCESS
);
133 MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace
,
134 PMEMORY_AREA MemoryArea
,
143 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
144 Address
, AddressSpace
->Process
->UniqueProcessId
);
147 * Check for paging out from a deleted virtual memory area.
149 if (MemoryArea
->DeleteInProgress
)
151 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
152 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
153 MmReleasePageOp(PageOp
);
154 return(STATUS_UNSUCCESSFUL
);
158 * Disable the virtual mapping.
160 MmDisableVirtualMapping(AddressSpace
->Process
, Address
,
169 * Paging out non-dirty data is easy.
173 MmLockAddressSpace(AddressSpace
);
174 MmDeleteVirtualMapping(AddressSpace
->Process
, Address
, FALSE
, NULL
, NULL
);
175 MmDeleteAllRmaps(Page
, NULL
, NULL
);
176 if ((SwapEntry
= MmGetSavedSwapEntryPage(Page
)) != 0)
178 MmCreatePageFileMapping(AddressSpace
->Process
, Address
, SwapEntry
);
179 MmSetSavedSwapEntryPage(Page
, 0);
181 MmUnlockAddressSpace(AddressSpace
);
182 MmReleasePageMemoryConsumer(MC_USER
, Page
);
183 PageOp
->Status
= STATUS_SUCCESS
;
184 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
185 MmReleasePageOp(PageOp
);
186 return(STATUS_SUCCESS
);
190 * If necessary, allocate an entry in the paging file for this page
192 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
195 SwapEntry
= MmAllocSwapPage();
198 MmShowOutOfSpaceMessagePagingFile();
199 MmEnableVirtualMapping(AddressSpace
->Process
, Address
);
200 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
201 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
202 MmReleasePageOp(PageOp
);
203 return(STATUS_PAGEFILE_QUOTA
);
208 * Write the page to the pagefile
210 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
211 if (!NT_SUCCESS(Status
))
213 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
215 MmEnableVirtualMapping(AddressSpace
->Process
, Address
);
216 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
217 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
218 MmReleasePageOp(PageOp
);
219 return(STATUS_UNSUCCESSFUL
);
223 * Otherwise we have succeeded, free the page
225 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page
<< PAGE_SHIFT
);
226 MmLockAddressSpace(AddressSpace
);
227 MmDeleteVirtualMapping(AddressSpace
->Process
, Address
, FALSE
, NULL
, NULL
);
228 MmCreatePageFileMapping(AddressSpace
->Process
, Address
, SwapEntry
);
229 MmUnlockAddressSpace(AddressSpace
);
230 MmDeleteAllRmaps(Page
, NULL
, NULL
);
231 MmSetSavedSwapEntryPage(Page
, 0);
232 MmReleasePageMemoryConsumer(MC_USER
, Page
);
233 PageOp
->Status
= STATUS_SUCCESS
;
234 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
235 MmReleasePageOp(PageOp
);
236 return(STATUS_SUCCESS
);
241 MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace
,
242 MEMORY_AREA
* MemoryArea
,
246 * FUNCTION: Move data into memory to satisfy a page not present fault
248 * AddressSpace = Address space within which the fault occurred
249 * MemoryArea = The memory area within which the fault occurred
250 * Address = The absolute address of fault
252 * NOTES: This function is called with the address space lock held.
261 * There is a window between taking the page fault and locking the
262 * address space when another thread could load the page so we check
265 if (MmIsPagePresent(NULL
, Address
))
269 MmLockPage(MmGetPfnForProcess(NULL
, Address
));
271 return(STATUS_SUCCESS
);
275 * Check for the virtual memory area being deleted.
277 if (MemoryArea
->DeleteInProgress
)
279 return(STATUS_UNSUCCESSFUL
);
283 * Get the segment corresponding to the virtual address
285 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
286 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
288 if (Region
->Type
== MEM_RESERVE
|| Region
->Protect
== PAGE_NOACCESS
)
290 return(STATUS_ACCESS_VIOLATION
);
294 * Get or create a page operation
296 PageOp
= MmGetPageOp(MemoryArea
, AddressSpace
->Process
->UniqueProcessId
,
297 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
298 MM_PAGEOP_PAGEIN
, FALSE
);
301 DPRINT1("MmGetPageOp failed");
306 * Check if someone else is already handling this fault, if so wait
309 if (PageOp
->Thread
!= PsGetCurrentThread())
311 MmUnlockAddressSpace(AddressSpace
);
312 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
318 * Check for various strange conditions
320 if (Status
!= STATUS_SUCCESS
)
322 DPRINT1("Failed to wait for page op\n");
325 if (PageOp
->Status
== STATUS_PENDING
)
327 DPRINT1("Woke for page op before completion\n");
331 * If this wasn't a pagein then we need to restart the handling
333 if (PageOp
->OpType
!= MM_PAGEOP_PAGEIN
)
335 MmLockAddressSpace(AddressSpace
);
336 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
337 MmReleasePageOp(PageOp
);
338 return(STATUS_MM_RESTART_OPERATION
);
341 * If the thread handling this fault has failed then we don't retry
343 if (!NT_SUCCESS(PageOp
->Status
))
345 MmLockAddressSpace(AddressSpace
);
346 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
347 Status
= PageOp
->Status
;
348 MmReleasePageOp(PageOp
);
351 MmLockAddressSpace(AddressSpace
);
354 MmLockPage(MmGetPfnForProcess(NULL
, Address
));
356 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
357 MmReleasePageOp(PageOp
);
358 return(STATUS_SUCCESS
);
362 * Try to allocate a page
364 Status
= MmRequestPageMemoryConsumer(MC_USER
, FALSE
, &Page
);
365 if (Status
== STATUS_NO_MEMORY
)
367 MmUnlockAddressSpace(AddressSpace
);
368 Status
= MmRequestPageMemoryConsumer(MC_USER
, TRUE
, &Page
);
369 MmLockAddressSpace(AddressSpace
);
371 if (!NT_SUCCESS(Status
))
373 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status
);
378 * Handle swapped out pages.
380 if (MmIsPageSwapEntry(NULL
, Address
))
384 MmDeletePageFileMapping(AddressSpace
->Process
, Address
, &SwapEntry
);
385 Status
= MmReadFromSwapPage(SwapEntry
, Page
);
386 if (!NT_SUCCESS(Status
))
390 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
394 * Set the page. If we fail because we are out of memory then
397 Status
= MmCreateVirtualMapping(AddressSpace
->Process
,
398 (PVOID
)PAGE_ROUND_DOWN(Address
),
402 while (Status
== STATUS_NO_MEMORY
)
404 MmUnlockAddressSpace(AddressSpace
);
405 Status
= MmCreateVirtualMapping(AddressSpace
->Process
,
410 MmLockAddressSpace(AddressSpace
);
412 if (!NT_SUCCESS(Status
))
414 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
420 * Add the page to the process's working set
422 MmInsertRmap(Page
, AddressSpace
->Process
, (PVOID
)PAGE_ROUND_DOWN(Address
));
425 * Finish the operation
431 PageOp
->Status
= STATUS_SUCCESS
;
432 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
433 MmReleasePageOp(PageOp
);
434 return(STATUS_SUCCESS
);
438 MmModifyAttributes(PMADDRESS_SPACE AddressSpace
,
446 * FUNCTION: Modify the attributes of a memory region
450 * If we are switching a previously committed region to reserved then
451 * free any allocated pages within the region
453 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
457 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
461 if (MmIsPageSwapEntry(AddressSpace
->Process
,
462 (char*)BaseAddress
+ (i
* PAGE_SIZE
)))
466 MmDeletePageFileMapping(AddressSpace
->Process
,
467 (char*)BaseAddress
+ (i
* PAGE_SIZE
),
469 MmFreeSwapPage(SwapEntry
);
473 MmDeleteVirtualMapping(AddressSpace
->Process
,
474 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
478 SWAPENTRY SavedSwapEntry
;
479 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
480 if (SavedSwapEntry
!= 0)
482 MmFreeSwapPage(SavedSwapEntry
);
483 MmSetSavedSwapEntryPage(Page
, 0);
485 MmDeleteRmap(Page
, AddressSpace
->Process
,
486 (char*)BaseAddress
+ (i
* PAGE_SIZE
));
487 MmReleasePageMemoryConsumer(MC_USER
, Page
);
494 * If we are changing the protection attributes of a committed region then
495 * alter the attributes for any allocated pages within the region
497 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
498 OldProtect
!= NewProtect
)
502 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
504 if (MmIsPagePresent(AddressSpace
->Process
,
505 (char*)BaseAddress
+ (i
*PAGE_SIZE
)))
507 MmSetPageProtect(AddressSpace
->Process
,
508 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
519 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
520 IN OUT PVOID
* UBaseAddress
,
522 IN OUT PULONG URegionSize
,
523 IN ULONG AllocationType
,
526 * FUNCTION: Allocates a block of virtual memory in the process address space
528 * ProcessHandle = The handle of the process which owns the virtual memory
529 * BaseAddress = A pointer to the virtual memory allocated. If you
530 * supply a non zero value the system will try to
531 * allocate the memory at the address supplied. It round
532 * it down to a multiple of the page size.
533 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
534 * that must be zero, ensuring that the memory will be
535 * allocated at a address below a certain value.
536 * RegionSize = The number of bytes to allocate
537 * AllocationType = Indicates the type of virtual memory you like to
538 * allocated, can be a combination of MEM_COMMIT,
539 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
540 * Protect = Indicates the protection type of the pages allocated, can be
541 * a combination of PAGE_READONLY, PAGE_READWRITE,
542 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
545 * NOTES: Must run at IRQL PASSIVE_LEVEL? (or is APC_LEVEL cool too?)
546 * MSDN states that ZwAllocateVirtualMemory IRQL must be PASSIVE_LEVEL,
547 * but why wouldn't APC_LEVEL be valid (or is that only for the Zw* version
548 * and Nt* can indeed run at APC_LEVEL?)
552 MEMORY_AREA
* MemoryArea
;
553 ULONG_PTR MemoryAreaLength
;
556 PMADDRESS_SPACE AddressSpace
;
561 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
562 KPROCESSOR_MODE PreviousMode
;
564 // TMN: Someone Pick one of these. Until it's clear which
565 // level is allowed, I play it safe and check for <= APC_LEVEL
567 // ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
569 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
570 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
571 *UBaseAddress
,ZeroBits
,*URegionSize
,AllocationType
,
574 /* Check for valid protection flags */
575 if ((Protect
& PAGE_FLAGS_VALID_FROM_USER_MODE
) != Protect
)
577 DPRINT1("Invalid page protection\n");
578 return STATUS_INVALID_PAGE_PROTECTION
;
581 /* Check for valid Zero bits */
584 DPRINT1("Too many zero bits\n");
585 return STATUS_INVALID_PARAMETER_3
;
588 /* Check for valid Allocation Types */
589 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
590 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
592 DPRINT1("Invalid Allocation Type\n");
593 return STATUS_INVALID_PARAMETER_5
;
596 /* Check for at least one of these Allocation Types to be set */
597 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
599 DPRINT1("No memory allocation base type\n");
600 return STATUS_INVALID_PARAMETER_5
;
603 /* MEM_RESET is an exclusive flag, make sure that is valid too */
604 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
606 DPRINT1("Invalid use of MEM_RESET\n");
607 return STATUS_INVALID_PARAMETER_5
;
610 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
611 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
613 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
614 return STATUS_INVALID_PARAMETER_5
;
617 /* MEM_PHYSICAL can only be used with MEM_RESERVE, and can only be R/W */
618 if (AllocationType
& MEM_PHYSICAL
)
620 /* First check for MEM_RESERVE exclusivity */
621 if (AllocationType
!= (MEM_RESERVE
| MEM_PHYSICAL
))
623 DPRINT1("MEM_PHYSICAL used with other flags then MEM_RESERVE or"
624 "MEM_RESERVE was not present at all\n");
625 return STATUS_INVALID_PARAMETER_5
;
628 /* Then make sure PAGE_READWRITE is used */
629 if (Protect
!= PAGE_READWRITE
)
631 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
632 return STATUS_INVALID_PAGE_PROTECTION
;
636 PreviousMode
= KeGetPreviousMode();
640 if (PreviousMode
!= KernelMode
)
642 ProbeForWritePointer(UBaseAddress
);
643 ProbeForWriteUlong(URegionSize
);
645 PBaseAddress
= *UBaseAddress
;
646 PRegionSize
= *URegionSize
;
650 /* Get the exception code */
651 Status
= _SEH_GetExceptionCode();
652 _SEH_YIELD(return Status
);
656 BoundaryAddressMultiple
.QuadPart
= 0;
658 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
659 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)PBaseAddress
+ PRegionSize
) -
660 PAGE_ROUND_DOWN(PBaseAddress
);
663 * We've captured and calculated the data, now do more checks
664 * Yes, MmCreateMemoryArea does similar checks, but they don't return
665 * the right status codes that a caller of this routine would expect.
667 if (BaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
669 DPRINT1("Virtual allocation base above User Space\n");
670 return STATUS_INVALID_PARAMETER_2
;
674 DPRINT1("Region size is invalid (zero)\n");
675 return STATUS_INVALID_PARAMETER_4
;
677 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
679 DPRINT1("Region size would overflow into kernel-memory\n");
680 return STATUS_INVALID_PARAMETER_4
;
684 * Copy on Write is reserved for system use. This case is a certain failure
685 * but there may be other cases...needs more testing
687 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
688 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
)))
690 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
691 return STATUS_INVALID_PAGE_PROTECTION
;
695 Status
= ObReferenceObjectByHandle(ProcessHandle
,
696 PROCESS_VM_OPERATION
,
701 if (!NT_SUCCESS(Status
))
703 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
707 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
708 DPRINT("Type %x\n", Type
);
710 AddressSpace
= (PMADDRESS_SPACE
)&Process
->VadRoot
;
711 MmLockAddressSpace(AddressSpace
);
713 if (PBaseAddress
!= 0)
715 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
717 if (MemoryArea
!= NULL
)
719 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
720 (ULONG_PTR
)MemoryArea
->StartingAddress
;
721 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
722 MemoryAreaLength
>= RegionSize
)
725 MmAlterRegion(AddressSpace
,
726 MemoryArea
->StartingAddress
,
727 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
728 BaseAddress
, RegionSize
,
729 Type
, Protect
, MmModifyAttributes
);
730 MmUnlockAddressSpace(AddressSpace
);
731 ObDereferenceObject(Process
);
732 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
735 else if (MemoryAreaLength
>= RegionSize
)
738 MmAlterRegion(AddressSpace
,
739 MemoryArea
->StartingAddress
,
740 &MemoryArea
->Data
.SectionData
.RegionListHead
,
741 BaseAddress
, RegionSize
,
742 Type
, Protect
, MmModifyAttributes
);
743 MmUnlockAddressSpace(AddressSpace
);
744 ObDereferenceObject(Process
);
745 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
750 MmUnlockAddressSpace(AddressSpace
);
751 ObDereferenceObject(Process
);
752 return(STATUS_UNSUCCESSFUL
);
757 Status
= MmCreateMemoryArea(AddressSpace
,
758 MEMORY_AREA_VIRTUAL_MEMORY
,
764 AllocationType
& MEM_TOP_DOWN
,
765 BoundaryAddressMultiple
);
766 if (!NT_SUCCESS(Status
))
768 MmUnlockAddressSpace(AddressSpace
);
769 ObDereferenceObject(Process
);
770 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
774 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
775 (ULONG_PTR
)MemoryArea
->StartingAddress
;
777 MmInitializeRegion(&MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
778 MemoryAreaLength
, Type
, Protect
);
780 if ((AllocationType
& MEM_COMMIT
) &&
781 (Protect
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)))
783 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
784 MmReserveSwapPages(nPages
);
787 *UBaseAddress
= BaseAddress
;
788 *URegionSize
= MemoryAreaLength
;
789 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
791 MmUnlockAddressSpace(AddressSpace
);
792 ObDereferenceObject(Process
);
793 return(STATUS_SUCCESS
);
797 MmFreeVirtualMemoryPage(PVOID Context
,
798 MEMORY_AREA
* MemoryArea
,
804 PEPROCESS Process
= (PEPROCESS
)Context
;
808 SWAPENTRY SavedSwapEntry
;
809 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
810 if (SavedSwapEntry
!= 0)
812 MmFreeSwapPage(SavedSwapEntry
);
813 MmSetSavedSwapEntryPage(Page
, 0);
815 MmDeleteRmap(Page
, Process
, Address
);
816 MmReleasePageMemoryConsumer(MC_USER
, Page
);
818 else if (SwapEntry
!= 0)
820 MmFreeSwapPage(SwapEntry
);
826 MmFreeVirtualMemory(PEPROCESS Process
,
827 PMEMORY_AREA MemoryArea
)
829 PLIST_ENTRY current_entry
;
833 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process
,
836 /* Mark this memory area as about to be deleted. */
837 MemoryArea
->DeleteInProgress
= TRUE
;
840 * Wait for any ongoing paging operations. Notice that since we have
841 * flagged this memory area as deleted no more page ops will be added.
843 if (MemoryArea
->PageOpCount
> 0)
845 ULONG_PTR MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
846 (ULONG_PTR
)MemoryArea
->StartingAddress
;
847 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
849 for (i
= 0; i
< nPages
&& MemoryArea
->PageOpCount
!= 0; ++i
)
852 PageOp
= MmCheckForPageOp(MemoryArea
, Process
->UniqueProcessId
,
853 (PVOID
)((ULONG_PTR
)MemoryArea
->StartingAddress
+ (i
* PAGE_SIZE
)),
858 MmUnlockAddressSpace((PMADDRESS_SPACE
)&Process
->VadRoot
);
859 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
864 if (Status
!= STATUS_SUCCESS
)
866 DPRINT1("Failed to wait for page op\n");
869 MmLockAddressSpace((PMADDRESS_SPACE
)&Process
->VadRoot
);
870 MmReleasePageOp(PageOp
);
875 /* Free all the individual segments. */
876 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
.Flink
;
877 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
)
879 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
880 current_entry
= current_entry
->Flink
;
884 /* Actually free the memory area. */
885 MmFreeMemoryArea((PMADDRESS_SPACE
)&Process
->VadRoot
,
887 MmFreeVirtualMemoryPage
,
895 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
896 IN PVOID
* PBaseAddress
,
897 IN PULONG PRegionSize
,
900 * FUNCTION: Frees a range of virtual memory
902 * ProcessHandle = Points to the process that allocated the virtual
904 * BaseAddress = Points to the memory address, rounded down to a
905 * multiple of the pagesize
906 * RegionSize = Limits the range to free, rounded up to a multiple of
908 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
912 MEMORY_AREA
* MemoryArea
;
915 PMADDRESS_SPACE AddressSpace
;
919 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
920 "*PRegionSize %x, FreeType %x)\n",ProcessHandle
,*PBaseAddress
,
921 *PRegionSize
,FreeType
);
923 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((*PBaseAddress
));
924 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)(*PBaseAddress
) + (*PRegionSize
)) -
925 PAGE_ROUND_DOWN((*PBaseAddress
));
927 Status
= ObReferenceObjectByHandle(ProcessHandle
,
928 PROCESS_VM_OPERATION
,
933 if (!NT_SUCCESS(Status
))
938 AddressSpace
= (PMADDRESS_SPACE
)&Process
->VadRoot
;
940 MmLockAddressSpace(AddressSpace
);
941 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
942 if (MemoryArea
== NULL
)
944 Status
= STATUS_UNSUCCESSFUL
;
945 goto unlock_deref_and_return
;
951 /* We can only free a memory area in one step. */
952 if (MemoryArea
->StartingAddress
!= BaseAddress
||
953 MemoryArea
->Type
!= MEMORY_AREA_VIRTUAL_MEMORY
)
955 Status
= STATUS_UNSUCCESSFUL
;
956 goto unlock_deref_and_return
;
959 MmFreeVirtualMemory(Process
, MemoryArea
);
960 Status
= STATUS_SUCCESS
;
961 goto unlock_deref_and_return
;
965 MmAlterRegion(AddressSpace
,
966 MemoryArea
->StartingAddress
,
967 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
973 goto unlock_deref_and_return
;
976 Status
= STATUS_NOT_IMPLEMENTED
;
978 unlock_deref_and_return
:
980 MmUnlockAddressSpace(AddressSpace
);
981 ObDereferenceObject(Process
);
988 MmProtectAnonMem(PMADDRESS_SPACE AddressSpace
,
989 PMEMORY_AREA MemoryArea
,
998 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
999 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1001 *OldProtect
= Region
->Protect
;
1002 Status
= MmAlterRegion(AddressSpace
, MemoryArea
->StartingAddress
,
1003 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1004 BaseAddress
, Length
, Region
->Type
, Protect
,
1005 MmModifyAttributes
);
1010 MmQueryAnonMem(PMEMORY_AREA MemoryArea
,
1012 PMEMORY_BASIC_INFORMATION Info
,
1013 PULONG ResultLength
)
1016 PVOID RegionBase
= NULL
;
1018 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
1020 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1021 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1022 Address
, &RegionBase
);
1023 Info
->BaseAddress
= RegionBase
;
1024 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
1025 Info
->AllocationProtect
= MemoryArea
->Protect
;
1026 Info
->RegionSize
= Region
->Length
;
1027 Info
->State
= Region
->Type
;
1028 Info
->Protect
= Region
->Protect
;
1029 Info
->Type
= MEM_PRIVATE
;
1031 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1032 return(STATUS_SUCCESS
);