1 /* $Id: virtual.c,v 1.41 2001/03/08 22:06:02 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>
22 #include <internal/pool.h>
25 #include <internal/debug.h>
27 /* TYPES *********************************************************************/
29 typedef struct _MM_SEGMENT
34 LIST_ENTRY SegmentListEntry
;
35 } MM_SEGMENT
, *PMM_SEGMENT
;
37 /* GLOBALS *******************************************************************/
39 #define TAG_MM_SEGMENT TAG('M', 'S', 'E', 'G')
41 /* FUNCTIONS *****************************************************************/
44 MmGetSegmentForAddress(PMEMORY_AREA MArea
,
46 PVOID
* PCurrentAddress
)
48 * FUNCTION: Get the segment corresponding to a particular memory area and
51 * MArea (IN) = The memory area
52 * Address (IN) = The address to get the segment for
53 * PCurrentAddress (OUT) = The start of the segment
55 * The corresponding segment or NULL if an error occurred
59 PMM_SEGMENT CurrentSegment
;
62 if (Address
< MArea
->BaseAddress
||
63 Address
>= (MArea
->BaseAddress
+ MArea
->Length
))
66 *PCurrentAddress
= NULL
;
70 Current
= MArea
->Data
.VirtualMemoryData
.SegmentListHead
.Flink
;
71 CurrentAddress
= MArea
->BaseAddress
;
72 while (Current
!= &MArea
->Data
.VirtualMemoryData
.SegmentListHead
)
74 CurrentSegment
= CONTAINING_RECORD(Current
,
77 if (Address
>= CurrentAddress
&&
78 Address
< (CurrentAddress
+ CurrentSegment
->Length
))
80 *PCurrentAddress
= CurrentAddress
;
81 return(CurrentSegment
);
83 CurrentAddress
= CurrentAddress
+ CurrentSegment
->Length
;
84 Current
= Current
->Flink
;
91 MmWritePageVirtualMemory(PMADDRESS_SPACE AddressSpace
,
97 PHYSICAL_ADDRESS PhysicalAddress
;
102 * FIXME: What should we do if an i/o operation is pending on
107 * If the memory area is readonly then there is nothing to do
109 if (MArea
->Attributes
& PAGE_READONLY
||
110 MArea
->Attributes
& PAGE_EXECUTE_READ
)
112 return(STATUS_SUCCESS
);
115 * Set the page to readonly. This ensures the current contents aren't
116 * modified while we are writing it to swap.
118 MmSetPageProtect(AddressSpace
->Process
,
122 * If the page isn't dirty then there is nothing to do.
124 if (!MmIsPageDirty(AddressSpace
->Process
, Address
))
126 MmSetPageProtect(AddressSpace
->Process
,
129 return(STATUS_SUCCESS
);
131 PhysicalAddress
= MmGetPhysicalAddress(Address
);
133 * If we haven't already allocated a swap entry for this page
136 if ((se
= MmGetSavedSwapEntryPage((PVOID
)PhysicalAddress
.u
.LowPart
)) != 0)
138 se
= MmAllocSwapPage();
141 MmSetPageProtect(AddressSpace
->Process
,
144 return(STATUS_UNSUCCESSFUL
);
146 MmSetSavedSwapEntryPage((PVOID
)PhysicalAddress
.u
.LowPart
, se
);
149 * Set the flags so other threads will know what we are doing
151 Flags
= MmGetFlagsPage((PVOID
)PhysicalAddress
.u
.LowPart
);
152 Flags
= Flags
| MM_PHYSICAL_PAGE_MPW_PENDING
;
153 MmSetFlagsPage((PVOID
)PhysicalAddress
.u
.LowPart
, Flags
);
155 * Build an mdl to hold the page for writeout
157 Mdl
= MmCreateMdl(NULL
, NULL
, PAGESIZE
);
158 MmBuildMdlFromPages(Mdl
, (PULONG
)&PhysicalAddress
.u
.LowPart
);
160 * Unlock the address space and write out the page to swap.
162 MmUnlockAddressSpace(AddressSpace
);
163 Status
= MmWriteToSwapPage(se
, Mdl
);
167 MmLockAddressSpace(AddressSpace
);
168 Flags
= MmGetFlagsPage((PVOID
)PhysicalAddress
.u
.LowPart
);
169 Flags
= Flags
& (~MM_PHYSICAL_PAGE_MPW_PENDING
);
170 MmSetFlagsPage((PVOID
)PhysicalAddress
.u
.LowPart
,Flags
);
172 * If we successfully wrote the page then reset the dirty bit
174 if (NT_SUCCESS(Status
))
176 MmSetCleanPage(AddressSpace
->Process
, Address
);
182 ULONG
MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace
,
183 PMEMORY_AREA MemoryArea
,
187 PHYSICAL_ADDRESS PhysicalAddress
;
189 if ((MemoryArea
->Attributes
& PAGE_READONLY
) ||
190 (MemoryArea
->Attributes
& PAGE_EXECUTE_READ
) ||
191 !MmIsPageDirty(PsGetCurrentProcess(), Address
))
193 PhysicalAddress
= MmGetPhysicalAddress(Address
);
195 MmRemovePageFromWorkingSet(AddressSpace
->Process
,
197 MmDeleteVirtualMapping(PsGetCurrentProcess(), Address
, FALSE
);
198 MmDereferencePage((PVOID
)PhysicalAddress
.u
.LowPart
);
207 MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace
,
208 MEMORY_AREA
* MemoryArea
,
212 * FUNCTION: Move data into memory to satisfy a page not present fault
214 * AddressSpace = Address space within which the fault occurred
215 * MemoryArea = The memory area within which the fault occurred
216 * Address = The absolute address of fault
218 * NOTES: This function is called with the address space lock held.
224 PVOID CurrentAddress
;
227 * Get the segment corresponding to the virtual address
229 Segment
= MmGetSegmentForAddress(MemoryArea
, Address
, &CurrentAddress
);
232 return(STATUS_UNSUCCESSFUL
);
234 if (Segment
->Type
== MEM_RESERVE
)
236 return(STATUS_UNSUCCESSFUL
);
239 if (MmIsPagePresent(NULL
, Address
))
243 MmLockPage((PVOID
)MmGetPhysicalAddressForProcess(NULL
, Address
));
245 return(STATUS_SUCCESS
);
249 * Try to allocate a page
251 Page
= MmAllocPage(0);
254 MmUnlockAddressSpace(AddressSpace
);
255 MmWaitForFreePages();
256 MmLockAddressSpace(AddressSpace
);
257 if (MmIsPagePresent(NULL
, Address
))
261 MmLockPage((PVOID
)MmGetPhysicalAddressForProcess(NULL
,
264 return(STATUS_SUCCESS
);
266 Page
= MmAllocPage(0);
270 * Add the page to the process's working set
272 MmAddPageToWorkingSet(PsGetCurrentProcess(),
273 (PVOID
)PAGE_ROUND_DOWN(Address
));
276 * Set the page. If we fail because we are out of memory then
279 Status
= MmCreateVirtualMapping(PsGetCurrentProcess(),
281 MemoryArea
->Attributes
,
283 while (Status
== STATUS_NO_MEMORY
)
285 MmUnlockAddressSpace(AddressSpace
);
286 MmWaitForFreePages();
287 MmLockAddressSpace(AddressSpace
);
288 if (MmIsPagePresent(NULL
, Address
))
292 MmLockPage((PVOID
)MmGetPhysicalAddressForProcess(NULL
,
295 MmDereferencePage(Page
);
296 return(STATUS_SUCCESS
);
298 Status
= MmCreateVirtualMapping(PsGetCurrentProcess(),
300 MemoryArea
->Attributes
,
303 if (!NT_SUCCESS(Status
))
311 MmLockPage((PVOID
)MmGetPhysicalAddressForProcess(NULL
, Address
));
313 return(STATUS_SUCCESS
);
318 MmModifyAttributes(PMADDRESS_SPACE AddressSpace
,
326 * FUNCTION: Modify the attributes of a memory region
330 * If we are switching a previously committed region to reserved then
331 * free any allocated pages within the region
333 if (NewType
== MEM_RESERVE
&& OldType
== MEM_COMMIT
)
337 for (i
=0; i
<= (RegionSize
/PAGESIZE
); i
++)
339 LARGE_INTEGER PhysicalAddr
;
341 PhysicalAddr
= MmGetPhysicalAddress(BaseAddress
+ (i
*PAGESIZE
));
342 MmDeleteVirtualMapping(AddressSpace
->Process
,
343 BaseAddress
+ (i
*PAGESIZE
),
345 if (PhysicalAddr
.u
.LowPart
!= 0)
347 MmRemovePageFromWorkingSet(AddressSpace
->Process
,
348 BaseAddress
+ (i
*PAGESIZE
));
349 MmDereferencePage((PVOID
)(ULONG
)(PhysicalAddr
.u
.LowPart
));
355 * If we are changing the protection attributes of a committed region then
356 * alter the attributes for any allocated pages within the region
358 if (NewType
== MEM_COMMIT
&& OldType
== MEM_COMMIT
&&
359 OldProtect
!= NewProtect
)
363 for (i
=0; i
<= (RegionSize
/PAGESIZE
); i
++)
365 if (MmIsPagePresent(AddressSpace
->Process
,
366 BaseAddress
+ (i
*PAGESIZE
)))
368 MmSetPageProtect(AddressSpace
->Process
,
369 BaseAddress
+ (i
*PAGESIZE
),
377 InsertAfterEntry(PLIST_ENTRY Previous
,
380 * FUNCTION: Insert a list entry after another entry in the list
383 Previous
->Flink
->Blink
= Entry
;
385 Entry
->Flink
= Previous
->Flink
;
386 Entry
->Blink
= Previous
;
388 Previous
->Flink
= Entry
;
393 MmDumpSegmentsMemoryArea(PMEMORY_AREA MemoryArea
)
395 PVOID CurrentAddress
;
396 PLIST_ENTRY CurrentEntry
;
397 PMM_SEGMENT CurrentSegment
;
398 PLIST_ENTRY ListHead
;
400 CurrentEntry
= MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
.Flink
;
401 ListHead
= &MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
;
403 CurrentAddress
= MemoryArea
->BaseAddress
;
404 while (CurrentEntry
!= ListHead
)
406 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
410 DbgPrint("0x%x 0x%x %d %d\n",
412 CurrentSegment
->Length
,
413 CurrentSegment
->Type
,
414 CurrentSegment
->Protect
);
416 CurrentAddress
= CurrentAddress
+ CurrentSegment
->Length
;
417 CurrentEntry
= CurrentEntry
->Flink
;
423 MmSplitSegment(PMADDRESS_SPACE AddressSpace
,
424 PMEMORY_AREA MemoryArea
,
429 PMM_SEGMENT FirstSegment
,
432 * FUNCTION: Split a memory segment internally
435 PMM_SEGMENT NewTopSegment
;
436 PMM_SEGMENT RegionSegment
;
442 * Save the type and protection and length of the current segment
444 OldType
= FirstSegment
->Type
;
445 OldProtect
= FirstSegment
->Protect
;
446 OldLength
= FirstSegment
->Length
;
449 * If the segment is already of the right type and protection then
450 * there is nothing to do.
452 if (FirstSegment
->Type
== Type
&& FirstSegment
->Protect
== Protect
)
454 return(STATUS_SUCCESS
);
458 * Allocate the segment we might need here because if the allocation
459 * fails below it will be difficult to undo what we've done already.
461 NewTopSegment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
463 if (NewTopSegment
== NULL
)
465 return(STATUS_NO_MEMORY
);
468 if (FirstAddress
< RegionAddress
)
471 * If the region to be affected starts at a higher address than
472 * the current segment then create a new segment for the
475 RegionSegment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
477 if (RegionSegment
== NULL
)
479 ExFreePool(NewTopSegment
);
480 return(STATUS_NO_MEMORY
);
483 RegionSegment
->Type
= Type
;
484 RegionSegment
->Protect
= Protect
;
485 RegionSegment
->Length
= RegionLength
;
487 FirstSegment
->Length
= RegionAddress
- FirstAddress
;
489 InsertAfterEntry(&FirstSegment
->SegmentListEntry
,
490 &RegionSegment
->SegmentListEntry
);
495 * Otherwise just set its type and protection and length
498 FirstSegment
->Type
= Type
;
499 FirstSegment
->Protect
= Protect
;
500 FirstSegment
->Length
= RegionLength
;
502 RegionSegment
= FirstSegment
;
505 if ((FirstAddress
+ OldLength
) > (RegionAddress
+ RegionLength
))
508 * If the top of the current segment extends after the affected
509 * region then create a segment for the unaffected portion
512 NewTopSegment
->Type
= OldType
;
513 NewTopSegment
->Protect
= OldProtect
;
514 NewTopSegment
->Length
= (FirstAddress
+ OldLength
) -
515 (RegionAddress
+ RegionLength
);
517 InsertAfterEntry(&RegionSegment
->SegmentListEntry
,
518 &NewTopSegment
->SegmentListEntry
);
522 ExFreePool(NewTopSegment
);
523 NewTopSegment
= NULL
;
527 * Actually set the type and protection of the affected region
529 MmModifyAttributes(AddressSpace
,
536 return(STATUS_SUCCESS
);
539 NTSTATUS
MmGatherSegment(PMADDRESS_SPACE AddressSpace
,
540 PMEMORY_AREA MemoryArea
,
545 PMM_SEGMENT FirstSegment
,
548 * FUNCTION: Do a virtual memory operation that will effect several
551 * AddressSpace (IN) = Address space to affect
552 * MemoryArea (IN) = Memory area to affect
553 * BaseAddress (IN) = Base address of the region to affect
554 * RegionSize (IN) = Size of the region to affect
555 * Type (IN) = New type of the region
556 * Protect (IN) = New protection of the region
557 * CurrentSegment (IN) = First segment intersecting with the region
558 * CurrentAddress (IN) = Start address of the first segment
559 * interesting with the region
563 PMM_SEGMENT RegionSegment
;
564 PVOID CurrentAddress
;
565 ULONG RemainingLength
;
566 PLIST_ENTRY CurrentEntry
;
567 PLIST_ENTRY ListHead
;
568 PMM_SEGMENT CurrentSegment
;
570 if (FirstAddress
< RegionAddress
)
573 * If a portion of the first segment is not covered by the region then
574 * we need to split it into two segments
577 RegionSegment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
579 if (RegionSegment
== NULL
)
581 return(STATUS_NO_MEMORY
);
584 RegionSegment
->Type
= Type
;
585 RegionSegment
->Protect
= Protect
;
586 RegionSegment
->Length
= (FirstAddress
+ FirstSegment
->Length
) -
589 FirstSegment
->Length
= RegionAddress
- FirstAddress
;
591 InsertAfterEntry(&FirstSegment
->SegmentListEntry
,
592 &RegionSegment
->SegmentListEntry
);
594 MmModifyAttributes(AddressSpace
,
596 RegionSegment
->Length
,
598 FirstSegment
->Protect
,
602 CurrentAddress
= FirstAddress
+ FirstSegment
->Length
+
603 RegionSegment
->Length
;
608 * Otherwise just change the attributes of the segment
614 OldType
= FirstSegment
->Type
;
615 OldProtect
= FirstSegment
->Protect
;
617 FirstSegment
->Type
= Type
;
618 FirstSegment
->Protect
= Protect
;
620 RegionSegment
= FirstSegment
;
622 MmModifyAttributes(AddressSpace
,
624 FirstSegment
->Length
,
630 CurrentAddress
= FirstAddress
+ RegionSegment
->Length
;
634 * Change the attributes of all the complete segments lying inside the
637 RemainingLength
= RegionLength
- RegionSegment
->Length
;
638 CurrentEntry
= RegionSegment
->SegmentListEntry
.Flink
;
639 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
642 ListHead
= &MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
;
644 while (CurrentEntry
!= ListHead
&& RemainingLength
> 0)
651 * If this segment will not be completely covered by the
652 * affected region then break
654 if (CurrentSegment
->Length
> RemainingLength
)
659 OldType
= CurrentSegment
->Type
;
660 OldProtect
= CurrentSegment
->Protect
;
661 OldLength
= CurrentSegment
->Length
;
664 * Extend the length of the previous segment to cover this one
666 RegionSegment
->Length
= RegionSegment
->Length
+ OldLength
;
667 RemainingLength
= RemainingLength
- OldLength
;
668 CurrentAddress
= CurrentAddress
+ OldLength
;
669 CurrentEntry
= CurrentEntry
->Flink
;
672 * Remove the current segment from the list
674 RemoveEntryList(&CurrentSegment
->SegmentListEntry
);
675 ExFreePool(CurrentSegment
);
677 MmModifyAttributes(AddressSpace
,
685 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
691 * If we've run off the top of the memory area then bug check
693 if (CurrentEntry
== ListHead
&& RemainingLength
> 0)
699 * We've only affected a portion of a segment then split it in two
701 if (RemainingLength
> 0)
703 CurrentSegment
->Length
= CurrentSegment
->Length
- RemainingLength
;
705 RegionSegment
->Length
= RegionSegment
->Length
+ RemainingLength
;
707 MmModifyAttributes(AddressSpace
,
710 CurrentSegment
->Type
,
711 CurrentSegment
->Protect
,
716 return(STATUS_SUCCESS
);
719 NTSTATUS
MmComplexVirtualMemoryOperation(PMADDRESS_SPACE AddressSpace
,
720 PMEMORY_AREA MemoryArea
,
726 PMM_SEGMENT CurrentSegment
;
727 PVOID CurrentAddress
;
729 CurrentSegment
= MmGetSegmentForAddress(MemoryArea
,
732 if (CurrentSegment
== NULL
)
737 if (BaseAddress
>= CurrentAddress
&&
738 (BaseAddress
+ RegionSize
) <= (CurrentAddress
+ CurrentSegment
->Length
))
740 return((MmSplitSegment(AddressSpace
,
751 return((MmGatherSegment(AddressSpace
,
764 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
765 IN OUT PVOID
* UBaseAddress
,
767 IN OUT PULONG URegionSize
,
768 IN ULONG AllocationType
,
771 * FUNCTION: Allocates a block of virtual memory in the process address space
773 * ProcessHandle = The handle of the process which owns the virtual memory
774 * BaseAddress = A pointer to the virtual memory allocated. If you
775 * supply a non zero value the system will try to
776 * allocate the memory at the address supplied. It round
777 * it down to a multiple of the page size.
778 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
779 * that must be zero, ensuring that the memory will be
780 * allocated at a address below a certain value.
781 * RegionSize = The number of bytes to allocate
782 * AllocationType = Indicates the type of virtual memory you like to
783 * allocated, can be one of the values : MEM_COMMIT,
784 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN
785 * Protect = Indicates the protection type of the pages allocated, can be
786 * a combination of PAGE_READONLY, PAGE_READWRITE,
787 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
790 * This function maps to the win32 VirtualAllocEx. Virtual memory is
791 * process based so the protocol starts with a ProcessHandle. I
792 * splitted the functionality of obtaining the actual address and
793 * specifying the start address in two parameters ( BaseAddress and
794 * StartAddress ) The NumberOfBytesAllocated specify the range and the
795 * AllocationType and ProctectionType map to the other two parameters.
800 MEMORY_AREA
* MemoryArea
;
803 PMADDRESS_SPACE AddressSpace
;
810 DPRINT("NtAllocateVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
811 "ZeroBits %d, *RegionSize %x, AllocationType %x, Protect %x)\n",
812 ProcessHandle
,*BaseAddress
,ZeroBits
,*RegionSize
,AllocationType
,
816 * Check the validity of the parameters
818 if ((Protect
& PAGE_FLAGS_VALID_FROM_USER_MODE
) != Protect
)
820 return(STATUS_INVALID_PAGE_PROTECTION
);
822 PBaseAddress
= *UBaseAddress
;
823 PRegionSize
= *URegionSize
;
826 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
827 RegionSize
= PAGE_ROUND_UP(PBaseAddress
+ PRegionSize
) -
828 PAGE_ROUND_DOWN(PBaseAddress
);
830 Status
= ObReferenceObjectByHandle(ProcessHandle
,
831 PROCESS_VM_OPERATION
,
836 if (!NT_SUCCESS(Status
))
838 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
842 if (AllocationType
& MEM_RESERVE
)
851 AddressSpace
= &Process
->AddressSpace
;
852 MmLockAddressSpace(AddressSpace
);
854 if (BaseAddress
!= 0)
856 MemoryArea
= MmOpenMemoryAreaByAddress(&Process
->AddressSpace
,
859 if (MemoryArea
!= NULL
&&
860 MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
861 MemoryArea
->Length
>= RegionSize
)
863 Status
= MmComplexVirtualMemoryOperation(AddressSpace
,
869 /* FIXME: Reserve/dereserve swap pages */
870 MmUnlockAddressSpace(AddressSpace
);
871 ObDereferenceObject(Process
);
874 else if (MemoryArea
!= NULL
)
876 MmUnlockAddressSpace(AddressSpace
);
877 ObDereferenceObject(Process
);
878 return(STATUS_UNSUCCESSFUL
);
882 Segment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
886 MmUnlockAddressSpace(AddressSpace
);
887 ObDereferenceObject(Process
);
888 return(STATUS_UNSUCCESSFUL
);
891 Status
= MmCreateMemoryArea(Process
,
892 &Process
->AddressSpace
,
893 MEMORY_AREA_VIRTUAL_MEMORY
,
899 if (!NT_SUCCESS(Status
))
901 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
902 MmUnlockAddressSpace(AddressSpace
);
903 ObDereferenceObject(Process
);
907 InitializeListHead(&MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
);
909 Segment
->Type
= Type
;
910 Segment
->Protect
= Protect
;
911 Segment
->Length
= RegionSize
;
912 InsertTailList(&MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
,
913 &Segment
->SegmentListEntry
);
915 DPRINT("*BaseAddress %x\n",*BaseAddress
);
916 if ((AllocationType
& MEM_COMMIT
) &&
917 ((Protect
& PAGE_READWRITE
) ||
918 (Protect
& PAGE_EXECUTE_READWRITE
)))
920 MmReserveSwapPages(RegionSize
);
923 *UBaseAddress
= BaseAddress
;
924 *URegionSize
= RegionSize
;
926 MmUnlockAddressSpace(AddressSpace
);
927 ObDereferenceObject(Process
);
928 return(STATUS_SUCCESS
);
933 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
934 IN PVOID BaseAddress
,
935 IN ULONG NumberOfBytesToFlush
,
936 OUT PULONG NumberOfBytesFlushed OPTIONAL
)
938 * FUNCTION: Flushes virtual memory to file
940 * ProcessHandle = Points to the process that allocated the virtual
942 * BaseAddress = Points to the memory address
943 * NumberOfBytesToFlush = Limits the range to flush,
944 * NumberOfBytesFlushed = Actual number of bytes flushed
952 MmFreeVirtualMemoryPage(PVOID Context
, PVOID Address
, ULONG PhysicalAddr
)
954 PEPROCESS Process
= (PEPROCESS
)Context
;
956 if (PhysicalAddr
!= 0)
958 MmRemovePageFromWorkingSet(Process
, Address
);
959 MmDereferencePage((PVOID
)PhysicalAddr
);
964 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
965 IN PVOID
* PBaseAddress
,
966 IN PULONG PRegionSize
,
969 * FUNCTION: Frees a range of virtual memory
971 * ProcessHandle = Points to the process that allocated the virtual
973 * BaseAddress = Points to the memory address, rounded down to a
974 * multiple of the pagesize
975 * RegionSize = Limits the range to free, rounded up to a multiple of
977 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
981 MEMORY_AREA
* MemoryArea
;
984 PMADDRESS_SPACE AddressSpace
;
988 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
989 "*RegionSize %x, FreeType %x)\n",ProcessHandle
,*BaseAddress
,
990 *RegionSize
,FreeType
);
992 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((*PBaseAddress
));
993 RegionSize
= PAGE_ROUND_UP((*PBaseAddress
) + (*PRegionSize
)) -
994 PAGE_ROUND_DOWN((*PBaseAddress
));
996 Status
= ObReferenceObjectByHandle(ProcessHandle
,
997 PROCESS_VM_OPERATION
,
1002 if (!NT_SUCCESS(Status
))
1007 AddressSpace
= &Process
->AddressSpace
;
1009 MmLockAddressSpace(AddressSpace
);
1010 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1012 if (MemoryArea
== NULL
)
1014 MmUnlockAddressSpace(AddressSpace
);
1015 ObDereferenceObject(Process
);
1016 return(STATUS_UNSUCCESSFUL
);
1022 if (MemoryArea
->BaseAddress
!= BaseAddress
)
1024 MmUnlockAddressSpace(AddressSpace
);
1025 ObDereferenceObject(Process
);
1026 return(STATUS_UNSUCCESSFUL
);
1029 if ((MemoryArea
->Type
== MEMORY_AREA_COMMIT
) &&
1030 ((MemoryArea
->Attributes
& PAGE_READWRITE
) ||
1031 (MemoryArea
->Attributes
& PAGE_EXECUTE_READWRITE
)))
1033 MmDereserveSwapPages(PAGE_ROUND_UP(MemoryArea
->Length
));
1037 MmFreeMemoryArea(&Process
->AddressSpace
,
1040 MmFreeVirtualMemoryPage
,
1042 MmUnlockAddressSpace(AddressSpace
);
1043 ObDereferenceObject(Process
);
1044 return(STATUS_SUCCESS
);
1047 Status
= MmComplexVirtualMemoryOperation(AddressSpace
,
1053 MmUnlockAddressSpace(AddressSpace
);
1054 ObDereferenceObject(Process
);
1057 MmUnlockAddressSpace(AddressSpace
);
1058 ObDereferenceObject(Process
);
1059 return(STATUS_NOT_IMPLEMENTED
);
1064 NtLockVirtualMemory(HANDLE ProcessHandle
,
1066 ULONG NumberOfBytesToLock
,
1067 PULONG NumberOfBytesLocked
)
1074 MmChangeAreaProtection(PEPROCESS Process
,
1081 for (i
=0; i
<(Length
/PAGESIZE
); i
++)
1083 if (MmIsPagePresent(Process
, BaseAddress
+ (i
*PAGESIZE
)))
1085 MmSetPageProtect(Process
,
1086 BaseAddress
+ (i
*PAGESIZE
),
1093 NTSTATUS STDCALL
NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1094 IN PVOID BaseAddress
,
1095 IN ULONG NumberOfBytesToProtect
,
1096 IN ULONG NewAccessProtection
,
1097 OUT PULONG OldAccessProtection
)
1099 PMEMORY_AREA MemoryArea
;
1102 PMADDRESS_SPACE AddressSpace
;
1104 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1105 PROCESS_VM_OPERATION
,
1110 if (Status
!= STATUS_SUCCESS
)
1112 DPRINT("NtProtectVirtualMemory() = %x\n",Status
);
1116 AddressSpace
= &Process
->AddressSpace
;
1118 MmLockAddressSpace(AddressSpace
);
1119 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1121 if (MemoryArea
== NULL
)
1123 DPRINT("NtProtectVirtualMemory() = %x\n",STATUS_UNSUCCESSFUL
);
1124 MmUnlockAddressSpace(AddressSpace
);
1125 ObDereferenceObject(Process
);
1126 return(STATUS_UNSUCCESSFUL
);
1129 *OldAccessProtection
= MemoryArea
->Attributes
;
1131 if (MemoryArea
->BaseAddress
== BaseAddress
&&
1132 MemoryArea
->Length
== NumberOfBytesToProtect
)
1134 MemoryArea
->Attributes
= NewAccessProtection
;
1138 MemoryArea
= MmSplitMemoryArea(Process
,
1139 &Process
->AddressSpace
,
1142 NumberOfBytesToProtect
,
1144 NewAccessProtection
);
1146 MmChangeAreaProtection(Process
,
1148 NumberOfBytesToProtect
,
1149 NewAccessProtection
);
1150 MmUnlockAddressSpace(AddressSpace
);
1151 ObDereferenceObject(Process
);
1152 return(STATUS_SUCCESS
);
1156 NTSTATUS STDCALL
NtQueryVirtualMemory (IN HANDLE ProcessHandle
,
1158 IN CINT VirtualMemoryInformationClass
,
1159 OUT PVOID VirtualMemoryInformation
,
1161 OUT PULONG ResultLength
)
1165 MEMORY_AREA
* MemoryArea
;
1167 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
1168 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
1169 "Length %lu ResultLength %x)\n",ProcessHandle
,Address
,
1170 VirtualMemoryInformationClass
,VirtualMemoryInformation
,
1171 Length
,ResultLength
);
1173 switch(VirtualMemoryInformationClass
)
1175 case MemoryBasicInformation
:
1177 PMEMORY_BASIC_INFORMATION Info
=
1178 (PMEMORY_BASIC_INFORMATION
)VirtualMemoryInformation
;
1179 PMADDRESS_SPACE AddressSpace
;
1181 if (Length
< sizeof(MEMORY_BASIC_INFORMATION
))
1183 ObDereferenceObject(Process
);
1184 return STATUS_INFO_LENGTH_MISMATCH
;
1189 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1192 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1193 PROCESS_QUERY_INFORMATION
,
1199 if (!NT_SUCCESS(Status
))
1201 DPRINT("NtQueryVirtualMemory() = %x\n",Status
);
1205 AddressSpace
= &Process
->AddressSpace
;
1206 MmLockAddressSpace(AddressSpace
);
1207 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1210 if (MemoryArea
== NULL
)
1212 Info
->State
= MEM_FREE
;
1213 DPRINT("Virtual memory at %p is free.\n", Address
);
1214 MmUnlockAddressSpace(AddressSpace
);
1215 ObDereferenceObject(Process
);
1216 return (STATUS_SUCCESS
);
1220 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
1222 Info
->State
= MEM_COMMIT
;
1226 Info
->State
= MEM_RESERVE
;
1230 Info
->BaseAddress
= MemoryArea
->BaseAddress
;
1231 Info
->RegionSize
= MemoryArea
->Length
;
1233 DPRINT("BaseAddress %p, RegionSize %x State %x\n",
1234 Info
->BaseAddress
, Info
->RegionSize
, Info
->State
);
1236 MmUnlockAddressSpace(AddressSpace
);
1237 ObDereferenceObject(Process
);
1238 return STATUS_SUCCESS
;
1243 return STATUS_INVALID_INFO_CLASS
;
1248 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1249 IN PVOID BaseAddress
,
1251 IN ULONG NumberOfBytesToRead
,
1252 OUT PULONG NumberOfBytesRead
)
1256 PVOID SystemAddress
;
1259 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1260 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle
,BaseAddress
,
1261 Buffer
,NumberOfBytesToRead
);
1263 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1269 if (Status
!= STATUS_SUCCESS
)
1274 Mdl
= MmCreateMdl(NULL
,
1276 NumberOfBytesToRead
);
1277 MmProbeAndLockPages(Mdl
,
1281 KeAttachProcess(Process
);
1283 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
1284 memcpy(SystemAddress
, BaseAddress
, NumberOfBytesToRead
);
1288 ObDereferenceObject(Process
);
1290 *NumberOfBytesRead
= NumberOfBytesToRead
;
1291 return(STATUS_SUCCESS
);
1296 NtUnlockVirtualMemory(HANDLE ProcessHandle
,
1298 ULONG NumberOfBytesToUnlock
,
1299 PULONG NumberOfBytesUnlocked OPTIONAL
)
1306 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1307 IN PVOID BaseAddress
,
1309 IN ULONG NumberOfBytesToWrite
,
1310 OUT PULONG NumberOfBytesWritten
)
1314 PVOID SystemAddress
;
1317 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1318 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle
,BaseAddress
,
1319 Buffer
,NumberOfBytesToWrite
);
1321 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1327 if (Status
!= STATUS_SUCCESS
)
1332 Mdl
= MmCreateMdl(NULL
,
1334 NumberOfBytesToWrite
);
1335 MmProbeAndLockPages(Mdl
,
1339 KeAttachProcess(Process
);
1341 DPRINT("Attached to process copying memory\n");
1343 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
1344 memcpy(BaseAddress
, SystemAddress
, NumberOfBytesToWrite
);
1346 DPRINT("Done copy\n");
1350 ObDereferenceObject(Process
);
1352 *NumberOfBytesWritten
= NumberOfBytesToWrite
;
1354 DPRINT("Finished NtWriteVirtualMemory()\n");
1356 return(STATUS_SUCCESS
);
1362 MmSecureVirtualMemory (
1375 MmUnsecureVirtualMemory (