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 * Check the reference count to ensure this page can be paged out
81 Page
= MmGetPfnForProcess(Process
, Address
);
82 if (MmGetReferenceCountPage(Page
) != 1)
84 DPRINT1("Cannot page out locked virtual memory page: 0x%p (RefCount: %d)\n",
85 Page
, MmGetReferenceCountPage(Page
));
86 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
87 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
88 MmReleasePageOp(PageOp
);
89 return(STATUS_UNSUCCESSFUL
);
93 * Disable the virtual mapping.
95 MmDisableVirtualMapping(Process
, Address
,
100 KeBugCheck(MEMORY_MANAGEMENT
);
104 * Paging out non-dirty data is easy.
108 MmLockAddressSpace(AddressSpace
);
109 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
110 MmDeleteAllRmaps(Page
, NULL
, NULL
);
111 if ((SwapEntry
= MmGetSavedSwapEntryPage(Page
)) != 0)
113 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
114 MmSetSavedSwapEntryPage(Page
, 0);
116 MmUnlockAddressSpace(AddressSpace
);
117 MmReleasePageMemoryConsumer(MC_USER
, Page
);
118 PageOp
->Status
= STATUS_SUCCESS
;
119 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
120 MmReleasePageOp(PageOp
);
121 return(STATUS_SUCCESS
);
125 * If necessary, allocate an entry in the paging file for this page
127 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
130 SwapEntry
= MmAllocSwapPage();
133 MmShowOutOfSpaceMessagePagingFile();
134 MmEnableVirtualMapping(Process
, Address
);
135 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
136 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
137 MmReleasePageOp(PageOp
);
138 return(STATUS_PAGEFILE_QUOTA
);
143 * Write the page to the pagefile
145 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
146 if (!NT_SUCCESS(Status
))
148 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
150 MmEnableVirtualMapping(Process
, Address
);
151 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
152 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
153 MmReleasePageOp(PageOp
);
154 return(STATUS_UNSUCCESSFUL
);
158 * Otherwise we have succeeded, free the page
160 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page
<< PAGE_SHIFT
);
161 MmLockAddressSpace(AddressSpace
);
162 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
163 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
164 MmUnlockAddressSpace(AddressSpace
);
165 MmDeleteAllRmaps(Page
, NULL
, NULL
);
166 MmSetSavedSwapEntryPage(Page
, 0);
167 MmReleasePageMemoryConsumer(MC_USER
, Page
);
168 PageOp
->Status
= STATUS_SUCCESS
;
169 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
170 MmReleasePageOp(PageOp
);
171 return(STATUS_SUCCESS
);
176 MmNotPresentFaultVirtualMemory(PMMSUPPORT AddressSpace
,
177 MEMORY_AREA
* MemoryArea
,
180 * FUNCTION: Move data into memory to satisfy a page not present fault
182 * AddressSpace = Address space within which the fault occurred
183 * MemoryArea = The memory area within which the fault occurred
184 * Address = The absolute address of fault
186 * NOTES: This function is called with the address space lock held.
193 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
196 * There is a window between taking the page fault and locking the
197 * address space when another thread could load the page so we check
200 if (MmIsPagePresent(Process
, Address
))
202 return(STATUS_SUCCESS
);
206 * Check for the virtual memory area being deleted.
208 if (MemoryArea
->DeleteInProgress
)
210 return(STATUS_UNSUCCESSFUL
);
214 * Get the segment corresponding to the virtual address
216 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
217 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
220 if (Region
->Type
== MEM_RESERVE
|| Region
->Protect
== PAGE_NOACCESS
)
222 return(STATUS_ACCESS_VIOLATION
);
228 if (Region
->Protect
& PAGE_GUARD
)
230 return(STATUS_GUARD_PAGE_VIOLATION
);
234 * Get or create a page operation
236 PageOp
= MmGetPageOp(MemoryArea
, Process
->UniqueProcessId
,
237 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
238 MM_PAGEOP_PAGEIN
, FALSE
);
241 DPRINT1("MmGetPageOp failed");
242 KeBugCheck(MEMORY_MANAGEMENT
);
246 * Check if someone else is already handling this fault, if so wait
249 if (PageOp
->Thread
!= PsGetCurrentThread())
251 MmUnlockAddressSpace(AddressSpace
);
252 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
258 * Check for various strange conditions
260 if (Status
!= STATUS_SUCCESS
)
262 DPRINT1("Failed to wait for page op\n");
263 KeBugCheck(MEMORY_MANAGEMENT
);
265 if (PageOp
->Status
== STATUS_PENDING
)
267 DPRINT1("Woke for page op before completion\n");
268 KeBugCheck(MEMORY_MANAGEMENT
);
271 * If this wasn't a pagein then we need to restart the handling
273 if (PageOp
->OpType
!= MM_PAGEOP_PAGEIN
)
275 MmLockAddressSpace(AddressSpace
);
276 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
277 MmReleasePageOp(PageOp
);
278 return(STATUS_MM_RESTART_OPERATION
);
281 * If the thread handling this fault has failed then we don't retry
283 if (!NT_SUCCESS(PageOp
->Status
))
285 MmLockAddressSpace(AddressSpace
);
286 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
287 Status
= PageOp
->Status
;
288 MmReleasePageOp(PageOp
);
291 MmLockAddressSpace(AddressSpace
);
292 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
293 MmReleasePageOp(PageOp
);
294 return(STATUS_SUCCESS
);
298 * Try to allocate a page
300 MI_SET_USAGE(MI_USAGE_VAD
);
301 MI_SET_PROCESS2(Process
->ImageFileName
);
302 Status
= MmRequestPageMemoryConsumer(MC_USER
, FALSE
, &Page
);
303 if (Status
== STATUS_NO_MEMORY
)
305 MmUnlockAddressSpace(AddressSpace
);
306 Status
= MmRequestPageMemoryConsumer(MC_USER
, TRUE
, &Page
);
307 MmLockAddressSpace(AddressSpace
);
309 if (!NT_SUCCESS(Status
))
311 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status
);
312 KeBugCheck(MEMORY_MANAGEMENT
);
316 * Handle swapped out pages.
318 if (MmIsPageSwapEntry(Process
, Address
))
322 MmDeletePageFileMapping(Process
, Address
, &SwapEntry
);
323 Status
= MmReadFromSwapPage(SwapEntry
, Page
);
324 if (!NT_SUCCESS(Status
))
326 KeBugCheck(MEMORY_MANAGEMENT
);
328 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
332 * Set the page. If we fail because we are out of memory then
335 Status
= MmCreateVirtualMapping(Process
,
336 (PVOID
)PAGE_ROUND_DOWN(Address
),
340 while (Status
== STATUS_NO_MEMORY
)
342 MmUnlockAddressSpace(AddressSpace
);
343 Status
= MmCreateVirtualMapping(Process
,
344 (PVOID
)PAGE_ROUND_DOWN(Address
),
348 MmLockAddressSpace(AddressSpace
);
350 if (!NT_SUCCESS(Status
))
352 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
353 KeBugCheck(MEMORY_MANAGEMENT
);
358 * Add the page to the process's working set
360 MmInsertRmap(Page
, Process
, (PVOID
)PAGE_ROUND_DOWN(Address
));
363 * Finish the operation
365 PageOp
->Status
= STATUS_SUCCESS
;
366 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
367 MmReleasePageOp(PageOp
);
368 return(STATUS_SUCCESS
);
372 MmModifyAttributes(PMMSUPPORT AddressSpace
,
380 * FUNCTION: Modify the attributes of a memory region
383 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
386 * If we are switching a previously committed region to reserved then
387 * free any allocated pages within the region
389 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
393 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
397 if (MmIsPageSwapEntry(Process
,
398 (char*)BaseAddress
+ (i
* PAGE_SIZE
)))
402 MmDeletePageFileMapping(Process
,
403 (char*)BaseAddress
+ (i
* PAGE_SIZE
),
405 MmFreeSwapPage(SwapEntry
);
409 MmDeleteVirtualMapping(Process
,
410 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
414 SWAPENTRY SavedSwapEntry
;
415 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
416 if (SavedSwapEntry
!= 0)
418 MmFreeSwapPage(SavedSwapEntry
);
419 MmSetSavedSwapEntryPage(Page
, 0);
421 MmDeleteRmap(Page
, Process
,
422 (char*)BaseAddress
+ (i
* PAGE_SIZE
));
423 MmReleasePageMemoryConsumer(MC_USER
, Page
);
430 * If we are changing the protection attributes of a committed region then
431 * alter the attributes for any allocated pages within the region
433 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
434 OldProtect
!= NewProtect
)
439 char* addr
= (char*)BaseAddress
;
441 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
443 MArea
= MmLocateMemoryAreaByAddress(AddressSpace
, addr
);
446 PageOp
= MmGetPageOp(MArea
, Process
->UniqueProcessId
, addr
,
447 NULL
, 0, MM_PAGEOP_CHANGEPROTECT
, TRUE
);
448 } while(PageOp
== NULL
);
450 /* Should we enable/disable virtual mapping? */
451 if((NewProtect
& PAGE_NOACCESS
) &&
452 !(OldProtect
& PAGE_NOACCESS
) &&
453 (MmIsPagePresent(Process
, addr
)))
455 /* Set other flags if any */
456 if(NewProtect
!= PAGE_NOACCESS
)
457 MmSetPageProtect(Process
, addr
, NewProtect
& ~PAGE_NOACCESS
);
458 MmDisableVirtualMapping(Process
, addr
, NULL
, NULL
);
460 else if((OldProtect
& PAGE_NOACCESS
) && !(NewProtect
& PAGE_NOACCESS
))
462 MmEnableVirtualMapping(Process
, addr
);
465 /* Set new protection flags */
466 if(MmIsPagePresent(Process
, addr
))
468 MmSetPageProtect(Process
, addr
, NewProtect
);
470 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
471 MmReleasePageOp(PageOp
);
478 MiProtectVirtualMemory(IN PEPROCESS Process
,
479 IN OUT PVOID
*BaseAddress
,
480 IN OUT PSIZE_T NumberOfBytesToProtect
,
481 IN ULONG NewAccessProtection
,
482 OUT PULONG OldAccessProtection OPTIONAL
)
484 PMEMORY_AREA MemoryArea
;
485 PMMSUPPORT AddressSpace
;
486 ULONG OldAccessProtection_
;
489 *NumberOfBytesToProtect
=
490 PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) -
491 PAGE_ROUND_DOWN(*BaseAddress
);
492 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
494 AddressSpace
= &Process
->Vm
;
496 MmLockAddressSpace(AddressSpace
);
497 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
498 if (MemoryArea
== NULL
)
500 MmUnlockAddressSpace(AddressSpace
);
501 return STATUS_UNSUCCESSFUL
;
504 if (OldAccessProtection
== NULL
)
505 OldAccessProtection
= &OldAccessProtection_
;
507 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
509 Status
= MmProtectAnonMem(AddressSpace
, MemoryArea
, *BaseAddress
,
510 *NumberOfBytesToProtect
, NewAccessProtection
,
511 OldAccessProtection
);
513 else if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
515 Status
= MmProtectSectionView(AddressSpace
, MemoryArea
, *BaseAddress
,
516 *NumberOfBytesToProtect
,
518 OldAccessProtection
);
522 /* FIXME: Should we return failure or success in this case? */
523 Status
= STATUS_CONFLICTING_ADDRESSES
;
526 MmUnlockAddressSpace(AddressSpace
);
536 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
537 IN OUT PVOID
* UBaseAddress
,
538 IN ULONG_PTR ZeroBits
,
539 IN OUT PSIZE_T URegionSize
,
540 IN ULONG AllocationType
,
544 MEMORY_AREA
* MemoryArea
;
545 ULONG_PTR MemoryAreaLength
;
548 PMMSUPPORT AddressSpace
;
552 ULONG_PTR PRegionSize
;
553 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
554 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
555 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
557 ULONG ProtectionMask
;
558 BOOLEAN Attached
= FALSE
;
559 BoundaryAddressMultiple
.QuadPart
= 0;
562 /* Check for valid Zero bits */
565 DPRINT1("Too many zero bits\n");
566 return STATUS_INVALID_PARAMETER_3
;
569 /* Check for valid Allocation Types */
570 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
571 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
573 DPRINT1("Invalid Allocation Type\n");
574 return STATUS_INVALID_PARAMETER_5
;
577 /* Check for at least one of these Allocation Types to be set */
578 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
580 DPRINT1("No memory allocation base type\n");
581 return STATUS_INVALID_PARAMETER_5
;
584 /* MEM_RESET is an exclusive flag, make sure that is valid too */
585 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
587 DPRINT1("Invalid use of MEM_RESET\n");
588 return STATUS_INVALID_PARAMETER_5
;
591 /* Check if large pages are being used */
592 if (AllocationType
& MEM_LARGE_PAGES
)
594 /* Large page allocations MUST be committed */
595 if (!(AllocationType
& MEM_COMMIT
))
597 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
598 return STATUS_INVALID_PARAMETER_5
;
601 /* These flags are not allowed with large page allocations */
602 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
604 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
605 return STATUS_INVALID_PARAMETER_5
;
609 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
610 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
612 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
613 return STATUS_INVALID_PARAMETER_5
;
616 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
617 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
619 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
620 return STATUS_INVALID_PARAMETER_5
;
623 /* Check for valid MEM_PHYSICAL usage */
624 if (AllocationType
& MEM_PHYSICAL
)
626 /* Only these flags are allowed with MEM_PHYSIAL */
627 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
629 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
630 return STATUS_INVALID_PARAMETER_5
;
633 /* Then make sure PAGE_READWRITE is used */
634 if (Protect
!= PAGE_READWRITE
)
636 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
637 return STATUS_INVALID_PARAMETER_6
;
641 /* Calculate the protection mask and make sure it's valid */
642 ProtectionMask
= MiMakeProtectionMask(Protect
);
643 if (ProtectionMask
== MM_INVALID_PROTECTION
)
645 DPRINT1("Invalid protection mask\n");
646 return STATUS_INVALID_PAGE_PROTECTION
;
652 /* Check for user-mode parameters */
653 if (PreviousMode
!= KernelMode
)
655 /* Make sure they are writable */
656 ProbeForWritePointer(UBaseAddress
);
657 ProbeForWriteUlong(URegionSize
);
660 /* Capture their values */
661 PBaseAddress
= *UBaseAddress
;
662 PRegionSize
= *URegionSize
;
664 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
666 /* Return the exception code */
667 _SEH2_YIELD(return _SEH2_GetExceptionCode());
671 /* Make sure the allocation isn't past the VAD area */
672 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
674 DPRINT1("Virtual allocation base above User Space\n");
675 return STATUS_INVALID_PARAMETER_2
;
678 /* Make sure the allocation wouldn't overflow past the VAD area */
679 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
681 DPRINT1("Region size would overflow into kernel-memory\n");
682 return STATUS_INVALID_PARAMETER_4
;
685 /* Make sure there's a size specified */
688 DPRINT1("Region size is invalid (zero)\n");
689 return STATUS_INVALID_PARAMETER_4
;
692 /* Check if this is for the current process */
693 if (ProcessHandle
== NtCurrentProcess())
695 /* We already have the current process, no need to go through Ob */
696 Process
= CurrentProcess
;
700 /* Reference the handle for correct permissions */
701 Status
= ObReferenceObjectByHandle(ProcessHandle
,
702 PROCESS_VM_OPERATION
,
707 if (!NT_SUCCESS(Status
)) return Status
;
709 /* Check if not running in the current process */
710 if (CurrentProcess
!= Process
)
713 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
718 /* Check for large page allocations */
719 if (AllocationType
& MEM_LARGE_PAGES
)
721 /* The lock memory privilege is required */
722 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
724 /* Fail without it */
725 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
726 if (Attached
) KeUnstackDetachProcess(&ApcState
);
727 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
728 return STATUS_PRIVILEGE_NOT_HELD
;
732 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
733 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)PBaseAddress
+ PRegionSize
) -
734 PAGE_ROUND_DOWN(PBaseAddress
);
738 * Copy on Write is reserved for system use. This case is a certain failure
739 * but there may be other cases...needs more testing
741 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
742 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
)))
744 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
745 if (Attached
) KeUnstackDetachProcess(&ApcState
);
746 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
747 return STATUS_INVALID_PAGE_PROTECTION
;
750 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
751 DPRINT("Type %x\n", Type
);
753 AddressSpace
= &Process
->Vm
;
754 MmLockAddressSpace(AddressSpace
);
756 if (PBaseAddress
!= 0)
758 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
760 if (MemoryArea
!= NULL
)
762 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
763 (ULONG_PTR
)MemoryArea
->StartingAddress
;
765 if (((ULONG_PTR
)BaseAddress
+ RegionSize
) > (ULONG_PTR
)MemoryArea
->EndingAddress
)
767 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
768 (ULONG_PTR
)BaseAddress
+ RegionSize
, MemoryArea
->EndingAddress
);
770 MmUnlockAddressSpace(AddressSpace
);
771 if (Attached
) KeUnstackDetachProcess(&ApcState
);
772 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
773 return STATUS_MEMORY_NOT_ALLOCATED
;
776 if (AllocationType
== MEM_RESET
)
778 if (MmIsPagePresent(Process
, BaseAddress
))
780 /* FIXME: mark pages as not modified */
784 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
787 MmUnlockAddressSpace(AddressSpace
);
788 if (Attached
) KeUnstackDetachProcess(&ApcState
);
789 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
791 /* MEM_RESET does not modify any attributes of region */
792 return STATUS_SUCCESS
;
795 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
796 MemoryAreaLength
>= RegionSize
)
798 Status
= MmAlterRegion(AddressSpace
,
799 MemoryArea
->StartingAddress
,
800 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
801 BaseAddress
, RegionSize
,
802 Type
, Protect
, MmModifyAttributes
);
803 MmUnlockAddressSpace(AddressSpace
);
804 if (Attached
) KeUnstackDetachProcess(&ApcState
);
805 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
806 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
808 /* Give the caller rounded BaseAddress and area length */
809 if (NT_SUCCESS(Status
))
811 *UBaseAddress
= BaseAddress
;
812 *URegionSize
= RegionSize
;
813 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
818 else if (MemoryAreaLength
>= RegionSize
)
820 /* Region list initialized? */
821 if (MemoryArea
->Data
.SectionData
.RegionListHead
.Flink
)
823 Status
= MmAlterRegion(AddressSpace
,
824 MemoryArea
->StartingAddress
,
825 &MemoryArea
->Data
.SectionData
.RegionListHead
,
826 BaseAddress
, RegionSize
,
827 Type
, Protect
, MmModifyAttributes
);
831 Status
= STATUS_ACCESS_VIOLATION
;
834 MmUnlockAddressSpace(AddressSpace
);
835 if (Attached
) KeUnstackDetachProcess(&ApcState
);
836 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
837 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
839 /* Give the caller rounded BaseAddress and area length */
840 if (NT_SUCCESS(Status
))
842 *UBaseAddress
= BaseAddress
;
843 *URegionSize
= RegionSize
;
844 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
851 MmUnlockAddressSpace(AddressSpace
);
852 if (Attached
) KeUnstackDetachProcess(&ApcState
);
853 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
854 return(STATUS_UNSUCCESSFUL
);
859 Status
= MmCreateMemoryArea(AddressSpace
,
860 MEMORY_AREA_VIRTUAL_MEMORY
,
866 AllocationType
& MEM_TOP_DOWN
,
867 BoundaryAddressMultiple
);
868 if (!NT_SUCCESS(Status
))
870 MmUnlockAddressSpace(AddressSpace
);
871 if (Attached
) KeUnstackDetachProcess(&ApcState
);
872 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
873 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
877 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
878 (ULONG_PTR
)MemoryArea
->StartingAddress
;
880 MmInitializeRegion(&MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
881 MemoryAreaLength
, Type
, Protect
);
883 if ((AllocationType
& MEM_COMMIT
) &&
884 (Protect
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)))
886 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
887 MmReserveSwapPages(nPages
);
890 MmUnlockAddressSpace(AddressSpace
);
891 if (Attached
) KeUnstackDetachProcess(&ApcState
);
892 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
894 *UBaseAddress
= BaseAddress
;
895 *URegionSize
= MemoryAreaLength
;
896 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
898 return(STATUS_SUCCESS
);
902 MmFreeVirtualMemoryPage(PVOID Context
,
903 MEMORY_AREA
* MemoryArea
,
909 PEPROCESS Process
= (PEPROCESS
)Context
;
913 SWAPENTRY SavedSwapEntry
;
914 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
915 if (SavedSwapEntry
!= 0)
917 MmFreeSwapPage(SavedSwapEntry
);
918 MmSetSavedSwapEntryPage(Page
, 0);
920 MmDeleteRmap(Page
, Process
, Address
);
921 MmReleasePageMemoryConsumer(MC_USER
, Page
);
923 else if (SwapEntry
!= 0)
925 MmFreeSwapPage(SwapEntry
);
931 MmFreeVirtualMemory(PEPROCESS Process
,
932 PMEMORY_AREA MemoryArea
)
934 PLIST_ENTRY current_entry
;
938 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process
,
941 /* Mark this memory area as about to be deleted. */
942 MemoryArea
->DeleteInProgress
= TRUE
;
945 * Wait for any ongoing paging operations. Notice that since we have
946 * flagged this memory area as deleted no more page ops will be added.
948 if (MemoryArea
->PageOpCount
> 0)
950 ULONG_PTR MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
951 (ULONG_PTR
)MemoryArea
->StartingAddress
;
952 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
954 for (i
= 0; i
< nPages
&& MemoryArea
->PageOpCount
!= 0; ++i
)
957 PageOp
= MmCheckForPageOp(MemoryArea
, Process
->UniqueProcessId
,
958 (PVOID
)((ULONG_PTR
)MemoryArea
->StartingAddress
+ (i
* PAGE_SIZE
)),
963 MmUnlockAddressSpace(&Process
->Vm
);
964 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
969 if (Status
!= STATUS_SUCCESS
)
971 DPRINT1("Failed to wait for page op\n");
972 KeBugCheck(MEMORY_MANAGEMENT
);
974 MmLockAddressSpace(&Process
->Vm
);
975 MmReleasePageOp(PageOp
);
980 /* Free all the individual segments. */
981 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
.Flink
;
982 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
)
984 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
985 current_entry
= current_entry
->Flink
;
989 /* Actually free the memory area. */
990 MmFreeMemoryArea(&Process
->Vm
,
992 MmFreeVirtualMemoryPage
,
1000 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
1001 IN PVOID
* UBaseAddress
,
1002 IN PSIZE_T URegionSize
,
1005 * FUNCTION: Frees a range of virtual memory
1007 * ProcessHandle = Points to the process that allocated the virtual
1009 * BaseAddress = Points to the memory address, rounded down to a
1010 * multiple of the pagesize
1011 * RegionSize = Limits the range to free, rounded up to a multiple of
1013 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
1017 MEMORY_AREA
* MemoryArea
;
1020 PMMSUPPORT AddressSpace
;
1021 PVOID BaseAddress
= NULL
, PBaseAddress
;
1022 SIZE_T RegionSize
= 0, PRegionSize
;
1023 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1024 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
1025 KAPC_STATE ApcState
;
1026 BOOLEAN Attached
= FALSE
;
1029 /* Only two flags are supported */
1030 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
1032 DPRINT1("Invalid FreeType\n");
1033 return STATUS_INVALID_PARAMETER_4
;
1036 /* Check if no flag was used, or if both flags were used */
1037 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
1038 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
1040 DPRINT1("Invalid FreeType combination\n");
1041 return STATUS_INVALID_PARAMETER_4
;
1047 /* Check for user-mode parameters */
1048 if (PreviousMode
!= KernelMode
)
1050 /* Make sure they are writeable */
1051 ProbeForWritePointer(UBaseAddress
);
1052 ProbeForWriteUlong(URegionSize
);
1055 /* Capture their values */
1056 PBaseAddress
= *UBaseAddress
;
1057 PRegionSize
= *URegionSize
;
1059 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1061 /* Return the exception code */
1062 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1066 /* Make sure the allocation isn't past the user area */
1067 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
1069 DPRINT1("Virtual free base above User Space\n");
1070 return STATUS_INVALID_PARAMETER_2
;
1073 /* Make sure the allocation wouldn't overflow past the user area */
1074 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
1076 DPRINT1("Region size would overflow into kernel-memory\n");
1077 return STATUS_INVALID_PARAMETER_3
;
1080 /* Check if this is for the current process */
1081 if (ProcessHandle
== NtCurrentProcess())
1083 /* We already have the current process, no need to go through Ob */
1084 Process
= CurrentProcess
;
1088 /* Reference the handle for correct permissions */
1089 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1090 PROCESS_VM_OPERATION
,
1095 if (!NT_SUCCESS(Status
)) return Status
;
1097 /* Check if not running in the current process */
1098 if (CurrentProcess
!= Process
)
1101 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1106 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((PBaseAddress
));
1108 AddressSpace
= &Process
->Vm
;
1110 MmLockAddressSpace(AddressSpace
);
1111 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
1112 if (MemoryArea
== NULL
)
1114 DPRINT1("Unable to find memory area from address 0x%p\n", BaseAddress
);
1115 Status
= STATUS_UNABLE_TO_FREE_VM
;
1116 goto unlock_deref_and_return
;
1119 if (PRegionSize
!= 0)
1121 /* Use the region the caller wanted, rounded to whole pages */
1122 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)(PBaseAddress
) + (PRegionSize
)) -
1123 PAGE_ROUND_DOWN((PBaseAddress
));
1127 /* The caller wanted the whole memory area */
1128 RegionSize
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
1129 (ULONG_PTR
)MemoryArea
->StartingAddress
;
1135 /* MEM_RELEASE must be used with the exact base and length
1136 * that was returned by NtAllocateVirtualMemory */
1138 /* Verify the base address is correct */
1139 if (MemoryArea
->StartingAddress
!= BaseAddress
)
1141 DPRINT1("Base address is illegal for MEM_RELEASE (0x%p != 0x%p)\n",
1142 MemoryArea
->StartingAddress
,
1144 Status
= STATUS_UNABLE_TO_FREE_VM
;
1145 goto unlock_deref_and_return
;
1148 /* Verify the region size is correct */
1149 if ((ULONG_PTR
)MemoryArea
->StartingAddress
+ RegionSize
!=
1150 (ULONG_PTR
)MemoryArea
->EndingAddress
)
1152 DPRINT1("Region size is illegal for MEM_RELEASE (0x%x)\n", RegionSize
);
1153 Status
= STATUS_UNABLE_TO_FREE_VM
;
1154 //goto unlock_deref_and_return;
1157 if (MemoryArea
->Type
!= MEMORY_AREA_VIRTUAL_MEMORY
)
1159 DPRINT1("Memory area is not VM\n");
1160 Status
= STATUS_UNABLE_TO_FREE_VM
;
1161 goto unlock_deref_and_return
;
1164 MmFreeVirtualMemory(Process
, MemoryArea
);
1165 Status
= STATUS_SUCCESS
;
1169 if ((ULONG_PTR
)BaseAddress
+ RegionSize
>
1170 (ULONG_PTR
)MemoryArea
->EndingAddress
)
1172 DPRINT1("Invald base address (0x%p) and region size (0x%x) for MEM_DECOMMIT\n",
1173 BaseAddress
, RegionSize
);
1174 Status
= STATUS_UNABLE_TO_FREE_VM
;
1175 goto unlock_deref_and_return
;
1177 Status
= MmAlterRegion(AddressSpace
,
1178 MemoryArea
->StartingAddress
,
1179 (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
) ?
1180 &MemoryArea
->Data
.SectionData
.RegionListHead
:
1181 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1186 MmModifyAttributes
);
1187 if (!NT_SUCCESS(Status
))
1189 DPRINT1("MmAlterRegion failed with status 0x%x\n", Status
);
1190 Status
= STATUS_UNABLE_TO_FREE_VM
;
1191 goto unlock_deref_and_return
;
1196 Status
= STATUS_NOT_IMPLEMENTED
;
1197 goto unlock_deref_and_return
;
1200 unlock_deref_and_return
:
1201 MmUnlockAddressSpace(AddressSpace
);
1203 /* Copy rounded values back in success case */
1204 if (NT_SUCCESS(Status
))
1209 *UBaseAddress
= BaseAddress
;
1210 *URegionSize
= RegionSize
;
1212 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1214 Status
= _SEH2_GetExceptionCode();
1215 DPRINT1("Failed to copy values back! (Status: 0x%x)\n", Status
);
1220 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1221 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1228 MmProtectAnonMem(PMMSUPPORT AddressSpace
,
1229 PMEMORY_AREA MemoryArea
,
1236 NTSTATUS Status
= STATUS_SUCCESS
;
1237 ULONG_PTR LengthCount
= 0;
1239 /* Search all Regions in MemoryArea up to Length */
1240 /* Every Region up to Length must be committed for success */
1243 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1244 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1245 (PVOID
)((ULONG_PTR
)BaseAddress
+ LengthCount
), NULL
);
1247 /* If a Region was found and it is committed */
1248 if ((Region
) && (Region
->Type
== MEM_COMMIT
))
1250 LengthCount
+= Region
->Length
;
1251 if (Length
<= LengthCount
) break;
1254 /* If Region was found and it is not commited */
1257 Status
= STATUS_NOT_COMMITTED
;
1260 /* If no Region was found at all */
1261 else if (LengthCount
== 0)
1263 Status
= STATUS_INVALID_ADDRESS
;
1268 if (NT_SUCCESS(Status
))
1270 *OldProtect
= Region
->Protect
;
1271 Status
= MmAlterRegion(AddressSpace
, MemoryArea
->StartingAddress
,
1272 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1273 BaseAddress
, Length
, Region
->Type
, Protect
,
1274 MmModifyAttributes
);
1281 MmQueryAnonMem(PMEMORY_AREA MemoryArea
,
1283 PMEMORY_BASIC_INFORMATION Info
,
1284 PSIZE_T ResultLength
)
1287 PVOID RegionBase
= NULL
;
1289 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
1291 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1292 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1293 Address
, &RegionBase
);
1294 Info
->BaseAddress
= RegionBase
;
1295 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
1296 Info
->AllocationProtect
= MemoryArea
->Protect
;
1297 Info
->RegionSize
= Region
->Length
;
1298 Info
->State
= Region
->Type
;
1299 Info
->Protect
= Region
->Protect
;
1300 Info
->Type
= MEM_PRIVATE
;
1302 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1303 return(STATUS_SUCCESS
);