1 /* $Id: virtual.c,v 1.49 2001/09/27 02:14:35 dwelch Exp $
3 * COPYRIGHT: See COPYING in the top directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/mm/virtual.c
6 * PURPOSE: implementing the Virtualxxx section of the win32 api
7 * PROGRAMMER: David Welch
10 * 10/6/98: Corrections from Iwan Fatahi (i_fatahi@hotmail.com)
11 * 30/9/98: Implemented ZwxxxVirtualMemory functions
14 /* INCLUDE *****************************************************************/
16 #include <ddk/ntddk.h>
17 #include <internal/mm.h>
18 #include <internal/ob.h>
19 #include <internal/io.h>
20 #include <internal/ps.h>
21 #include <internal/pool.h>
24 #include <internal/debug.h>
26 /* TYPES *********************************************************************/
28 typedef struct _MM_SEGMENT
33 LIST_ENTRY SegmentListEntry
;
34 } MM_SEGMENT
, *PMM_SEGMENT
;
36 /* GLOBALS *******************************************************************/
38 #define TAG_MM_SEGMENT TAG('M', 'S', 'E', 'G')
40 /* FUNCTIONS *****************************************************************/
43 MmGetSegmentForAddress(PMEMORY_AREA MArea
,
45 PVOID
* PCurrentAddress
)
47 * FUNCTION: Get the segment corresponding to a particular memory area and
50 * MArea (IN) = The memory area
51 * Address (IN) = The address to get the segment for
52 * PCurrentAddress (OUT) = The start of the segment
54 * The corresponding segment or NULL if an error occurred
58 PMM_SEGMENT CurrentSegment
;
61 if (Address
< MArea
->BaseAddress
||
62 Address
>= (MArea
->BaseAddress
+ MArea
->Length
))
65 *PCurrentAddress
= NULL
;
69 Current
= MArea
->Data
.VirtualMemoryData
.SegmentListHead
.Flink
;
70 CurrentAddress
= MArea
->BaseAddress
;
71 while (Current
!= &MArea
->Data
.VirtualMemoryData
.SegmentListHead
)
73 CurrentSegment
= CONTAINING_RECORD(Current
,
76 if (Address
>= CurrentAddress
&&
77 Address
< (CurrentAddress
+ CurrentSegment
->Length
))
79 *PCurrentAddress
= CurrentAddress
;
80 return(CurrentSegment
);
82 CurrentAddress
= CurrentAddress
+ CurrentSegment
->Length
;
83 Current
= Current
->Flink
;
90 MmWritePageVirtualMemory(PMADDRESS_SPACE AddressSpace
,
94 return(STATUS_UNSUCCESSFUL
);
98 ULONG
MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace
,
99 PMEMORY_AREA MemoryArea
,
103 ULONG PhysicalAddress
;
111 * Get or create a pageop
113 PageOp
= MmGetPageOp(MemoryArea
, AddressSpace
->Process
->UniqueProcessId
,
114 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
116 if (PageOp
->Thread
!= PsGetCurrentThread())
119 * On the assumption that handling pageouts speedly rather than
120 * in strict order is better abandon this one.
123 MmReleasePageOp(PageOp
);
124 return(STATUS_UNSUCCESSFUL
);
128 * Paging out code or readonly data is easy.
130 if ((MemoryArea
->Attributes
& PAGE_READONLY
) ||
131 (MemoryArea
->Attributes
& PAGE_EXECUTE_READ
))
133 MmRemovePageFromWorkingSet(AddressSpace
->Process
, Address
);
134 MmDeleteVirtualMapping(PsGetCurrentProcess(), Address
, FALSE
,
135 NULL
, &PhysicalAddress
);
136 MmDereferencePage((PVOID
)PhysicalAddress
);
139 PageOp
->Status
= STATUS_SUCCESS
;
140 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
141 MmReleasePageOp(PageOp
);
146 * Otherwise this is read-write data
148 MmDeleteVirtualMapping(PsGetCurrentProcess(), Address
, FALSE
,
149 &WasDirty
, &PhysicalAddress
);
152 MmRemovePageFromWorkingSet(AddressSpace
->Process
, Address
);
153 MmDereferencePage((PVOID
)PhysicalAddress
);
155 PageOp
->Status
= STATUS_SUCCESS
;
156 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
157 MmReleasePageOp(PageOp
);
162 * If necessary, allocate an entry in the paging file for this page
164 SwapEntry
= MmGetSavedSwapEntryPage((PVOID
)PhysicalAddress
);
167 SwapEntry
= MmAllocSwapPage();
170 Status
= MmCreateVirtualMapping(PsGetCurrentProcess(),
172 MemoryArea
->Attributes
,
175 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
176 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
177 MmReleasePageOp(PageOp
);
183 * Write the page to the pagefile
185 Mdl
= MmCreateMdl(NULL
, NULL
, PAGESIZE
);
186 MmBuildMdlFromPages(Mdl
, &PhysicalAddress
);
187 Status
= MmWriteToSwapPage(SwapEntry
, Mdl
);
188 if (!NT_SUCCESS(Status
))
190 DPRINT1("MM: Failed to write to swap page\n");
191 Status
= MmCreateVirtualMapping(PsGetCurrentProcess(),
193 MemoryArea
->Attributes
,
196 PageOp
->Status
= STATUS_UNSUCCESSFUL
;
197 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
198 MmReleasePageOp(PageOp
);
203 * Otherwise we have succeeded, free the page
205 MmRemovePageFromWorkingSet(AddressSpace
->Process
, Address
);
206 MmDereferencePage((PVOID
)PhysicalAddress
);
208 PageOp
->Status
= STATUS_SUCCESS
;
209 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
210 MmReleasePageOp(PageOp
);
215 MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace
,
216 MEMORY_AREA
* MemoryArea
,
220 * FUNCTION: Move data into memory to satisfy a page not present fault
222 * AddressSpace = Address space within which the fault occurred
223 * MemoryArea = The memory area within which the fault occurred
224 * Address = The absolute address of fault
226 * NOTES: This function is called with the address space lock held.
232 PVOID CurrentAddress
;
236 * There is a window between taking the page fault and locking the
237 * address space when another thread could load the page so we check
240 if (MmIsPagePresent(NULL
, Address
))
244 MmLockPage((PVOID
)MmGetPhysicalAddressForProcess(NULL
, Address
));
246 return(STATUS_SUCCESS
);
250 * Get the segment corresponding to the virtual address
252 Segment
= MmGetSegmentForAddress(MemoryArea
, Address
, &CurrentAddress
);
255 return(STATUS_UNSUCCESSFUL
);
257 if (Segment
->Type
== MEM_RESERVE
)
259 return(STATUS_UNSUCCESSFUL
);
263 * Get or create a page operation
265 PageOp
= MmGetPageOp(MemoryArea
, (ULONG
)PsGetCurrentProcessId(),
266 (PVOID
)PAGE_ROUND_DOWN(Address
), NULL
, 0,
270 DPRINT1("MmGetPageOp failed");
275 * Check if someone else is already handling this fault, if so wait
278 if (PageOp
->Thread
!= PsGetCurrentThread())
280 MmUnlockAddressSpace(AddressSpace
);
281 Status
= KeWaitForSingleObject(&PageOp
->CompletionEvent
,
287 * Check for various strange conditions
289 if (Status
!= STATUS_SUCCESS
)
291 DPRINT1("Failed to wait for page op\n");
294 if (PageOp
->Status
== STATUS_PENDING
)
296 DPRINT1("Woke for page op before completion\n");
300 * If this wasn't a pagein then we need to restart the handling
302 if (PageOp
->OpType
!= MM_PAGEOP_PAGEIN
)
304 MmReleasePageOp(PageOp
);
305 return(STATUS_MM_RESTART_OPERATION
);
308 * If the thread handling this fault has failed then we don't retry
310 if (!NT_SUCCESS(PageOp
->Status
))
312 MmReleasePageOp(PageOp
);
315 MmLockAddressSpace(AddressSpace
);
318 MmLockPage((PVOID
)MmGetPhysicalAddressForProcess(NULL
, Address
));
320 MmReleasePageOp(PageOp
);
321 return(STATUS_SUCCESS
);
325 * Try to allocate a page
327 Page
= MmAllocPage(0);
330 MmUnlockAddressSpace(AddressSpace
);
331 MmWaitForFreePages();
332 MmLockAddressSpace(AddressSpace
);
333 Page
= MmAllocPage(0);
337 * Add the page to the process's working set
339 MmAddPageToWorkingSet(PsGetCurrentProcess(),
340 (PVOID
)PAGE_ROUND_DOWN(Address
));
343 * Set the page. If we fail because we are out of memory then
346 Status
= MmCreateVirtualMapping(PsGetCurrentProcess(),
348 MemoryArea
->Attributes
,
350 while (Status
== STATUS_NO_MEMORY
)
352 MmUnlockAddressSpace(AddressSpace
);
353 MmWaitForFreePages();
354 MmLockAddressSpace(AddressSpace
);
355 Status
= MmCreateVirtualMapping(PsGetCurrentProcess(),
357 MemoryArea
->Attributes
,
360 if (!NT_SUCCESS(Status
))
362 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
368 * Finish the operation
372 MmLockPage((PVOID
)MmGetPhysicalAddressForProcess(NULL
, Address
));
374 PageOp
->Status
= STATUS_SUCCESS
;
375 KeSetEvent(&PageOp
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
376 MmReleasePageOp(PageOp
);
377 return(STATUS_SUCCESS
);
381 MmModifyAttributes(PMADDRESS_SPACE AddressSpace
,
389 * FUNCTION: Modify the attributes of a memory region
393 * If we are switching a previously committed region to reserved then
394 * free any allocated pages within the region
396 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
400 for (i
=0; i
<= (RegionSize
/PAGESIZE
); i
++)
402 LARGE_INTEGER PhysicalAddr
;
404 PhysicalAddr
= MmGetPhysicalAddress(BaseAddress
+ (i
*PAGESIZE
));
405 MmDeleteVirtualMapping(AddressSpace
->Process
,
406 BaseAddress
+ (i
*PAGESIZE
),
408 if (PhysicalAddr
.u
.LowPart
!= 0)
410 MmRemovePageFromWorkingSet(AddressSpace
->Process
,
411 BaseAddress
+ (i
*PAGESIZE
));
412 MmDereferencePage((PVOID
)(ULONG
)(PhysicalAddr
.u
.LowPart
));
418 * If we are changing the protection attributes of a committed region then
419 * alter the attributes for any allocated pages within the region
421 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
422 OldProtect
!= NewProtect
)
426 for (i
=0; i
<= (RegionSize
/PAGESIZE
); i
++)
428 if (MmIsPagePresent(AddressSpace
->Process
,
429 BaseAddress
+ (i
*PAGESIZE
)))
431 MmSetPageProtect(AddressSpace
->Process
,
432 BaseAddress
+ (i
*PAGESIZE
),
440 InsertAfterEntry(PLIST_ENTRY Previous
,
443 * FUNCTION: Insert a list entry after another entry in the list
446 Previous
->Flink
->Blink
= Entry
;
448 Entry
->Flink
= Previous
->Flink
;
449 Entry
->Blink
= Previous
;
451 Previous
->Flink
= Entry
;
456 MmDumpSegmentsMemoryArea(PMEMORY_AREA MemoryArea
)
458 PVOID CurrentAddress
;
459 PLIST_ENTRY CurrentEntry
;
460 PMM_SEGMENT CurrentSegment
;
461 PLIST_ENTRY ListHead
;
463 CurrentEntry
= MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
.Flink
;
464 ListHead
= &MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
;
466 CurrentAddress
= MemoryArea
->BaseAddress
;
467 while (CurrentEntry
!= ListHead
)
469 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
473 DbgPrint("0x%x 0x%x %d %d\n",
475 CurrentSegment
->Length
,
476 CurrentSegment
->Type
,
477 CurrentSegment
->Protect
);
479 CurrentAddress
= CurrentAddress
+ CurrentSegment
->Length
;
480 CurrentEntry
= CurrentEntry
->Flink
;
486 MmSplitSegment(PMADDRESS_SPACE AddressSpace
,
487 PMEMORY_AREA MemoryArea
,
492 PMM_SEGMENT FirstSegment
,
495 * FUNCTION: Split a memory segment internally
498 PMM_SEGMENT NewTopSegment
;
499 PMM_SEGMENT RegionSegment
;
504 DPRINT("MmSplitSegment()\n");
506 * Save the type and protection and length of the current segment
508 OldType
= FirstSegment
->Type
;
509 OldProtect
= FirstSegment
->Protect
;
510 OldLength
= FirstSegment
->Length
;
513 * If the segment is already of the right type and protection then
514 * there is nothing to do.
516 if (FirstSegment
->Type
== Type
&& FirstSegment
->Protect
== Protect
)
518 return(STATUS_SUCCESS
);
522 * Allocate the segment we might need here because if the allocation
523 * fails below it will be difficult to undo what we've done already.
525 NewTopSegment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
527 if (NewTopSegment
== NULL
)
529 return(STATUS_NO_MEMORY
);
532 if (FirstAddress
< RegionAddress
)
535 * If the region to be affected starts at a higher address than
536 * the current segment then create a new segment for the
539 RegionSegment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
541 if (RegionSegment
== NULL
)
543 ExFreePool(NewTopSegment
);
544 return(STATUS_NO_MEMORY
);
547 RegionSegment
->Type
= Type
;
548 RegionSegment
->Protect
= Protect
;
549 RegionSegment
->Length
= RegionLength
;
551 FirstSegment
->Length
= RegionAddress
- FirstAddress
;
553 InsertAfterEntry(&FirstSegment
->SegmentListEntry
,
554 &RegionSegment
->SegmentListEntry
);
559 * Otherwise just set its type and protection and length
562 FirstSegment
->Type
= Type
;
563 FirstSegment
->Protect
= Protect
;
564 FirstSegment
->Length
= RegionLength
;
566 RegionSegment
= FirstSegment
;
569 if ((FirstAddress
+ OldLength
) > (RegionAddress
+ RegionLength
))
572 * If the top of the current segment extends after the affected
573 * region then create a segment for the unaffected portion
576 NewTopSegment
->Type
= OldType
;
577 NewTopSegment
->Protect
= OldProtect
;
578 NewTopSegment
->Length
= (FirstAddress
+ OldLength
) -
579 (RegionAddress
+ RegionLength
);
581 InsertAfterEntry(&RegionSegment
->SegmentListEntry
,
582 &NewTopSegment
->SegmentListEntry
);
586 ExFreePool(NewTopSegment
);
587 NewTopSegment
= NULL
;
591 * Actually set the type and protection of the affected region
593 MmModifyAttributes(AddressSpace
,
600 return(STATUS_SUCCESS
);
603 NTSTATUS
MmGatherSegment(PMADDRESS_SPACE AddressSpace
,
604 PMEMORY_AREA MemoryArea
,
609 PMM_SEGMENT FirstSegment
,
612 * FUNCTION: Do a virtual memory operation that will effect several
615 * AddressSpace (IN) = Address space to affect
616 * MemoryArea (IN) = Memory area to affect
617 * BaseAddress (IN) = Base address of the region to affect
618 * RegionSize (IN) = Size of the region to affect
619 * Type (IN) = New type of the region
620 * Protect (IN) = New protection of the region
621 * CurrentSegment (IN) = First segment intersecting with the region
622 * CurrentAddress (IN) = Start address of the first segment
623 * interesting with the region
627 PMM_SEGMENT RegionSegment
;
628 PVOID CurrentAddress
;
629 ULONG RemainingLength
;
630 PLIST_ENTRY CurrentEntry
;
631 PLIST_ENTRY ListHead
;
632 PMM_SEGMENT CurrentSegment
;
634 if (FirstAddress
< RegionAddress
)
637 * If a portion of the first segment is not covered by the region then
638 * we need to split it into two segments
641 RegionSegment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
643 if (RegionSegment
== NULL
)
645 return(STATUS_NO_MEMORY
);
648 RegionSegment
->Type
= Type
;
649 RegionSegment
->Protect
= Protect
;
650 RegionSegment
->Length
= (FirstAddress
+ FirstSegment
->Length
) -
653 FirstSegment
->Length
= RegionAddress
- FirstAddress
;
655 InsertAfterEntry(&FirstSegment
->SegmentListEntry
,
656 &RegionSegment
->SegmentListEntry
);
658 MmModifyAttributes(AddressSpace
,
660 RegionSegment
->Length
,
662 FirstSegment
->Protect
,
666 CurrentAddress
= FirstAddress
+ FirstSegment
->Length
+
667 RegionSegment
->Length
;
672 * Otherwise just change the attributes of the segment
678 OldType
= FirstSegment
->Type
;
679 OldProtect
= FirstSegment
->Protect
;
681 FirstSegment
->Type
= Type
;
682 FirstSegment
->Protect
= Protect
;
684 RegionSegment
= FirstSegment
;
686 MmModifyAttributes(AddressSpace
,
688 FirstSegment
->Length
,
694 CurrentAddress
= FirstAddress
+ RegionSegment
->Length
;
698 * Change the attributes of all the complete segments lying inside the
701 RemainingLength
= RegionLength
- RegionSegment
->Length
;
702 CurrentEntry
= RegionSegment
->SegmentListEntry
.Flink
;
703 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
706 ListHead
= &MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
;
708 while (CurrentEntry
!= ListHead
&& RemainingLength
> 0)
715 * If this segment will not be completely covered by the
716 * affected region then break
718 if (CurrentSegment
->Length
> RemainingLength
)
723 OldType
= CurrentSegment
->Type
;
724 OldProtect
= CurrentSegment
->Protect
;
725 OldLength
= CurrentSegment
->Length
;
728 * Extend the length of the previous segment to cover this one
730 RegionSegment
->Length
= RegionSegment
->Length
+ OldLength
;
731 RemainingLength
= RemainingLength
- OldLength
;
732 CurrentAddress
= CurrentAddress
+ OldLength
;
733 CurrentEntry
= CurrentEntry
->Flink
;
736 * Remove the current segment from the list
738 RemoveEntryList(&CurrentSegment
->SegmentListEntry
);
739 ExFreePool(CurrentSegment
);
741 MmModifyAttributes(AddressSpace
,
749 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
755 * If we've run off the top of the memory area then bug check
757 if (CurrentEntry
== ListHead
&& RemainingLength
> 0)
763 * We've only affected a portion of a segment then split it in two
765 if (RemainingLength
> 0)
767 CurrentSegment
->Length
= CurrentSegment
->Length
- RemainingLength
;
769 RegionSegment
->Length
= RegionSegment
->Length
+ RemainingLength
;
771 MmModifyAttributes(AddressSpace
,
774 CurrentSegment
->Type
,
775 CurrentSegment
->Protect
,
780 return(STATUS_SUCCESS
);
783 NTSTATUS
MmComplexVirtualMemoryOperation(PMADDRESS_SPACE AddressSpace
,
784 PMEMORY_AREA MemoryArea
,
790 PMM_SEGMENT CurrentSegment
;
791 PVOID CurrentAddress
;
793 CurrentSegment
= MmGetSegmentForAddress(MemoryArea
,
796 if (CurrentSegment
== NULL
)
801 if (BaseAddress
>= CurrentAddress
&&
802 (BaseAddress
+ RegionSize
) <= (CurrentAddress
+ CurrentSegment
->Length
))
804 return((MmSplitSegment(AddressSpace
,
815 return((MmGatherSegment(AddressSpace
,
828 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
829 IN OUT PVOID
* UBaseAddress
,
831 IN OUT PULONG URegionSize
,
832 IN ULONG AllocationType
,
835 * FUNCTION: Allocates a block of virtual memory in the process address space
837 * ProcessHandle = The handle of the process which owns the virtual memory
838 * BaseAddress = A pointer to the virtual memory allocated. If you
839 * supply a non zero value the system will try to
840 * allocate the memory at the address supplied. It round
841 * it down to a multiple of the page size.
842 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
843 * that must be zero, ensuring that the memory will be
844 * allocated at a address below a certain value.
845 * RegionSize = The number of bytes to allocate
846 * AllocationType = Indicates the type of virtual memory you like to
847 * allocated, can be a combination of MEM_COMMIT,
848 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
849 * Protect = Indicates the protection type of the pages allocated, can be
850 * a combination of PAGE_READONLY, PAGE_READWRITE,
851 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
854 * This function maps to the win32 VirtualAllocEx. Virtual memory is
855 * process based so the protocol starts with a ProcessHandle. I
856 * splitted the functionality of obtaining the actual address and
857 * specifying the start address in two parameters ( BaseAddress and
858 * StartAddress ) The NumberOfBytesAllocated specify the range and the
859 * AllocationType and ProctectionType map to the other two parameters.
864 MEMORY_AREA
* MemoryArea
;
867 PMADDRESS_SPACE AddressSpace
;
874 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
875 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
876 *UBaseAddress
,ZeroBits
,*URegionSize
,AllocationType
,
880 * Check the validity of the parameters
882 if ((Protect
& PAGE_FLAGS_VALID_FROM_USER_MODE
) != Protect
)
884 return(STATUS_INVALID_PAGE_PROTECTION
);
886 if ((AllocationType
& (MEM_COMMIT
| MEM_RESERVE
)) == 0)
888 return(STATUS_INVALID_PARAMETER
);
890 if (((AllocationType
& (MEM_COMMIT
| MEM_RESERVE
)) == MEM_COMMIT
) &&
891 (*UBaseAddress
== 0))
893 /* Fix for badly behaved vc applications. */
894 AllocationType
|= MEM_RESERVE
;
897 PBaseAddress
= *UBaseAddress
;
898 PRegionSize
= *URegionSize
;
901 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
902 RegionSize
= PAGE_ROUND_UP(PBaseAddress
+ PRegionSize
) -
903 PAGE_ROUND_DOWN(PBaseAddress
);
905 Status
= ObReferenceObjectByHandle(ProcessHandle
,
906 PROCESS_VM_OPERATION
,
911 if (!NT_SUCCESS(Status
))
913 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
917 Type
= (AllocationType
& MEM_COMMIT
) ? MEM_COMMIT
: MEM_RESERVE
;
918 DPRINT("Type %x\n", Type
);
920 AddressSpace
= &Process
->AddressSpace
;
921 MmLockAddressSpace(AddressSpace
);
923 if ((PBaseAddress
!= 0) &&
924 ((AllocationType
& (MEM_COMMIT
| MEM_RESERVE
)) == MEM_COMMIT
))
926 MemoryArea
= MmOpenMemoryAreaByAddress(&Process
->AddressSpace
,
929 if (MemoryArea
!= NULL
&&
930 MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
931 MemoryArea
->Length
>= RegionSize
)
933 Status
= MmComplexVirtualMemoryOperation(AddressSpace
,
939 /* FIXME: Reserve/dereserve swap pages */
940 MmUnlockAddressSpace(AddressSpace
);
941 ObDereferenceObject(Process
);
942 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
945 else if (MemoryArea
!= NULL
)
947 MmUnlockAddressSpace(AddressSpace
);
948 ObDereferenceObject(Process
);
949 return(STATUS_UNSUCCESSFUL
);
953 Segment
= ExAllocatePoolWithTag(NonPagedPool
,
958 MmUnlockAddressSpace(AddressSpace
);
959 ObDereferenceObject(Process
);
960 return(STATUS_UNSUCCESSFUL
);
963 Status
= MmCreateMemoryArea(Process
,
964 &Process
->AddressSpace
,
965 MEMORY_AREA_VIRTUAL_MEMORY
,
972 if (!NT_SUCCESS(Status
))
974 MmUnlockAddressSpace(AddressSpace
);
975 ObDereferenceObject(Process
);
976 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
980 InitializeListHead(&MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
);
982 Segment
->Type
= Type
;
983 Segment
->Protect
= Protect
;
984 Segment
->Length
= RegionSize
;
985 InsertTailList(&MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
,
986 &Segment
->SegmentListEntry
);
988 if ((AllocationType
& MEM_COMMIT
) &&
989 ((Protect
& PAGE_READWRITE
) ||
990 (Protect
& PAGE_EXECUTE_READWRITE
)))
992 MmReserveSwapPages(RegionSize
);
995 *UBaseAddress
= BaseAddress
;
996 *URegionSize
= RegionSize
;
997 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress
, RegionSize
);
999 MmUnlockAddressSpace(AddressSpace
);
1000 ObDereferenceObject(Process
);
1001 return(STATUS_SUCCESS
);
1006 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
1007 IN PVOID BaseAddress
,
1008 IN ULONG NumberOfBytesToFlush
,
1009 OUT PULONG NumberOfBytesFlushed OPTIONAL
)
1011 * FUNCTION: Flushes virtual memory to file
1013 * ProcessHandle = Points to the process that allocated the virtual
1015 * BaseAddress = Points to the memory address
1016 * NumberOfBytesToFlush = Limits the range to flush,
1017 * NumberOfBytesFlushed = Actual number of bytes flushed
1025 MmFreeVirtualMemoryPage(PVOID Context
,
1029 PEPROCESS Process
= (PEPROCESS
)Context
;
1031 if (PhysicalAddr
!= 0)
1033 MmRemovePageFromWorkingSet(Process
, Address
);
1034 MmDereferencePage((PVOID
)PhysicalAddr
);
1039 MmFreeVirtualMemory(PEPROCESS Process
,
1040 PMEMORY_AREA MemoryArea
)
1042 PLIST_ENTRY current_entry
;
1043 PMM_SEGMENT current
;
1045 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process
, MemoryArea
);
1047 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
.Flink
;
1048 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
)
1050 current
= CONTAINING_RECORD(current_entry
, MM_SEGMENT
, SegmentListEntry
);
1051 current_entry
= current_entry
->Flink
;
1052 DPRINT("ExFreePool(%p)\n", current
);
1053 ExFreePool(current
);
1056 MmFreeMemoryArea(&Process
->AddressSpace
,
1057 MemoryArea
->BaseAddress
,
1059 MmFreeVirtualMemoryPage
,
1064 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
1065 IN PVOID
* PBaseAddress
,
1066 IN PULONG PRegionSize
,
1069 * FUNCTION: Frees a range of virtual memory
1071 * ProcessHandle = Points to the process that allocated the virtual
1073 * BaseAddress = Points to the memory address, rounded down to a
1074 * multiple of the pagesize
1075 * RegionSize = Limits the range to free, rounded up to a multiple of
1077 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
1081 MEMORY_AREA
* MemoryArea
;
1084 PMADDRESS_SPACE AddressSpace
;
1088 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
1089 "*PRegionSize %x, FreeType %x)\n",ProcessHandle
,*PBaseAddress
,
1090 *PRegionSize
,FreeType
);
1092 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((*PBaseAddress
));
1093 RegionSize
= PAGE_ROUND_UP((*PBaseAddress
) + (*PRegionSize
)) -
1094 PAGE_ROUND_DOWN((*PBaseAddress
));
1096 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1097 PROCESS_VM_OPERATION
,
1102 if (!NT_SUCCESS(Status
))
1107 AddressSpace
= &Process
->AddressSpace
;
1109 MmLockAddressSpace(AddressSpace
);
1110 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1112 if (MemoryArea
== NULL
)
1114 MmUnlockAddressSpace(AddressSpace
);
1115 ObDereferenceObject(Process
);
1116 return(STATUS_UNSUCCESSFUL
);
1122 if (MemoryArea
->BaseAddress
!= BaseAddress
)
1124 MmUnlockAddressSpace(AddressSpace
);
1125 ObDereferenceObject(Process
);
1126 return(STATUS_UNSUCCESSFUL
);
1129 if ((MemoryArea
->Type
== MEMORY_AREA_COMMIT
) &&
1130 ((MemoryArea
->Attributes
& PAGE_READWRITE
) ||
1131 (MemoryArea
->Attributes
& PAGE_EXECUTE_READWRITE
)))
1133 MmDereserveSwapPages(PAGE_ROUND_UP(MemoryArea
->Length
));
1137 MmFreeVirtualMemory(Process
, MemoryArea
);
1138 MmUnlockAddressSpace(AddressSpace
);
1139 ObDereferenceObject(Process
);
1140 return(STATUS_SUCCESS
);
1143 Status
= MmComplexVirtualMemoryOperation(AddressSpace
,
1149 MmUnlockAddressSpace(AddressSpace
);
1150 ObDereferenceObject(Process
);
1153 MmUnlockAddressSpace(AddressSpace
);
1154 ObDereferenceObject(Process
);
1155 return(STATUS_NOT_IMPLEMENTED
);
1160 NtLockVirtualMemory(HANDLE ProcessHandle
,
1162 ULONG NumberOfBytesToLock
,
1163 PULONG NumberOfBytesLocked
)
1170 MmChangeAreaProtection(PEPROCESS Process
,
1177 for (i
=0; i
<(Length
/PAGESIZE
); i
++)
1179 if (MmIsPagePresent(Process
, BaseAddress
+ (i
*PAGESIZE
)))
1181 MmSetPageProtect(Process
,
1182 BaseAddress
+ (i
*PAGESIZE
),
1190 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1191 IN PVOID BaseAddress
,
1192 IN ULONG NumberOfBytesToProtect
,
1193 IN ULONG NewAccessProtection
,
1194 OUT PULONG OldAccessProtection
)
1196 PMEMORY_AREA MemoryArea
;
1199 PMADDRESS_SPACE AddressSpace
;
1201 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1202 PROCESS_VM_OPERATION
,
1207 if (Status
!= STATUS_SUCCESS
)
1209 DPRINT("NtProtectVirtualMemory() = %x\n",Status
);
1213 AddressSpace
= &Process
->AddressSpace
;
1215 MmLockAddressSpace(AddressSpace
);
1216 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1218 if (MemoryArea
== NULL
)
1220 DPRINT("NtProtectVirtualMemory() = %x\n",STATUS_UNSUCCESSFUL
);
1221 MmUnlockAddressSpace(AddressSpace
);
1222 ObDereferenceObject(Process
);
1223 return(STATUS_UNSUCCESSFUL
);
1226 *OldAccessProtection
= MemoryArea
->Attributes
;
1228 if (MemoryArea
->BaseAddress
== BaseAddress
&&
1229 MemoryArea
->Length
== NumberOfBytesToProtect
)
1231 MemoryArea
->Attributes
= NewAccessProtection
;
1235 MemoryArea
= MmSplitMemoryArea(Process
,
1236 &Process
->AddressSpace
,
1239 NumberOfBytesToProtect
,
1241 NewAccessProtection
);
1243 MmChangeAreaProtection(Process
,
1245 NumberOfBytesToProtect
,
1246 NewAccessProtection
);
1248 MmUnlockAddressSpace(AddressSpace
);
1249 ObDereferenceObject(Process
);
1250 return(STATUS_SUCCESS
);
1254 NTSTATUS STDCALL
NtQueryVirtualMemory (IN HANDLE ProcessHandle
,
1256 IN CINT VirtualMemoryInformationClass
,
1257 OUT PVOID VirtualMemoryInformation
,
1259 OUT PULONG ResultLength
)
1263 MEMORY_AREA
* MemoryArea
;
1265 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
1266 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
1267 "Length %lu ResultLength %x)\n",ProcessHandle
,Address
,
1268 VirtualMemoryInformationClass
,VirtualMemoryInformation
,
1269 Length
,ResultLength
);
1271 switch(VirtualMemoryInformationClass
)
1273 case MemoryBasicInformation
:
1275 PMEMORY_BASIC_INFORMATION Info
=
1276 (PMEMORY_BASIC_INFORMATION
)VirtualMemoryInformation
;
1277 PMADDRESS_SPACE AddressSpace
;
1279 if (Length
< sizeof(MEMORY_BASIC_INFORMATION
))
1281 ObDereferenceObject(Process
);
1282 return STATUS_INFO_LENGTH_MISMATCH
;
1287 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1290 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1291 PROCESS_QUERY_INFORMATION
,
1297 if (!NT_SUCCESS(Status
))
1299 DPRINT("NtQueryVirtualMemory() = %x\n",Status
);
1303 AddressSpace
= &Process
->AddressSpace
;
1304 MmLockAddressSpace(AddressSpace
);
1305 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1308 if (MemoryArea
== NULL
)
1310 Info
->State
= MEM_FREE
;
1311 DPRINT("Virtual memory at %p is free.\n", Address
);
1312 MmUnlockAddressSpace(AddressSpace
);
1313 ObDereferenceObject(Process
);
1314 return (STATUS_SUCCESS
);
1318 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
1320 Info
->State
= MEM_COMMIT
;
1324 Info
->State
= MEM_RESERVE
;
1328 Info
->BaseAddress
= MemoryArea
->BaseAddress
;
1329 Info
->RegionSize
= MemoryArea
->Length
;
1331 DPRINT("BaseAddress %p, RegionSize %x State %x\n",
1332 Info
->BaseAddress
, Info
->RegionSize
, Info
->State
);
1334 MmUnlockAddressSpace(AddressSpace
);
1335 ObDereferenceObject(Process
);
1336 return STATUS_SUCCESS
;
1341 return STATUS_INVALID_INFO_CLASS
;
1346 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1347 IN PVOID BaseAddress
,
1349 IN ULONG NumberOfBytesToRead
,
1350 OUT PULONG NumberOfBytesRead
)
1354 PVOID SystemAddress
;
1357 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1358 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle
,BaseAddress
,
1359 Buffer
,NumberOfBytesToRead
);
1361 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1367 if (Status
!= STATUS_SUCCESS
)
1372 Mdl
= MmCreateMdl(NULL
,
1374 NumberOfBytesToRead
);
1375 MmProbeAndLockPages(Mdl
,
1379 KeAttachProcess(Process
);
1381 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
1382 memcpy(SystemAddress
, BaseAddress
, NumberOfBytesToRead
);
1386 if (Mdl
->MappedSystemVa
!= NULL
)
1388 MmUnmapLockedPages(Mdl
->MappedSystemVa
, Mdl
);
1393 ObDereferenceObject(Process
);
1395 *NumberOfBytesRead
= NumberOfBytesToRead
;
1396 return(STATUS_SUCCESS
);
1401 NtUnlockVirtualMemory(HANDLE ProcessHandle
,
1403 ULONG NumberOfBytesToUnlock
,
1404 PULONG NumberOfBytesUnlocked OPTIONAL
)
1411 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1412 IN PVOID BaseAddress
,
1414 IN ULONG NumberOfBytesToWrite
,
1415 OUT PULONG NumberOfBytesWritten
)
1419 PVOID SystemAddress
;
1422 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1423 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle
,BaseAddress
,
1424 Buffer
,NumberOfBytesToWrite
);
1426 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1432 if (Status
!= STATUS_SUCCESS
)
1437 Mdl
= MmCreateMdl(NULL
,
1439 NumberOfBytesToWrite
);
1440 MmProbeAndLockPages(Mdl
,
1444 KeAttachProcess(Process
);
1446 DPRINT("Attached to process copying memory\n");
1448 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
1449 memcpy(BaseAddress
, SystemAddress
, NumberOfBytesToWrite
);
1451 DPRINT("Done copy\n");
1455 ObDereferenceObject(Process
);
1457 if (Mdl
->MappedSystemVa
!= NULL
)
1459 MmUnmapLockedPages(Mdl
->MappedSystemVa
, Mdl
);
1464 *NumberOfBytesWritten
= NumberOfBytesToWrite
;
1466 DPRINT("Finished NtWriteVirtualMemory()\n");
1468 return(STATUS_SUCCESS
);
1474 MmSecureVirtualMemory (
1487 MmUnsecureVirtualMemory (