2 * Copyright (C) 2002-2005 ReactOS Team (and the authors from the programmers section)
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/anonmem.c
21 * PURPOSE: Implementing anonymous memory.
23 * PROGRAMMERS: David Welch
32 * Gunnar Andre' Dalsnes
34 * Thomas Weidenmueller
40 /* INCLUDE *****************************************************************/
46 #define MODULE_INVOLVED_IN_ARM3
47 #include "ARM3/miarm.h"
49 /* FUNCTIONS *****************************************************************/
53 MmWritePageVirtualMemory(PMMSUPPORT AddressSpace
,
54 PMEMORY_AREA MemoryArea
,
61 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
64 * Check for paging out from a deleted virtual memory area.
66 if (MemoryArea
->DeleteInProgress
)
68 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
69 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
70 MmReleasePageOp(PageOp
);
71 return(STATUS_UNSUCCESSFUL
);
74 Page
= MmGetPfnForProcess(Process
, Address
);
77 * Get that the page actually is dirty.
79 if (!MmIsDirtyPage(Process
, Address
))
81 PageOp
->Status
= STATUS_SUCCESS
;
82 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
83 MmReleasePageOp(PageOp
);
84 return(STATUS_SUCCESS
);
88 * Speculatively set the mapping to clean.
90 MmSetCleanPage(Process
, Address
);
93 * If necessary, allocate an entry in the paging file for this page
95 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
98 SwapEntry
= MmAllocSwapPage();
101 MmSetDirtyPage(Process
, Address
);
102 PageOp
->Status
= STATUS_PAGEFILE_QUOTA_EXCEEDED
;
103 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
104 MmReleasePageOp(PageOp
);
105 return(STATUS_PAGEFILE_QUOTA_EXCEEDED
);
110 * Write the page to the pagefile
112 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
113 if (!NT_SUCCESS(Status
))
115 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
117 MmSetDirtyPage(Process
, Address
);
118 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
119 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
120 MmReleasePageOp(PageOp
);
121 return(STATUS_UNSUCCESSFUL
);
125 * Otherwise we have succeeded.
127 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
128 PageOp
->Status
= STATUS_SUCCESS
;
129 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
130 MmReleasePageOp(PageOp
);
131 return(STATUS_SUCCESS
);
136 MmPageOutVirtualMemory(PMMSUPPORT AddressSpace
,
137 PMEMORY_AREA MemoryArea
,
145 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
147 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
148 Address
, Process
->UniqueProcessId
);
151 * Check for paging out from a deleted virtual memory area.
153 if (MemoryArea
->DeleteInProgress
)
155 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
156 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
157 MmReleasePageOp(PageOp
);
158 return(STATUS_UNSUCCESSFUL
);
162 * Disable the virtual mapping.
164 MmDisableVirtualMapping(Process
, Address
,
169 KeBugCheck(MEMORY_MANAGEMENT
);
173 * Paging out non-dirty data is easy.
177 MmLockAddressSpace(AddressSpace
);
178 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
179 MmDeleteAllRmaps(Page
, NULL
, NULL
);
180 if ((SwapEntry
= MmGetSavedSwapEntryPage(Page
)) != 0)
182 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
183 MmSetSavedSwapEntryPage(Page
, 0);
185 MmUnlockAddressSpace(AddressSpace
);
186 MmReleasePageMemoryConsumer(MC_USER
, Page
);
187 PageOp
->Status
= STATUS_SUCCESS
;
188 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
189 MmReleasePageOp(PageOp
);
190 return(STATUS_SUCCESS
);
194 * If necessary, allocate an entry in the paging file for this page
196 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
199 SwapEntry
= MmAllocSwapPage();
202 MmShowOutOfSpaceMessagePagingFile();
203 MmEnableVirtualMapping(Process
, Address
);
204 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
205 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
206 MmReleasePageOp(PageOp
);
207 return(STATUS_PAGEFILE_QUOTA
);
212 * Write the page to the pagefile
214 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
215 if (!NT_SUCCESS(Status
))
217 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
219 MmEnableVirtualMapping(Process
, Address
);
220 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
221 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
222 MmReleasePageOp(PageOp
);
223 return(STATUS_UNSUCCESSFUL
);
227 * Otherwise we have succeeded, free the page
229 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page
<< PAGE_SHIFT
);
230 MmLockAddressSpace(AddressSpace
);
231 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
232 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
233 MmUnlockAddressSpace(AddressSpace
);
234 MmDeleteAllRmaps(Page
, NULL
, NULL
);
235 MmSetSavedSwapEntryPage(Page
, 0);
236 MmReleasePageMemoryConsumer(MC_USER
, Page
);
237 PageOp
->Status
= STATUS_SUCCESS
;
238 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
239 MmReleasePageOp(PageOp
);
240 return(STATUS_SUCCESS
);
245 MmNotPresentFaultVirtualMemory(PMMSUPPORT AddressSpace
,
246 MEMORY_AREA
* MemoryArea
,
250 * FUNCTION: Move data into memory to satisfy a page not present fault
252 * AddressSpace = Address space within which the fault occurred
253 * MemoryArea = The memory area within which the fault occurred
254 * Address = The absolute address of fault
256 * NOTES: This function is called with the address space lock held.
263 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
266 * There is a window between taking the page fault and locking the
267 * address space when another thread could load the page so we check
270 if (MmIsPagePresent(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
,
290 if (Region
->Type
== MEM_RESERVE
|| Region
->Protect
== PAGE_NOACCESS
)
292 return(STATUS_ACCESS_VIOLATION
);
298 if (Region
->Protect
& PAGE_GUARD
)
300 return(STATUS_GUARD_PAGE_VIOLATION
);
304 * Get or create a page operation
306 PageOp
= MmGetPageOp(MemoryArea
, Process
->UniqueProcessId
,
307 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
308 MM_PAGEOP_PAGEIN
, FALSE
);
311 DPRINT1("MmGetPageOp failed");
312 KeBugCheck(MEMORY_MANAGEMENT
);
316 * Check if someone else is already handling this fault, if so wait
319 if (PageOp
->Thread
!= PsGetCurrentThread())
321 MmUnlockAddressSpace(AddressSpace
);
322 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
328 * Check for various strange conditions
330 if (Status
!= STATUS_SUCCESS
)
332 DPRINT1("Failed to wait for page op\n");
333 KeBugCheck(MEMORY_MANAGEMENT
);
335 if (PageOp
->Status
== STATUS_PENDING
)
337 DPRINT1("Woke for page op before completion\n");
338 KeBugCheck(MEMORY_MANAGEMENT
);
341 * If this wasn't a pagein then we need to restart the handling
343 if (PageOp
->OpType
!= MM_PAGEOP_PAGEIN
)
345 MmLockAddressSpace(AddressSpace
);
346 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
347 MmReleasePageOp(PageOp
);
348 return(STATUS_MM_RESTART_OPERATION
);
351 * If the thread handling this fault has failed then we don't retry
353 if (!NT_SUCCESS(PageOp
->Status
))
355 MmLockAddressSpace(AddressSpace
);
356 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
357 Status
= PageOp
->Status
;
358 MmReleasePageOp(PageOp
);
361 MmLockAddressSpace(AddressSpace
);
362 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
363 MmReleasePageOp(PageOp
);
364 return(STATUS_SUCCESS
);
368 * Try to allocate a page
370 MI_SET_USAGE(MI_USAGE_VAD
);
371 MI_SET_PROCESS2(Process
->ImageFileName
);
372 Status
= MmRequestPageMemoryConsumer(MC_USER
, FALSE
, &Page
);
373 if (Status
== STATUS_NO_MEMORY
)
375 MmUnlockAddressSpace(AddressSpace
);
376 Status
= MmRequestPageMemoryConsumer(MC_USER
, TRUE
, &Page
);
377 MmLockAddressSpace(AddressSpace
);
379 if (!NT_SUCCESS(Status
))
381 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status
);
382 KeBugCheck(MEMORY_MANAGEMENT
);
386 * Handle swapped out pages.
388 if (MmIsPageSwapEntry(NULL
, Address
))
392 MmDeletePageFileMapping(Process
, Address
, &SwapEntry
);
393 Status
= MmReadFromSwapPage(SwapEntry
, Page
);
394 if (!NT_SUCCESS(Status
))
396 KeBugCheck(MEMORY_MANAGEMENT
);
398 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
402 * Set the page. If we fail because we are out of memory then
405 Status
= MmCreateVirtualMapping(Process
,
406 (PVOID
)PAGE_ROUND_DOWN(Address
),
410 while (Status
== STATUS_NO_MEMORY
)
412 MmUnlockAddressSpace(AddressSpace
);
413 Status
= MmCreateVirtualMapping(Process
,
418 MmLockAddressSpace(AddressSpace
);
420 if (!NT_SUCCESS(Status
))
422 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
423 KeBugCheck(MEMORY_MANAGEMENT
);
428 * Add the page to the process's working set
430 MmInsertRmap(Page
, Process
, (PVOID
)PAGE_ROUND_DOWN(Address
));
433 * Finish the operation
435 PageOp
->Status
= STATUS_SUCCESS
;
436 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
437 MmReleasePageOp(PageOp
);
438 return(STATUS_SUCCESS
);
442 MmModifyAttributes(PMMSUPPORT AddressSpace
,
450 * FUNCTION: Modify the attributes of a memory region
453 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
456 * If we are switching a previously committed region to reserved then
457 * free any allocated pages within the region
459 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
463 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
467 if (MmIsPageSwapEntry(Process
,
468 (char*)BaseAddress
+ (i
* PAGE_SIZE
)))
472 MmDeletePageFileMapping(Process
,
473 (char*)BaseAddress
+ (i
* PAGE_SIZE
),
475 MmFreeSwapPage(SwapEntry
);
479 MmDeleteVirtualMapping(Process
,
480 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
484 SWAPENTRY SavedSwapEntry
;
485 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
486 if (SavedSwapEntry
!= 0)
488 MmFreeSwapPage(SavedSwapEntry
);
489 MmSetSavedSwapEntryPage(Page
, 0);
491 MmDeleteRmap(Page
, Process
,
492 (char*)BaseAddress
+ (i
* PAGE_SIZE
));
493 MmReleasePageMemoryConsumer(MC_USER
, Page
);
500 * If we are changing the protection attributes of a committed region then
501 * alter the attributes for any allocated pages within the region
503 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
504 OldProtect
!= NewProtect
)
508 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
510 if (MmIsPagePresent(Process
,
511 (char*)BaseAddress
+ (i
*PAGE_SIZE
)))
513 MmSetPageProtect(Process
,
514 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
522 MiProtectVirtualMemory(IN PEPROCESS Process
,
523 IN OUT PVOID
*BaseAddress
,
524 IN OUT PSIZE_T NumberOfBytesToProtect
,
525 IN ULONG NewAccessProtection
,
526 OUT PULONG OldAccessProtection OPTIONAL
)
528 PMEMORY_AREA MemoryArea
;
529 PMMSUPPORT AddressSpace
;
530 ULONG OldAccessProtection_
;
533 *NumberOfBytesToProtect
=
534 PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) -
535 PAGE_ROUND_DOWN(*BaseAddress
);
536 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
538 AddressSpace
= &Process
->Vm
;
540 MmLockAddressSpace(AddressSpace
);
541 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
542 if (MemoryArea
== NULL
)
544 MmUnlockAddressSpace(AddressSpace
);
545 return STATUS_UNSUCCESSFUL
;
548 if (OldAccessProtection
== NULL
)
549 OldAccessProtection
= &OldAccessProtection_
;
551 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
553 Status
= MmProtectAnonMem(AddressSpace
, MemoryArea
, *BaseAddress
,
554 *NumberOfBytesToProtect
, NewAccessProtection
,
555 OldAccessProtection
);
557 else if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
559 Status
= MmProtectSectionView(AddressSpace
, MemoryArea
, *BaseAddress
,
560 *NumberOfBytesToProtect
,
562 OldAccessProtection
);
566 /* FIXME: Should we return failure or success in this case? */
567 Status
= STATUS_CONFLICTING_ADDRESSES
;
570 MmUnlockAddressSpace(AddressSpace
);
580 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
581 IN OUT PVOID
* UBaseAddress
,
582 IN ULONG_PTR ZeroBits
,
583 IN OUT PSIZE_T URegionSize
,
584 IN ULONG AllocationType
,
588 MEMORY_AREA
* MemoryArea
;
589 ULONG_PTR MemoryAreaLength
;
592 PMMSUPPORT AddressSpace
;
597 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
598 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
599 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
601 ULONG ProtectionMask
;
602 BOOLEAN Attached
= FALSE
;
603 BoundaryAddressMultiple
.QuadPart
= 0;
606 /* Check for valid Zero bits */
609 DPRINT1("Too many zero bits\n");
610 return STATUS_INVALID_PARAMETER_3
;
613 /* Check for valid Allocation Types */
614 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
615 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
617 DPRINT1("Invalid Allocation Type\n");
618 return STATUS_INVALID_PARAMETER_5
;
621 /* Check for at least one of these Allocation Types to be set */
622 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
624 DPRINT1("No memory allocation base type\n");
625 return STATUS_INVALID_PARAMETER_5
;
628 /* MEM_RESET is an exclusive flag, make sure that is valid too */
629 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
631 DPRINT1("Invalid use of MEM_RESET\n");
632 return STATUS_INVALID_PARAMETER_5
;
635 /* Check if large pages are being used */
636 if (AllocationType
& MEM_LARGE_PAGES
)
638 /* Large page allocations MUST be committed */
639 if (!(AllocationType
& MEM_COMMIT
))
641 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
642 return STATUS_INVALID_PARAMETER_5
;
645 /* These flags are not allowed with large page allocations */
646 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
648 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
649 return STATUS_INVALID_PARAMETER_5
;
653 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
654 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
656 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
657 return STATUS_INVALID_PARAMETER_5
;
660 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
661 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
663 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
664 return STATUS_INVALID_PARAMETER_5
;
667 /* Check for valid MEM_PHYSICAL usage */
668 if (AllocationType
& MEM_PHYSICAL
)
670 /* Only these flags are allowed with MEM_PHYSIAL */
671 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
673 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
674 return STATUS_INVALID_PARAMETER_5
;
677 /* Then make sure PAGE_READWRITE is used */
678 if (Protect
!= PAGE_READWRITE
)
680 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
681 return STATUS_INVALID_PARAMETER_6
;
685 /* Calculate the protection mask and make sure it's valid */
686 ProtectionMask
= MiMakeProtectionMask(Protect
);
687 if (ProtectionMask
== MM_INVALID_PROTECTION
)
689 DPRINT1("Invalid protection mask\n");
690 return STATUS_INVALID_PAGE_PROTECTION
;
696 /* Check for user-mode parameters */
697 if (PreviousMode
!= KernelMode
)
699 /* Make sure they are writable */
700 ProbeForWritePointer(UBaseAddress
);
701 ProbeForWriteUlong(URegionSize
);
704 /* Capture their values */
705 PBaseAddress
= *UBaseAddress
;
706 PRegionSize
= *URegionSize
;
708 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
710 /* Return the exception code */
711 _SEH2_YIELD(return _SEH2_GetExceptionCode());
715 /* Make sure the allocation isn't past the VAD area */
716 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
718 DPRINT1("Virtual allocation base above User Space\n");
719 return STATUS_INVALID_PARAMETER_2
;
722 /* Make sure the allocation wouldn't overflow past the VAD area */
723 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
725 DPRINT1("Region size would overflow into kernel-memory\n");
726 return STATUS_INVALID_PARAMETER_4
;
729 /* Make sure there's a size specified */
732 DPRINT1("Region size is invalid (zero)\n");
733 return STATUS_INVALID_PARAMETER_4
;
736 /* Check if this is for the current process */
737 if (ProcessHandle
== NtCurrentProcess())
739 /* We already have the current process, no need to go through Ob */
740 Process
= CurrentProcess
;
744 /* Reference the handle for correct permissions */
745 Status
= ObReferenceObjectByHandle(ProcessHandle
,
746 PROCESS_VM_OPERATION
,
751 if (!NT_SUCCESS(Status
)) return Status
;
753 /* Check if not running in the current process */
754 if (CurrentProcess
!= Process
)
757 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
762 /* Check for large page allocations */
763 if (AllocationType
& MEM_LARGE_PAGES
)
765 /* The lock memory privilege is required */
766 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
768 /* Fail without it */
769 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
770 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
771 return STATUS_PRIVILEGE_NOT_HELD
;
775 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
776 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)PBaseAddress
+ PRegionSize
) -
777 PAGE_ROUND_DOWN(PBaseAddress
);
781 * Copy on Write is reserved for system use. This case is a certain failure
782 * but there may be other cases...needs more testing
784 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
785 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
)))
787 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
788 return STATUS_INVALID_PAGE_PROTECTION
;
791 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
792 DPRINT("Type %x\n", Type
);
794 AddressSpace
= &Process
->Vm
;
795 MmLockAddressSpace(AddressSpace
);
797 if (PBaseAddress
!= 0)
799 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
801 if (MemoryArea
!= NULL
)
803 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
804 (ULONG_PTR
)MemoryArea
->StartingAddress
;
806 if (((ULONG_PTR
)BaseAddress
+ RegionSize
) > (ULONG_PTR
)MemoryArea
->EndingAddress
)
808 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
809 (ULONG_PTR
)BaseAddress
+ RegionSize
, MemoryArea
->EndingAddress
);
811 MmUnlockAddressSpace(AddressSpace
);
812 if (Attached
) KeUnstackDetachProcess(&ApcState
);
813 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
814 return STATUS_MEMORY_NOT_ALLOCATED
;
817 if (AllocationType
== MEM_RESET
)
819 if (MmIsPagePresent(Process
, BaseAddress
))
821 /* FIXME: mark pages as not modified */
825 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
828 MmUnlockAddressSpace(AddressSpace
);
829 if (Attached
) KeUnstackDetachProcess(&ApcState
);
830 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
832 /* MEM_RESET does not modify any attributes of region */
833 return STATUS_SUCCESS
;
836 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
837 MemoryAreaLength
>= RegionSize
)
840 MmAlterRegion(AddressSpace
,
841 MemoryArea
->StartingAddress
,
842 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
843 BaseAddress
, RegionSize
,
844 Type
, Protect
, MmModifyAttributes
);
845 MmUnlockAddressSpace(AddressSpace
);
846 if (Attached
) KeUnstackDetachProcess(&ApcState
);
847 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
848 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
850 /* Give the caller rounded BaseAddress and area length */
851 if (NT_SUCCESS(Status
))
853 *UBaseAddress
= BaseAddress
;
854 *URegionSize
= RegionSize
;
855 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
860 else if (MemoryAreaLength
>= RegionSize
)
862 /* Region list initialized? */
863 if (MemoryArea
->Data
.SectionData
.RegionListHead
.Flink
)
866 MmAlterRegion(AddressSpace
,
867 MemoryArea
->StartingAddress
,
868 &MemoryArea
->Data
.SectionData
.RegionListHead
,
869 BaseAddress
, RegionSize
,
870 Type
, Protect
, MmModifyAttributes
);
874 Status
= STATUS_ACCESS_VIOLATION
;
877 MmUnlockAddressSpace(AddressSpace
);
878 if (Attached
) KeUnstackDetachProcess(&ApcState
);
879 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
880 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
882 /* Give the caller rounded BaseAddress and area length */
883 if (NT_SUCCESS(Status
))
885 *UBaseAddress
= BaseAddress
;
886 *URegionSize
= RegionSize
;
887 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
894 MmUnlockAddressSpace(AddressSpace
);
895 if (Attached
) KeUnstackDetachProcess(&ApcState
);
896 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
897 return(STATUS_UNSUCCESSFUL
);
902 Status
= MmCreateMemoryArea(AddressSpace
,
903 MEMORY_AREA_VIRTUAL_MEMORY
,
909 AllocationType
& MEM_TOP_DOWN
,
910 BoundaryAddressMultiple
);
911 if (!NT_SUCCESS(Status
))
913 MmUnlockAddressSpace(AddressSpace
);
914 if (Attached
) KeUnstackDetachProcess(&ApcState
);
915 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
916 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
920 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
921 (ULONG_PTR
)MemoryArea
->StartingAddress
;
923 MmInitializeRegion(&MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
924 MemoryAreaLength
, Type
, Protect
);
926 if ((AllocationType
& MEM_COMMIT
) &&
927 (Protect
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)))
929 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
930 MmReserveSwapPages(nPages
);
933 MmUnlockAddressSpace(AddressSpace
);
934 if (Attached
) KeUnstackDetachProcess(&ApcState
);
935 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
937 *UBaseAddress
= BaseAddress
;
938 *URegionSize
= MemoryAreaLength
;
939 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
941 return(STATUS_SUCCESS
);
945 MmFreeVirtualMemoryPage(PVOID Context
,
946 MEMORY_AREA
* MemoryArea
,
952 PEPROCESS Process
= (PEPROCESS
)Context
;
956 SWAPENTRY SavedSwapEntry
;
957 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
958 if (SavedSwapEntry
!= 0)
960 MmFreeSwapPage(SavedSwapEntry
);
961 MmSetSavedSwapEntryPage(Page
, 0);
963 MmDeleteRmap(Page
, Process
, Address
);
964 MmReleasePageMemoryConsumer(MC_USER
, Page
);
966 else if (SwapEntry
!= 0)
968 MmFreeSwapPage(SwapEntry
);
974 MmFreeVirtualMemory(PEPROCESS Process
,
975 PMEMORY_AREA MemoryArea
)
977 PLIST_ENTRY current_entry
;
981 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process
,
984 /* Mark this memory area as about to be deleted. */
985 MemoryArea
->DeleteInProgress
= TRUE
;
988 * Wait for any ongoing paging operations. Notice that since we have
989 * flagged this memory area as deleted no more page ops will be added.
991 if (MemoryArea
->PageOpCount
> 0)
993 ULONG_PTR MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
994 (ULONG_PTR
)MemoryArea
->StartingAddress
;
995 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
997 for (i
= 0; i
< nPages
&& MemoryArea
->PageOpCount
!= 0; ++i
)
1000 PageOp
= MmCheckForPageOp(MemoryArea
, Process
->UniqueProcessId
,
1001 (PVOID
)((ULONG_PTR
)MemoryArea
->StartingAddress
+ (i
* PAGE_SIZE
)),
1006 MmUnlockAddressSpace(&Process
->Vm
);
1007 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
1012 if (Status
!= STATUS_SUCCESS
)
1014 DPRINT1("Failed to wait for page op\n");
1015 KeBugCheck(MEMORY_MANAGEMENT
);
1017 MmLockAddressSpace(&Process
->Vm
);
1018 MmReleasePageOp(PageOp
);
1023 /* Free all the individual segments. */
1024 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
.Flink
;
1025 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
)
1027 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
1028 current_entry
= current_entry
->Flink
;
1029 ExFreePool(current
);
1032 /* Actually free the memory area. */
1033 MmFreeMemoryArea(&Process
->Vm
,
1035 MmFreeVirtualMemoryPage
,
1043 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
1044 IN PVOID
* UBaseAddress
,
1045 IN PSIZE_T URegionSize
,
1048 * FUNCTION: Frees a range of virtual memory
1050 * ProcessHandle = Points to the process that allocated the virtual
1052 * BaseAddress = Points to the memory address, rounded down to a
1053 * multiple of the pagesize
1054 * RegionSize = Limits the range to free, rounded up to a multiple of
1056 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
1060 MEMORY_AREA
* MemoryArea
;
1063 PMMSUPPORT AddressSpace
;
1064 PVOID BaseAddress
, PBaseAddress
;
1065 ULONG RegionSize
, PRegionSize
;
1066 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1067 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
1068 KAPC_STATE ApcState
;
1069 BOOLEAN Attached
= FALSE
;
1072 /* Only two flags are supported */
1073 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
1075 DPRINT1("Invalid FreeType\n");
1076 return STATUS_INVALID_PARAMETER_4
;
1079 /* Check if no flag was used, or if both flags were used */
1080 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
1081 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
1083 DPRINT1("Invalid FreeType combination\n");
1084 return STATUS_INVALID_PARAMETER_4
;
1090 /* Check for user-mode parameters */
1091 if (PreviousMode
!= KernelMode
)
1093 /* Make sure they are writable */
1094 ProbeForWritePointer(UBaseAddress
);
1095 ProbeForWriteUlong(URegionSize
);
1098 /* Capture their values */
1099 PBaseAddress
= *UBaseAddress
;
1100 PRegionSize
= *URegionSize
;
1102 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1104 /* Return the exception code */
1105 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1109 /* Make sure the allocation isn't past the user area */
1110 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
1112 DPRINT1("Virtual free base above User Space\n");
1113 return STATUS_INVALID_PARAMETER_2
;
1116 /* Make sure the allocation wouldn't overflow past the user area */
1117 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
1119 DPRINT1("Region size would overflow into kernel-memory\n");
1120 return STATUS_INVALID_PARAMETER_3
;
1123 /* Check if this is for the current process */
1124 if (ProcessHandle
== NtCurrentProcess())
1126 /* We already have the current process, no need to go through Ob */
1127 Process
= CurrentProcess
;
1131 /* Reference the handle for correct permissions */
1132 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1133 PROCESS_VM_OPERATION
,
1138 if (!NT_SUCCESS(Status
)) return Status
;
1140 /* Check if not running in the current process */
1141 if (CurrentProcess
!= Process
)
1144 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1149 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((PBaseAddress
));
1150 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)(PBaseAddress
) + (PRegionSize
)) -
1151 PAGE_ROUND_DOWN((PBaseAddress
));
1153 AddressSpace
= &Process
->Vm
;
1155 MmLockAddressSpace(AddressSpace
);
1156 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
1157 if (MemoryArea
== NULL
)
1159 Status
= STATUS_UNSUCCESSFUL
;
1160 goto unlock_deref_and_return
;
1166 /* We can only free a memory area in one step. */
1167 if (MemoryArea
->StartingAddress
!= BaseAddress
||
1168 MemoryArea
->Type
!= MEMORY_AREA_VIRTUAL_MEMORY
)
1170 Status
= STATUS_UNSUCCESSFUL
;
1171 goto unlock_deref_and_return
;
1174 MmFreeVirtualMemory(Process
, MemoryArea
);
1175 Status
= STATUS_SUCCESS
;
1176 goto unlock_deref_and_return
;
1180 MmAlterRegion(AddressSpace
,
1181 MemoryArea
->StartingAddress
,
1182 (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
) ?
1183 &MemoryArea
->Data
.SectionData
.RegionListHead
:
1184 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1189 MmModifyAttributes
);
1190 goto unlock_deref_and_return
;
1193 Status
= STATUS_NOT_IMPLEMENTED
;
1195 unlock_deref_and_return
:
1197 MmUnlockAddressSpace(AddressSpace
);
1198 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1199 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1206 MmProtectAnonMem(PMMSUPPORT AddressSpace
,
1207 PMEMORY_AREA MemoryArea
,
1214 NTSTATUS Status
= STATUS_SUCCESS
;
1215 ULONG LengthCount
= 0;
1217 /* Search all Regions in MemoryArea up to Length */
1218 /* Every Region up to Length must be committed for success */
1221 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1222 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1223 (PVOID
)((ULONG_PTR
)BaseAddress
+ (ULONG_PTR
)LengthCount
), NULL
);
1225 /* If a Region was found and it is committed */
1226 if ((Region
) && (Region
->Type
== MEM_COMMIT
))
1228 LengthCount
+= Region
->Length
;
1229 if (Length
<= LengthCount
) break;
1232 /* If Region was found and it is not commited */
1235 Status
= STATUS_NOT_COMMITTED
;
1238 /* If no Region was found at all */
1239 else if (LengthCount
== 0)
1241 Status
= STATUS_INVALID_ADDRESS
;
1246 if (NT_SUCCESS(Status
))
1248 *OldProtect
= Region
->Protect
;
1249 Status
= MmAlterRegion(AddressSpace
, MemoryArea
->StartingAddress
,
1250 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1251 BaseAddress
, Length
, Region
->Type
, Protect
,
1252 MmModifyAttributes
);
1259 MmQueryAnonMem(PMEMORY_AREA MemoryArea
,
1261 PMEMORY_BASIC_INFORMATION Info
,
1262 PSIZE_T ResultLength
)
1265 PVOID RegionBase
= NULL
;
1267 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
1269 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1270 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1271 Address
, &RegionBase
);
1272 Info
->BaseAddress
= RegionBase
;
1273 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
1274 Info
->AllocationProtect
= MemoryArea
->Protect
;
1275 Info
->RegionSize
= Region
->Length
;
1276 Info
->State
= Region
->Type
;
1277 Info
->Protect
= Region
->Protect
;
1278 Info
->Type
= MEM_PRIVATE
;
1280 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1281 return(STATUS_SUCCESS
);