3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/virtual.c
23 * PURPOSE: Implementing operations on virtual memory.
24 * PROGRAMMER: David Welch
27 /* INCLUDE *****************************************************************/
32 #include <internal/debug.h>
34 /* FUNCTIONS *****************************************************************/
37 NtFlushVirtualMemory(IN HANDLE ProcessHandle
,
39 IN ULONG NumberOfBytesToFlush
,
40 OUT PULONG NumberOfBytesFlushed OPTIONAL
)
42 * FUNCTION: Flushes virtual memory to file
44 * ProcessHandle = Points to the process that allocated the virtual
46 * BaseAddress = Points to the memory address
47 * NumberOfBytesToFlush = Limits the range to flush,
48 * NumberOfBytesFlushed = Actual number of bytes flushed
53 return(STATUS_NOT_IMPLEMENTED
);
58 MiLockVirtualMemory(HANDLE ProcessHandle
,
60 ULONG NumberOfBytesToLock
,
61 PULONG NumberOfBytesLocked
,
62 PObReferenceObjectByHandle pObReferenceObjectByHandle
,
63 PMmCreateMdl pMmCreateMdl
,
64 PObDereferenceObject pObDereferenceObject
,
65 PMmProbeAndLockPages pMmProbeAndLockPages
,
66 PExFreePool pExFreePool
)
72 Status
= pObReferenceObjectByHandle(ProcessHandle
,
78 if (!NT_SUCCESS(Status
))
81 Mdl
= pMmCreateMdl(NULL
,
86 pObDereferenceObject(Process
);
87 return(STATUS_NO_MEMORY
);
90 pMmProbeAndLockPages(Mdl
,
96 pObDereferenceObject(Process
);
98 *NumberOfBytesLocked
= NumberOfBytesToLock
;
99 return(STATUS_SUCCESS
);
104 NtLockVirtualMemory(HANDLE ProcessHandle
,
106 ULONG NumberOfBytesToLock
,
107 PULONG NumberOfBytesLocked
)
109 DPRINT("NtLockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
110 "NumberOfBytesToLock %d, NumberOfBytesLocked %x)\n",
114 NumberOfBytesLocked
);
116 return MiLockVirtualMemory(ProcessHandle
,
120 ObReferenceObjectByHandle
,
122 ObfDereferenceObject
,
129 MiQueryVirtualMemory (IN HANDLE ProcessHandle
,
131 IN CINT VirtualMemoryInformationClass
,
132 OUT PVOID VirtualMemoryInformation
,
134 OUT PULONG ResultLength
)
138 MEMORY_AREA
* MemoryArea
;
139 PMADDRESS_SPACE AddressSpace
;
141 if (Address
< (PVOID
)KERNEL_BASE
)
143 Status
= ObReferenceObjectByHandle(ProcessHandle
,
144 PROCESS_QUERY_INFORMATION
,
150 if (!NT_SUCCESS(Status
))
152 DPRINT("NtQueryVirtualMemory() = %x\n",Status
);
155 AddressSpace
= &Process
->AddressSpace
;
159 AddressSpace
= MmGetKernelAddressSpace();
161 MmLockAddressSpace(AddressSpace
);
162 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, Address
);
163 switch(VirtualMemoryInformationClass
)
165 case MemoryBasicInformation
:
167 PMEMORY_BASIC_INFORMATION Info
=
168 (PMEMORY_BASIC_INFORMATION
)VirtualMemoryInformation
;
169 if (Length
!= sizeof(MEMORY_BASIC_INFORMATION
))
171 MmUnlockAddressSpace(AddressSpace
);
172 ObDereferenceObject(Process
);
173 return(STATUS_INFO_LENGTH_MISMATCH
);
176 if (MemoryArea
== NULL
)
179 Info
->State
= MEM_FREE
;
180 Info
->Protect
= PAGE_NOACCESS
;
181 Info
->AllocationProtect
= 0;
182 Info
->BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(Address
);
183 Info
->AllocationBase
= NULL
;
184 Info
->RegionSize
= MmFindGapAtAddress(AddressSpace
, Info
->BaseAddress
);
185 Status
= STATUS_SUCCESS
;
186 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
190 switch(MemoryArea
->Type
)
192 case MEMORY_AREA_VIRTUAL_MEMORY
:
193 Status
= MmQueryAnonMem(MemoryArea
, Address
, Info
,
196 case MEMORY_AREA_SECTION_VIEW
:
197 Status
= MmQuerySectionView(MemoryArea
, Address
, Info
,
200 case MEMORY_AREA_NO_ACCESS
:
202 Info
->State
= MEM_FREE
;
203 Info
->Protect
= MemoryArea
->Attributes
;
204 Info
->AllocationProtect
= MemoryArea
->Attributes
;
205 Info
->BaseAddress
= MemoryArea
->StartingAddress
;
206 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
207 Info
->RegionSize
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
208 (ULONG_PTR
)MemoryArea
->StartingAddress
;
209 Status
= STATUS_SUCCESS
;
210 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
212 case MEMORY_AREA_SHARED_DATA
:
214 Info
->State
= MEM_COMMIT
;
215 Info
->Protect
= MemoryArea
->Attributes
;
216 Info
->AllocationProtect
= MemoryArea
->Attributes
;
217 Info
->BaseAddress
= MemoryArea
->StartingAddress
;
218 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
219 Info
->RegionSize
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
220 (ULONG_PTR
)MemoryArea
->StartingAddress
;
221 Status
= STATUS_SUCCESS
;
222 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
224 case MEMORY_AREA_SYSTEM
:
226 static int warned
= 0;
229 DPRINT1("FIXME: MEMORY_AREA_SYSTEM case incomplete (or possibly wrong) for NtQueryVirtualMemory()\n");
233 /* FIXME - don't have a clue if this is right, but it's better than nothing */
235 Info
->State
= MEM_COMMIT
;
236 Info
->Protect
= MemoryArea
->Attributes
;
237 Info
->AllocationProtect
= MemoryArea
->Attributes
;
238 Info
->BaseAddress
= MemoryArea
->StartingAddress
;
239 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
240 Info
->RegionSize
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
241 (ULONG_PTR
)MemoryArea
->StartingAddress
;
242 Status
= STATUS_SUCCESS
;
243 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
245 case MEMORY_AREA_KERNEL_STACK
:
247 static int warned
= 0;
250 DPRINT1("FIXME: MEMORY_AREA_KERNEL_STACK case incomplete (or possibly wrong) for NtQueryVirtualMemory()\n");
254 /* FIXME - don't have a clue if this is right, but it's better than nothing */
256 Info
->State
= MEM_COMMIT
;
257 Info
->Protect
= MemoryArea
->Attributes
;
258 Info
->AllocationProtect
= MemoryArea
->Attributes
;
259 Info
->BaseAddress
= MemoryArea
->StartingAddress
;
260 Info
->AllocationBase
= MemoryArea
->StartingAddress
;
261 Info
->RegionSize
= (ULONG_PTR
)MemoryArea
->EndingAddress
-
262 (ULONG_PTR
)MemoryArea
->StartingAddress
;
263 Status
= STATUS_SUCCESS
;
264 *ResultLength
= sizeof(MEMORY_BASIC_INFORMATION
);
267 DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea
->Type
);
268 Status
= STATUS_UNSUCCESSFUL
;
277 Status
= STATUS_INVALID_INFO_CLASS
;
283 MmUnlockAddressSpace(AddressSpace
);
284 if (Address
< (PVOID
)KERNEL_BASE
)
286 ObDereferenceObject(Process
);
294 * Called from VirtualQueryEx (lib\kernel32\mem\virtual.c)
298 NtQueryVirtualMemory (IN HANDLE ProcessHandle
,
300 IN CINT VirtualMemoryInformationClass
,
301 OUT PVOID VirtualMemoryInformation
,
303 OUT PULONG UnsafeResultLength
)
306 ULONG ResultLength
= 0;
307 KPROCESSOR_MODE PrevMode
;
310 MEMORY_BASIC_INFORMATION BasicInfo
;
314 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
315 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
316 "Length %lu ResultLength %x)\n",ProcessHandle
,Address
,
317 VirtualMemoryInformationClass
,VirtualMemoryInformation
,
318 Length
,ResultLength
);
320 PrevMode
= ExGetPreviousMode();
322 if (PrevMode
== UserMode
&& Address
>= (PVOID
)KERNEL_BASE
)
324 DPRINT1("Invalid parameter\n");
325 return STATUS_INVALID_PARAMETER
;
328 Status
= MiQueryVirtualMemory ( ProcessHandle
,
330 VirtualMemoryInformationClass
,
335 if (NT_SUCCESS(Status
) && ResultLength
> 0)
337 Status
= MmCopyToCaller(VirtualMemoryInformation
, &VirtualMemoryInfo
, ResultLength
);
338 if (!NT_SUCCESS(Status
))
344 if (UnsafeResultLength
!= NULL
)
346 MmCopyToCaller(UnsafeResultLength
, &ResultLength
, sizeof(ULONG
));
353 MiProtectVirtualMemory(IN PEPROCESS Process
,
354 IN OUT PVOID
*BaseAddress
,
355 IN OUT PULONG NumberOfBytesToProtect
,
356 IN ULONG NewAccessProtection
,
357 OUT PULONG OldAccessProtection OPTIONAL
)
359 PMEMORY_AREA MemoryArea
;
360 PMADDRESS_SPACE AddressSpace
;
361 ULONG OldAccessProtection_
;
364 *NumberOfBytesToProtect
=
365 PAGE_ROUND_UP((*BaseAddress
) + (*NumberOfBytesToProtect
)) -
366 PAGE_ROUND_DOWN(*BaseAddress
);
367 *BaseAddress
= (PVOID
)PAGE_ROUND_DOWN(*BaseAddress
);
369 AddressSpace
= &Process
->AddressSpace
;
371 MmLockAddressSpace(AddressSpace
);
372 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, *BaseAddress
);
373 if (MemoryArea
== NULL
)
375 MmUnlockAddressSpace(AddressSpace
);
376 return STATUS_UNSUCCESSFUL
;
379 if (OldAccessProtection
== NULL
)
380 OldAccessProtection
= &OldAccessProtection_
;
382 if (MemoryArea
->Type
== MEMORY_AREA_VIRTUAL_MEMORY
)
384 Status
= MmProtectAnonMem(AddressSpace
, MemoryArea
, *BaseAddress
,
385 *NumberOfBytesToProtect
, NewAccessProtection
,
386 OldAccessProtection
);
388 else if (MemoryArea
->Type
== MEMORY_AREA_SECTION_VIEW
)
390 Status
= MmProtectSectionView(AddressSpace
, MemoryArea
, *BaseAddress
,
391 *NumberOfBytesToProtect
,
393 OldAccessProtection
);
397 /* FIXME: Should we return failure or success in this case? */
398 Status
= STATUS_SUCCESS
;
401 MmUnlockAddressSpace(AddressSpace
);
409 * Called from VirtualProtectEx (lib\kernel32\mem\virtual.c)
413 NtProtectVirtualMemory(IN HANDLE ProcessHandle
,
414 IN OUT PVOID
*UnsafeBaseAddress
,
415 IN OUT ULONG
*UnsafeNumberOfBytesToProtect
,
416 IN ULONG NewAccessProtection
,
417 OUT PULONG UnsafeOldAccessProtection
)
421 ULONG OldAccessProtection
;
423 ULONG NumberOfBytesToProtect
;
425 Status
= MmCopyFromCaller(&BaseAddress
, UnsafeBaseAddress
, sizeof(PVOID
));
426 if (!NT_SUCCESS(Status
))
428 Status
= MmCopyFromCaller(&NumberOfBytesToProtect
, UnsafeNumberOfBytesToProtect
, sizeof(ULONG
));
429 if (!NT_SUCCESS(Status
))
432 /* (tMk 2004.II.5) in Microsoft SDK I read:
433 * 'if this parameter is NULL or does not point to a valid variable, the function fails'
435 if(UnsafeOldAccessProtection
== NULL
)
437 return(STATUS_INVALID_PARAMETER
);
440 Status
= ObReferenceObjectByHandle(ProcessHandle
,
441 PROCESS_VM_OPERATION
,
446 if (!NT_SUCCESS(Status
))
448 DPRINT("NtProtectVirtualMemory() = %x\n",Status
);
452 Status
= MiProtectVirtualMemory(Process
,
454 &NumberOfBytesToProtect
,
456 &OldAccessProtection
);
458 ObDereferenceObject(Process
);
460 MmCopyToCaller(UnsafeOldAccessProtection
, &OldAccessProtection
, sizeof(ULONG
));
461 MmCopyToCaller(UnsafeBaseAddress
, &BaseAddress
, sizeof(PVOID
));
462 MmCopyToCaller(UnsafeNumberOfBytesToProtect
, &NumberOfBytesToProtect
, sizeof(ULONG
));
470 * Called from ReadProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
472 * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
475 NtReadVirtualMemory(IN HANDLE ProcessHandle
,
476 IN PVOID BaseAddress
,
478 IN ULONG NumberOfBytesToRead
,
479 OUT PULONG NumberOfBytesRead OPTIONAL
)
484 PEPROCESS Process
, CurrentProcess
;
487 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
488 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle
,BaseAddress
,
489 Buffer
,NumberOfBytesToRead
);
491 Status
= ObReferenceObjectByHandle(ProcessHandle
,
497 if (!NT_SUCCESS(Status
))
502 CurrentProcess
= PsGetCurrentProcess();
504 if (Process
== CurrentProcess
)
506 memcpy(Buffer
, BaseAddress
, NumberOfBytesToRead
);
510 Mdl
= MmCreateMdl(NULL
,
512 NumberOfBytesToRead
);
515 ObDereferenceObject(Process
);
516 return(STATUS_NO_MEMORY
);
518 MmProbeAndLockPages(Mdl
,
522 KeAttachProcess(&Process
->Pcb
);
524 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
526 Status
= STATUS_SUCCESS
;
528 ProbeForRead(BaseAddress
, NumberOfBytesToRead
, 1);
529 Status
= STATUS_PARTIAL_COPY
;
530 memcpy(SystemAddress
, BaseAddress
, NumberOfBytesToRead
);
531 Status
= STATUS_SUCCESS
;
533 if(Status
!= STATUS_PARTIAL_COPY
)
534 Status
= _SEH_GetExceptionCode();
539 if (Mdl
->MappedSystemVa
!= NULL
)
541 MmUnmapLockedPages(Mdl
->MappedSystemVa
, Mdl
);
547 ObDereferenceObject(Process
);
549 if (NumberOfBytesRead
)
550 *NumberOfBytesRead
= NumberOfBytesToRead
;
555 * FUNCTION: THIS function doesn't make a sense...
556 * Called from VirtualUnlock (lib\kernel32\mem\virtual.c)
559 NtUnlockVirtualMemory(HANDLE ProcessHandle
,
561 ULONG NumberOfBytesToUnlock
,
562 PULONG NumberOfBytesUnlocked OPTIONAL
)
564 // AG [08-20-03] : I have *no* idea if this is correct, I just used the
565 // other functions as a template and made a few intelligent guesses...
571 DPRINT("NtUnlockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
572 "NumberOfBytesToUnlock %d), NumberOfBytesUnlocked %x\n",ProcessHandle
,BaseAddress
,
573 NumberOfBytesToUnlock
, NumberOfBytesUnlocked
);
575 Status
= ObReferenceObjectByHandle(ProcessHandle
,
581 if (!NT_SUCCESS(Status
))
586 Mdl
= MmCreateMdl(NULL
,
588 NumberOfBytesToUnlock
);
591 ObDereferenceObject(Process
);
592 return(STATUS_NO_MEMORY
);
595 ObDereferenceObject(Process
);
597 if (Mdl
->MappedSystemVa
!= NULL
)
599 MmUnmapLockedPages(Mdl
->MappedSystemVa
, Mdl
);
604 *NumberOfBytesUnlocked
= NumberOfBytesToUnlock
;
606 return(STATUS_SUCCESS
);
612 * Called from WriteProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
614 * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
617 NtWriteVirtualMemory(IN HANDLE ProcessHandle
,
618 IN PVOID BaseAddress
,
620 IN ULONG NumberOfBytesToWrite
,
621 OUT PULONG NumberOfBytesWritten OPTIONAL
)
627 ULONG OldProtection
= 0;
628 PVOID ProtectBaseAddress
;
629 ULONG ProtectNumberOfBytes
;
631 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
632 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle
,BaseAddress
,
633 Buffer
,NumberOfBytesToWrite
);
635 Status
= ObReferenceObjectByHandle(ProcessHandle
,
641 if (!NT_SUCCESS(Status
))
646 /* We have to make sure the target memory is writable.
648 * I am not sure if it is correct to do this in any case, but it has to be
649 * done at least in some cases because you can use WriteProcessMemory to
650 * write into the .text section of a module where memcpy() would crash.
651 * -blight (2005/01/09)
653 ProtectBaseAddress
= BaseAddress
;
654 ProtectNumberOfBytes
= NumberOfBytesToWrite
;
657 if (Process
== PsGetCurrentProcess())
659 Status
= MiProtectVirtualMemory(Process
,
661 &ProtectNumberOfBytes
,
664 if (!NT_SUCCESS(Status
))
666 ObDereferenceObject(Process
);
669 memcpy(BaseAddress
, Buffer
, NumberOfBytesToWrite
);
673 /* Create MDL describing the source buffer. */
674 Mdl
= MmCreateMdl(NULL
,
676 NumberOfBytesToWrite
);
679 ObDereferenceObject(Process
);
680 return(STATUS_NO_MEMORY
);
683 /* Make the target area writable. */
684 Status
= MiProtectVirtualMemory(Process
,
686 &ProtectNumberOfBytes
,
689 if (!NT_SUCCESS(Status
))
691 ObDereferenceObject(Process
);
697 MmProbeAndLockPages(Mdl
,
701 /* Copy memory from the mapped MDL into the target buffer. */
702 KeAttachProcess(&Process
->Pcb
);
704 SystemAddress
= MmGetSystemAddressForMdl(Mdl
);
705 memcpy(BaseAddress
, SystemAddress
, NumberOfBytesToWrite
);
710 if (Mdl
->MappedSystemVa
!= NULL
)
712 MmUnmapLockedPages(Mdl
->MappedSystemVa
, Mdl
);
718 /* Reset the protection of the target memory. */
719 Status
= MiProtectVirtualMemory(Process
,
721 &ProtectNumberOfBytes
,
724 if (!NT_SUCCESS(Status
))
726 DPRINT1("Failed to reset protection of the target memory! (Status 0x%x)\n", Status
);
727 /* FIXME: Should we bugcheck here? */
730 ObDereferenceObject(Process
);
732 if (NumberOfBytesWritten
!= NULL
)
733 MmCopyToCaller(NumberOfBytesWritten
, &NumberOfBytesToWrite
, sizeof(ULONG
));
735 return(STATUS_SUCCESS
);
744 MmGetVirtualForPhysical (
745 IN PHYSICAL_ADDRESS PhysicalAddress
753 * Called from EngSecureMem (subsys\win32k\eng\mem.c)
757 MmSecureVirtualMemory (PVOID Address
,
761 /* Only works for user space */
762 if (MmHighestUserAddress
< Address
)
774 * Called from EngUnsecureMem (subsys\win32k\eng\mem.c)
778 MmUnsecureVirtualMemory(PVOID SecureMem
)
780 if (NULL
== SecureMem
)
793 ProbeForRead (IN CONST VOID
*Address
,
797 ASSERT(Alignment
==1 || Alignment
== 2 || Alignment
== 4 || Alignment
== 8);
802 if (((ULONG_PTR
)Address
& (Alignment
- 1)) != 0)
804 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT
);
806 else if ((ULONG_PTR
)Address
+ Length
< (ULONG_PTR
)Address
||
807 (ULONG_PTR
)Address
+ Length
> (ULONG_PTR
)MmUserProbeAddress
)
809 ExRaiseStatus (STATUS_ACCESS_VIOLATION
);
818 ProbeForWrite (IN CONST VOID
*Address
,
826 ASSERT(Alignment
==1 || Alignment
== 2 || Alignment
== 4 || Alignment
== 8);
831 if (((ULONG_PTR
)Address
& (Alignment
- 1)) != 0)
833 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT
);
835 else if ((ULONG_PTR
)Address
+ Length
< (ULONG_PTR
)Address
||
836 (ULONG_PTR
)Address
+ Length
> (ULONG_PTR
)MmUserProbeAddress
)
838 ExRaiseStatus (STATUS_ACCESS_VIOLATION
);
841 /* Check for accessible pages */
842 for (i
= 0; i
< Length
; i
+= PAGE_SIZE
)
844 Ptr
= (PULONG
)(((ULONG_PTR
)Address
& ~(PAGE_SIZE
- 1)) + i
);