1 /* $Id: virtual.c,v 1.37 2001/01/21 14:54:29 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 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/mmhal.h>
19 #include <internal/ob.h>
20 #include <internal/io.h>
21 #include <internal/ps.h>
24 #include <internal/debug.h>
26 /* TYPES *********************************************************************/
28 typedef struct _MM_SEGMENT
33 LIST_ENTRY SegmentListEntry
;
34 } MM_SEGMENT
, *PMM_SEGMENT
;
36 /* FUNCTIONS *****************************************************************/
39 MmGetSegmentForAddress(PMEMORY_AREA MArea
,
41 PVOID
* PCurrentAddress
)
43 * FUNCTION: Get the segment corresponding to a particular memory area and
46 * MArea (IN) = The memory area
47 * Address (IN) = The address to get the segment for
48 * PCurrentAddress (OUT) = The start of the segment
50 * The corresponding segment or NULL if an error occurred
54 PMM_SEGMENT CurrentSegment
;
57 if (Address
< MArea
->BaseAddress
||
58 Address
>= (MArea
->BaseAddress
+ MArea
->Length
))
61 *PCurrentAddress
= NULL
;
65 Current
= MArea
->Data
.VirtualMemoryData
.SegmentListHead
.Flink
;
66 CurrentAddress
= MArea
->BaseAddress
;
67 while (Current
!= &MArea
->Data
.VirtualMemoryData
.SegmentListHead
)
69 CurrentSegment
= CONTAINING_RECORD(Current
,
72 if (Address
>= CurrentAddress
&&
73 Address
< (CurrentAddress
+ CurrentSegment
->Length
))
75 *PCurrentAddress
= CurrentAddress
;
76 return(CurrentSegment
);
78 CurrentAddress
= CurrentAddress
+ CurrentSegment
->Length
;
79 Current
= Current
->Flink
;
86 MmWritePageVirtualMemory(PMADDRESS_SPACE AddressSpace
,
92 PHYSICAL_ADDRESS PhysicalAddress
;
97 * FIXME: What should we do if an i/o operation is pending on
102 * If the memory area is readonly then there is nothing to do
104 if (MArea
->Attributes
& PAGE_READONLY
||
105 MArea
->Attributes
& PAGE_EXECUTE_READ
)
107 return(STATUS_SUCCESS
);
110 * Set the page to readonly. This ensures the current contents aren't
111 * modified while we are writing it to swap.
113 MmSetPageProtect(AddressSpace
->Process
,
117 * If the page isn't dirty then there is nothing to do.
119 if (!MmIsPageDirty(AddressSpace
->Process
, Address
))
121 MmSetPageProtect(AddressSpace
->Process
,
124 return(STATUS_SUCCESS
);
126 PhysicalAddress
= MmGetPhysicalAddress(Address
);
128 * If we haven't already allocated a swap entry for this page
131 if ((se
= MmGetSavedSwapEntryPage((PVOID
)PhysicalAddress
.u
.LowPart
)) != 0)
133 se
= MmAllocSwapPage();
136 MmSetPageProtect(AddressSpace
->Process
,
139 return(STATUS_UNSUCCESSFUL
);
141 MmSetSavedSwapEntryPage((PVOID
)PhysicalAddress
.u
.LowPart
, se
);
144 * Set the flags so other threads will know what we are doing
146 Flags
= MmGetFlagsPage((PVOID
)PhysicalAddress
.u
.LowPart
);
147 Flags
= Flags
| MM_PHYSICAL_PAGE_MPW_PENDING
;
148 MmSetFlagsPage((PVOID
)PhysicalAddress
.u
.LowPart
, Flags
);
150 * Build an mdl to hold the page for writeout
152 Mdl
= MmCreateMdl(NULL
, NULL
, PAGESIZE
);
153 MmBuildMdlFromPages(Mdl
, (PULONG
)&PhysicalAddress
.u
.LowPart
);
155 * Unlock the address space and write out the page to swap.
157 MmUnlockAddressSpace(AddressSpace
);
158 Status
= MmWriteToSwapPage(se
, Mdl
);
162 MmLockAddressSpace(AddressSpace
);
163 Flags
= MmGetFlagsPage((PVOID
)PhysicalAddress
.u
.LowPart
);
164 Flags
= Flags
& (~MM_PHYSICAL_PAGE_MPW_PENDING
);
165 MmSetFlagsPage((PVOID
)PhysicalAddress
.u
.LowPart
,Flags
);
167 * If we successfully wrote the page then reset the dirty bit
169 if (NT_SUCCESS(Status
))
171 MmSetCleanPage(AddressSpace
->Process
, Address
);
177 ULONG
MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace
,
178 PMEMORY_AREA MemoryArea
,
182 PHYSICAL_ADDRESS PhysicalAddress
;
184 if ((MemoryArea
->Attributes
& PAGE_READONLY
) ||
185 (MemoryArea
->Attributes
& PAGE_EXECUTE_READ
) ||
186 !MmIsPageDirty(PsGetCurrentProcess(), Address
))
188 PhysicalAddress
= MmGetPhysicalAddress(Address
);
190 MmRemovePageFromWorkingSet(AddressSpace
->Process
,
192 MmDeleteVirtualMapping(PsGetCurrentProcess(), Address
, FALSE
);
193 MmDereferencePage((PVOID
)PhysicalAddress
.u
.LowPart
);
202 MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace
,
203 MEMORY_AREA
* MemoryArea
,
206 * FUNCTION: Move data into memory to satisfy a page not present fault
208 * AddressSpace = Address space within which the fault occurred
209 * MemoryArea = The memory area within which the fault occurred
210 * Address = The absolute address of fault
212 * NOTES: This function is called with the address space lock held.
218 PVOID CurrentAddress
;
221 * Get the segment corresponding to the virtual address
223 Segment
= MmGetSegmentForAddress(MemoryArea
, Address
, &CurrentAddress
);
226 return(STATUS_UNSUCCESSFUL
);
228 if (Segment
->Type
== MEM_RESERVE
)
230 return(STATUS_UNSUCCESSFUL
);
233 if (MmIsPagePresent(NULL
, Address
))
235 return(STATUS_SUCCESS
);
239 * Try to allocate a page
241 Page
= MmAllocPage(0);
244 MmUnlockAddressSpace(AddressSpace
);
245 MmWaitForFreePages();
246 MmLockAddressSpace(AddressSpace
);
247 if (MmIsPagePresent(NULL
, Address
))
249 return(STATUS_SUCCESS
);
251 Page
= MmAllocPage(0);
255 * Add the page to the process's working set
257 MmAddPageToWorkingSet(PsGetCurrentProcess(), Address
);
260 * Set the page. If we fail because we are out of memory then
263 Status
= MmCreateVirtualMapping(PsGetCurrentProcess(),
265 MemoryArea
->Attributes
,
267 while (Status
== STATUS_NO_MEMORY
)
269 MmUnlockAddressSpace(AddressSpace
);
270 MmWaitForFreePages();
271 MmLockAddressSpace(AddressSpace
);
272 if (MmIsPagePresent(NULL
, Address
))
274 MmDereferencePage(Page
);
275 return(STATUS_SUCCESS
);
277 Status
= MmCreateVirtualMapping(PsGetCurrentProcess(),
279 MemoryArea
->Attributes
,
282 if (!NT_SUCCESS(Status
))
287 return(STATUS_SUCCESS
);
291 MmModifyAttributes(PMADDRESS_SPACE AddressSpace
,
299 * FUNCTION: Modify the attributes of a memory region
303 * If we are switching a previously committed region to reserved then
304 * free any allocated pages within the region
306 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
310 for (i
=0; i
<= (RegionSize
/PAGESIZE
); i
++)
312 LARGE_INTEGER PhysicalAddr
;
314 PhysicalAddr
= MmGetPhysicalAddress(BaseAddress
+ (i
*PAGESIZE
));
315 if (PhysicalAddr
.u
.LowPart
!= 0)
317 MmRemovePageFromWorkingSet(AddressSpace
->Process
,
318 BaseAddress
+ (i
*PAGESIZE
));
319 MmDereferencePage((PVOID
)(ULONG
)(PhysicalAddr
.u
.LowPart
));
321 MmDeleteVirtualMapping(AddressSpace
->Process
,
322 BaseAddress
+ (i
*PAGESIZE
),
328 * If we are changing the protection attributes of a committed region then
329 * alter the attributes for any allocated pages within the region
331 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
332 OldProtect
!= NewProtect
)
336 for (i
=0; i
<= (RegionSize
/PAGESIZE
); i
++)
338 if (MmIsPagePresent(AddressSpace
->Process
,
339 BaseAddress
+ (i
*PAGESIZE
)))
341 MmSetPageProtect(AddressSpace
->Process
,
342 BaseAddress
+ (i
*PAGESIZE
),
350 InsertAfterEntry(PLIST_ENTRY Previous
,
353 * FUNCTION: Insert a list entry after another entry in the list
356 Previous
->Flink
->Blink
= Entry
;
358 Entry
->Flink
= Previous
->Flink
;
359 Entry
->Blink
= Previous
;
361 Previous
->Flink
= Entry
;
366 MmDumpSegmentsMemoryArea(PMEMORY_AREA MemoryArea
)
368 PVOID CurrentAddress
;
369 PLIST_ENTRY CurrentEntry
;
370 PMM_SEGMENT CurrentSegment
;
371 PLIST_ENTRY ListHead
;
373 CurrentEntry
= MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
.Flink
;
374 ListHead
= &MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
;
376 CurrentAddress
= MemoryArea
->BaseAddress
;
377 while (CurrentEntry
!= ListHead
)
379 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
383 DbgPrint("0x%x 0x%x %d %d\n",
385 CurrentSegment
->Length
,
386 CurrentSegment
->Type
,
387 CurrentSegment
->Protect
);
389 CurrentAddress
= CurrentAddress
+ CurrentSegment
->Length
;
390 CurrentEntry
= CurrentEntry
->Flink
;
396 MmSplitSegment(PMADDRESS_SPACE AddressSpace
,
397 PMEMORY_AREA MemoryArea
,
402 PMM_SEGMENT FirstSegment
,
405 * FUNCTION: Split a memory segment internally
408 PMM_SEGMENT NewTopSegment
;
409 PMM_SEGMENT RegionSegment
;
415 * Save the type and protection and length of the current segment
417 OldType
= FirstSegment
->Type
;
418 OldProtect
= FirstSegment
->Protect
;
419 OldLength
= FirstSegment
->Length
;
422 * If the segment is already of the right type and protection then
423 * there is nothing to do.
425 if (FirstSegment
->Type
== Type
&& FirstSegment
->Protect
== Protect
)
427 return(STATUS_SUCCESS
);
431 * Allocate the segment we might need here because if the allocation
432 * fails below it will be difficult to undo what we've done already.
434 NewTopSegment
= ExAllocatePool(NonPagedPool
, sizeof(MM_SEGMENT
));
435 if (NewTopSegment
== NULL
)
437 return(STATUS_NO_MEMORY
);
440 if (FirstAddress
< RegionAddress
)
443 * If the region to be affected starts at a higher address than
444 * the current segment then create a new segment for the
447 RegionSegment
= ExAllocatePool(NonPagedPool
, sizeof(MM_SEGMENT
));
448 if (RegionSegment
== NULL
)
450 ExFreePool(NewTopSegment
);
451 return(STATUS_NO_MEMORY
);
454 RegionSegment
->Type
= Type
;
455 RegionSegment
->Protect
= Protect
;
456 RegionSegment
->Length
= RegionLength
;
458 FirstSegment
->Length
= RegionAddress
- FirstAddress
;
460 InsertAfterEntry(&FirstSegment
->SegmentListEntry
,
461 &RegionSegment
->SegmentListEntry
);
466 * Otherwise just set its type and protection and length
469 FirstSegment
->Type
= Type
;
470 FirstSegment
->Protect
= Protect
;
471 FirstSegment
->Length
= RegionLength
;
473 RegionSegment
= FirstSegment
;
476 if ((FirstAddress
+ OldLength
) > (RegionAddress
+ RegionLength
))
479 * If the top of the current segment extends after the affected
480 * region then create a segment for the unaffected portion
483 NewTopSegment
->Type
= OldType
;
484 NewTopSegment
->Protect
= OldProtect
;
485 NewTopSegment
->Length
= (FirstAddress
+ OldLength
) -
486 (RegionAddress
+ RegionLength
);
488 InsertAfterEntry(&RegionSegment
->SegmentListEntry
,
489 &NewTopSegment
->SegmentListEntry
);
493 ExFreePool(NewTopSegment
);
494 NewTopSegment
= NULL
;
498 * Actually set the type and protection of the affected region
500 MmModifyAttributes(AddressSpace
,
507 return(STATUS_SUCCESS
);
510 NTSTATUS
MmGatherSegment(PMADDRESS_SPACE AddressSpace
,
511 PMEMORY_AREA MemoryArea
,
516 PMM_SEGMENT FirstSegment
,
519 * FUNCTION: Do a virtual memory operation that will effect several
522 * AddressSpace (IN) = Address space to affect
523 * MemoryArea (IN) = Memory area to affect
524 * BaseAddress (IN) = Base address of the region to affect
525 * RegionSize (IN) = Size of the region to affect
526 * Type (IN) = New type of the region
527 * Protect (IN) = New protection of the region
528 * CurrentSegment (IN) = First segment intersecting with the region
529 * CurrentAddress (IN) = Start address of the first segment
530 * interesting with the region
534 PMM_SEGMENT RegionSegment
;
535 PVOID CurrentAddress
;
536 ULONG RemainingLength
;
537 PLIST_ENTRY CurrentEntry
;
538 PLIST_ENTRY ListHead
;
539 PMM_SEGMENT CurrentSegment
;
541 if (FirstAddress
< RegionAddress
)
544 * If a portion of the first segment is not covered by the region then
545 * we need to split it into two segments
548 RegionSegment
= ExAllocatePool(NonPagedPool
, sizeof(MM_SEGMENT
));
549 if (RegionSegment
== NULL
)
551 return(STATUS_NO_MEMORY
);
554 RegionSegment
->Type
= Type
;
555 RegionSegment
->Protect
= Protect
;
556 RegionSegment
->Length
= (FirstAddress
+ FirstSegment
->Length
) -
559 FirstSegment
->Length
= RegionAddress
- FirstAddress
;
561 InsertAfterEntry(&FirstSegment
->SegmentListEntry
,
562 &RegionSegment
->SegmentListEntry
);
564 MmModifyAttributes(AddressSpace
,
566 RegionSegment
->Length
,
568 FirstSegment
->Protect
,
572 CurrentAddress
= FirstAddress
+ FirstSegment
->Length
+
573 RegionSegment
->Length
;
578 * Otherwise just change the attributes of the segment
584 OldType
= FirstSegment
->Type
;
585 OldProtect
= FirstSegment
->Protect
;
587 FirstSegment
->Type
= Type
;
588 FirstSegment
->Protect
= Protect
;
590 RegionSegment
= FirstSegment
;
592 MmModifyAttributes(AddressSpace
,
594 FirstSegment
->Length
,
600 CurrentAddress
= FirstAddress
+ RegionSegment
->Length
;
604 * Change the attributes of all the complete segments lying inside the
607 RemainingLength
= RegionLength
- RegionSegment
->Length
;
608 CurrentEntry
= RegionSegment
->SegmentListEntry
.Flink
;
609 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
612 ListHead
= &MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
;
614 while (CurrentEntry
!= ListHead
&& RemainingLength
> 0)
621 * If this segment will not be completely covered by the
622 * affected region then break
624 if (CurrentSegment
->Length
> RemainingLength
)
629 OldType
= CurrentSegment
->Type
;
630 OldProtect
= CurrentSegment
->Protect
;
631 OldLength
= CurrentSegment
->Length
;
634 * Extend the length of the previous segment to cover this one
636 RegionSegment
->Length
= RegionSegment
->Length
+ OldLength
;
637 RemainingLength
= RemainingLength
- OldLength
;
638 CurrentAddress
= CurrentAddress
+ OldLength
;
639 CurrentEntry
= CurrentEntry
->Flink
;
642 * Remove the current segment from the list
644 RemoveEntryList(&CurrentSegment
->SegmentListEntry
);
645 ExFreePool(CurrentSegment
);
647 MmModifyAttributes(AddressSpace
,
655 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
661 * If we've run off the top of the memory area then bug check
663 if (CurrentEntry
== ListHead
&& RemainingLength
> 0)
669 * We've only affected a portion of a segment then split it in two
671 if (RemainingLength
> 0)
673 CurrentSegment
->Length
= CurrentSegment
->Length
- RemainingLength
;
675 RegionSegment
->Length
= RegionSegment
->Length
+ RemainingLength
;
677 MmModifyAttributes(AddressSpace
,
680 CurrentSegment
->Type
,
681 CurrentSegment
->Protect
,
686 return(STATUS_SUCCESS
);
689 NTSTATUS
MmComplexVirtualMemoryOperation(PMADDRESS_SPACE AddressSpace
,
690 PMEMORY_AREA MemoryArea
,
696 PMM_SEGMENT CurrentSegment
;
697 PVOID CurrentAddress
;
699 CurrentSegment
= MmGetSegmentForAddress(MemoryArea
,
702 if (CurrentSegment
== NULL
)
707 if (BaseAddress
>= CurrentAddress
&&
708 (BaseAddress
+ RegionSize
) <= (CurrentAddress
+ CurrentSegment
->Length
))
710 return((MmSplitSegment(AddressSpace
,
721 return((MmGatherSegment(AddressSpace
,
734 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
735 IN OUT PVOID
* UBaseAddress
,
737 IN OUT PULONG URegionSize
,
738 IN ULONG AllocationType
,
741 * FUNCTION: Allocates a block of virtual memory in the process address space
743 * ProcessHandle = The handle of the process which owns the virtual memory
744 * BaseAddress = A pointer to the virtual memory allocated. If you
745 * supply a non zero value the system will try to
746 * allocate the memory at the address supplied. It round
747 * it down to a multiple of the page size.
748 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
749 * that must be zero, ensuring that the memory will be
750 * allocated at a address below a certain value.
751 * RegionSize = The number of bytes to allocate
752 * AllocationType = Indicates the type of virtual memory you like to
753 * allocated, can be one of the values : MEM_COMMIT,
754 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN
755 * Protect = Indicates the protection type of the pages allocated, can be
756 * a combination of PAGE_READONLY, PAGE_READWRITE,
757 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
760 * This function maps to the win32 VirtualAllocEx. Virtual memory is
761 * process based so the protocol starts with a ProcessHandle. I
762 * splitted the functionality of obtaining the actual address and
763 * specifying the start address in two parameters ( BaseAddress and
764 * StartAddress ) The NumberOfBytesAllocated specify the range and the
765 * AllocationType and ProctectionType map to the other two parameters.
770 MEMORY_AREA
* MemoryArea
;
773 PMADDRESS_SPACE AddressSpace
;
780 DPRINT("NtAllocateVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
781 "ZeroBits %d, *RegionSize %x, AllocationType %x, Protect %x)\n",
782 ProcessHandle
,*BaseAddress
,ZeroBits
,*RegionSize
,AllocationType
,
786 * Check the validity of the parameters
788 if ((Protect
& PAGE_FLAGS_VALID_FROM_USER_MODE
) != Protect
)
790 return(STATUS_INVALID_PAGE_PROTECTION
);
792 PBaseAddress
= *UBaseAddress
;
793 PRegionSize
= *URegionSize
;
796 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
797 RegionSize
= PAGE_ROUND_UP(PBaseAddress
+ PRegionSize
) -
798 PAGE_ROUND_DOWN(PBaseAddress
);
800 Status
= ObReferenceObjectByHandle(ProcessHandle
,
801 PROCESS_VM_OPERATION
,
806 if (!NT_SUCCESS(Status
))
808 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
812 if (AllocationType
& MEM_RESERVE
)
821 AddressSpace
= &Process
->AddressSpace
;
822 MmLockAddressSpace(AddressSpace
);
824 if (BaseAddress
!= 0)
826 MemoryArea
= MmOpenMemoryAreaByAddress(&Process
->AddressSpace
,
829 if (MemoryArea
!= NULL
&&
830 MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
831 MemoryArea
->Length
>= RegionSize
)
833 Status
= MmComplexVirtualMemoryOperation(AddressSpace
,
839 /* FIXME: Reserve/dereserve swap pages */
840 MmUnlockAddressSpace(AddressSpace
);
841 ObDereferenceObject(Process
);
844 else if (MemoryArea
!= NULL
)
846 MmUnlockAddressSpace(AddressSpace
);
847 ObDereferenceObject(Process
);
848 return(STATUS_UNSUCCESSFUL
);
852 Segment
= ExAllocatePool(NonPagedPool
, sizeof(MM_SEGMENT
));
855 MmUnlockAddressSpace(AddressSpace
);
856 ObDereferenceObject(Process
);
857 return(STATUS_UNSUCCESSFUL
);
860 Status
= MmCreateMemoryArea(Process
,
861 &Process
->AddressSpace
,
862 MEMORY_AREA_VIRTUAL_MEMORY
,
868 if (!NT_SUCCESS(Status
))
870 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
871 MmUnlockAddressSpace(AddressSpace
);
872 ObDereferenceObject(Process
);
876 InitializeListHead(&MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
);
878 Segment
->Type
= Type
;
879 Segment
->Protect
= Protect
;
880 Segment
->Length
= RegionSize
;
881 InsertTailList(&MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
,
882 &Segment
->SegmentListEntry
);
884 DPRINT("*BaseAddress %x\n",*BaseAddress
);
885 if ((AllocationType
& MEM_COMMIT
) &&
886 ((Protect
& PAGE_READWRITE
) ||
887 (Protect
& PAGE_EXECUTE_READWRITE
)))
889 MmReserveSwapPages(RegionSize
);
892 *UBaseAddress
= BaseAddress
;
893 *URegionSize
= RegionSize
;
895 MmUnlockAddressSpace(AddressSpace
);
896 ObDereferenceObject(Process
);
897 return(STATUS_SUCCESS
);
902 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
903 IN PVOID BaseAddress
,
904 IN ULONG NumberOfBytesToFlush
,
905 OUT PULONG NumberOfBytesFlushed OPTIONAL
)
907 * FUNCTION: Flushes virtual memory to file
909 * ProcessHandle = Points to the process that allocated the virtual
911 * BaseAddress = Points to the memory address
912 * NumberOfBytesToFlush = Limits the range to flush,
913 * NumberOfBytesFlushed = Actual number of bytes flushed
922 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
923 IN PVOID
* PBaseAddress
,
924 IN PULONG PRegionSize
,
927 * FUNCTION: Frees a range of virtual memory
929 * ProcessHandle = Points to the process that allocated the virtual
931 * BaseAddress = Points to the memory address, rounded down to a
932 * multiple of the pagesize
933 * RegionSize = Limits the range to free, rounded up to a multiple of
935 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
939 MEMORY_AREA
* MemoryArea
;
942 PMADDRESS_SPACE AddressSpace
;
947 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
948 "*RegionSize %x, FreeType %x)\n",ProcessHandle
,*BaseAddress
,
949 *RegionSize
,FreeType
);
951 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((*PBaseAddress
));
952 RegionSize
= PAGE_ROUND_UP((*PBaseAddress
) + (*PRegionSize
)) -
953 PAGE_ROUND_DOWN((*PBaseAddress
));
955 Status
= ObReferenceObjectByHandle(ProcessHandle
,
956 PROCESS_VM_OPERATION
,
961 if (!NT_SUCCESS(Status
))
966 AddressSpace
= &Process
->AddressSpace
;
968 MmLockAddressSpace(AddressSpace
);
969 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
971 if (MemoryArea
== NULL
)
973 MmUnlockAddressSpace(AddressSpace
);
974 ObDereferenceObject(Process
);
975 return(STATUS_UNSUCCESSFUL
);
981 if (MemoryArea
->BaseAddress
!= BaseAddress
)
983 MmUnlockAddressSpace(AddressSpace
);
984 ObDereferenceObject(Process
);
985 return(STATUS_UNSUCCESSFUL
);
988 if ((MemoryArea
->Type
== MEMORY_AREA_COMMIT
) &&
989 ((MemoryArea
->Attributes
& PAGE_READWRITE
) ||
990 (MemoryArea
->Attributes
& PAGE_EXECUTE_READWRITE
)))
992 MmDereserveSwapPages(PAGE_ROUND_UP(MemoryArea
->Length
));
996 for (i
=0; i
<=(MemoryArea
->Length
/PAGESIZE
); i
++)
1001 MmGetPhysicalAddressForProcess(Process
,
1002 MemoryArea
->BaseAddress
+
1004 if (PhysicalAddr
!= 0)
1006 MmRemovePageFromWorkingSet(AddressSpace
->Process
,
1007 MemoryArea
->BaseAddress
+
1009 MmDereferencePage((PVOID
)(ULONG
)(PhysicalAddr
));
1013 MmFreeMemoryArea(&Process
->AddressSpace
,
1017 MmUnlockAddressSpace(AddressSpace
);
1018 ObDereferenceObject(Process
);
1019 return(STATUS_SUCCESS
);
1022 Status
= MmComplexVirtualMemoryOperation(AddressSpace
,
1028 MmUnlockAddressSpace(AddressSpace
);
1029 ObDereferenceObject(Process
);
1032 MmUnlockAddressSpace(AddressSpace
);
1033 ObDereferenceObject(Process
);
1034 return(STATUS_NOT_IMPLEMENTED
);
1039 NtLockVirtualMemory(HANDLE ProcessHandle
,
1041 ULONG NumberOfBytesToLock
,
1042 PULONG NumberOfBytesLocked
)
1049 MmChangeAreaProtection(PEPROCESS Process
,
1056 for (i
=0; i
<(Length
/PAGESIZE
); i
++)
1058 if (MmIsPagePresent(Process
, BaseAddress
+ (i
*PAGESIZE
)))
1060 MmSetPageProtect(Process
,
1061 BaseAddress
+ (i
*PAGESIZE
),
1068 NTSTATUS STDCALL
NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1069 IN PVOID BaseAddress
,
1070 IN ULONG NumberOfBytesToProtect
,
1071 IN ULONG NewAccessProtection
,
1072 OUT PULONG OldAccessProtection
)
1074 PMEMORY_AREA MemoryArea
;
1077 PMADDRESS_SPACE AddressSpace
;
1079 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1080 PROCESS_VM_OPERATION
,
1085 if (Status
!= STATUS_SUCCESS
)
1087 DPRINT("NtProtectVirtualMemory() = %x\n",Status
);
1091 AddressSpace
= &Process
->AddressSpace
;
1093 MmLockAddressSpace(AddressSpace
);
1094 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1096 if (MemoryArea
== NULL
)
1098 DPRINT("NtProtectVirtualMemory() = %x\n",STATUS_UNSUCCESSFUL
);
1099 MmUnlockAddressSpace(AddressSpace
);
1100 ObDereferenceObject(Process
);
1101 return(STATUS_UNSUCCESSFUL
);
1104 *OldAccessProtection
= MemoryArea
->Attributes
;
1106 if (MemoryArea
->BaseAddress
== BaseAddress
&&
1107 MemoryArea
->Length
== NumberOfBytesToProtect
)
1109 MemoryArea
->Attributes
= NewAccessProtection
;
1113 MemoryArea
= MmSplitMemoryArea(Process
,
1114 &Process
->AddressSpace
,
1117 NumberOfBytesToProtect
,
1119 NewAccessProtection
);
1121 MmChangeAreaProtection(Process
,
1123 NumberOfBytesToProtect
,
1124 NewAccessProtection
);
1125 MmUnlockAddressSpace(AddressSpace
);
1126 ObDereferenceObject(Process
);
1127 return(STATUS_SUCCESS
);
1131 NTSTATUS STDCALL
NtQueryVirtualMemory (IN HANDLE ProcessHandle
,
1133 IN CINT VirtualMemoryInformationClass
,
1134 OUT PVOID VirtualMemoryInformation
,
1136 OUT PULONG ResultLength
)
1140 MEMORY_AREA
* MemoryArea
;
1142 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
1143 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
1144 "Length %lu ResultLength %x)\n",ProcessHandle
,Address
,
1145 VirtualMemoryInformationClass
,VirtualMemoryInformation
,
1146 Length
,ResultLength
);
1148 switch(VirtualMemoryInformationClass
)
1150 case MemoryBasicInformation
:
1152 PMEMORY_BASIC_INFORMATION Info
=
1153 (PMEMORY_BASIC_INFORMATION
)VirtualMemoryInformation
;
1154 PMADDRESS_SPACE AddressSpace
;
1156 if (Length
< sizeof(MEMORY_BASIC_INFORMATION
))
1158 ObDereferenceObject(Process
);
1159 return STATUS_INFO_LENGTH_MISMATCH
;
1164 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1167 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1168 PROCESS_QUERY_INFORMATION
,
1174 if (!NT_SUCCESS(Status
))
1176 DPRINT("NtQueryVirtualMemory() = %x\n",Status
);
1180 AddressSpace
= &Process
->AddressSpace
;
1181 MmLockAddressSpace(AddressSpace
);
1182 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1185 if (MemoryArea
== NULL
)
1187 Info
->State
= MEM_FREE
;
1188 DPRINT("Virtual memory at %p is free.\n", Address
);
1189 MmUnlockAddressSpace(AddressSpace
);
1190 ObDereferenceObject(Process
);
1191 return (STATUS_SUCCESS
);
1195 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
1197 Info
->State
= MEM_COMMIT
;
1201 Info
->State
= MEM_RESERVE
;
1205 Info
->BaseAddress
= MemoryArea
->BaseAddress
;
1206 Info
->RegionSize
= MemoryArea
->Length
;
1208 DPRINT("BaseAddress %p, RegionSize %x State %x\n",
1209 Info
->BaseAddress
, Info
->RegionSize
, Info
->State
);
1211 MmUnlockAddressSpace(AddressSpace
);
1212 ObDereferenceObject(Process
);
1213 return STATUS_SUCCESS
;
1218 return STATUS_INVALID_INFO_CLASS
;
1223 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1224 IN PVOID BaseAddress
,
1226 IN ULONG NumberOfBytesToRead
,
1227 OUT PULONG NumberOfBytesRead
)
1231 PVOID SystemAddress
;
1234 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1235 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle
,BaseAddress
,
1236 Buffer
,NumberOfBytesToRead
);
1238 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1244 if (Status
!= STATUS_SUCCESS
)
1249 Mdl
= MmCreateMdl(NULL
,
1251 NumberOfBytesToRead
);
1252 MmProbeAndLockPages(Mdl
,
1256 KeAttachProcess(Process
);
1258 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
1259 memcpy(SystemAddress
, BaseAddress
, NumberOfBytesToRead
);
1263 ObDereferenceObject(Process
);
1265 *NumberOfBytesRead
= NumberOfBytesToRead
;
1266 return(STATUS_SUCCESS
);
1271 NtUnlockVirtualMemory(HANDLE ProcessHandle
,
1273 ULONG NumberOfBytesToUnlock
,
1274 PULONG NumberOfBytesUnlocked OPTIONAL
)
1281 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1282 IN PVOID BaseAddress
,
1284 IN ULONG NumberOfBytesToWrite
,
1285 OUT PULONG NumberOfBytesWritten
)
1289 PVOID SystemAddress
;
1292 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1293 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle
,BaseAddress
,
1294 Buffer
,NumberOfBytesToWrite
);
1296 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1302 if (Status
!= STATUS_SUCCESS
)
1307 Mdl
= MmCreateMdl(NULL
,
1309 NumberOfBytesToWrite
);
1310 MmProbeAndLockPages(Mdl
,
1314 KeAttachProcess(Process
);
1316 DPRINT("Attached to process copying memory\n");
1318 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
1319 memcpy(BaseAddress
, SystemAddress
, NumberOfBytesToWrite
);
1321 DPRINT("Done copy\n");
1325 ObDereferenceObject(Process
);
1327 *NumberOfBytesWritten
= NumberOfBytesToWrite
;
1329 DPRINT("Finished NtWriteVirtualMemory()\n");
1331 return(STATUS_SUCCESS
);
1337 MmSecureVirtualMemory (
1350 MmUnsecureVirtualMemory (