2 * Copyright (C) 2002-2005 ReactOS Team (and the authors from the programmers section)
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/anonmem.c
21 * PURPOSE: Implementing anonymous memory.
23 * PROGRAMMERS: David Welch
32 * Gunnar Andre' Dalsnes
34 * Thomas Weidenmueller
40 /* INCLUDE *****************************************************************/
46 #define MODULE_INVOLVED_IN_ARM3
47 #include "ARM3/miarm.h"
49 /* FUNCTIONS *****************************************************************/
53 MmWritePageVirtualMemory(PMMSUPPORT AddressSpace
,
54 PMEMORY_AREA MemoryArea
,
61 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
64 * Check for paging out from a deleted virtual memory area.
66 if (MemoryArea
->DeleteInProgress
)
68 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
69 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
70 MmReleasePageOp(PageOp
);
71 return(STATUS_UNSUCCESSFUL
);
74 Page
= MmGetPfnForProcess(Process
, Address
);
77 * Get that the page actually is dirty.
79 if (!MmIsDirtyPage(Process
, Address
))
81 PageOp
->Status
= STATUS_SUCCESS
;
82 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
83 MmReleasePageOp(PageOp
);
84 return(STATUS_SUCCESS
);
88 * Speculatively set the mapping to clean.
90 MmSetCleanPage(Process
, Address
);
93 * If necessary, allocate an entry in the paging file for this page
95 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
98 SwapEntry
= MmAllocSwapPage();
101 MmSetDirtyPage(Process
, Address
);
102 PageOp
->Status
= STATUS_PAGEFILE_QUOTA_EXCEEDED
;
103 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
104 MmReleasePageOp(PageOp
);
105 return(STATUS_PAGEFILE_QUOTA_EXCEEDED
);
110 * Write the page to the pagefile
112 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
113 if (!NT_SUCCESS(Status
))
115 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
117 MmSetDirtyPage(Process
, Address
);
118 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
119 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
120 MmReleasePageOp(PageOp
);
121 return(STATUS_UNSUCCESSFUL
);
125 * Otherwise we have succeeded.
127 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
128 PageOp
->Status
= STATUS_SUCCESS
;
129 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
130 MmReleasePageOp(PageOp
);
131 return(STATUS_SUCCESS
);
136 MmPageOutVirtualMemory(PMMSUPPORT AddressSpace
,
137 PMEMORY_AREA MemoryArea
,
145 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
147 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
148 Address
, Process
->UniqueProcessId
);
151 * Check for paging out from a deleted virtual memory area.
153 if (MemoryArea
->DeleteInProgress
)
155 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
156 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
157 MmReleasePageOp(PageOp
);
158 return(STATUS_UNSUCCESSFUL
);
162 * Disable the virtual mapping.
164 MmDisableVirtualMapping(Process
, Address
,
169 KeBugCheck(MEMORY_MANAGEMENT
);
173 * Paging out non-dirty data is easy.
177 MmLockAddressSpace(AddressSpace
);
178 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
179 MmDeleteAllRmaps(Page
, NULL
, NULL
);
180 if ((SwapEntry
= MmGetSavedSwapEntryPage(Page
)) != 0)
182 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
183 MmSetSavedSwapEntryPage(Page
, 0);
185 MmUnlockAddressSpace(AddressSpace
);
186 MmReleasePageMemoryConsumer(MC_USER
, Page
);
187 PageOp
->Status
= STATUS_SUCCESS
;
188 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
189 MmReleasePageOp(PageOp
);
190 return(STATUS_SUCCESS
);
194 * If necessary, allocate an entry in the paging file for this page
196 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
199 SwapEntry
= MmAllocSwapPage();
202 MmShowOutOfSpaceMessagePagingFile();
203 MmEnableVirtualMapping(Process
, Address
);
204 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
205 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
206 MmReleasePageOp(PageOp
);
207 return(STATUS_PAGEFILE_QUOTA
);
212 * Write the page to the pagefile
214 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
215 if (!NT_SUCCESS(Status
))
217 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
219 MmEnableVirtualMapping(Process
, Address
);
220 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
221 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
222 MmReleasePageOp(PageOp
);
223 return(STATUS_UNSUCCESSFUL
);
227 * Otherwise we have succeeded, free the page
229 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page
<< PAGE_SHIFT
);
230 MmLockAddressSpace(AddressSpace
);
231 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
232 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
233 MmUnlockAddressSpace(AddressSpace
);
234 MmDeleteAllRmaps(Page
, NULL
, NULL
);
235 MmSetSavedSwapEntryPage(Page
, 0);
236 MmReleasePageMemoryConsumer(MC_USER
, Page
);
237 PageOp
->Status
= STATUS_SUCCESS
;
238 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
239 MmReleasePageOp(PageOp
);
240 return(STATUS_SUCCESS
);
245 MmNotPresentFaultVirtualMemory(PMMSUPPORT AddressSpace
,
246 MEMORY_AREA
* MemoryArea
,
250 * FUNCTION: Move data into memory to satisfy a page not present fault
252 * AddressSpace = Address space within which the fault occurred
253 * MemoryArea = The memory area within which the fault occurred
254 * Address = The absolute address of fault
256 * NOTES: This function is called with the address space lock held.
263 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
266 * There is a window between taking the page fault and locking the
267 * address space when another thread could load the page so we check
270 if (MmIsPagePresent(NULL
, Address
))
272 return(STATUS_SUCCESS
);
276 * Check for the virtual memory area being deleted.
278 if (MemoryArea
->DeleteInProgress
)
280 return(STATUS_UNSUCCESSFUL
);
284 * Get the segment corresponding to the virtual address
286 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
287 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
290 if (Region
->Type
== MEM_RESERVE
|| Region
->Protect
== PAGE_NOACCESS
)
292 return(STATUS_ACCESS_VIOLATION
);
298 if (Region
->Protect
& PAGE_GUARD
)
300 return(STATUS_GUARD_PAGE_VIOLATION
);
304 * Get or create a page operation
306 PageOp
= MmGetPageOp(MemoryArea
, Process
->UniqueProcessId
,
307 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
308 MM_PAGEOP_PAGEIN
, FALSE
);
311 DPRINT1("MmGetPageOp failed");
312 KeBugCheck(MEMORY_MANAGEMENT
);
316 * Check if someone else is already handling this fault, if so wait
319 if (PageOp
->Thread
!= PsGetCurrentThread())
321 MmUnlockAddressSpace(AddressSpace
);
322 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
328 * Check for various strange conditions
330 if (Status
!= STATUS_SUCCESS
)
332 DPRINT1("Failed to wait for page op\n");
333 KeBugCheck(MEMORY_MANAGEMENT
);
335 if (PageOp
->Status
== STATUS_PENDING
)
337 DPRINT1("Woke for page op before completion\n");
338 KeBugCheck(MEMORY_MANAGEMENT
);
341 * If this wasn't a pagein then we need to restart the handling
343 if (PageOp
->OpType
!= MM_PAGEOP_PAGEIN
)
345 MmLockAddressSpace(AddressSpace
);
346 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
347 MmReleasePageOp(PageOp
);
348 return(STATUS_MM_RESTART_OPERATION
);
351 * If the thread handling this fault has failed then we don't retry
353 if (!NT_SUCCESS(PageOp
->Status
))
355 MmLockAddressSpace(AddressSpace
);
356 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
357 Status
= PageOp
->Status
;
358 MmReleasePageOp(PageOp
);
361 MmLockAddressSpace(AddressSpace
);
362 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
363 MmReleasePageOp(PageOp
);
364 return(STATUS_SUCCESS
);
368 * Try to allocate a page
370 Status
= MmRequestPageMemoryConsumer(MC_USER
, FALSE
, &Page
);
371 if (Status
== STATUS_NO_MEMORY
)
373 MmUnlockAddressSpace(AddressSpace
);
374 Status
= MmRequestPageMemoryConsumer(MC_USER
, TRUE
, &Page
);
375 MmLockAddressSpace(AddressSpace
);
377 if (!NT_SUCCESS(Status
))
379 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status
);
380 KeBugCheck(MEMORY_MANAGEMENT
);
384 * Handle swapped out pages.
386 if (MmIsPageSwapEntry(NULL
, Address
))
390 MmDeletePageFileMapping(Process
, Address
, &SwapEntry
);
391 Status
= MmReadFromSwapPage(SwapEntry
, Page
);
392 if (!NT_SUCCESS(Status
))
394 KeBugCheck(MEMORY_MANAGEMENT
);
396 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
400 * Set the page. If we fail because we are out of memory then
403 Status
= MmCreateVirtualMapping(Process
,
404 (PVOID
)PAGE_ROUND_DOWN(Address
),
408 while (Status
== STATUS_NO_MEMORY
)
410 MmUnlockAddressSpace(AddressSpace
);
411 Status
= MmCreateVirtualMapping(Process
,
416 MmLockAddressSpace(AddressSpace
);
418 if (!NT_SUCCESS(Status
))
420 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
421 KeBugCheck(MEMORY_MANAGEMENT
);
426 * Add the page to the process's working set
428 MmInsertRmap(Page
, Process
, (PVOID
)PAGE_ROUND_DOWN(Address
));
431 * Finish the operation
433 PageOp
->Status
= STATUS_SUCCESS
;
434 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
435 MmReleasePageOp(PageOp
);
436 return(STATUS_SUCCESS
);
440 MmModifyAttributes(PMMSUPPORT AddressSpace
,
448 * FUNCTION: Modify the attributes of a memory region
451 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
454 * If we are switching a previously committed region to reserved then
455 * free any allocated pages within the region
457 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
461 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
465 if (MmIsPageSwapEntry(Process
,
466 (char*)BaseAddress
+ (i
* PAGE_SIZE
)))
470 MmDeletePageFileMapping(Process
,
471 (char*)BaseAddress
+ (i
* PAGE_SIZE
),
473 MmFreeSwapPage(SwapEntry
);
477 MmDeleteVirtualMapping(Process
,
478 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
482 SWAPENTRY SavedSwapEntry
;
483 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
484 if (SavedSwapEntry
!= 0)
486 MmFreeSwapPage(SavedSwapEntry
);
487 MmSetSavedSwapEntryPage(Page
, 0);
489 MmDeleteRmap(Page
, Process
,
490 (char*)BaseAddress
+ (i
* PAGE_SIZE
));
491 MmReleasePageMemoryConsumer(MC_USER
, Page
);
498 * If we are changing the protection attributes of a committed region then
499 * alter the attributes for any allocated pages within the region
501 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
502 OldProtect
!= NewProtect
)
506 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
508 if (MmIsPagePresent(Process
,
509 (char*)BaseAddress
+ (i
*PAGE_SIZE
)))
511 MmSetPageProtect(Process
,
512 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
520 MiProtectVirtualMemory(IN PEPROCESS Process
,
521 IN OUT PVOID
*BaseAddress
,
522 IN OUT PSIZE_T NumberOfBytesToProtect
,
523 IN ULONG NewAccessProtection
,
524 OUT PULONG OldAccessProtection OPTIONAL
)
526 PMEMORY_AREA MemoryArea
;
527 PMMSUPPORT AddressSpace
;
528 ULONG OldAccessProtection_
;
531 *NumberOfBytesToProtect
=
532 PAGE_ROUND_UP((ULONG_PTR
)(*BaseAddress
) + (*NumberOfBytesToProtect
)) -
533 PAGE_ROUND_DOWN(*BaseAddress
);
534 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
536 AddressSpace
= &Process
->Vm
;
538 MmLockAddressSpace(AddressSpace
);
539 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
540 if (MemoryArea
== NULL
)
542 MmUnlockAddressSpace(AddressSpace
);
543 return STATUS_UNSUCCESSFUL
;
546 if (OldAccessProtection
== NULL
)
547 OldAccessProtection
= &OldAccessProtection_
;
549 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
551 Status
= MmProtectAnonMem(AddressSpace
, MemoryArea
, *BaseAddress
,
552 *NumberOfBytesToProtect
, NewAccessProtection
,
553 OldAccessProtection
);
555 else if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
557 Status
= MmProtectSectionView(AddressSpace
, MemoryArea
, *BaseAddress
,
558 *NumberOfBytesToProtect
,
560 OldAccessProtection
);
564 /* FIXME: Should we return failure or success in this case? */
565 Status
= STATUS_CONFLICTING_ADDRESSES
;
568 MmUnlockAddressSpace(AddressSpace
);
578 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
579 IN OUT PVOID
* UBaseAddress
,
580 IN ULONG_PTR ZeroBits
,
581 IN OUT PSIZE_T URegionSize
,
582 IN ULONG AllocationType
,
586 MEMORY_AREA
* MemoryArea
;
587 ULONG_PTR MemoryAreaLength
;
590 PMMSUPPORT AddressSpace
;
595 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
596 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
597 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
599 ULONG ProtectionMask
;
600 BOOLEAN Attached
= FALSE
;
601 BoundaryAddressMultiple
.QuadPart
= 0;
604 /* Check for valid Zero bits */
607 DPRINT1("Too many zero bits\n");
608 return STATUS_INVALID_PARAMETER_3
;
611 /* Check for valid Allocation Types */
612 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
613 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
615 DPRINT1("Invalid Allocation Type\n");
616 return STATUS_INVALID_PARAMETER_5
;
619 /* Check for at least one of these Allocation Types to be set */
620 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
622 DPRINT1("No memory allocation base type\n");
623 return STATUS_INVALID_PARAMETER_5
;
626 /* MEM_RESET is an exclusive flag, make sure that is valid too */
627 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
629 DPRINT1("Invalid use of MEM_RESET\n");
630 return STATUS_INVALID_PARAMETER_5
;
633 /* Check if large pages are being used */
634 if (AllocationType
& MEM_LARGE_PAGES
)
636 /* Large page allocations MUST be committed */
637 if (!(AllocationType
& MEM_COMMIT
))
639 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
640 return STATUS_INVALID_PARAMETER_5
;
643 /* These flags are not allowed with large page allocations */
644 if (AllocationType
& (MEM_PHYSICAL
| MEM_RESET
| MEM_WRITE_WATCH
))
646 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
647 return STATUS_INVALID_PARAMETER_5
;
651 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
652 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
654 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
655 return STATUS_INVALID_PARAMETER_5
;
658 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
659 if ((AllocationType
& MEM_PHYSICAL
) && !(AllocationType
& MEM_RESERVE
))
661 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
662 return STATUS_INVALID_PARAMETER_5
;
665 /* Check for valid MEM_PHYSICAL usage */
666 if (AllocationType
& MEM_PHYSICAL
)
668 /* Only these flags are allowed with MEM_PHYSIAL */
669 if (AllocationType
& ~(MEM_RESERVE
| MEM_TOP_DOWN
| MEM_PHYSICAL
))
671 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
672 return STATUS_INVALID_PARAMETER_5
;
675 /* Then make sure PAGE_READWRITE is used */
676 if (Protect
!= PAGE_READWRITE
)
678 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
679 return STATUS_INVALID_PARAMETER_6
;
683 /* Calculate the protection mask and make sure it's valid */
684 ProtectionMask
= MiMakeProtectionMask(Protect
);
685 if (ProtectionMask
== MM_INVALID_PROTECTION
)
687 DPRINT1("Invalid protection mask\n");
688 return STATUS_INVALID_PAGE_PROTECTION
;
694 /* Check for user-mode parameters */
695 if (PreviousMode
!= KernelMode
)
697 /* Make sure they are writable */
698 ProbeForWritePointer(UBaseAddress
);
699 ProbeForWriteUlong(URegionSize
);
702 /* Capture their values */
703 PBaseAddress
= *UBaseAddress
;
704 PRegionSize
= *URegionSize
;
706 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
708 /* Return the exception code */
709 _SEH2_YIELD(return _SEH2_GetExceptionCode());
713 /* Make sure the allocation isn't past the VAD area */
714 if (PBaseAddress
>= MM_HIGHEST_VAD_ADDRESS
)
716 DPRINT1("Virtual allocation base above User Space\n");
717 return STATUS_INVALID_PARAMETER_2
;
720 /* Make sure the allocation wouldn't overflow past the VAD area */
721 if ((((ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1) - (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
723 DPRINT1("Region size would overflow into kernel-memory\n");
724 return STATUS_INVALID_PARAMETER_4
;
727 /* Make sure there's a size specified */
730 DPRINT1("Region size is invalid (zero)\n");
731 return STATUS_INVALID_PARAMETER_4
;
734 /* Check if this is for the current process */
735 if (ProcessHandle
== NtCurrentProcess())
737 /* We already have the current process, no need to go through Ob */
738 Process
= CurrentProcess
;
742 /* Reference the handle for correct permissions */
743 Status
= ObReferenceObjectByHandle(ProcessHandle
,
744 PROCESS_VM_OPERATION
,
749 if (!NT_SUCCESS(Status
)) return Status
;
751 /* Check if not running in the current process */
752 if (CurrentProcess
!= Process
)
755 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
760 /* Check for large page allocations */
761 if (AllocationType
& MEM_LARGE_PAGES
)
763 /* The lock memory privilege is required */
764 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege
, PreviousMode
))
766 /* Fail without it */
767 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
768 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
769 return STATUS_PRIVILEGE_NOT_HELD
;
773 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
774 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)PBaseAddress
+ PRegionSize
) -
775 PAGE_ROUND_DOWN(PBaseAddress
);
779 * Copy on Write is reserved for system use. This case is a certain failure
780 * but there may be other cases...needs more testing
782 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
783 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
)))
785 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
786 return STATUS_INVALID_PAGE_PROTECTION
;
789 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
790 DPRINT("Type %x\n", Type
);
792 AddressSpace
= &Process
->Vm
;
793 MmLockAddressSpace(AddressSpace
);
795 if (PBaseAddress
!= 0)
797 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
799 if (MemoryArea
!= NULL
)
801 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
802 (ULONG_PTR
)MemoryArea
->StartingAddress
;
804 if (((ULONG_PTR
)BaseAddress
+ RegionSize
) > (ULONG_PTR
)MemoryArea
->EndingAddress
)
806 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
807 (ULONG_PTR
)BaseAddress
+ RegionSize
, MemoryArea
->EndingAddress
);
809 MmUnlockAddressSpace(AddressSpace
);
810 if (Attached
) KeUnstackDetachProcess(&ApcState
);
811 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
812 return STATUS_MEMORY_NOT_ALLOCATED
;
815 if (AllocationType
== MEM_RESET
)
817 if (MmIsPagePresent(Process
, BaseAddress
))
819 /* FIXME: mark pages as not modified */
823 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
826 MmUnlockAddressSpace(AddressSpace
);
827 if (Attached
) KeUnstackDetachProcess(&ApcState
);
828 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
830 /* MEM_RESET does not modify any attributes of region */
831 return STATUS_SUCCESS
;
834 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
835 MemoryAreaLength
>= RegionSize
)
838 MmAlterRegion(AddressSpace
,
839 MemoryArea
->StartingAddress
,
840 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
841 BaseAddress
, RegionSize
,
842 Type
, Protect
, MmModifyAttributes
);
843 MmUnlockAddressSpace(AddressSpace
);
844 if (Attached
) KeUnstackDetachProcess(&ApcState
);
845 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
846 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
848 /* Give the caller rounded BaseAddress and area length */
849 if (NT_SUCCESS(Status
))
851 *UBaseAddress
= BaseAddress
;
852 *URegionSize
= RegionSize
;
853 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
858 else if (MemoryAreaLength
>= RegionSize
)
860 /* Region list initialized? */
861 if (MemoryArea
->Data
.SectionData
.RegionListHead
.Flink
)
864 MmAlterRegion(AddressSpace
,
865 MemoryArea
->StartingAddress
,
866 &MemoryArea
->Data
.SectionData
.RegionListHead
,
867 BaseAddress
, RegionSize
,
868 Type
, Protect
, MmModifyAttributes
);
872 Status
= STATUS_ACCESS_VIOLATION
;
875 MmUnlockAddressSpace(AddressSpace
);
876 if (Attached
) KeUnstackDetachProcess(&ApcState
);
877 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
878 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
880 /* Give the caller rounded BaseAddress and area length */
881 if (NT_SUCCESS(Status
))
883 *UBaseAddress
= BaseAddress
;
884 *URegionSize
= RegionSize
;
885 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
892 MmUnlockAddressSpace(AddressSpace
);
893 if (Attached
) KeUnstackDetachProcess(&ApcState
);
894 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
895 return(STATUS_UNSUCCESSFUL
);
900 Status
= MmCreateMemoryArea(AddressSpace
,
901 MEMORY_AREA_VIRTUAL_MEMORY
,
907 AllocationType
& MEM_TOP_DOWN
,
908 BoundaryAddressMultiple
);
909 if (!NT_SUCCESS(Status
))
911 MmUnlockAddressSpace(AddressSpace
);
912 if (Attached
) KeUnstackDetachProcess(&ApcState
);
913 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
914 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
918 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
919 (ULONG_PTR
)MemoryArea
->StartingAddress
;
921 MmInitializeRegion(&MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
922 MemoryAreaLength
, Type
, Protect
);
924 if ((AllocationType
& MEM_COMMIT
) &&
925 (Protect
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)))
927 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
928 MmReserveSwapPages(nPages
);
931 MmUnlockAddressSpace(AddressSpace
);
932 if (Attached
) KeUnstackDetachProcess(&ApcState
);
933 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
935 *UBaseAddress
= BaseAddress
;
936 *URegionSize
= MemoryAreaLength
;
937 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
939 return(STATUS_SUCCESS
);
943 MmFreeVirtualMemoryPage(PVOID Context
,
944 MEMORY_AREA
* MemoryArea
,
950 PEPROCESS Process
= (PEPROCESS
)Context
;
954 SWAPENTRY SavedSwapEntry
;
955 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
956 if (SavedSwapEntry
!= 0)
958 MmFreeSwapPage(SavedSwapEntry
);
959 MmSetSavedSwapEntryPage(Page
, 0);
961 MmDeleteRmap(Page
, Process
, Address
);
962 MmReleasePageMemoryConsumer(MC_USER
, Page
);
964 else if (SwapEntry
!= 0)
966 MmFreeSwapPage(SwapEntry
);
972 MmFreeVirtualMemory(PEPROCESS Process
,
973 PMEMORY_AREA MemoryArea
)
975 PLIST_ENTRY current_entry
;
979 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process
,
982 /* Mark this memory area as about to be deleted. */
983 MemoryArea
->DeleteInProgress
= TRUE
;
986 * Wait for any ongoing paging operations. Notice that since we have
987 * flagged this memory area as deleted no more page ops will be added.
989 if (MemoryArea
->PageOpCount
> 0)
991 ULONG_PTR MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
992 (ULONG_PTR
)MemoryArea
->StartingAddress
;
993 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
995 for (i
= 0; i
< nPages
&& MemoryArea
->PageOpCount
!= 0; ++i
)
998 PageOp
= MmCheckForPageOp(MemoryArea
, Process
->UniqueProcessId
,
999 (PVOID
)((ULONG_PTR
)MemoryArea
->StartingAddress
+ (i
* PAGE_SIZE
)),
1004 MmUnlockAddressSpace(&Process
->Vm
);
1005 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
1010 if (Status
!= STATUS_SUCCESS
)
1012 DPRINT1("Failed to wait for page op\n");
1013 KeBugCheck(MEMORY_MANAGEMENT
);
1015 MmLockAddressSpace(&Process
->Vm
);
1016 MmReleasePageOp(PageOp
);
1021 /* Free all the individual segments. */
1022 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
.Flink
;
1023 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
)
1025 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
1026 current_entry
= current_entry
->Flink
;
1027 ExFreePool(current
);
1030 /* Actually free the memory area. */
1031 MmFreeMemoryArea(&Process
->Vm
,
1033 MmFreeVirtualMemoryPage
,
1041 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
1042 IN PVOID
* UBaseAddress
,
1043 IN PSIZE_T URegionSize
,
1046 * FUNCTION: Frees a range of virtual memory
1048 * ProcessHandle = Points to the process that allocated the virtual
1050 * BaseAddress = Points to the memory address, rounded down to a
1051 * multiple of the pagesize
1052 * RegionSize = Limits the range to free, rounded up to a multiple of
1054 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
1058 MEMORY_AREA
* MemoryArea
;
1061 PMMSUPPORT AddressSpace
;
1062 PVOID BaseAddress
, PBaseAddress
;
1063 ULONG RegionSize
, PRegionSize
;
1064 PEPROCESS CurrentProcess
= PsGetCurrentProcess();
1065 KPROCESSOR_MODE PreviousMode
= KeGetPreviousMode();
1066 KAPC_STATE ApcState
;
1067 BOOLEAN Attached
= FALSE
;
1070 /* Only two flags are supported */
1071 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
1073 DPRINT1("Invalid FreeType\n");
1074 return STATUS_INVALID_PARAMETER_4
;
1077 /* Check if no flag was used, or if both flags were used */
1078 if (!((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
))) ||
1079 ((FreeType
& (MEM_DECOMMIT
| MEM_RELEASE
)) == (MEM_DECOMMIT
| MEM_RELEASE
)))
1081 DPRINT1("Invalid FreeType combination\n");
1082 return STATUS_INVALID_PARAMETER_4
;
1088 /* Check for user-mode parameters */
1089 if (PreviousMode
!= KernelMode
)
1091 /* Make sure they are writable */
1092 ProbeForWritePointer(UBaseAddress
);
1093 ProbeForWriteUlong(URegionSize
);
1096 /* Capture their values */
1097 PBaseAddress
= *UBaseAddress
;
1098 PRegionSize
= *URegionSize
;
1100 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1102 /* Return the exception code */
1103 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1107 /* Make sure the allocation isn't past the user area */
1108 if (PBaseAddress
>= MM_HIGHEST_USER_ADDRESS
)
1110 DPRINT1("Virtual free base above User Space\n");
1111 return STATUS_INVALID_PARAMETER_2
;
1114 /* Make sure the allocation wouldn't overflow past the user area */
1115 if (((ULONG_PTR
)MM_HIGHEST_USER_ADDRESS
- (ULONG_PTR
)PBaseAddress
) < PRegionSize
)
1117 DPRINT1("Region size would overflow into kernel-memory\n");
1118 return STATUS_INVALID_PARAMETER_3
;
1121 /* Check if this is for the current process */
1122 if (ProcessHandle
== NtCurrentProcess())
1124 /* We already have the current process, no need to go through Ob */
1125 Process
= CurrentProcess
;
1129 /* Reference the handle for correct permissions */
1130 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1131 PROCESS_VM_OPERATION
,
1136 if (!NT_SUCCESS(Status
)) return Status
;
1138 /* Check if not running in the current process */
1139 if (CurrentProcess
!= Process
)
1142 KeStackAttachProcess(&Process
->Pcb
, &ApcState
);
1147 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((PBaseAddress
));
1148 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)(PBaseAddress
) + (PRegionSize
)) -
1149 PAGE_ROUND_DOWN((PBaseAddress
));
1151 AddressSpace
= &Process
->Vm
;
1153 MmLockAddressSpace(AddressSpace
);
1154 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
1155 if (MemoryArea
== NULL
)
1157 Status
= STATUS_UNSUCCESSFUL
;
1158 goto unlock_deref_and_return
;
1164 /* We can only free a memory area in one step. */
1165 if (MemoryArea
->StartingAddress
!= BaseAddress
||
1166 MemoryArea
->Type
!= MEMORY_AREA_VIRTUAL_MEMORY
)
1168 Status
= STATUS_UNSUCCESSFUL
;
1169 goto unlock_deref_and_return
;
1172 MmFreeVirtualMemory(Process
, MemoryArea
);
1173 Status
= STATUS_SUCCESS
;
1174 goto unlock_deref_and_return
;
1178 MmAlterRegion(AddressSpace
,
1179 MemoryArea
->StartingAddress
,
1180 (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
) ?
1181 &MemoryArea
->Data
.SectionData
.RegionListHead
:
1182 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1187 MmModifyAttributes
);
1188 goto unlock_deref_and_return
;
1191 Status
= STATUS_NOT_IMPLEMENTED
;
1193 unlock_deref_and_return
:
1195 MmUnlockAddressSpace(AddressSpace
);
1196 if (Attached
) KeUnstackDetachProcess(&ApcState
);
1197 if (ProcessHandle
!= NtCurrentProcess()) ObDereferenceObject(Process
);
1204 MmProtectAnonMem(PMMSUPPORT AddressSpace
,
1205 PMEMORY_AREA MemoryArea
,
1212 NTSTATUS Status
= STATUS_SUCCESS
;
1213 ULONG LengthCount
= 0;
1215 /* Search all Regions in MemoryArea up to Length */
1216 /* Every Region up to Length must be committed for success */
1219 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1220 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1221 (PVOID
)((ULONG_PTR
)BaseAddress
+ (ULONG_PTR
)LengthCount
), NULL
);
1223 /* If a Region was found and it is committed */
1224 if ((Region
) && (Region
->Type
== MEM_COMMIT
))
1226 LengthCount
+= Region
->Length
;
1227 if (Length
<= LengthCount
) break;
1230 /* If Region was found and it is not commited */
1233 Status
= STATUS_NOT_COMMITTED
;
1236 /* If no Region was found at all */
1237 else if (LengthCount
== 0)
1239 Status
= STATUS_INVALID_ADDRESS
;
1244 if (NT_SUCCESS(Status
))
1246 *OldProtect
= Region
->Protect
;
1247 Status
= MmAlterRegion(AddressSpace
, MemoryArea
->StartingAddress
,
1248 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1249 BaseAddress
, Length
, Region
->Type
, Protect
,
1250 MmModifyAttributes
);
1257 MmQueryAnonMem(PMEMORY_AREA MemoryArea
,
1259 PMEMORY_BASIC_INFORMATION Info
,
1260 PSIZE_T ResultLength
)
1263 PVOID RegionBase
= NULL
;
1265 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
1267 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1268 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1269 Address
, &RegionBase
);
1270 Info
->BaseAddress
= RegionBase
;
1271 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
1272 Info
->AllocationProtect
= MemoryArea
->Protect
;
1273 Info
->RegionSize
= Region
->Length
;
1274 Info
->State
= Region
->Type
;
1275 Info
->Protect
= Region
->Protect
;
1276 Info
->Type
= MEM_PRIVATE
;
1278 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1279 return(STATUS_SUCCESS
);