1 /* $Id: virtual.c,v 1.47 2001/05/05 19:13:10 chorns 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
;
505 * Save the type and protection and length of the current segment
507 OldType
= FirstSegment
->Type
;
508 OldProtect
= FirstSegment
->Protect
;
509 OldLength
= FirstSegment
->Length
;
512 * If the segment is already of the right type and protection then
513 * there is nothing to do.
515 if (FirstSegment
->Type
== Type
&& FirstSegment
->Protect
== Protect
)
517 return(STATUS_SUCCESS
);
521 * Allocate the segment we might need here because if the allocation
522 * fails below it will be difficult to undo what we've done already.
524 NewTopSegment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
526 if (NewTopSegment
== NULL
)
528 return(STATUS_NO_MEMORY
);
531 if (FirstAddress
< RegionAddress
)
534 * If the region to be affected starts at a higher address than
535 * the current segment then create a new segment for the
538 RegionSegment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
540 if (RegionSegment
== NULL
)
542 ExFreePool(NewTopSegment
);
543 return(STATUS_NO_MEMORY
);
546 RegionSegment
->Type
= Type
;
547 RegionSegment
->Protect
= Protect
;
548 RegionSegment
->Length
= RegionLength
;
550 FirstSegment
->Length
= RegionAddress
- FirstAddress
;
552 InsertAfterEntry(&FirstSegment
->SegmentListEntry
,
553 &RegionSegment
->SegmentListEntry
);
558 * Otherwise just set its type and protection and length
561 FirstSegment
->Type
= Type
;
562 FirstSegment
->Protect
= Protect
;
563 FirstSegment
->Length
= RegionLength
;
565 RegionSegment
= FirstSegment
;
568 if ((FirstAddress
+ OldLength
) > (RegionAddress
+ RegionLength
))
571 * If the top of the current segment extends after the affected
572 * region then create a segment for the unaffected portion
575 NewTopSegment
->Type
= OldType
;
576 NewTopSegment
->Protect
= OldProtect
;
577 NewTopSegment
->Length
= (FirstAddress
+ OldLength
) -
578 (RegionAddress
+ RegionLength
);
580 InsertAfterEntry(&RegionSegment
->SegmentListEntry
,
581 &NewTopSegment
->SegmentListEntry
);
585 ExFreePool(NewTopSegment
);
586 NewTopSegment
= NULL
;
590 * Actually set the type and protection of the affected region
592 MmModifyAttributes(AddressSpace
,
599 return(STATUS_SUCCESS
);
602 NTSTATUS
MmGatherSegment(PMADDRESS_SPACE AddressSpace
,
603 PMEMORY_AREA MemoryArea
,
608 PMM_SEGMENT FirstSegment
,
611 * FUNCTION: Do a virtual memory operation that will effect several
614 * AddressSpace (IN) = Address space to affect
615 * MemoryArea (IN) = Memory area to affect
616 * BaseAddress (IN) = Base address of the region to affect
617 * RegionSize (IN) = Size of the region to affect
618 * Type (IN) = New type of the region
619 * Protect (IN) = New protection of the region
620 * CurrentSegment (IN) = First segment intersecting with the region
621 * CurrentAddress (IN) = Start address of the first segment
622 * interesting with the region
626 PMM_SEGMENT RegionSegment
;
627 PVOID CurrentAddress
;
628 ULONG RemainingLength
;
629 PLIST_ENTRY CurrentEntry
;
630 PLIST_ENTRY ListHead
;
631 PMM_SEGMENT CurrentSegment
;
633 if (FirstAddress
< RegionAddress
)
636 * If a portion of the first segment is not covered by the region then
637 * we need to split it into two segments
640 RegionSegment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
642 if (RegionSegment
== NULL
)
644 return(STATUS_NO_MEMORY
);
647 RegionSegment
->Type
= Type
;
648 RegionSegment
->Protect
= Protect
;
649 RegionSegment
->Length
= (FirstAddress
+ FirstSegment
->Length
) -
652 FirstSegment
->Length
= RegionAddress
- FirstAddress
;
654 InsertAfterEntry(&FirstSegment
->SegmentListEntry
,
655 &RegionSegment
->SegmentListEntry
);
657 MmModifyAttributes(AddressSpace
,
659 RegionSegment
->Length
,
661 FirstSegment
->Protect
,
665 CurrentAddress
= FirstAddress
+ FirstSegment
->Length
+
666 RegionSegment
->Length
;
671 * Otherwise just change the attributes of the segment
677 OldType
= FirstSegment
->Type
;
678 OldProtect
= FirstSegment
->Protect
;
680 FirstSegment
->Type
= Type
;
681 FirstSegment
->Protect
= Protect
;
683 RegionSegment
= FirstSegment
;
685 MmModifyAttributes(AddressSpace
,
687 FirstSegment
->Length
,
693 CurrentAddress
= FirstAddress
+ RegionSegment
->Length
;
697 * Change the attributes of all the complete segments lying inside the
700 RemainingLength
= RegionLength
- RegionSegment
->Length
;
701 CurrentEntry
= RegionSegment
->SegmentListEntry
.Flink
;
702 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
705 ListHead
= &MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
;
707 while (CurrentEntry
!= ListHead
&& RemainingLength
> 0)
714 * If this segment will not be completely covered by the
715 * affected region then break
717 if (CurrentSegment
->Length
> RemainingLength
)
722 OldType
= CurrentSegment
->Type
;
723 OldProtect
= CurrentSegment
->Protect
;
724 OldLength
= CurrentSegment
->Length
;
727 * Extend the length of the previous segment to cover this one
729 RegionSegment
->Length
= RegionSegment
->Length
+ OldLength
;
730 RemainingLength
= RemainingLength
- OldLength
;
731 CurrentAddress
= CurrentAddress
+ OldLength
;
732 CurrentEntry
= CurrentEntry
->Flink
;
735 * Remove the current segment from the list
737 RemoveEntryList(&CurrentSegment
->SegmentListEntry
);
738 ExFreePool(CurrentSegment
);
740 MmModifyAttributes(AddressSpace
,
748 CurrentSegment
= CONTAINING_RECORD(CurrentEntry
,
754 * If we've run off the top of the memory area then bug check
756 if (CurrentEntry
== ListHead
&& RemainingLength
> 0)
762 * We've only affected a portion of a segment then split it in two
764 if (RemainingLength
> 0)
766 CurrentSegment
->Length
= CurrentSegment
->Length
- RemainingLength
;
768 RegionSegment
->Length
= RegionSegment
->Length
+ RemainingLength
;
770 MmModifyAttributes(AddressSpace
,
773 CurrentSegment
->Type
,
774 CurrentSegment
->Protect
,
779 return(STATUS_SUCCESS
);
782 NTSTATUS
MmComplexVirtualMemoryOperation(PMADDRESS_SPACE AddressSpace
,
783 PMEMORY_AREA MemoryArea
,
789 PMM_SEGMENT CurrentSegment
;
790 PVOID CurrentAddress
;
792 CurrentSegment
= MmGetSegmentForAddress(MemoryArea
,
795 if (CurrentSegment
== NULL
)
800 if (BaseAddress
>= CurrentAddress
&&
801 (BaseAddress
+ RegionSize
) <= (CurrentAddress
+ CurrentSegment
->Length
))
803 return((MmSplitSegment(AddressSpace
,
814 return((MmGatherSegment(AddressSpace
,
827 NtAllocateVirtualMemory(IN HANDLE ProcessHandle
,
828 IN OUT PVOID
* UBaseAddress
,
830 IN OUT PULONG URegionSize
,
831 IN ULONG AllocationType
,
834 * FUNCTION: Allocates a block of virtual memory in the process address space
836 * ProcessHandle = The handle of the process which owns the virtual memory
837 * BaseAddress = A pointer to the virtual memory allocated. If you
838 * supply a non zero value the system will try to
839 * allocate the memory at the address supplied. It round
840 * it down to a multiple of the page size.
841 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
842 * that must be zero, ensuring that the memory will be
843 * allocated at a address below a certain value.
844 * RegionSize = The number of bytes to allocate
845 * AllocationType = Indicates the type of virtual memory you like to
846 * allocated, can be one of the values : MEM_COMMIT,
847 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN
848 * Protect = Indicates the protection type of the pages allocated, can be
849 * a combination of PAGE_READONLY, PAGE_READWRITE,
850 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
853 * This function maps to the win32 VirtualAllocEx. Virtual memory is
854 * process based so the protocol starts with a ProcessHandle. I
855 * splitted the functionality of obtaining the actual address and
856 * specifying the start address in two parameters ( BaseAddress and
857 * StartAddress ) The NumberOfBytesAllocated specify the range and the
858 * AllocationType and ProctectionType map to the other two parameters.
863 MEMORY_AREA
* MemoryArea
;
866 PMADDRESS_SPACE AddressSpace
;
873 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
874 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
875 *UBaseAddress
,ZeroBits
,*URegionSize
,AllocationType
,
879 * Check the validity of the parameters
881 if ((Protect
& PAGE_FLAGS_VALID_FROM_USER_MODE
) != Protect
)
883 return(STATUS_INVALID_PAGE_PROTECTION
);
885 PBaseAddress
= *UBaseAddress
;
886 PRegionSize
= *URegionSize
;
889 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(PBaseAddress
);
890 RegionSize
= PAGE_ROUND_UP(PBaseAddress
+ PRegionSize
) -
891 PAGE_ROUND_DOWN(PBaseAddress
);
893 Status
= ObReferenceObjectByHandle(ProcessHandle
,
894 PROCESS_VM_OPERATION
,
899 if (!NT_SUCCESS(Status
))
901 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
905 if (AllocationType
& MEM_RESERVE
)
914 AddressSpace
= &Process
->AddressSpace
;
915 MmLockAddressSpace(AddressSpace
);
917 if (PBaseAddress
!= 0)
919 MemoryArea
= MmOpenMemoryAreaByAddress(&Process
->AddressSpace
,
922 if (MemoryArea
!= NULL
&&
923 MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
&&
924 MemoryArea
->Length
>= RegionSize
)
926 Status
= MmComplexVirtualMemoryOperation(AddressSpace
,
932 /* FIXME: Reserve/dereserve swap pages */
933 MmUnlockAddressSpace(AddressSpace
);
934 ObDereferenceObject(Process
);
937 else if (MemoryArea
!= NULL
)
939 MmUnlockAddressSpace(AddressSpace
);
940 ObDereferenceObject(Process
);
941 return(STATUS_UNSUCCESSFUL
);
945 Segment
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MM_SEGMENT
),
949 MmUnlockAddressSpace(AddressSpace
);
950 ObDereferenceObject(Process
);
951 return(STATUS_UNSUCCESSFUL
);
954 Status
= MmCreateMemoryArea(Process
,
955 &Process
->AddressSpace
,
956 MEMORY_AREA_VIRTUAL_MEMORY
,
963 if (!NT_SUCCESS(Status
))
965 DPRINT("NtAllocateVirtualMemory() = %x\n",Status
);
966 MmUnlockAddressSpace(AddressSpace
);
967 ObDereferenceObject(Process
);
971 InitializeListHead(&MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
);
973 Segment
->Type
= Type
;
974 Segment
->Protect
= Protect
;
975 Segment
->Length
= RegionSize
;
976 InsertTailList(&MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
,
977 &Segment
->SegmentListEntry
);
979 DPRINT("*UBaseAddress %x\n",*UBaseAddress
);
980 if ((AllocationType
& MEM_COMMIT
) &&
981 ((Protect
& PAGE_READWRITE
) ||
982 (Protect
& PAGE_EXECUTE_READWRITE
)))
984 MmReserveSwapPages(RegionSize
);
987 *UBaseAddress
= BaseAddress
;
988 *URegionSize
= RegionSize
;
990 MmUnlockAddressSpace(AddressSpace
);
991 ObDereferenceObject(Process
);
992 return(STATUS_SUCCESS
);
997 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
998 IN PVOID BaseAddress
,
999 IN ULONG NumberOfBytesToFlush
,
1000 OUT PULONG NumberOfBytesFlushed OPTIONAL
)
1002 * FUNCTION: Flushes virtual memory to file
1004 * ProcessHandle = Points to the process that allocated the virtual
1006 * BaseAddress = Points to the memory address
1007 * NumberOfBytesToFlush = Limits the range to flush,
1008 * NumberOfBytesFlushed = Actual number of bytes flushed
1016 MmFreeVirtualMemoryPage(PVOID Context
, PVOID Address
, ULONG PhysicalAddr
)
1018 PEPROCESS Process
= (PEPROCESS
)Context
;
1020 if (PhysicalAddr
!= 0)
1022 MmRemovePageFromWorkingSet(Process
, Address
);
1023 MmDereferencePage((PVOID
)PhysicalAddr
);
1028 MmFreeVirtualMemory(PEPROCESS Process
, PMEMORY_AREA MemoryArea
)
1030 PLIST_ENTRY current_entry
;
1031 PMM_SEGMENT current
;
1033 current_entry
= MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
.Flink
;
1034 while (current_entry
!= &MemoryArea
->Data
.VirtualMemoryData
.SegmentListHead
)
1036 current
= CONTAINING_RECORD(current_entry
, MM_SEGMENT
, SegmentListEntry
);
1037 current_entry
= current_entry
->Flink
;
1038 ExFreePool(current
);
1041 MmFreeMemoryArea(&Process
->AddressSpace
,
1042 MemoryArea
->BaseAddress
,
1044 MmFreeVirtualMemoryPage
,
1049 NtFreeVirtualMemory(IN HANDLE ProcessHandle
,
1050 IN PVOID
* PBaseAddress
,
1051 IN PULONG PRegionSize
,
1054 * FUNCTION: Frees a range of virtual memory
1056 * ProcessHandle = Points to the process that allocated the virtual
1058 * BaseAddress = Points to the memory address, rounded down to a
1059 * multiple of the pagesize
1060 * RegionSize = Limits the range to free, rounded up to a multiple of
1062 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
1066 MEMORY_AREA
* MemoryArea
;
1069 PMADDRESS_SPACE AddressSpace
;
1073 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
1074 "*PRegionSize %x, FreeType %x)\n",ProcessHandle
,*PBaseAddress
,
1075 *PRegionSize
,FreeType
);
1077 BaseAddress
= (PVOID
)PAGE_ROUND_DOWN((*PBaseAddress
));
1078 RegionSize
= PAGE_ROUND_UP((*PBaseAddress
) + (*PRegionSize
)) -
1079 PAGE_ROUND_DOWN((*PBaseAddress
));
1081 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1082 PROCESS_VM_OPERATION
,
1087 if (!NT_SUCCESS(Status
))
1092 AddressSpace
= &Process
->AddressSpace
;
1094 MmLockAddressSpace(AddressSpace
);
1095 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1097 if (MemoryArea
== NULL
)
1099 MmUnlockAddressSpace(AddressSpace
);
1100 ObDereferenceObject(Process
);
1101 return(STATUS_UNSUCCESSFUL
);
1107 if (MemoryArea
->BaseAddress
!= BaseAddress
)
1109 MmUnlockAddressSpace(AddressSpace
);
1110 ObDereferenceObject(Process
);
1111 return(STATUS_UNSUCCESSFUL
);
1114 if ((MemoryArea
->Type
== MEMORY_AREA_COMMIT
) &&
1115 ((MemoryArea
->Attributes
& PAGE_READWRITE
) ||
1116 (MemoryArea
->Attributes
& PAGE_EXECUTE_READWRITE
)))
1118 MmDereserveSwapPages(PAGE_ROUND_UP(MemoryArea
->Length
));
1122 MmFreeVirtualMemory(Process
, MemoryArea
);
1123 MmUnlockAddressSpace(AddressSpace
);
1124 ObDereferenceObject(Process
);
1125 return(STATUS_SUCCESS
);
1128 Status
= MmComplexVirtualMemoryOperation(AddressSpace
,
1134 MmUnlockAddressSpace(AddressSpace
);
1135 ObDereferenceObject(Process
);
1138 MmUnlockAddressSpace(AddressSpace
);
1139 ObDereferenceObject(Process
);
1140 return(STATUS_NOT_IMPLEMENTED
);
1145 NtLockVirtualMemory(HANDLE ProcessHandle
,
1147 ULONG NumberOfBytesToLock
,
1148 PULONG NumberOfBytesLocked
)
1155 MmChangeAreaProtection(PEPROCESS Process
,
1162 for (i
=0; i
<(Length
/PAGESIZE
); i
++)
1164 if (MmIsPagePresent(Process
, BaseAddress
+ (i
*PAGESIZE
)))
1166 MmSetPageProtect(Process
,
1167 BaseAddress
+ (i
*PAGESIZE
),
1174 NTSTATUS STDCALL
NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
1175 IN PVOID BaseAddress
,
1176 IN ULONG NumberOfBytesToProtect
,
1177 IN ULONG NewAccessProtection
,
1178 OUT PULONG OldAccessProtection
)
1180 PMEMORY_AREA MemoryArea
;
1183 PMADDRESS_SPACE AddressSpace
;
1185 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1186 PROCESS_VM_OPERATION
,
1191 if (Status
!= STATUS_SUCCESS
)
1193 DPRINT("NtProtectVirtualMemory() = %x\n",Status
);
1197 AddressSpace
= &Process
->AddressSpace
;
1199 MmLockAddressSpace(AddressSpace
);
1200 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1202 if (MemoryArea
== NULL
)
1204 DPRINT("NtProtectVirtualMemory() = %x\n",STATUS_UNSUCCESSFUL
);
1205 MmUnlockAddressSpace(AddressSpace
);
1206 ObDereferenceObject(Process
);
1207 return(STATUS_UNSUCCESSFUL
);
1210 *OldAccessProtection
= MemoryArea
->Attributes
;
1212 if (MemoryArea
->BaseAddress
== BaseAddress
&&
1213 MemoryArea
->Length
== NumberOfBytesToProtect
)
1215 MemoryArea
->Attributes
= NewAccessProtection
;
1219 MemoryArea
= MmSplitMemoryArea(Process
,
1220 &Process
->AddressSpace
,
1223 NumberOfBytesToProtect
,
1225 NewAccessProtection
);
1227 MmChangeAreaProtection(Process
,
1229 NumberOfBytesToProtect
,
1230 NewAccessProtection
);
1231 MmUnlockAddressSpace(AddressSpace
);
1232 ObDereferenceObject(Process
);
1233 return(STATUS_SUCCESS
);
1237 NTSTATUS STDCALL
NtQueryVirtualMemory (IN HANDLE ProcessHandle
,
1239 IN CINT VirtualMemoryInformationClass
,
1240 OUT PVOID VirtualMemoryInformation
,
1242 OUT PULONG ResultLength
)
1246 MEMORY_AREA
* MemoryArea
;
1248 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
1249 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
1250 "Length %lu ResultLength %x)\n",ProcessHandle
,Address
,
1251 VirtualMemoryInformationClass
,VirtualMemoryInformation
,
1252 Length
,ResultLength
);
1254 switch(VirtualMemoryInformationClass
)
1256 case MemoryBasicInformation
:
1258 PMEMORY_BASIC_INFORMATION Info
=
1259 (PMEMORY_BASIC_INFORMATION
)VirtualMemoryInformation
;
1260 PMADDRESS_SPACE AddressSpace
;
1262 if (Length
< sizeof(MEMORY_BASIC_INFORMATION
))
1264 ObDereferenceObject(Process
);
1265 return STATUS_INFO_LENGTH_MISMATCH
;
1270 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
1273 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1274 PROCESS_QUERY_INFORMATION
,
1280 if (!NT_SUCCESS(Status
))
1282 DPRINT("NtQueryVirtualMemory() = %x\n",Status
);
1286 AddressSpace
= &Process
->AddressSpace
;
1287 MmLockAddressSpace(AddressSpace
);
1288 MemoryArea
= MmOpenMemoryAreaByAddress(AddressSpace
,
1291 if (MemoryArea
== NULL
)
1293 Info
->State
= MEM_FREE
;
1294 DPRINT("Virtual memory at %p is free.\n", Address
);
1295 MmUnlockAddressSpace(AddressSpace
);
1296 ObDereferenceObject(Process
);
1297 return (STATUS_SUCCESS
);
1301 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
1303 Info
->State
= MEM_COMMIT
;
1307 Info
->State
= MEM_RESERVE
;
1311 Info
->BaseAddress
= MemoryArea
->BaseAddress
;
1312 Info
->RegionSize
= MemoryArea
->Length
;
1314 DPRINT("BaseAddress %p, RegionSize %x State %x\n",
1315 Info
->BaseAddress
, Info
->RegionSize
, Info
->State
);
1317 MmUnlockAddressSpace(AddressSpace
);
1318 ObDereferenceObject(Process
);
1319 return STATUS_SUCCESS
;
1324 return STATUS_INVALID_INFO_CLASS
;
1329 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
1330 IN PVOID BaseAddress
,
1332 IN ULONG NumberOfBytesToRead
,
1333 OUT PULONG NumberOfBytesRead
)
1337 PVOID SystemAddress
;
1340 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1341 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle
,BaseAddress
,
1342 Buffer
,NumberOfBytesToRead
);
1344 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1350 if (Status
!= STATUS_SUCCESS
)
1355 Mdl
= MmCreateMdl(NULL
,
1357 NumberOfBytesToRead
);
1358 MmProbeAndLockPages(Mdl
,
1362 KeAttachProcess(Process
);
1364 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
1365 memcpy(SystemAddress
, BaseAddress
, NumberOfBytesToRead
);
1369 if (Mdl
->MappedSystemVa
!= NULL
)
1371 MmUnmapLockedPages(Mdl
->MappedSystemVa
, Mdl
);
1376 ObDereferenceObject(Process
);
1378 *NumberOfBytesRead
= NumberOfBytesToRead
;
1379 return(STATUS_SUCCESS
);
1384 NtUnlockVirtualMemory(HANDLE ProcessHandle
,
1386 ULONG NumberOfBytesToUnlock
,
1387 PULONG NumberOfBytesUnlocked OPTIONAL
)
1394 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
1395 IN PVOID BaseAddress
,
1397 IN ULONG NumberOfBytesToWrite
,
1398 OUT PULONG NumberOfBytesWritten
)
1402 PVOID SystemAddress
;
1405 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1406 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle
,BaseAddress
,
1407 Buffer
,NumberOfBytesToWrite
);
1409 Status
= ObReferenceObjectByHandle(ProcessHandle
,
1415 if (Status
!= STATUS_SUCCESS
)
1420 Mdl
= MmCreateMdl(NULL
,
1422 NumberOfBytesToWrite
);
1423 MmProbeAndLockPages(Mdl
,
1427 KeAttachProcess(Process
);
1429 DPRINT("Attached to process copying memory\n");
1431 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
1432 memcpy(BaseAddress
, SystemAddress
, NumberOfBytesToWrite
);
1434 DPRINT("Done copy\n");
1438 ObDereferenceObject(Process
);
1440 if (Mdl
->MappedSystemVa
!= NULL
)
1442 MmUnmapLockedPages(Mdl
->MappedSystemVa
, Mdl
);
1447 *NumberOfBytesWritten
= NumberOfBytesToWrite
;
1449 DPRINT("Finished NtWriteVirtualMemory()\n");
1451 return(STATUS_SUCCESS
);
1457 MmSecureVirtualMemory (
1470 MmUnsecureVirtualMemory (