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 /* FUNCTIONS *****************************************************************/
50 MmWritePageVirtualMemory(PMMSUPPORT AddressSpace
,
51 PMEMORY_AREA MemoryArea
,
58 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
61 * Check for paging out from a deleted virtual memory area.
63 if (MemoryArea
->DeleteInProgress
)
65 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
66 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
67 MmReleasePageOp(PageOp
);
68 return(STATUS_UNSUCCESSFUL
);
71 Page
= MmGetPfnForProcess(Process
, Address
);
74 * Get that the page actually is dirty.
76 if (!MmIsDirtyPage(Process
, Address
))
78 PageOp
->Status
= STATUS_SUCCESS
;
79 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
80 MmReleasePageOp(PageOp
);
81 return(STATUS_SUCCESS
);
85 * Speculatively set the mapping to clean.
87 MmSetCleanPage(Process
, Address
);
90 * If necessary, allocate an entry in the paging file for this page
92 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
95 SwapEntry
= MmAllocSwapPage();
98 MmSetDirtyPage(Process
, Address
);
99 PageOp
->Status
= STATUS_PAGEFILE_QUOTA_EXCEEDED
;
100 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
101 MmReleasePageOp(PageOp
);
102 return(STATUS_PAGEFILE_QUOTA_EXCEEDED
);
107 * Write the page to the pagefile
109 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
110 if (!NT_SUCCESS(Status
))
112 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
114 MmSetDirtyPage(Process
, Address
);
115 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
116 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
117 MmReleasePageOp(PageOp
);
118 return(STATUS_UNSUCCESSFUL
);
122 * Otherwise we have succeeded.
124 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
125 PageOp
->Status
= STATUS_SUCCESS
;
126 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
127 MmReleasePageOp(PageOp
);
128 return(STATUS_SUCCESS
);
133 MmPageOutVirtualMemory(PMMSUPPORT AddressSpace
,
134 PMEMORY_AREA MemoryArea
,
142 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
144 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
145 Address
, Process
->UniqueProcessId
);
148 * Check for paging out from a deleted virtual memory area.
150 if (MemoryArea
->DeleteInProgress
)
152 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
153 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
154 MmReleasePageOp(PageOp
);
155 return(STATUS_UNSUCCESSFUL
);
159 * Disable the virtual mapping.
161 MmDisableVirtualMapping(Process
, Address
,
166 KeBugCheck(MEMORY_MANAGEMENT
);
170 * Paging out non-dirty data is easy.
174 MmLockAddressSpace(AddressSpace
);
175 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
176 MmDeleteAllRmaps(Page
, NULL
, NULL
);
177 if ((SwapEntry
= MmGetSavedSwapEntryPage(Page
)) != 0)
179 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
180 MmSetSavedSwapEntryPage(Page
, 0);
182 MmUnlockAddressSpace(AddressSpace
);
183 MmReleasePageMemoryConsumer(MC_USER
, Page
);
184 PageOp
->Status
= STATUS_SUCCESS
;
185 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
186 MmReleasePageOp(PageOp
);
187 return(STATUS_SUCCESS
);
191 * If necessary, allocate an entry in the paging file for this page
193 SwapEntry
= MmGetSavedSwapEntryPage(Page
);
196 SwapEntry
= MmAllocSwapPage();
199 MmShowOutOfSpaceMessagePagingFile();
200 MmEnableVirtualMapping(Process
, Address
);
201 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
202 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
203 MmReleasePageOp(PageOp
);
204 return(STATUS_PAGEFILE_QUOTA
);
209 * Write the page to the pagefile
211 Status
= MmWriteToSwapPage(SwapEntry
, Page
);
212 if (!NT_SUCCESS(Status
))
214 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
216 MmEnableVirtualMapping(Process
, Address
);
217 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
218 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
219 MmReleasePageOp(PageOp
);
220 return(STATUS_UNSUCCESSFUL
);
224 * Otherwise we have succeeded, free the page
226 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page
<< PAGE_SHIFT
);
227 MmLockAddressSpace(AddressSpace
);
228 MmDeleteVirtualMapping(Process
, Address
, FALSE
, NULL
, NULL
);
229 MmCreatePageFileMapping(Process
, Address
, SwapEntry
);
230 MmUnlockAddressSpace(AddressSpace
);
231 MmDeleteAllRmaps(Page
, NULL
, NULL
);
232 MmSetSavedSwapEntryPage(Page
, 0);
233 MmReleasePageMemoryConsumer(MC_USER
, Page
);
234 PageOp
->Status
= STATUS_SUCCESS
;
235 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
236 MmReleasePageOp(PageOp
);
237 return(STATUS_SUCCESS
);
242 MmNotPresentFaultVirtualMemory(PMMSUPPORT AddressSpace
,
243 MEMORY_AREA
* MemoryArea
,
247 * FUNCTION: Move data into memory to satisfy a page not present fault
249 * AddressSpace = Address space within which the fault occurred
250 * MemoryArea = The memory area within which the fault occurred
251 * Address = The absolute address of fault
253 * NOTES: This function is called with the address space lock held.
260 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
264 * There is a window between taking the page fault and locking the
265 * address space when another thread could load the page so we check
268 if (MmIsPagePresent(NULL
, Address
))
272 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
273 MmLockPage(MmGetPfnForProcess(NULL
, Address
));
274 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
276 return(STATUS_SUCCESS
);
280 * Check for the virtual memory area being deleted.
282 if (MemoryArea
->DeleteInProgress
)
284 return(STATUS_UNSUCCESSFUL
);
288 * Get the segment corresponding to the virtual address
290 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
291 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
294 if (Region
->Type
== MEM_RESERVE
|| Region
->Protect
== PAGE_NOACCESS
)
296 return(STATUS_ACCESS_VIOLATION
);
302 if (Region
->Protect
& PAGE_GUARD
)
304 return(STATUS_GUARD_PAGE_VIOLATION
);
308 * Get or create a page operation
310 PageOp
= MmGetPageOp(MemoryArea
, Process
->UniqueProcessId
,
311 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
312 MM_PAGEOP_PAGEIN
, FALSE
);
315 DPRINT1("MmGetPageOp failed");
316 KeBugCheck(MEMORY_MANAGEMENT
);
320 * Check if someone else is already handling this fault, if so wait
323 if (PageOp
->Thread
!= PsGetCurrentThread())
325 MmUnlockAddressSpace(AddressSpace
);
326 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
332 * Check for various strange conditions
334 if (Status
!= STATUS_SUCCESS
)
336 DPRINT1("Failed to wait for page op\n");
337 KeBugCheck(MEMORY_MANAGEMENT
);
339 if (PageOp
->Status
== STATUS_PENDING
)
341 DPRINT1("Woke for page op before completion\n");
342 KeBugCheck(MEMORY_MANAGEMENT
);
345 * If this wasn't a pagein then we need to restart the handling
347 if (PageOp
->OpType
!= MM_PAGEOP_PAGEIN
)
349 MmLockAddressSpace(AddressSpace
);
350 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
351 MmReleasePageOp(PageOp
);
352 return(STATUS_MM_RESTART_OPERATION
);
355 * If the thread handling this fault has failed then we don't retry
357 if (!NT_SUCCESS(PageOp
->Status
))
359 MmLockAddressSpace(AddressSpace
);
360 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
361 Status
= PageOp
->Status
;
362 MmReleasePageOp(PageOp
);
365 MmLockAddressSpace(AddressSpace
);
368 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
369 MmLockPage(MmGetPfnForProcess(NULL
, Address
));
370 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
372 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
373 MmReleasePageOp(PageOp
);
374 return(STATUS_SUCCESS
);
378 * Try to allocate a page
380 Status
= MmRequestPageMemoryConsumer(MC_USER
, FALSE
, &Page
);
381 if (Status
== STATUS_NO_MEMORY
)
383 MmUnlockAddressSpace(AddressSpace
);
384 Status
= MmRequestPageMemoryConsumer(MC_USER
, TRUE
, &Page
);
385 MmLockAddressSpace(AddressSpace
);
387 if (!NT_SUCCESS(Status
))
389 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status
);
390 KeBugCheck(MEMORY_MANAGEMENT
);
394 * Handle swapped out pages.
396 if (MmIsPageSwapEntry(NULL
, Address
))
400 MmDeletePageFileMapping(Process
, Address
, &SwapEntry
);
401 Status
= MmReadFromSwapPage(SwapEntry
, Page
);
402 if (!NT_SUCCESS(Status
))
404 KeBugCheck(MEMORY_MANAGEMENT
);
406 MmSetSavedSwapEntryPage(Page
, SwapEntry
);
410 * Set the page. If we fail because we are out of memory then
413 Status
= MmCreateVirtualMapping(Process
,
414 (PVOID
)PAGE_ROUND_DOWN(Address
),
418 while (Status
== STATUS_NO_MEMORY
)
420 MmUnlockAddressSpace(AddressSpace
);
421 Status
= MmCreateVirtualMapping(Process
,
426 MmLockAddressSpace(AddressSpace
);
428 if (!NT_SUCCESS(Status
))
430 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
431 KeBugCheck(MEMORY_MANAGEMENT
);
436 * Add the page to the process's working set
438 MmInsertRmap(Page
, Process
, (PVOID
)PAGE_ROUND_DOWN(Address
));
441 * Finish the operation
445 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
447 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
449 PageOp
->Status
= STATUS_SUCCESS
;
450 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
451 MmReleasePageOp(PageOp
);
452 return(STATUS_SUCCESS
);
456 MmModifyAttributes(PMMSUPPORT AddressSpace
,
464 * FUNCTION: Modify the attributes of a memory region
467 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
470 * If we are switching a previously committed region to reserved then
471 * free any allocated pages within the region
473 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
477 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
481 if (MmIsPageSwapEntry(Process
,
482 (char*)BaseAddress
+ (i
* PAGE_SIZE
)))
486 MmDeletePageFileMapping(Process
,
487 (char*)BaseAddress
+ (i
* PAGE_SIZE
),
489 MmFreeSwapPage(SwapEntry
);
493 MmDeleteVirtualMapping(Process
,
494 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
498 SWAPENTRY SavedSwapEntry
;
499 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
500 if (SavedSwapEntry
!= 0)
502 MmFreeSwapPage(SavedSwapEntry
);
503 MmSetSavedSwapEntryPage(Page
, 0);
505 MmDeleteRmap(Page
, Process
,
506 (char*)BaseAddress
+ (i
* PAGE_SIZE
));
507 MmReleasePageMemoryConsumer(MC_USER
, Page
);
514 * If we are changing the protection attributes of a committed region then
515 * alter the attributes for any allocated pages within the region
517 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
518 OldProtect
!= NewProtect
)
522 for (i
=0; i
< PAGE_ROUND_UP(RegionSize
)/PAGE_SIZE
; i
++)
524 if (MmIsPagePresent(Process
,
525 (char*)BaseAddress
+ (i
*PAGE_SIZE
)))
527 MmSetPageProtect(Process
,
528 (char*)BaseAddress
+ (i
*PAGE_SIZE
),
539 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
540 IN OUT PVOID
* UBaseAddress
,
541 IN ULONG_PTR ZeroBits
,
542 IN OUT PSIZE_T URegionSize
,
543 IN ULONG AllocationType
,
546 * FUNCTION: Allocates a block of virtual memory in the process address space
548 * ProcessHandle = The handle of the process which owns the virtual memory
549 * BaseAddress = A pointer to the virtual memory allocated. If you
550 * supply a non zero value the system will try to
551 * allocate the memory at the address supplied. It round
552 * it down to a multiple of the page size.
553 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
554 * that must be zero, ensuring that the memory will be
555 * allocated at a address below a certain value.
556 * RegionSize = The number of bytes to allocate
557 * AllocationType = Indicates the type of virtual memory you like to
558 * allocated, can be a combination of MEM_COMMIT,
559 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
560 * Protect = Indicates the protection type of the pages allocated.
565 MEMORY_AREA
* MemoryArea
;
566 ULONG_PTR MemoryAreaLength
;
569 PMMSUPPORT AddressSpace
;
575 PHYSICAL_ADDRESS BoundaryAddressMultiple
;
576 KPROCESSOR_MODE PreviousMode
;
580 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
581 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
582 *UBaseAddress
,ZeroBits
,*URegionSize
,AllocationType
,
585 /* Check for valid protection flags */
586 MemProtection
= Protect
& ~(PAGE_GUARD
|PAGE_NOCACHE
);
587 if (MemProtection
!= PAGE_NOACCESS
&&
588 MemProtection
!= PAGE_READONLY
&&
589 MemProtection
!= PAGE_READWRITE
&&
590 MemProtection
!= PAGE_WRITECOPY
&&
591 MemProtection
!= PAGE_EXECUTE
&&
592 MemProtection
!= PAGE_EXECUTE_READ
&&
593 MemProtection
!= PAGE_EXECUTE_READWRITE
&&
594 MemProtection
!= PAGE_EXECUTE_WRITECOPY
)
596 DPRINT1("Invalid page protection\n");
597 return STATUS_INVALID_PAGE_PROTECTION
;
600 /* Check for valid Zero bits */
603 DPRINT1("Too many zero bits\n");
604 return STATUS_INVALID_PARAMETER_3
;
607 /* Check for valid Allocation Types */
608 if ((AllocationType
& ~(MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
| MEM_PHYSICAL
|
609 MEM_TOP_DOWN
| MEM_WRITE_WATCH
)))
611 DPRINT1("Invalid Allocation Type\n");
612 return STATUS_INVALID_PARAMETER_5
;
615 /* Check for at least one of these Allocation Types to be set */
616 if (!(AllocationType
& (MEM_COMMIT
| MEM_RESERVE
| MEM_RESET
)))
618 DPRINT1("No memory allocation base type\n");
619 return STATUS_INVALID_PARAMETER_5
;
622 /* MEM_RESET is an exclusive flag, make sure that is valid too */
623 if ((AllocationType
& MEM_RESET
) && (AllocationType
!= MEM_RESET
))
625 DPRINT1("Invalid use of MEM_RESET\n");
626 return STATUS_INVALID_PARAMETER_5
;
629 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
630 if ((AllocationType
& MEM_WRITE_WATCH
) && !(AllocationType
& MEM_RESERVE
))
632 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
633 return STATUS_INVALID_PARAMETER_5
;
636 /* MEM_PHYSICAL can only be used with MEM_RESERVE, and can only be R/W */
637 if (AllocationType
& MEM_PHYSICAL
)
639 /* First check for MEM_RESERVE exclusivity */
640 if (AllocationType
!= (MEM_RESERVE
| MEM_PHYSICAL
))
642 DPRINT1("MEM_PHYSICAL used with other flags then MEM_RESERVE or"
643 "MEM_RESERVE was not present at all\n");
644 return STATUS_INVALID_PARAMETER_5
;
647 /* Then make sure PAGE_READWRITE is used */
648 if (Protect
!= PAGE_READWRITE
)
650 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
651 return STATUS_INVALID_PAGE_PROTECTION
;
655 PreviousMode
= KeGetPreviousMode();
659 if (PreviousMode
!= KernelMode
)
661 ProbeForWritePointer(UBaseAddress
);
662 ProbeForWriteUlong(URegionSize
);
664 PBaseAddress
= *UBaseAddress
;
665 PRegionSize
= *URegionSize
;
667 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
669 /* Return the exception code */
670 _SEH2_YIELD(return _SEH2_GetExceptionCode());
674 BoundaryAddressMultiple
.QuadPart
= 0;
676 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
677 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)PBaseAddress
+ PRegionSize
) -
678 PAGE_ROUND_DOWN(PBaseAddress
);
681 * We've captured and calculated the data, now do more checks
682 * Yes, MmCreateMemoryArea does similar checks, but they don't return
683 * the right status codes that a caller of this routine would expect.
685 if ((ULONG_PTR
)BaseAddress
>= USER_SHARED_DATA
)
687 DPRINT1("Virtual allocation base above User Space\n");
688 return STATUS_INVALID_PARAMETER_2
;
692 DPRINT1("Region size is invalid (zero)\n");
693 return STATUS_INVALID_PARAMETER_4
;
695 if ((USER_SHARED_DATA
- (ULONG_PTR
)BaseAddress
) < RegionSize
)
697 DPRINT1("Region size would overflow into kernel-memory\n");
698 return STATUS_INVALID_PARAMETER_4
;
702 * Copy on Write is reserved for system use. This case is a certain failure
703 * but there may be other cases...needs more testing
705 if ((!BaseAddress
|| (AllocationType
& MEM_RESERVE
)) &&
706 (Protect
& (PAGE_WRITECOPY
| PAGE_EXECUTE_WRITECOPY
)))
708 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
709 return STATUS_INVALID_PAGE_PROTECTION
;
713 Status
= ObReferenceObjectByHandle(ProcessHandle
,
714 PROCESS_VM_OPERATION
,
719 if (!NT_SUCCESS(Status
))
721 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
725 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
726 DPRINT("Type %x\n", Type
);
728 AddressSpace
= &Process
->Vm
;
729 MmLockAddressSpace(AddressSpace
);
731 if (PBaseAddress
!= 0)
733 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
735 if (MemoryArea
!= NULL
)
737 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
738 (ULONG_PTR
)MemoryArea
->StartingAddress
;
740 if (((ULONG_PTR
)BaseAddress
+ RegionSize
) > (ULONG_PTR
)MemoryArea
->EndingAddress
)
742 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
743 (ULONG_PTR
)BaseAddress
+ RegionSize
, MemoryArea
->EndingAddress
);
745 MmUnlockAddressSpace(AddressSpace
);
746 ObDereferenceObject(Process
);
748 return STATUS_MEMORY_NOT_ALLOCATED
;
751 if (AllocationType
== MEM_RESET
)
753 if (MmIsPagePresent(Process
, BaseAddress
))
755 /* FIXME: mark pages as not modified */
759 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
762 MmUnlockAddressSpace(AddressSpace
);
763 ObDereferenceObject(Process
);
765 /* MEM_RESET does not modify any attributes of region */
766 return STATUS_SUCCESS
;
769 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
770 MemoryAreaLength
>= RegionSize
)
773 MmAlterRegion(AddressSpace
,
774 MemoryArea
->StartingAddress
,
775 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
776 BaseAddress
, RegionSize
,
777 Type
, Protect
, MmModifyAttributes
);
778 MmUnlockAddressSpace(AddressSpace
);
779 ObDereferenceObject(Process
);
780 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
782 /* Give the caller rounded BaseAddress and area length */
783 if (NT_SUCCESS(Status
))
785 *UBaseAddress
= BaseAddress
;
786 *URegionSize
= RegionSize
;
787 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
792 else if (MemoryAreaLength
>= RegionSize
)
794 /* Region list initialized? */
795 if (MemoryArea
->Data
.SectionData
.RegionListHead
.Flink
)
798 MmAlterRegion(AddressSpace
,
799 MemoryArea
->StartingAddress
,
800 &MemoryArea
->Data
.SectionData
.RegionListHead
,
801 BaseAddress
, RegionSize
,
802 Type
, Protect
, MmModifyAttributes
);
806 Status
= STATUS_ACCESS_VIOLATION
;
809 MmUnlockAddressSpace(AddressSpace
);
810 ObDereferenceObject(Process
);
811 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
813 /* Give the caller rounded BaseAddress and area length */
814 if (NT_SUCCESS(Status
))
816 *UBaseAddress
= BaseAddress
;
817 *URegionSize
= RegionSize
;
818 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
825 MmUnlockAddressSpace(AddressSpace
);
826 ObDereferenceObject(Process
);
827 return(STATUS_UNSUCCESSFUL
);
832 Status
= MmCreateMemoryArea(AddressSpace
,
833 MEMORY_AREA_VIRTUAL_MEMORY
,
839 AllocationType
& MEM_TOP_DOWN
,
840 BoundaryAddressMultiple
);
841 if (!NT_SUCCESS(Status
))
843 MmUnlockAddressSpace(AddressSpace
);
844 ObDereferenceObject(Process
);
845 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
849 MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
850 (ULONG_PTR
)MemoryArea
->StartingAddress
;
852 MmInitializeRegion(&MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
853 MemoryAreaLength
, Type
, Protect
);
855 if ((AllocationType
& MEM_COMMIT
) &&
856 (Protect
& (PAGE_READWRITE
| PAGE_EXECUTE_READWRITE
)))
858 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
859 MmReserveSwapPages(nPages
);
862 *UBaseAddress
= BaseAddress
;
863 *URegionSize
= MemoryAreaLength
;
864 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
866 MmUnlockAddressSpace(AddressSpace
);
867 ObDereferenceObject(Process
);
868 return(STATUS_SUCCESS
);
872 MmFreeVirtualMemoryPage(PVOID Context
,
873 MEMORY_AREA
* MemoryArea
,
879 PEPROCESS Process
= (PEPROCESS
)Context
;
883 SWAPENTRY SavedSwapEntry
;
884 SavedSwapEntry
= MmGetSavedSwapEntryPage(Page
);
885 if (SavedSwapEntry
!= 0)
887 MmFreeSwapPage(SavedSwapEntry
);
888 MmSetSavedSwapEntryPage(Page
, 0);
890 MmDeleteRmap(Page
, Process
, Address
);
891 MmReleasePageMemoryConsumer(MC_USER
, Page
);
893 else if (SwapEntry
!= 0)
895 MmFreeSwapPage(SwapEntry
);
901 MmFreeVirtualMemory(PEPROCESS Process
,
902 PMEMORY_AREA MemoryArea
)
904 PLIST_ENTRY current_entry
;
908 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process
,
911 /* Mark this memory area as about to be deleted. */
912 MemoryArea
->DeleteInProgress
= TRUE
;
915 * Wait for any ongoing paging operations. Notice that since we have
916 * flagged this memory area as deleted no more page ops will be added.
918 if (MemoryArea
->PageOpCount
> 0)
920 ULONG_PTR MemoryAreaLength
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
921 (ULONG_PTR
)MemoryArea
->StartingAddress
;
922 const ULONG nPages
= PAGE_ROUND_UP(MemoryAreaLength
) >> PAGE_SHIFT
;
924 for (i
= 0; i
< nPages
&& MemoryArea
->PageOpCount
!= 0; ++i
)
927 PageOp
= MmCheckForPageOp(MemoryArea
, Process
->UniqueProcessId
,
928 (PVOID
)((ULONG_PTR
)MemoryArea
->StartingAddress
+ (i
* PAGE_SIZE
)),
933 MmUnlockAddressSpace(&Process
->Vm
);
934 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
939 if (Status
!= STATUS_SUCCESS
)
941 DPRINT1("Failed to wait for page op\n");
942 KeBugCheck(MEMORY_MANAGEMENT
);
944 MmLockAddressSpace(&Process
->Vm
);
945 MmReleasePageOp(PageOp
);
950 /* Free all the individual segments. */
951 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
.Flink
;
952 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
)
954 current
= CONTAINING_RECORD(current_entry
, MM_REGION
, RegionListEntry
);
955 current_entry
= current_entry
->Flink
;
959 /* Actually free the memory area. */
960 MmFreeMemoryArea(&Process
->Vm
,
962 MmFreeVirtualMemoryPage
,
970 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
971 IN PVOID
* PBaseAddress
,
972 IN PSIZE_T PRegionSize
,
975 * FUNCTION: Frees a range of virtual memory
977 * ProcessHandle = Points to the process that allocated the virtual
979 * BaseAddress = Points to the memory address, rounded down to a
980 * multiple of the pagesize
981 * RegionSize = Limits the range to free, rounded up to a multiple of
983 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
987 MEMORY_AREA
* MemoryArea
;
990 PMMSUPPORT AddressSpace
;
996 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
997 "*PRegionSize %x, FreeType %x)\n",ProcessHandle
,*PBaseAddress
,
998 *PRegionSize
,FreeType
);
1000 if (!(FreeType
& (MEM_RELEASE
| MEM_DECOMMIT
)))
1002 DPRINT1("Invalid FreeType\n");
1003 return STATUS_INVALID_PARAMETER_4
;
1006 if (ExGetPreviousMode() != KernelMode
)
1010 /* Probe user pointers */
1011 ProbeForWriteSize_t(PRegionSize
);
1012 ProbeForWritePointer(PBaseAddress
);
1014 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1016 /* Return the exception code */
1017 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1022 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((*PBaseAddress
));
1023 RegionSize
= PAGE_ROUND_UP((ULONG_PTR
)(*PBaseAddress
) + (*PRegionSize
)) -
1024 PAGE_ROUND_DOWN((*PBaseAddress
));
1026 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1027 PROCESS_VM_OPERATION
,
1032 if (!NT_SUCCESS(Status
))
1037 AddressSpace
= &Process
->Vm
;
1039 MmLockAddressSpace(AddressSpace
);
1040 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, BaseAddress
);
1041 if (MemoryArea
== NULL
)
1043 Status
= STATUS_UNSUCCESSFUL
;
1044 goto unlock_deref_and_return
;
1050 /* We can only free a memory area in one step. */
1051 if (MemoryArea
->StartingAddress
!= BaseAddress
||
1052 MemoryArea
->Type
!= MEMORY_AREA_VIRTUAL_MEMORY
)
1054 Status
= STATUS_UNSUCCESSFUL
;
1055 goto unlock_deref_and_return
;
1058 MmFreeVirtualMemory(Process
, MemoryArea
);
1059 Status
= STATUS_SUCCESS
;
1060 goto unlock_deref_and_return
;
1064 MmAlterRegion(AddressSpace
,
1065 MemoryArea
->StartingAddress
,
1066 (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
) ?
1067 &MemoryArea
->Data
.SectionData
.RegionListHead
:
1068 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1073 MmModifyAttributes
);
1074 goto unlock_deref_and_return
;
1077 Status
= STATUS_NOT_IMPLEMENTED
;
1079 unlock_deref_and_return
:
1081 MmUnlockAddressSpace(AddressSpace
);
1082 ObDereferenceObject(Process
);
1089 MmProtectAnonMem(PMMSUPPORT AddressSpace
,
1090 PMEMORY_AREA MemoryArea
,
1097 NTSTATUS Status
= STATUS_SUCCESS
;
1098 ULONG LengthCount
= 0;
1100 /* Search all Regions in MemoryArea up to Length */
1101 /* Every Region up to Length must be committed for success */
1104 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1105 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1106 (PVOID
)((ULONG_PTR
)BaseAddress
+ (ULONG_PTR
)LengthCount
), NULL
);
1108 /* If a Region was found and it is committed */
1109 if ((Region
) && (Region
->Type
== MEM_COMMIT
))
1111 LengthCount
+= Region
->Length
;
1112 if (Length
<= LengthCount
) break;
1115 /* If Region was found and it is not commited */
1118 Status
= STATUS_NOT_COMMITTED
;
1121 /* If no Region was found at all */
1122 else if (LengthCount
== 0)
1124 Status
= STATUS_INVALID_ADDRESS
;
1129 if (NT_SUCCESS(Status
))
1131 *OldProtect
= Region
->Protect
;
1132 Status
= MmAlterRegion(AddressSpace
, MemoryArea
->StartingAddress
,
1133 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1134 BaseAddress
, Length
, Region
->Type
, Protect
,
1135 MmModifyAttributes
);
1142 MmQueryAnonMem(PMEMORY_AREA MemoryArea
,
1144 PMEMORY_BASIC_INFORMATION Info
,
1145 PSIZE_T ResultLength
)
1148 PVOID RegionBase
= NULL
;
1150 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
1152 Region
= MmFindRegion(MemoryArea
->StartingAddress
,
1153 &MemoryArea
->Data
.VirtualMemoryData
.RegionListHead
,
1154 Address
, &RegionBase
);
1155 Info
->BaseAddress
= RegionBase
;
1156 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
1157 Info
->AllocationProtect
= MemoryArea
->Protect
;
1158 Info
->RegionSize
= Region
->Length
;
1159 Info
->State
= Region
->Type
;
1160 Info
->Protect
= Region
->Protect
;
1161 Info
->Type
= MEM_PRIVATE
;
1163 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1164 return(STATUS_SUCCESS
);