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 MmPageOutVirtualMemory(PMMSUPPORT AddressSpace
,
54 PMEMORY_AREA MemoryArea
,
62 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
64 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
65 Address
, Process
->UniqueProcessId
);
68 * Check for paging out from a deleted virtual memory area.
70 if (MemoryArea
->DeleteInProgress
)
72 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
73 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
74 MmReleasePageOp(PageOp
);
75 return(STATUS_UNSUCCESSFUL
);
79 * Disable the virtual mapping.
81 MmDisableVirtualMapping(Process
, Address
,
86 KeBugCheck(MEMORY_MANAGEMENT
);
90 * Paging out non-dirty data is easy.
94 MmLockAddressSpace(AddressSpace
);
95 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
96 MmDeleteAllRmaps(Page
, NULL
, NULL
);
97 if ((SwapEntry
= MmGetSavedSwapEntryPage(Page
)) != 0)
99 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
100 MmSetSavedSwapEntryPage(Page
, 0);
102 MmUnlockAddressSpace(AddressSpace
);
103 MmReleasePageMemoryConsumer(MC_USER
, Page
);
104 PageOp
->Status
= STATUS_SUCCESS
;
105 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
106 MmReleasePageOp(PageOp
);
107 return(STATUS_SUCCESS
);
111 * If necessary, allocate an entry in the paging file for this page
113 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
116 SwapEntry
= MmAllocSwapPage();
119 MmShowOutOfSpaceMessagePagingFile();
120 MmEnableVirtualMapping(Process
, Address
);
121 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
122 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
123 MmReleasePageOp(PageOp
);
124 return(STATUS_PAGEFILE_QUOTA
);
129 * Write the page to the pagefile
131 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
132 if (!NT_SUCCESS(Status
))
134 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
136 MmEnableVirtualMapping(Process
, Address
);
137 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
138 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
139 MmReleasePageOp(PageOp
);
140 return(STATUS_UNSUCCESSFUL
);
144 * Otherwise we have succeeded, free the page
146 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page
<< PAGE_SHIFT
);
147 MmLockAddressSpace(AddressSpace
);
148 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
149 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
150 MmUnlockAddressSpace(AddressSpace
);
151 MmDeleteAllRmaps(Page
, NULL
, NULL
);
152 MmSetSavedSwapEntryPage(Page
, 0);
153 MmReleasePageMemoryConsumer(MC_USER
, Page
);
154 PageOp
->Status
= STATUS_SUCCESS
;
155 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
156 MmReleasePageOp(PageOp
);
157 return(STATUS_SUCCESS
);
162 MmNotPresentFaultVirtualMemory(PMMSUPPORT AddressSpace
,
163 MEMORY_AREA
* MemoryArea
,
167 * FUNCTION: Move data into memory to satisfy a page not present fault
169 * AddressSpace = Address space within which the fault occurred
170 * MemoryArea = The memory area within which the fault occurred
171 * Address = The absolute address of fault
173 * NOTES: This function is called with the address space lock held.
180 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
183 * There is a window between taking the page fault and locking the
184 * address space when another thread could load the page so we check
187 if (MmIsPagePresent(NULL
, Address
))
189 return(STATUS_SUCCESS
);
193 * Check for the virtual memory area being deleted.
195 if (MemoryArea
->DeleteInProgress
)
197 return(STATUS_UNSUCCESSFUL
);
201 * Get the segment corresponding to the virtual address
203 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
204 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
207 if (Region
->Type
== MEM_RESERVE
|| Region
->Protect
== PAGE_NOACCESS
)
209 return(STATUS_ACCESS_VIOLATION
);
215 if (Region
->Protect
& PAGE_GUARD
)
217 return(STATUS_GUARD_PAGE_VIOLATION
);
221 * Get or create a page operation
223 PageOp
= MmGetPageOp(MemoryArea
, Process
->UniqueProcessId
,
224 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
225 MM_PAGEOP_PAGEIN
, FALSE
);
228 DPRINT1("MmGetPageOp failed");
229 KeBugCheck(MEMORY_MANAGEMENT
);
233 * Check if someone else is already handling this fault, if so wait
236 if (PageOp
->Thread
!= PsGetCurrentThread())
238 MmUnlockAddressSpace(AddressSpace
);
239 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
245 * Check for various strange conditions
247 if (Status
!= STATUS_SUCCESS
)
249 DPRINT1("Failed to wait for page op\n");
250 KeBugCheck(MEMORY_MANAGEMENT
);
252 if (PageOp
->Status
== STATUS_PENDING
)
254 DPRINT1("Woke for page op before completion\n");
255 KeBugCheck(MEMORY_MANAGEMENT
);
258 * If this wasn't a pagein then we need to restart the handling
260 if (PageOp
->OpType
!= MM_PAGEOP_PAGEIN
)
262 MmLockAddressSpace(AddressSpace
);
263 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
264 MmReleasePageOp(PageOp
);
265 return(STATUS_MM_RESTART_OPERATION
);
268 * If the thread handling this fault has failed then we don't retry
270 if (!NT_SUCCESS(PageOp
->Status
))
272 MmLockAddressSpace(AddressSpace
);
273 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
274 Status
= PageOp
->Status
;
275 MmReleasePageOp(PageOp
);
278 MmLockAddressSpace(AddressSpace
);
279 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
280 MmReleasePageOp(PageOp
);
281 return(STATUS_SUCCESS
);
285 * Try to allocate a page
287 MI_SET_USAGE(MI_USAGE_VAD
);
288 MI_SET_PROCESS2(Process
->ImageFileName
);
289 Status
= MmRequestPageMemoryConsumer(MC_USER
, FALSE
, &Page
);
290 if (Status
== STATUS_NO_MEMORY
)
292 MmUnlockAddressSpace(AddressSpace
);
293 Status
= MmRequestPageMemoryConsumer(MC_USER
, TRUE
, &Page
);
294 MmLockAddressSpace(AddressSpace
);
296 if (!NT_SUCCESS(Status
))
298 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status
);
299 KeBugCheck(MEMORY_MANAGEMENT
);
303 * Handle swapped out pages.
305 if (MmIsPageSwapEntry(NULL
, Address
))
309 MmDeletePageFileMapping(Process
, Address
, &SwapEntry
);
310 Status
= MmReadFromSwapPage(SwapEntry
, Page
);
311 if (!NT_SUCCESS(Status
))
313 KeBugCheck(MEMORY_MANAGEMENT
);
315 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
319 * Set the page. If we fail because we are out of memory then
322 Status
= MmCreateVirtualMapping(Process
,
323 (PVOID
)PAGE_ROUND_DOWN(Address
),
327 while (Status
== STATUS_NO_MEMORY
)
329 MmUnlockAddressSpace(AddressSpace
);
330 Status
= MmCreateVirtualMapping(Process
,
335 MmLockAddressSpace(AddressSpace
);
337 if (!NT_SUCCESS(Status
))
339 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
340 KeBugCheck(MEMORY_MANAGEMENT
);
345 * Add the page to the process's working set
347 MmInsertRmap(Page
, Process
, (PVOID
)PAGE_ROUND_DOWN(Address
));
350 * Finish the operation
352 PageOp
->Status
= STATUS_SUCCESS
;
353 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
354 MmReleasePageOp(PageOp
);
355 return(STATUS_SUCCESS
);
359 MmModifyAttributes(PMMSUPPORT AddressSpace
,
367 * FUNCTION: Modify the attributes of a memory region
370 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
373 * If we are switching a previously committed region to reserved then
374 * free any allocated pages within the region
376 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
380 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
384 if (MmIsPageSwapEntry(Process
,
385 (char*)BaseAddress
+ (i
* PAGE_SIZE
)))
389 MmDeletePageFileMapping(Process
,
390 (char*)BaseAddress
+ (i
* PAGE_SIZE
),
392 MmFreeSwapPage(SwapEntry
);
396 MmDeleteVirtualMapping(Process
,
397 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
401 SWAPENTRY SavedSwapEntry
;
402 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
403 if (SavedSwapEntry
!= 0)
405 MmFreeSwapPage(SavedSwapEntry
);
406 MmSetSavedSwapEntryPage(Page
, 0);
408 MmDeleteRmap(Page
, Process
,
409 (char*)BaseAddress
+ (i
* PAGE_SIZE
));
410 MmReleasePageMemoryConsumer(MC_USER
, Page
);
417 * If we are changing the protection attributes of a committed region then
418 * alter the attributes for any allocated pages within the region
420 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
421 OldProtect
!= NewProtect
)
425 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
427 if (MmIsPagePresent(Process
,
428 (char*)BaseAddress
+ (i
*PAGE_SIZE
)))
430 MmSetPageProtect(Process
,
431 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
439 MiProtectVirtualMemory(IN PEPROCESS Process
,
440 IN OUT PVOID
*BaseAddress
,
441 IN OUT PSIZE_T NumberOfBytesToProtect
,
442 IN ULONG NewAccessProtection
,
443 OUT PULONG OldAccessProtection OPTIONAL
)
445 PMEMORY_AREA MemoryArea
;
446 PMMSUPPORT AddressSpace
;
447 ULONG OldAccessProtection_
;
450 *NumberOfBytesToProtect
=
451 PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) -
452 PAGE_ROUND_DOWN(*BaseAddress
);
453 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
455 AddressSpace
= &Process
->Vm
;
457 MmLockAddressSpace(AddressSpace
);
458 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
459 if (MemoryArea
== NULL
)
461 MmUnlockAddressSpace(AddressSpace
);
462 return STATUS_UNSUCCESSFUL
;
465 if (OldAccessProtection
== NULL
)
466 OldAccessProtection
= &OldAccessProtection_
;
468 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
470 Status
= MmProtectAnonMem(AddressSpace
, MemoryArea
, *BaseAddress
,
471 *NumberOfBytesToProtect
, NewAccessProtection
,
472 OldAccessProtection
);
474 else if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
476 Status
= MmProtectSectionView(AddressSpace
, MemoryArea
, *BaseAddress
,
477 *NumberOfBytesToProtect
,
479 OldAccessProtection
);
483 /* FIXME: Should we return failure or success in this case? */
484 Status
= STATUS_CONFLICTING_ADDRESSES
;
487 MmUnlockAddressSpace(AddressSpace
);
497 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
498 IN OUT PVOID
* UBaseAddress
,
499 IN ULONG_PTR ZeroBits
,
500 IN OUT PSIZE_T URegionSize
,
501 IN ULONG AllocationType
,
505 MEMORY_AREA
* MemoryArea
;
506 ULONG_PTR MemoryAreaLength
;
509 PMMSUPPORT AddressSpace
;
514 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
515 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
516 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
518 ULONG ProtectionMask
;
519 BOOLEAN Attached
= FALSE
;
520 BoundaryAddressMultiple
.QuadPart
= 0;
523 /* Check for valid Zero bits */
526 DPRINT1("Too many zero bits\n");
527 return STATUS_INVALID_PARAMETER_3
;
530 /* Check for valid Allocation Types */
531 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
532 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
534 DPRINT1("Invalid Allocation Type\n");
535 return STATUS_INVALID_PARAMETER_5
;
538 /* Check for at least one of these Allocation Types to be set */
539 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
541 DPRINT1("No memory allocation base type\n");
542 return STATUS_INVALID_PARAMETER_5
;
545 /* MEM_RESET is an exclusive flag, make sure that is valid too */
546 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
548 DPRINT1("Invalid use of MEM_RESET\n");
549 return STATUS_INVALID_PARAMETER_5
;
552 /* Check if large pages are being used */
553 if (AllocationType
& MEM_LARGE_PAGES
)
555 /* Large page allocations MUST be committed */
556 if (!(AllocationType
& MEM_COMMIT
))
558 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
559 return STATUS_INVALID_PARAMETER_5
;
562 /* These flags are not allowed with large page allocations */
563 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
565 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
566 return STATUS_INVALID_PARAMETER_5
;
570 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
571 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
573 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
574 return STATUS_INVALID_PARAMETER_5
;
577 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
578 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
580 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
581 return STATUS_INVALID_PARAMETER_5
;
584 /* Check for valid MEM_PHYSICAL usage */
585 if (AllocationType
& MEM_PHYSICAL
)
587 /* Only these flags are allowed with MEM_PHYSIAL */
588 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
590 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
591 return STATUS_INVALID_PARAMETER_5
;
594 /* Then make sure PAGE_READWRITE is used */
595 if (Protect
!= PAGE_READWRITE
)
597 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
598 return STATUS_INVALID_PARAMETER_6
;
602 /* Calculate the protection mask and make sure it's valid */
603 ProtectionMask
= MiMakeProtectionMask(Protect
);
604 if (ProtectionMask
== MM_INVALID_PROTECTION
)
606 DPRINT1("Invalid protection mask\n");
607 return STATUS_INVALID_PAGE_PROTECTION
;
613 /* Check for user-mode parameters */
614 if (PreviousMode
!= KernelMode
)
616 /* Make sure they are writable */
617 ProbeForWritePointer(UBaseAddress
);
618 ProbeForWriteUlong(URegionSize
);
621 /* Capture their values */
622 PBaseAddress
= *UBaseAddress
;
623 PRegionSize
= *URegionSize
;
625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
627 /* Return the exception code */
628 _SEH2_YIELD(return _SEH2_GetExceptionCode());
632 /* Make sure the allocation isn't past the VAD area */
633 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
635 DPRINT1("Virtual allocation base above User Space\n");
636 return STATUS_INVALID_PARAMETER_2
;
639 /* Make sure the allocation wouldn't overflow past the VAD area */
640 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
642 DPRINT1("Region size would overflow into kernel-memory\n");
643 return STATUS_INVALID_PARAMETER_4
;
646 /* Make sure there's a size specified */
649 DPRINT1("Region size is invalid (zero)\n");
650 return STATUS_INVALID_PARAMETER_4
;
653 /* Check if this is for the current process */
654 if (ProcessHandle
== NtCurrentProcess())
656 /* We already have the current process, no need to go through Ob */
657 Process
= CurrentProcess
;
661 /* Reference the handle for correct permissions */
662 Status
= ObReferenceObjectByHandle(ProcessHandle
,
663 PROCESS_VM_OPERATION
,
668 if (!NT_SUCCESS(Status
)) return Status
;
670 /* Check if not running in the current process */
671 if (CurrentProcess
!= Process
)
674 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
679 /* Check for large page allocations */
680 if (AllocationType
& MEM_LARGE_PAGES
)
682 /* The lock memory privilege is required */
683 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
685 /* Fail without it */
686 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
687 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
688 return STATUS_PRIVILEGE_NOT_HELD
;
692 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
693 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)PBaseAddress
+ PRegionSize
) -
694 PAGE_ROUND_DOWN(PBaseAddress
);
698 * Copy on Write is reserved for system use. This case is a certain failure
699 * but there may be other cases...needs more testing
701 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
702 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
)))
704 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
705 return STATUS_INVALID_PAGE_PROTECTION
;
708 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
709 DPRINT("Type %x\n", Type
);
711 AddressSpace
= &Process
->Vm
;
712 MmLockAddressSpace(AddressSpace
);
714 if (PBaseAddress
!= 0)
716 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
718 if (MemoryArea
!= NULL
)
720 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
721 (ULONG_PTR
)MemoryArea
->StartingAddress
;
723 if (((ULONG_PTR
)BaseAddress
+ RegionSize
) > (ULONG_PTR
)MemoryArea
->EndingAddress
)
725 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
726 (ULONG_PTR
)BaseAddress
+ RegionSize
, MemoryArea
->EndingAddress
);
728 MmUnlockAddressSpace(AddressSpace
);
729 if (Attached
) KeUnstackDetachProcess(&ApcState
);
730 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
731 return STATUS_MEMORY_NOT_ALLOCATED
;
734 if (AllocationType
== MEM_RESET
)
736 if (MmIsPagePresent(Process
, BaseAddress
))
738 /* FIXME: mark pages as not modified */
742 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
745 MmUnlockAddressSpace(AddressSpace
);
746 if (Attached
) KeUnstackDetachProcess(&ApcState
);
747 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
749 /* MEM_RESET does not modify any attributes of region */
750 return STATUS_SUCCESS
;
753 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
754 MemoryAreaLength
>= RegionSize
)
757 MmAlterRegion(AddressSpace
,
758 MemoryArea
->StartingAddress
,
759 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
760 BaseAddress
, RegionSize
,
761 Type
, Protect
, MmModifyAttributes
);
762 MmUnlockAddressSpace(AddressSpace
);
763 if (Attached
) KeUnstackDetachProcess(&ApcState
);
764 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
765 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
767 /* Give the caller rounded BaseAddress and area length */
768 if (NT_SUCCESS(Status
))
770 *UBaseAddress
= BaseAddress
;
771 *URegionSize
= RegionSize
;
772 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
777 else if (MemoryAreaLength
>= RegionSize
)
779 /* Region list initialized? */
780 if (MemoryArea
->Data
.SectionData
.RegionListHead
.Flink
)
783 MmAlterRegion(AddressSpace
,
784 MemoryArea
->StartingAddress
,
785 &MemoryArea
->Data
.SectionData
.RegionListHead
,
786 BaseAddress
, RegionSize
,
787 Type
, Protect
, MmModifyAttributes
);
791 Status
= STATUS_ACCESS_VIOLATION
;
794 MmUnlockAddressSpace(AddressSpace
);
795 if (Attached
) KeUnstackDetachProcess(&ApcState
);
796 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
797 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
799 /* Give the caller rounded BaseAddress and area length */
800 if (NT_SUCCESS(Status
))
802 *UBaseAddress
= BaseAddress
;
803 *URegionSize
= RegionSize
;
804 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
811 MmUnlockAddressSpace(AddressSpace
);
812 if (Attached
) KeUnstackDetachProcess(&ApcState
);
813 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
814 return(STATUS_UNSUCCESSFUL
);
819 Status
= MmCreateMemoryArea(AddressSpace
,
820 MEMORY_AREA_VIRTUAL_MEMORY
,
826 AllocationType
& MEM_TOP_DOWN
,
827 BoundaryAddressMultiple
);
828 if (!NT_SUCCESS(Status
))
830 MmUnlockAddressSpace(AddressSpace
);
831 if (Attached
) KeUnstackDetachProcess(&ApcState
);
832 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
833 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
837 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
838 (ULONG_PTR
)MemoryArea
->StartingAddress
;
840 MmInitializeRegion(&MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
841 MemoryAreaLength
, Type
, Protect
);
843 if ((AllocationType
& MEM_COMMIT
) &&
844 (Protect
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)))
846 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
847 MmReserveSwapPages(nPages
);
850 MmUnlockAddressSpace(AddressSpace
);
851 if (Attached
) KeUnstackDetachProcess(&ApcState
);
852 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
854 *UBaseAddress
= BaseAddress
;
855 *URegionSize
= MemoryAreaLength
;
856 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
858 return(STATUS_SUCCESS
);
862 MmFreeVirtualMemoryPage(PVOID Context
,
863 MEMORY_AREA
* MemoryArea
,
869 PEPROCESS Process
= (PEPROCESS
)Context
;
873 SWAPENTRY SavedSwapEntry
;
874 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
875 if (SavedSwapEntry
!= 0)
877 MmFreeSwapPage(SavedSwapEntry
);
878 MmSetSavedSwapEntryPage(Page
, 0);
880 MmDeleteRmap(Page
, Process
, Address
);
881 MmReleasePageMemoryConsumer(MC_USER
, Page
);
883 else if (SwapEntry
!= 0)
885 MmFreeSwapPage(SwapEntry
);
891 MmFreeVirtualMemory(PEPROCESS Process
,
892 PMEMORY_AREA MemoryArea
)
894 PLIST_ENTRY current_entry
;
898 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process
,
901 /* Mark this memory area as about to be deleted. */
902 MemoryArea
->DeleteInProgress
= TRUE
;
905 * Wait for any ongoing paging operations. Notice that since we have
906 * flagged this memory area as deleted no more page ops will be added.
908 if (MemoryArea
->PageOpCount
> 0)
910 ULONG_PTR MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
911 (ULONG_PTR
)MemoryArea
->StartingAddress
;
912 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
914 for (i
= 0; i
< nPages
&& MemoryArea
->PageOpCount
!= 0; ++i
)
917 PageOp
= MmCheckForPageOp(MemoryArea
, Process
->UniqueProcessId
,
918 (PVOID
)((ULONG_PTR
)MemoryArea
->StartingAddress
+ (i
* PAGE_SIZE
)),
923 MmUnlockAddressSpace(&Process
->Vm
);
924 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
929 if (Status
!= STATUS_SUCCESS
)
931 DPRINT1("Failed to wait for page op\n");
932 KeBugCheck(MEMORY_MANAGEMENT
);
934 MmLockAddressSpace(&Process
->Vm
);
935 MmReleasePageOp(PageOp
);
940 /* Free all the individual segments. */
941 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
.Flink
;
942 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
)
944 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
945 current_entry
= current_entry
->Flink
;
949 /* Actually free the memory area. */
950 MmFreeMemoryArea(&Process
->Vm
,
952 MmFreeVirtualMemoryPage
,
960 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
961 IN PVOID
* UBaseAddress
,
962 IN PSIZE_T URegionSize
,
965 * FUNCTION: Frees a range of virtual memory
967 * ProcessHandle = Points to the process that allocated the virtual
969 * BaseAddress = Points to the memory address, rounded down to a
970 * multiple of the pagesize
971 * RegionSize = Limits the range to free, rounded up to a multiple of
973 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
977 MEMORY_AREA
* MemoryArea
;
980 PMMSUPPORT AddressSpace
;
981 PVOID BaseAddress
, PBaseAddress
;
982 ULONG RegionSize
, PRegionSize
;
983 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
984 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
986 BOOLEAN Attached
= FALSE
;
989 /* Only two flags are supported */
990 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
992 DPRINT1("Invalid FreeType\n");
993 return STATUS_INVALID_PARAMETER_4
;
996 /* Check if no flag was used, or if both flags were used */
997 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
998 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
1000 DPRINT1("Invalid FreeType combination\n");
1001 return STATUS_INVALID_PARAMETER_4
;
1007 /* Check for user-mode parameters */
1008 if (PreviousMode
!= KernelMode
)
1010 /* Make sure they are writable */
1011 ProbeForWritePointer(UBaseAddress
);
1012 ProbeForWriteUlong(URegionSize
);
1015 /* Capture their values */
1016 PBaseAddress
= *UBaseAddress
;
1017 PRegionSize
= *URegionSize
;
1019 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1021 /* Return the exception code */
1022 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1026 /* Make sure the allocation isn't past the user area */
1027 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
1029 DPRINT1("Virtual free base above User Space\n");
1030 return STATUS_INVALID_PARAMETER_2
;
1033 /* Make sure the allocation wouldn't overflow past the user area */
1034 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
1036 DPRINT1("Region size would overflow into kernel-memory\n");
1037 return STATUS_INVALID_PARAMETER_3
;
1040 /* Check if this is for the current process */
1041 if (ProcessHandle
== NtCurrentProcess())
1043 /* We already have the current process, no need to go through Ob */
1044 Process
= CurrentProcess
;
1048 /* Reference the handle for correct permissions */
1049 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1050 PROCESS_VM_OPERATION
,
1055 if (!NT_SUCCESS(Status
)) return Status
;
1057 /* Check if not running in the current process */
1058 if (CurrentProcess
!= Process
)
1061 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1066 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((PBaseAddress
));
1067 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)(PBaseAddress
) + (PRegionSize
)) -
1068 PAGE_ROUND_DOWN((PBaseAddress
));
1070 AddressSpace
= &Process
->Vm
;
1072 MmLockAddressSpace(AddressSpace
);
1073 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
1074 if (MemoryArea
== NULL
)
1076 Status
= STATUS_UNSUCCESSFUL
;
1077 goto unlock_deref_and_return
;
1083 /* We can only free a memory area in one step. */
1084 if (MemoryArea
->StartingAddress
!= BaseAddress
||
1085 MemoryArea
->Type
!= MEMORY_AREA_VIRTUAL_MEMORY
)
1087 Status
= STATUS_UNSUCCESSFUL
;
1088 goto unlock_deref_and_return
;
1091 MmFreeVirtualMemory(Process
, MemoryArea
);
1092 Status
= STATUS_SUCCESS
;
1093 goto unlock_deref_and_return
;
1097 MmAlterRegion(AddressSpace
,
1098 MemoryArea
->StartingAddress
,
1099 (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
) ?
1100 &MemoryArea
->Data
.SectionData
.RegionListHead
:
1101 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1106 MmModifyAttributes
);
1107 goto unlock_deref_and_return
;
1110 Status
= STATUS_NOT_IMPLEMENTED
;
1112 unlock_deref_and_return
:
1114 MmUnlockAddressSpace(AddressSpace
);
1115 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1116 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1123 MmProtectAnonMem(PMMSUPPORT AddressSpace
,
1124 PMEMORY_AREA MemoryArea
,
1131 NTSTATUS Status
= STATUS_SUCCESS
;
1132 ULONG LengthCount
= 0;
1134 /* Search all Regions in MemoryArea up to Length */
1135 /* Every Region up to Length must be committed for success */
1138 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1139 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1140 (PVOID
)((ULONG_PTR
)BaseAddress
+ (ULONG_PTR
)LengthCount
), NULL
);
1142 /* If a Region was found and it is committed */
1143 if ((Region
) && (Region
->Type
== MEM_COMMIT
))
1145 LengthCount
+= Region
->Length
;
1146 if (Length
<= LengthCount
) break;
1149 /* If Region was found and it is not commited */
1152 Status
= STATUS_NOT_COMMITTED
;
1155 /* If no Region was found at all */
1156 else if (LengthCount
== 0)
1158 Status
= STATUS_INVALID_ADDRESS
;
1163 if (NT_SUCCESS(Status
))
1165 *OldProtect
= Region
->Protect
;
1166 Status
= MmAlterRegion(AddressSpace
, MemoryArea
->StartingAddress
,
1167 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1168 BaseAddress
, Length
, Region
->Type
, Protect
,
1169 MmModifyAttributes
);
1176 MmQueryAnonMem(PMEMORY_AREA MemoryArea
,
1178 PMEMORY_BASIC_INFORMATION Info
,
1179 PSIZE_T ResultLength
)
1182 PVOID RegionBase
= NULL
;
1184 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
1186 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1187 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1188 Address
, &RegionBase
);
1189 Info
->BaseAddress
= RegionBase
;
1190 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
1191 Info
->AllocationProtect
= MemoryArea
->Protect
;
1192 Info
->RegionSize
= Region
->Length
;
1193 Info
->State
= Region
->Type
;
1194 Info
->Protect
= Region
->Protect
;
1195 Info
->Type
= MEM_PRIVATE
;
1197 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1198 return(STATUS_SUCCESS
);