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
,
166 * FUNCTION: Move data into memory to satisfy a page not present fault
168 * AddressSpace = Address space within which the fault occurred
169 * MemoryArea = The memory area within which the fault occurred
170 * Address = The absolute address of fault
172 * NOTES: This function is called with the address space lock held.
179 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
182 * There is a window between taking the page fault and locking the
183 * address space when another thread could load the page so we check
186 if (MmIsPagePresent(Process
, Address
))
188 return(STATUS_SUCCESS
);
192 * Check for the virtual memory area being deleted.
194 if (MemoryArea
->DeleteInProgress
)
196 return(STATUS_UNSUCCESSFUL
);
200 * Get the segment corresponding to the virtual address
202 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
203 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
206 if (Region
->Type
== MEM_RESERVE
|| Region
->Protect
== PAGE_NOACCESS
)
208 return(STATUS_ACCESS_VIOLATION
);
214 if (Region
->Protect
& PAGE_GUARD
)
216 return(STATUS_GUARD_PAGE_VIOLATION
);
220 * Get or create a page operation
222 PageOp
= MmGetPageOp(MemoryArea
, Process
->UniqueProcessId
,
223 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
224 MM_PAGEOP_PAGEIN
, FALSE
);
227 DPRINT1("MmGetPageOp failed");
228 KeBugCheck(MEMORY_MANAGEMENT
);
232 * Check if someone else is already handling this fault, if so wait
235 if (PageOp
->Thread
!= PsGetCurrentThread())
237 MmUnlockAddressSpace(AddressSpace
);
238 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
244 * Check for various strange conditions
246 if (Status
!= STATUS_SUCCESS
)
248 DPRINT1("Failed to wait for page op\n");
249 KeBugCheck(MEMORY_MANAGEMENT
);
251 if (PageOp
->Status
== STATUS_PENDING
)
253 DPRINT1("Woke for page op before completion\n");
254 KeBugCheck(MEMORY_MANAGEMENT
);
257 * If this wasn't a pagein then we need to restart the handling
259 if (PageOp
->OpType
!= MM_PAGEOP_PAGEIN
)
261 MmLockAddressSpace(AddressSpace
);
262 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
263 MmReleasePageOp(PageOp
);
264 return(STATUS_MM_RESTART_OPERATION
);
267 * If the thread handling this fault has failed then we don't retry
269 if (!NT_SUCCESS(PageOp
->Status
))
271 MmLockAddressSpace(AddressSpace
);
272 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
273 Status
= PageOp
->Status
;
274 MmReleasePageOp(PageOp
);
277 MmLockAddressSpace(AddressSpace
);
278 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
279 MmReleasePageOp(PageOp
);
280 return(STATUS_SUCCESS
);
284 * Try to allocate a page
286 MI_SET_USAGE(MI_USAGE_VAD
);
287 MI_SET_PROCESS2(Process
->ImageFileName
);
288 Status
= MmRequestPageMemoryConsumer(MC_USER
, FALSE
, &Page
);
289 if (Status
== STATUS_NO_MEMORY
)
291 MmUnlockAddressSpace(AddressSpace
);
292 Status
= MmRequestPageMemoryConsumer(MC_USER
, TRUE
, &Page
);
293 MmLockAddressSpace(AddressSpace
);
295 if (!NT_SUCCESS(Status
))
297 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status
);
298 KeBugCheck(MEMORY_MANAGEMENT
);
302 * Handle swapped out pages.
304 if (MmIsPageSwapEntry(Process
, Address
))
308 MmDeletePageFileMapping(Process
, Address
, &SwapEntry
);
309 Status
= MmReadFromSwapPage(SwapEntry
, Page
);
310 if (!NT_SUCCESS(Status
))
312 KeBugCheck(MEMORY_MANAGEMENT
);
314 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
318 * Set the page. If we fail because we are out of memory then
321 Status
= MmCreateVirtualMapping(Process
,
322 (PVOID
)PAGE_ROUND_DOWN(Address
),
326 while (Status
== STATUS_NO_MEMORY
)
328 MmUnlockAddressSpace(AddressSpace
);
329 Status
= MmCreateVirtualMapping(Process
,
330 (PVOID
)PAGE_ROUND_DOWN(Address
),
334 MmLockAddressSpace(AddressSpace
);
336 if (!NT_SUCCESS(Status
))
338 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
339 KeBugCheck(MEMORY_MANAGEMENT
);
344 * Add the page to the process's working set
346 MmInsertRmap(Page
, Process
, (PVOID
)PAGE_ROUND_DOWN(Address
));
349 * Finish the operation
351 PageOp
->Status
= STATUS_SUCCESS
;
352 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
353 MmReleasePageOp(PageOp
);
354 return(STATUS_SUCCESS
);
358 MmModifyAttributes(PMMSUPPORT AddressSpace
,
366 * FUNCTION: Modify the attributes of a memory region
369 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
372 * If we are switching a previously committed region to reserved then
373 * free any allocated pages within the region
375 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
379 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
383 if (MmIsPageSwapEntry(Process
,
384 (char*)BaseAddress
+ (i
* PAGE_SIZE
)))
388 MmDeletePageFileMapping(Process
,
389 (char*)BaseAddress
+ (i
* PAGE_SIZE
),
391 MmFreeSwapPage(SwapEntry
);
395 MmDeleteVirtualMapping(Process
,
396 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
400 SWAPENTRY SavedSwapEntry
;
401 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
402 if (SavedSwapEntry
!= 0)
404 MmFreeSwapPage(SavedSwapEntry
);
405 MmSetSavedSwapEntryPage(Page
, 0);
407 MmDeleteRmap(Page
, Process
,
408 (char*)BaseAddress
+ (i
* PAGE_SIZE
));
409 MmReleasePageMemoryConsumer(MC_USER
, Page
);
416 * If we are changing the protection attributes of a committed region then
417 * alter the attributes for any allocated pages within the region
419 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
420 OldProtect
!= NewProtect
)
424 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
426 if (MmIsPagePresent(Process
,
427 (char*)BaseAddress
+ (i
*PAGE_SIZE
)))
429 MmSetPageProtect(Process
,
430 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
438 MiProtectVirtualMemory(IN PEPROCESS Process
,
439 IN OUT PVOID
*BaseAddress
,
440 IN OUT PSIZE_T NumberOfBytesToProtect
,
441 IN ULONG NewAccessProtection
,
442 OUT PULONG OldAccessProtection OPTIONAL
)
444 PMEMORY_AREA MemoryArea
;
445 PMMSUPPORT AddressSpace
;
446 ULONG OldAccessProtection_
;
449 *NumberOfBytesToProtect
=
450 PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) -
451 PAGE_ROUND_DOWN(*BaseAddress
);
452 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
454 AddressSpace
= &Process
->Vm
;
456 MmLockAddressSpace(AddressSpace
);
457 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
458 if (MemoryArea
== NULL
)
460 MmUnlockAddressSpace(AddressSpace
);
461 return STATUS_UNSUCCESSFUL
;
464 if (OldAccessProtection
== NULL
)
465 OldAccessProtection
= &OldAccessProtection_
;
467 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
469 Status
= MmProtectAnonMem(AddressSpace
, MemoryArea
, *BaseAddress
,
470 *NumberOfBytesToProtect
, NewAccessProtection
,
471 OldAccessProtection
);
473 else if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
475 Status
= MmProtectSectionView(AddressSpace
, MemoryArea
, *BaseAddress
,
476 *NumberOfBytesToProtect
,
478 OldAccessProtection
);
482 /* FIXME: Should we return failure or success in this case? */
483 Status
= STATUS_CONFLICTING_ADDRESSES
;
486 MmUnlockAddressSpace(AddressSpace
);
496 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
497 IN OUT PVOID
* UBaseAddress
,
498 IN ULONG_PTR ZeroBits
,
499 IN OUT PSIZE_T URegionSize
,
500 IN ULONG AllocationType
,
504 MEMORY_AREA
* MemoryArea
;
505 ULONG_PTR MemoryAreaLength
;
508 PMMSUPPORT AddressSpace
;
512 ULONG_PTR PRegionSize
;
513 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
514 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
515 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
517 ULONG ProtectionMask
;
518 BOOLEAN Attached
= FALSE
;
519 BoundaryAddressMultiple
.QuadPart
= 0;
522 /* Check for valid Zero bits */
525 DPRINT1("Too many zero bits\n");
526 return STATUS_INVALID_PARAMETER_3
;
529 /* Check for valid Allocation Types */
530 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
531 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
533 DPRINT1("Invalid Allocation Type\n");
534 return STATUS_INVALID_PARAMETER_5
;
537 /* Check for at least one of these Allocation Types to be set */
538 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
540 DPRINT1("No memory allocation base type\n");
541 return STATUS_INVALID_PARAMETER_5
;
544 /* MEM_RESET is an exclusive flag, make sure that is valid too */
545 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
547 DPRINT1("Invalid use of MEM_RESET\n");
548 return STATUS_INVALID_PARAMETER_5
;
551 /* Check if large pages are being used */
552 if (AllocationType
& MEM_LARGE_PAGES
)
554 /* Large page allocations MUST be committed */
555 if (!(AllocationType
& MEM_COMMIT
))
557 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
558 return STATUS_INVALID_PARAMETER_5
;
561 /* These flags are not allowed with large page allocations */
562 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
564 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
565 return STATUS_INVALID_PARAMETER_5
;
569 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
570 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
572 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
573 return STATUS_INVALID_PARAMETER_5
;
576 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
577 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
579 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
580 return STATUS_INVALID_PARAMETER_5
;
583 /* Check for valid MEM_PHYSICAL usage */
584 if (AllocationType
& MEM_PHYSICAL
)
586 /* Only these flags are allowed with MEM_PHYSIAL */
587 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
589 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
590 return STATUS_INVALID_PARAMETER_5
;
593 /* Then make sure PAGE_READWRITE is used */
594 if (Protect
!= PAGE_READWRITE
)
596 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
597 return STATUS_INVALID_PARAMETER_6
;
601 /* Calculate the protection mask and make sure it's valid */
602 ProtectionMask
= MiMakeProtectionMask(Protect
);
603 if (ProtectionMask
== MM_INVALID_PROTECTION
)
605 DPRINT1("Invalid protection mask\n");
606 return STATUS_INVALID_PAGE_PROTECTION
;
612 /* Check for user-mode parameters */
613 if (PreviousMode
!= KernelMode
)
615 /* Make sure they are writable */
616 ProbeForWritePointer(UBaseAddress
);
617 ProbeForWriteUlong(URegionSize
);
620 /* Capture their values */
621 PBaseAddress
= *UBaseAddress
;
622 PRegionSize
= *URegionSize
;
624 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
626 /* Return the exception code */
627 _SEH2_YIELD(return _SEH2_GetExceptionCode());
631 /* Make sure the allocation isn't past the VAD area */
632 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
634 DPRINT1("Virtual allocation base above User Space\n");
635 return STATUS_INVALID_PARAMETER_2
;
638 /* Make sure the allocation wouldn't overflow past the VAD area */
639 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
641 DPRINT1("Region size would overflow into kernel-memory\n");
642 return STATUS_INVALID_PARAMETER_4
;
645 /* Make sure there's a size specified */
648 DPRINT1("Region size is invalid (zero)\n");
649 return STATUS_INVALID_PARAMETER_4
;
652 /* Check if this is for the current process */
653 if (ProcessHandle
== NtCurrentProcess())
655 /* We already have the current process, no need to go through Ob */
656 Process
= CurrentProcess
;
660 /* Reference the handle for correct permissions */
661 Status
= ObReferenceObjectByHandle(ProcessHandle
,
662 PROCESS_VM_OPERATION
,
667 if (!NT_SUCCESS(Status
)) return Status
;
669 /* Check if not running in the current process */
670 if (CurrentProcess
!= Process
)
673 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
678 /* Check for large page allocations */
679 if (AllocationType
& MEM_LARGE_PAGES
)
681 /* The lock memory privilege is required */
682 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
684 /* Fail without it */
685 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
686 if (Attached
) KeUnstackDetachProcess(&ApcState
);
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 if (Attached
) KeUnstackDetachProcess(&ApcState
);
706 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
707 return STATUS_INVALID_PAGE_PROTECTION
;
710 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
711 DPRINT("Type %x\n", Type
);
713 AddressSpace
= &Process
->Vm
;
714 MmLockAddressSpace(AddressSpace
);
716 if (PBaseAddress
!= 0)
718 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
720 if (MemoryArea
!= NULL
)
722 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
723 (ULONG_PTR
)MemoryArea
->StartingAddress
;
725 if (((ULONG_PTR
)BaseAddress
+ RegionSize
) > (ULONG_PTR
)MemoryArea
->EndingAddress
)
727 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
728 (ULONG_PTR
)BaseAddress
+ RegionSize
, MemoryArea
->EndingAddress
);
730 MmUnlockAddressSpace(AddressSpace
);
731 if (Attached
) KeUnstackDetachProcess(&ApcState
);
732 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
733 return STATUS_MEMORY_NOT_ALLOCATED
;
736 if (AllocationType
== MEM_RESET
)
738 if (MmIsPagePresent(Process
, BaseAddress
))
740 /* FIXME: mark pages as not modified */
744 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
747 MmUnlockAddressSpace(AddressSpace
);
748 if (Attached
) KeUnstackDetachProcess(&ApcState
);
749 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
751 /* MEM_RESET does not modify any attributes of region */
752 return STATUS_SUCCESS
;
755 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
756 MemoryAreaLength
>= RegionSize
)
758 Status
= MmAlterRegion(AddressSpace
,
759 MemoryArea
->StartingAddress
,
760 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
761 BaseAddress
, RegionSize
,
762 Type
, Protect
, MmModifyAttributes
);
763 MmUnlockAddressSpace(AddressSpace
);
764 if (Attached
) KeUnstackDetachProcess(&ApcState
);
765 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
766 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
768 /* Give the caller rounded BaseAddress and area length */
769 if (NT_SUCCESS(Status
))
771 *UBaseAddress
= BaseAddress
;
772 *URegionSize
= RegionSize
;
773 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
778 else if (MemoryAreaLength
>= RegionSize
)
780 /* Region list initialized? */
781 if (MemoryArea
->Data
.SectionData
.RegionListHead
.Flink
)
783 Status
= 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 SIZE_T 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
;
1096 Status
= MmAlterRegion(AddressSpace
,
1097 MemoryArea
->StartingAddress
,
1098 (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
) ?
1099 &MemoryArea
->Data
.SectionData
.RegionListHead
:
1100 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1105 MmModifyAttributes
);
1106 goto unlock_deref_and_return
;
1109 Status
= STATUS_NOT_IMPLEMENTED
;
1111 unlock_deref_and_return
:
1113 MmUnlockAddressSpace(AddressSpace
);
1114 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1115 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1122 MmProtectAnonMem(PMMSUPPORT AddressSpace
,
1123 PMEMORY_AREA MemoryArea
,
1130 NTSTATUS Status
= STATUS_SUCCESS
;
1131 ULONG_PTR LengthCount
= 0;
1133 /* Search all Regions in MemoryArea up to Length */
1134 /* Every Region up to Length must be committed for success */
1137 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1138 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1139 (PVOID
)((ULONG_PTR
)BaseAddress
+ LengthCount
), NULL
);
1141 /* If a Region was found and it is committed */
1142 if ((Region
) && (Region
->Type
== MEM_COMMIT
))
1144 LengthCount
+= Region
->Length
;
1145 if (Length
<= LengthCount
) break;
1148 /* If Region was found and it is not commited */
1151 Status
= STATUS_NOT_COMMITTED
;
1154 /* If no Region was found at all */
1155 else if (LengthCount
== 0)
1157 Status
= STATUS_INVALID_ADDRESS
;
1162 if (NT_SUCCESS(Status
))
1164 *OldProtect
= Region
->Protect
;
1165 Status
= MmAlterRegion(AddressSpace
, MemoryArea
->StartingAddress
,
1166 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1167 BaseAddress
, Length
, Region
->Type
, Protect
,
1168 MmModifyAttributes
);
1175 MmQueryAnonMem(PMEMORY_AREA MemoryArea
,
1177 PMEMORY_BASIC_INFORMATION Info
,
1178 PSIZE_T ResultLength
)
1181 PVOID RegionBase
= NULL
;
1183 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
1185 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1186 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1187 Address
, &RegionBase
);
1188 Info
->BaseAddress
= RegionBase
;
1189 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
1190 Info
->AllocationProtect
= MemoryArea
->Protect
;
1191 Info
->RegionSize
= Region
->Length
;
1192 Info
->State
= Region
->Type
;
1193 Info
->Protect
= Region
->Protect
;
1194 Info
->Type
= MEM_PRIVATE
;
1196 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1197 return(STATUS_SUCCESS
);