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
)
425 char* addr
= (char*)BaseAddress
;
427 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
429 MArea
= MmLocateMemoryAreaByAddress(AddressSpace
, addr
);
432 PageOp
= MmGetPageOp(MArea
, Process
->UniqueProcessId
, addr
,
433 NULL
, 0, MM_PAGEOP_CHANGEPROTECT
, TRUE
);
434 } while(PageOp
== NULL
);
436 /* Should we enable/disable virtual mapping? */
437 if((NewProtect
& PAGE_NOACCESS
) &&
438 !(OldProtect
& PAGE_NOACCESS
) &&
439 (MmIsPagePresent(Process
, addr
)))
441 /* Set other flags if any */
442 if(NewProtect
!= PAGE_NOACCESS
)
443 MmSetPageProtect(Process
, addr
, NewProtect
& ~PAGE_NOACCESS
);
444 MmDisableVirtualMapping(Process
, addr
, NULL
, NULL
);
446 else if((OldProtect
& PAGE_NOACCESS
) && !(NewProtect
& PAGE_NOACCESS
))
448 MmEnableVirtualMapping(Process
, addr
);
451 /* Set new protection flags */
452 if(MmIsPagePresent(Process
, addr
))
454 MmSetPageProtect(Process
, addr
, NewProtect
);
456 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
457 MmReleasePageOp(PageOp
);
464 MiProtectVirtualMemory(IN PEPROCESS Process
,
465 IN OUT PVOID
*BaseAddress
,
466 IN OUT PSIZE_T NumberOfBytesToProtect
,
467 IN ULONG NewAccessProtection
,
468 OUT PULONG OldAccessProtection OPTIONAL
)
470 PMEMORY_AREA MemoryArea
;
471 PMMSUPPORT AddressSpace
;
472 ULONG OldAccessProtection_
;
475 *NumberOfBytesToProtect
=
476 PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) -
477 PAGE_ROUND_DOWN(*BaseAddress
);
478 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
480 AddressSpace
= &Process
->Vm
;
482 MmLockAddressSpace(AddressSpace
);
483 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
484 if (MemoryArea
== NULL
)
486 MmUnlockAddressSpace(AddressSpace
);
487 return STATUS_UNSUCCESSFUL
;
490 if (OldAccessProtection
== NULL
)
491 OldAccessProtection
= &OldAccessProtection_
;
493 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
495 Status
= MmProtectAnonMem(AddressSpace
, MemoryArea
, *BaseAddress
,
496 *NumberOfBytesToProtect
, NewAccessProtection
,
497 OldAccessProtection
);
499 else if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
501 Status
= MmProtectSectionView(AddressSpace
, MemoryArea
, *BaseAddress
,
502 *NumberOfBytesToProtect
,
504 OldAccessProtection
);
508 /* FIXME: Should we return failure or success in this case? */
509 Status
= STATUS_CONFLICTING_ADDRESSES
;
512 MmUnlockAddressSpace(AddressSpace
);
522 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
523 IN OUT PVOID
* UBaseAddress
,
524 IN ULONG_PTR ZeroBits
,
525 IN OUT PSIZE_T URegionSize
,
526 IN ULONG AllocationType
,
530 MEMORY_AREA
* MemoryArea
;
531 ULONG_PTR MemoryAreaLength
;
534 PMMSUPPORT AddressSpace
;
538 ULONG_PTR PRegionSize
;
539 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
540 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
541 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
543 ULONG ProtectionMask
;
544 BOOLEAN Attached
= FALSE
;
545 BoundaryAddressMultiple
.QuadPart
= 0;
548 /* Check for valid Zero bits */
551 DPRINT1("Too many zero bits\n");
552 return STATUS_INVALID_PARAMETER_3
;
555 /* Check for valid Allocation Types */
556 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
557 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
559 DPRINT1("Invalid Allocation Type\n");
560 return STATUS_INVALID_PARAMETER_5
;
563 /* Check for at least one of these Allocation Types to be set */
564 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
566 DPRINT1("No memory allocation base type\n");
567 return STATUS_INVALID_PARAMETER_5
;
570 /* MEM_RESET is an exclusive flag, make sure that is valid too */
571 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
573 DPRINT1("Invalid use of MEM_RESET\n");
574 return STATUS_INVALID_PARAMETER_5
;
577 /* Check if large pages are being used */
578 if (AllocationType
& MEM_LARGE_PAGES
)
580 /* Large page allocations MUST be committed */
581 if (!(AllocationType
& MEM_COMMIT
))
583 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
584 return STATUS_INVALID_PARAMETER_5
;
587 /* These flags are not allowed with large page allocations */
588 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
590 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
591 return STATUS_INVALID_PARAMETER_5
;
595 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
596 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
598 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
599 return STATUS_INVALID_PARAMETER_5
;
602 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
603 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
605 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
606 return STATUS_INVALID_PARAMETER_5
;
609 /* Check for valid MEM_PHYSICAL usage */
610 if (AllocationType
& MEM_PHYSICAL
)
612 /* Only these flags are allowed with MEM_PHYSIAL */
613 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
615 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
616 return STATUS_INVALID_PARAMETER_5
;
619 /* Then make sure PAGE_READWRITE is used */
620 if (Protect
!= PAGE_READWRITE
)
622 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
623 return STATUS_INVALID_PARAMETER_6
;
627 /* Calculate the protection mask and make sure it's valid */
628 ProtectionMask
= MiMakeProtectionMask(Protect
);
629 if (ProtectionMask
== MM_INVALID_PROTECTION
)
631 DPRINT1("Invalid protection mask\n");
632 return STATUS_INVALID_PAGE_PROTECTION
;
638 /* Check for user-mode parameters */
639 if (PreviousMode
!= KernelMode
)
641 /* Make sure they are writable */
642 ProbeForWritePointer(UBaseAddress
);
643 ProbeForWriteUlong(URegionSize
);
646 /* Capture their values */
647 PBaseAddress
= *UBaseAddress
;
648 PRegionSize
= *URegionSize
;
650 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
652 /* Return the exception code */
653 _SEH2_YIELD(return _SEH2_GetExceptionCode());
657 /* Make sure the allocation isn't past the VAD area */
658 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
660 DPRINT1("Virtual allocation base above User Space\n");
661 return STATUS_INVALID_PARAMETER_2
;
664 /* Make sure the allocation wouldn't overflow past the VAD area */
665 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
667 DPRINT1("Region size would overflow into kernel-memory\n");
668 return STATUS_INVALID_PARAMETER_4
;
671 /* Make sure there's a size specified */
674 DPRINT1("Region size is invalid (zero)\n");
675 return STATUS_INVALID_PARAMETER_4
;
678 /* Check if this is for the current process */
679 if (ProcessHandle
== NtCurrentProcess())
681 /* We already have the current process, no need to go through Ob */
682 Process
= CurrentProcess
;
686 /* Reference the handle for correct permissions */
687 Status
= ObReferenceObjectByHandle(ProcessHandle
,
688 PROCESS_VM_OPERATION
,
693 if (!NT_SUCCESS(Status
)) return Status
;
695 /* Check if not running in the current process */
696 if (CurrentProcess
!= Process
)
699 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
704 /* Check for large page allocations */
705 if (AllocationType
& MEM_LARGE_PAGES
)
707 /* The lock memory privilege is required */
708 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
710 /* Fail without it */
711 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
712 if (Attached
) KeUnstackDetachProcess(&ApcState
);
713 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
714 return STATUS_PRIVILEGE_NOT_HELD
;
718 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
719 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)PBaseAddress
+ PRegionSize
) -
720 PAGE_ROUND_DOWN(PBaseAddress
);
724 * Copy on Write is reserved for system use. This case is a certain failure
725 * but there may be other cases...needs more testing
727 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
728 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
)))
730 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
731 if (Attached
) KeUnstackDetachProcess(&ApcState
);
732 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
733 return STATUS_INVALID_PAGE_PROTECTION
;
736 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
737 DPRINT("Type %x\n", Type
);
739 AddressSpace
= &Process
->Vm
;
740 MmLockAddressSpace(AddressSpace
);
742 if (PBaseAddress
!= 0)
744 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
746 if (MemoryArea
!= NULL
)
748 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
749 (ULONG_PTR
)MemoryArea
->StartingAddress
;
751 if (((ULONG_PTR
)BaseAddress
+ RegionSize
) > (ULONG_PTR
)MemoryArea
->EndingAddress
)
753 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
754 (ULONG_PTR
)BaseAddress
+ RegionSize
, MemoryArea
->EndingAddress
);
756 MmUnlockAddressSpace(AddressSpace
);
757 if (Attached
) KeUnstackDetachProcess(&ApcState
);
758 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
759 return STATUS_MEMORY_NOT_ALLOCATED
;
762 if (AllocationType
== MEM_RESET
)
764 if (MmIsPagePresent(Process
, BaseAddress
))
766 /* FIXME: mark pages as not modified */
770 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
773 MmUnlockAddressSpace(AddressSpace
);
774 if (Attached
) KeUnstackDetachProcess(&ApcState
);
775 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
777 /* MEM_RESET does not modify any attributes of region */
778 return STATUS_SUCCESS
;
781 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
782 MemoryAreaLength
>= RegionSize
)
784 Status
= MmAlterRegion(AddressSpace
,
785 MemoryArea
->StartingAddress
,
786 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
787 BaseAddress
, RegionSize
,
788 Type
, Protect
, MmModifyAttributes
);
789 MmUnlockAddressSpace(AddressSpace
);
790 if (Attached
) KeUnstackDetachProcess(&ApcState
);
791 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
792 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
794 /* Give the caller rounded BaseAddress and area length */
795 if (NT_SUCCESS(Status
))
797 *UBaseAddress
= BaseAddress
;
798 *URegionSize
= RegionSize
;
799 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
804 else if (MemoryAreaLength
>= RegionSize
)
806 /* Region list initialized? */
807 if (MemoryArea
->Data
.SectionData
.RegionListHead
.Flink
)
809 Status
= MmAlterRegion(AddressSpace
,
810 MemoryArea
->StartingAddress
,
811 &MemoryArea
->Data
.SectionData
.RegionListHead
,
812 BaseAddress
, RegionSize
,
813 Type
, Protect
, MmModifyAttributes
);
817 Status
= STATUS_ACCESS_VIOLATION
;
820 MmUnlockAddressSpace(AddressSpace
);
821 if (Attached
) KeUnstackDetachProcess(&ApcState
);
822 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
823 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
825 /* Give the caller rounded BaseAddress and area length */
826 if (NT_SUCCESS(Status
))
828 *UBaseAddress
= BaseAddress
;
829 *URegionSize
= RegionSize
;
830 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
837 MmUnlockAddressSpace(AddressSpace
);
838 if (Attached
) KeUnstackDetachProcess(&ApcState
);
839 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
840 return(STATUS_UNSUCCESSFUL
);
845 Status
= MmCreateMemoryArea(AddressSpace
,
846 MEMORY_AREA_VIRTUAL_MEMORY
,
852 AllocationType
& MEM_TOP_DOWN
,
853 BoundaryAddressMultiple
);
854 if (!NT_SUCCESS(Status
))
856 MmUnlockAddressSpace(AddressSpace
);
857 if (Attached
) KeUnstackDetachProcess(&ApcState
);
858 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
859 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
863 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
864 (ULONG_PTR
)MemoryArea
->StartingAddress
;
866 MmInitializeRegion(&MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
867 MemoryAreaLength
, Type
, Protect
);
869 if ((AllocationType
& MEM_COMMIT
) &&
870 (Protect
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)))
872 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
873 MmReserveSwapPages(nPages
);
876 MmUnlockAddressSpace(AddressSpace
);
877 if (Attached
) KeUnstackDetachProcess(&ApcState
);
878 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
880 *UBaseAddress
= BaseAddress
;
881 *URegionSize
= MemoryAreaLength
;
882 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
884 return(STATUS_SUCCESS
);
888 MmFreeVirtualMemoryPage(PVOID Context
,
889 MEMORY_AREA
* MemoryArea
,
895 PEPROCESS Process
= (PEPROCESS
)Context
;
899 SWAPENTRY SavedSwapEntry
;
900 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
901 if (SavedSwapEntry
!= 0)
903 MmFreeSwapPage(SavedSwapEntry
);
904 MmSetSavedSwapEntryPage(Page
, 0);
906 MmDeleteRmap(Page
, Process
, Address
);
907 MmReleasePageMemoryConsumer(MC_USER
, Page
);
909 else if (SwapEntry
!= 0)
911 MmFreeSwapPage(SwapEntry
);
917 MmFreeVirtualMemory(PEPROCESS Process
,
918 PMEMORY_AREA MemoryArea
)
920 PLIST_ENTRY current_entry
;
924 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process
,
927 /* Mark this memory area as about to be deleted. */
928 MemoryArea
->DeleteInProgress
= TRUE
;
931 * Wait for any ongoing paging operations. Notice that since we have
932 * flagged this memory area as deleted no more page ops will be added.
934 if (MemoryArea
->PageOpCount
> 0)
936 ULONG_PTR MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
937 (ULONG_PTR
)MemoryArea
->StartingAddress
;
938 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
940 for (i
= 0; i
< nPages
&& MemoryArea
->PageOpCount
!= 0; ++i
)
943 PageOp
= MmCheckForPageOp(MemoryArea
, Process
->UniqueProcessId
,
944 (PVOID
)((ULONG_PTR
)MemoryArea
->StartingAddress
+ (i
* PAGE_SIZE
)),
949 MmUnlockAddressSpace(&Process
->Vm
);
950 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
955 if (Status
!= STATUS_SUCCESS
)
957 DPRINT1("Failed to wait for page op\n");
958 KeBugCheck(MEMORY_MANAGEMENT
);
960 MmLockAddressSpace(&Process
->Vm
);
961 MmReleasePageOp(PageOp
);
966 /* Free all the individual segments. */
967 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
.Flink
;
968 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
)
970 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
971 current_entry
= current_entry
->Flink
;
975 /* Actually free the memory area. */
976 MmFreeMemoryArea(&Process
->Vm
,
978 MmFreeVirtualMemoryPage
,
986 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
987 IN PVOID
* UBaseAddress
,
988 IN PSIZE_T URegionSize
,
991 * FUNCTION: Frees a range of virtual memory
993 * ProcessHandle = Points to the process that allocated the virtual
995 * BaseAddress = Points to the memory address, rounded down to a
996 * multiple of the pagesize
997 * RegionSize = Limits the range to free, rounded up to a multiple of
999 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
1003 MEMORY_AREA
* MemoryArea
;
1006 PMMSUPPORT AddressSpace
;
1007 PVOID BaseAddress
= NULL
, PBaseAddress
;
1008 SIZE_T RegionSize
= 0, PRegionSize
;
1009 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1010 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
1011 KAPC_STATE ApcState
;
1012 BOOLEAN Attached
= FALSE
;
1015 /* Only two flags are supported */
1016 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
1018 DPRINT1("Invalid FreeType\n");
1019 return STATUS_INVALID_PARAMETER_4
;
1022 /* Check if no flag was used, or if both flags were used */
1023 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
1024 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
1026 DPRINT1("Invalid FreeType combination\n");
1027 return STATUS_INVALID_PARAMETER_4
;
1033 /* Check for user-mode parameters */
1034 if (PreviousMode
!= KernelMode
)
1036 /* Make sure they are writeable */
1037 ProbeForWritePointer(UBaseAddress
);
1038 ProbeForWriteUlong(URegionSize
);
1041 /* Capture their values */
1042 PBaseAddress
= *UBaseAddress
;
1043 PRegionSize
= *URegionSize
;
1045 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1047 /* Return the exception code */
1048 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1052 /* Make sure the allocation isn't past the user area */
1053 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
1055 DPRINT1("Virtual free base above User Space\n");
1056 return STATUS_INVALID_PARAMETER_2
;
1059 /* Make sure the allocation wouldn't overflow past the user area */
1060 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
1062 DPRINT1("Region size would overflow into kernel-memory\n");
1063 return STATUS_INVALID_PARAMETER_3
;
1066 /* Check if this is for the current process */
1067 if (ProcessHandle
== NtCurrentProcess())
1069 /* We already have the current process, no need to go through Ob */
1070 Process
= CurrentProcess
;
1074 /* Reference the handle for correct permissions */
1075 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1076 PROCESS_VM_OPERATION
,
1081 if (!NT_SUCCESS(Status
)) return Status
;
1083 /* Check if not running in the current process */
1084 if (CurrentProcess
!= Process
)
1087 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1092 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((PBaseAddress
));
1094 AddressSpace
= &Process
->Vm
;
1096 MmLockAddressSpace(AddressSpace
);
1097 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
1098 if (MemoryArea
== NULL
)
1100 DPRINT1("Unable to find memory area from address 0x%p\n", BaseAddress
);
1101 Status
= STATUS_UNABLE_TO_FREE_VM
;
1102 goto unlock_deref_and_return
;
1105 if (PRegionSize
!= 0)
1107 /* Use the region the caller wanted, rounded to whole pages */
1108 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)(PBaseAddress
) + (PRegionSize
)) -
1109 PAGE_ROUND_DOWN((PBaseAddress
));
1113 /* The caller wanted the whole memory area */
1114 RegionSize
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
1115 (ULONG_PTR
)MemoryArea
->StartingAddress
;
1121 /* MEM_RELEASE must be used with the exact base and length
1122 * that was returned by NtAllocateVirtualMemory */
1124 /* Verify the base address is correct */
1125 if (MemoryArea
->StartingAddress
!= BaseAddress
)
1127 DPRINT1("Base address is illegal for MEM_RELEASE (0x%p != 0x%p)\n",
1128 MemoryArea
->StartingAddress
,
1130 Status
= STATUS_UNABLE_TO_FREE_VM
;
1131 goto unlock_deref_and_return
;
1134 /* Verify the region size is correct */
1135 if ((ULONG_PTR
)MemoryArea
->StartingAddress
+ RegionSize
!=
1136 (ULONG_PTR
)MemoryArea
->EndingAddress
)
1138 DPRINT1("Region size is illegal for MEM_RELEASE (0x%x)\n", RegionSize
);
1139 Status
= STATUS_UNABLE_TO_FREE_VM
;
1140 //goto unlock_deref_and_return;
1143 if (MemoryArea
->Type
!= MEMORY_AREA_VIRTUAL_MEMORY
)
1145 DPRINT1("Memory area is not VM\n");
1146 Status
= STATUS_UNABLE_TO_FREE_VM
;
1147 goto unlock_deref_and_return
;
1150 MmFreeVirtualMemory(Process
, MemoryArea
);
1151 Status
= STATUS_SUCCESS
;
1155 if ((ULONG_PTR
)BaseAddress
+ RegionSize
>
1156 (ULONG_PTR
)MemoryArea
->EndingAddress
)
1158 DPRINT1("Invald base address (0x%p) and region size (0x%x) for MEM_DECOMMIT\n",
1159 BaseAddress
, RegionSize
);
1160 Status
= STATUS_UNABLE_TO_FREE_VM
;
1161 goto unlock_deref_and_return
;
1163 Status
= MmAlterRegion(AddressSpace
,
1164 MemoryArea
->StartingAddress
,
1165 (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
) ?
1166 &MemoryArea
->Data
.SectionData
.RegionListHead
:
1167 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1172 MmModifyAttributes
);
1173 if (!NT_SUCCESS(Status
))
1175 DPRINT1("MmAlterRegion failed with status 0x%x\n", Status
);
1176 Status
= STATUS_UNABLE_TO_FREE_VM
;
1177 goto unlock_deref_and_return
;
1182 Status
= STATUS_NOT_IMPLEMENTED
;
1183 goto unlock_deref_and_return
;
1186 unlock_deref_and_return
:
1187 MmUnlockAddressSpace(AddressSpace
);
1189 /* Copy rounded values back in success case */
1190 if (NT_SUCCESS(Status
))
1195 *UBaseAddress
= BaseAddress
;
1196 *URegionSize
= RegionSize
;
1198 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1200 Status
= _SEH2_GetExceptionCode();
1201 DPRINT1("Failed to copy values back! (Status: 0x%x)\n", Status
);
1206 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1207 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1214 MmProtectAnonMem(PMMSUPPORT AddressSpace
,
1215 PMEMORY_AREA MemoryArea
,
1222 NTSTATUS Status
= STATUS_SUCCESS
;
1223 ULONG_PTR LengthCount
= 0;
1225 /* Search all Regions in MemoryArea up to Length */
1226 /* Every Region up to Length must be committed for success */
1229 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1230 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1231 (PVOID
)((ULONG_PTR
)BaseAddress
+ LengthCount
), NULL
);
1233 /* If a Region was found and it is committed */
1234 if ((Region
) && (Region
->Type
== MEM_COMMIT
))
1236 LengthCount
+= Region
->Length
;
1237 if (Length
<= LengthCount
) break;
1240 /* If Region was found and it is not commited */
1243 Status
= STATUS_NOT_COMMITTED
;
1246 /* If no Region was found at all */
1247 else if (LengthCount
== 0)
1249 Status
= STATUS_INVALID_ADDRESS
;
1254 if (NT_SUCCESS(Status
))
1256 *OldProtect
= Region
->Protect
;
1257 Status
= MmAlterRegion(AddressSpace
, MemoryArea
->StartingAddress
,
1258 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1259 BaseAddress
, Length
, Region
->Type
, Protect
,
1260 MmModifyAttributes
);
1267 MmQueryAnonMem(PMEMORY_AREA MemoryArea
,
1269 PMEMORY_BASIC_INFORMATION Info
,
1270 PSIZE_T ResultLength
)
1273 PVOID RegionBase
= NULL
;
1275 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
1277 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1278 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1279 Address
, &RegionBase
);
1280 Info
->BaseAddress
= RegionBase
;
1281 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
1282 Info
->AllocationProtect
= MemoryArea
->Protect
;
1283 Info
->RegionSize
= Region
->Length
;
1284 Info
->State
= Region
->Type
;
1285 Info
->Protect
= Region
->Protect
;
1286 Info
->Type
= MEM_PRIVATE
;
1288 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1289 return(STATUS_SUCCESS
);