2004-10-28 Casper S. Hornstrup <chorns@users.sourceforge.net>
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
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.
9 *
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.
14 *
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.
18 */
19 /* $Id: virtual.c,v 1.82 2004/10/28 19:01:58 chorns Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/virtual.c
23 * PURPOSE: Implementing operations on virtual memory.
24 * PROGRAMMER: David Welch
25 */
26
27 /* INCLUDE *****************************************************************/
28
29 #include <ntoskrnl.h>
30
31 #define NDEBUG
32 #include <internal/debug.h>
33
34 /* FUNCTIONS *****************************************************************/
35
36 NTSTATUS STDCALL
37 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
38 IN PVOID BaseAddress,
39 IN ULONG NumberOfBytesToFlush,
40 OUT PULONG NumberOfBytesFlushed OPTIONAL)
41 /*
42 * FUNCTION: Flushes virtual memory to file
43 * ARGUMENTS:
44 * ProcessHandle = Points to the process that allocated the virtual
45 * memory
46 * BaseAddress = Points to the memory address
47 * NumberOfBytesToFlush = Limits the range to flush,
48 * NumberOfBytesFlushed = Actual number of bytes flushed
49 * RETURNS: Status
50 */
51 {
52 UNIMPLEMENTED;
53 return(STATUS_NOT_IMPLEMENTED);
54 }
55
56
57 NTSTATUS STDCALL
58 NtLockVirtualMemoryInternal(HANDLE ProcessHandle,
59 PVOID BaseAddress,
60 ULONG NumberOfBytesToLock,
61 PULONG NumberOfBytesLocked,
62 PObReferenceObjectByHandle pObReferenceObjectByHandle,
63 PMmCreateMdl pMmCreateMdl,
64 PObDereferenceObject pObDereferenceObject,
65 PMmProbeAndLockPages pMmProbeAndLockPages,
66 PExFreePool pExFreePool)
67 {
68 PEPROCESS Process;
69 NTSTATUS Status;
70 PMDL Mdl;
71
72 Status = pObReferenceObjectByHandle(ProcessHandle,
73 PROCESS_VM_WRITE,
74 NULL,
75 UserMode,
76 (PVOID*)(&Process),
77 NULL);
78 if (!NT_SUCCESS(Status))
79 return(Status);
80
81 Mdl = pMmCreateMdl(NULL,
82 BaseAddress,
83 NumberOfBytesToLock);
84 if (Mdl == NULL)
85 {
86 pObDereferenceObject(Process);
87 return(STATUS_NO_MEMORY);
88 }
89
90 pMmProbeAndLockPages(Mdl,
91 UserMode,
92 IoWriteAccess);
93
94 pExFreePool(Mdl);
95
96 pObDereferenceObject(Process);
97
98 *NumberOfBytesLocked = NumberOfBytesToLock;
99 return(STATUS_SUCCESS);
100 }
101
102
103 NTSTATUS STDCALL
104 NtLockVirtualMemory(HANDLE ProcessHandle,
105 PVOID BaseAddress,
106 ULONG NumberOfBytesToLock,
107 PULONG NumberOfBytesLocked)
108 {
109 DPRINT("NtLockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
110 "NumberOfBytesToLock %d, NumberOfBytesLocked %x)\n",
111 ProcessHandle,
112 BaseAddress,
113 NumberOfBytesToLock,
114 NumberOfBytesLocked);
115
116 return NtLockVirtualMemoryInternal(ProcessHandle,
117 BaseAddress,
118 NumberOfBytesToLock,
119 NumberOfBytesLocked,
120 ObReferenceObjectByHandle,
121 MmCreateMdl,
122 ObfDereferenceObject,
123 MmProbeAndLockPages,
124 ExFreePool);
125 }
126
127
128 /* (tMk 2004.II.4)
129 * FUNCTION:
130 * Called from VirtualQueryEx (lib\kernel32\mem\virtual.c)
131 *
132 */
133 NTSTATUS STDCALL
134 NtQueryVirtualMemory (IN HANDLE ProcessHandle,
135 IN PVOID Address,
136 IN CINT VirtualMemoryInformationClass,
137 OUT PVOID VirtualMemoryInformation,
138 IN ULONG Length,
139 OUT PULONG UnsafeResultLength)
140 {
141 NTSTATUS Status;
142 PEPROCESS Process;
143 MEMORY_AREA* MemoryArea;
144 ULONG ResultLength = 0;
145 PMADDRESS_SPACE AddressSpace;
146 KPROCESSOR_MODE PrevMode;
147 union
148 {
149 MEMORY_BASIC_INFORMATION BasicInfo;
150 }
151 VirtualMemoryInfo;
152
153 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
154 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
155 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
156 VirtualMemoryInformationClass,VirtualMemoryInformation,
157 Length,ResultLength);
158
159 PrevMode = ExGetPreviousMode();
160
161 if (PrevMode == UserMode && Address >= (PVOID)KERNEL_BASE)
162 {
163 return STATUS_INVALID_PARAMETER;
164 }
165
166 if (Address < (PVOID)KERNEL_BASE)
167 {
168 Status = ObReferenceObjectByHandle(ProcessHandle,
169 PROCESS_QUERY_INFORMATION,
170 NULL,
171 UserMode,
172 (PVOID*)(&Process),
173 NULL);
174
175 if (!NT_SUCCESS(Status))
176 {
177 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
178 return(Status);
179 }
180 AddressSpace = &Process->AddressSpace;
181 }
182 else
183 {
184 AddressSpace = MmGetKernelAddressSpace();
185 }
186 MmLockAddressSpace(AddressSpace);
187 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
188 Address);
189 switch(VirtualMemoryInformationClass)
190 {
191 case MemoryBasicInformation:
192 {
193 PMEMORY_BASIC_INFORMATION Info = &VirtualMemoryInfo.BasicInfo;
194 if (Length != sizeof(MEMORY_BASIC_INFORMATION))
195 {
196 MmUnlockAddressSpace(AddressSpace);
197 ObDereferenceObject(Process);
198 return(STATUS_INFO_LENGTH_MISMATCH);
199 }
200
201 if (MemoryArea == NULL)
202 {
203 Info->Type = 0;
204 Info->State = MEM_FREE;
205 Info->Protect = PAGE_NOACCESS;
206 Info->AllocationProtect = 0;
207 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
208 Info->AllocationBase = NULL;
209 Info->RegionSize = MmFindGapAtAddress(AddressSpace, Info->BaseAddress);
210 Status = STATUS_SUCCESS;
211 ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
212 }
213 else
214 {
215 switch(MemoryArea->Type)
216 {
217 case MEMORY_AREA_VIRTUAL_MEMORY:
218 Status = MmQueryAnonMem(MemoryArea, Address, Info,
219 &ResultLength);
220 break;
221 case MEMORY_AREA_SECTION_VIEW:
222 Status = MmQuerySectionView(MemoryArea, Address, Info,
223 &ResultLength);
224 break;
225 case MEMORY_AREA_NO_ACCESS:
226 Info->Type = 0;
227 Info->State = MEM_FREE;
228 Info->Protect = MemoryArea->Attributes;
229 Info->AllocationProtect = MemoryArea->Attributes;
230 Info->BaseAddress = MemoryArea->BaseAddress;
231 Info->AllocationBase = MemoryArea->BaseAddress;
232 Info->RegionSize = MemoryArea->Length;
233 Status = STATUS_SUCCESS;
234 ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
235 break;
236 case MEMORY_AREA_SHARED_DATA:
237 Info->Type = 0;
238 Info->State = MEM_COMMIT;
239 Info->Protect = MemoryArea->Attributes;
240 Info->AllocationProtect = MemoryArea->Attributes;
241 Info->BaseAddress = MemoryArea->BaseAddress;
242 Info->AllocationBase = MemoryArea->BaseAddress;
243 Info->RegionSize = MemoryArea->Length;
244 Status = STATUS_SUCCESS;
245 ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
246 break;
247 default:
248 Status = STATUS_UNSUCCESSFUL;
249 ResultLength = 0;
250 }
251 }
252 break;
253 }
254
255 default:
256 {
257 Status = STATUS_INVALID_INFO_CLASS;
258 ResultLength = 0;
259 break;
260 }
261 }
262
263 MmUnlockAddressSpace(AddressSpace);
264 if (Address < (PVOID)KERNEL_BASE)
265 {
266 ObDereferenceObject(Process);
267 }
268
269 if (NT_SUCCESS(Status) && ResultLength > 0)
270 {
271 Status = MmCopyToCaller(VirtualMemoryInformation, &VirtualMemoryInfo, ResultLength);
272 if (!NT_SUCCESS(Status))
273 {
274 ResultLength = 0;
275 }
276 }
277
278 if (UnsafeResultLength != NULL)
279 {
280 MmCopyToCaller(UnsafeResultLength, &ResultLength, sizeof(ULONG));
281 }
282 return(Status);
283 }
284
285
286 /* (tMk 2004.II.5)
287 * FUNCTION:
288 * Called from VirtualProtectEx (lib\kernel32\mem\virtual.c)
289 *
290 */
291 NTSTATUS STDCALL
292 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
293 IN PVOID *UnsafeBaseAddress,
294 IN ULONG *UnsafeNumberOfBytesToProtect,
295 IN ULONG NewAccessProtection,
296 OUT PULONG UnsafeOldAccessProtection)
297 {
298 PMEMORY_AREA MemoryArea;
299 PEPROCESS Process;
300 NTSTATUS Status;
301 PMADDRESS_SPACE AddressSpace;
302 ULONG OldAccessProtection;
303 PVOID BaseAddress;
304 ULONG NumberOfBytesToProtect;
305
306 Status = MmCopyFromCaller(&BaseAddress, UnsafeBaseAddress, sizeof(PVOID));
307 if (!NT_SUCCESS(Status))
308 return Status;
309 Status = MmCopyFromCaller(&NumberOfBytesToProtect, UnsafeNumberOfBytesToProtect, sizeof(ULONG));
310 if (!NT_SUCCESS(Status))
311 return Status;
312
313 // (tMk 2004.II.5) in Microsoft SDK I read:
314 // 'if this parameter is NULL or does not point to a valid variable, the function fails'
315 if(UnsafeOldAccessProtection == NULL)
316 {
317 return(STATUS_INVALID_PARAMETER);
318 }
319
320 NumberOfBytesToProtect =
321 PAGE_ROUND_UP(BaseAddress + NumberOfBytesToProtect) -
322 PAGE_ROUND_DOWN(BaseAddress);
323 BaseAddress = (PVOID)PAGE_ROUND_DOWN(BaseAddress);
324
325 Status = ObReferenceObjectByHandle(ProcessHandle,
326 PROCESS_VM_OPERATION,
327 PsProcessType,
328 UserMode,
329 (PVOID*)(&Process),
330 NULL);
331 if (!NT_SUCCESS(Status))
332 {
333 DPRINT("NtProtectVirtualMemory() = %x\n",Status);
334 return(Status);
335 }
336
337 AddressSpace = &Process->AddressSpace;
338
339 MmLockAddressSpace(AddressSpace);
340 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
341 BaseAddress);
342 if (MemoryArea == NULL)
343 {
344 MmUnlockAddressSpace(AddressSpace);
345 ObDereferenceObject(Process);
346 return(STATUS_UNSUCCESSFUL);
347 }
348
349 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
350 {
351 Status = MmProtectAnonMem(AddressSpace, MemoryArea, BaseAddress,
352 NumberOfBytesToProtect, NewAccessProtection,
353 &OldAccessProtection);
354 }
355 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
356 {
357 Status = MmProtectSectionView(AddressSpace, MemoryArea, BaseAddress,
358 NumberOfBytesToProtect,
359 NewAccessProtection,
360 &OldAccessProtection);
361 }
362
363 MmUnlockAddressSpace(AddressSpace);
364 ObDereferenceObject(Process);
365
366 MmCopyToCaller(UnsafeOldAccessProtection, &OldAccessProtection, sizeof(ULONG));
367 MmCopyToCaller(UnsafeBaseAddress, &BaseAddress, sizeof(PVOID));
368 MmCopyToCaller(UnsafeNumberOfBytesToProtect, &NumberOfBytesToProtect, sizeof(ULONG));
369
370 return(Status);
371 }
372
373
374 /* (tMk 2004.II.05)
375 * FUNCTION:
376 * Called from ReadProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
377 *
378 * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
379 */
380 NTSTATUS STDCALL
381 NtReadVirtualMemory(IN HANDLE ProcessHandle,
382 IN PVOID BaseAddress,
383 OUT PVOID Buffer,
384 IN ULONG NumberOfBytesToRead,
385 OUT PULONG NumberOfBytesRead OPTIONAL)
386 {
387 NTSTATUS Status;
388 PMDL Mdl;
389 PVOID SystemAddress;
390 PEPROCESS Process, CurrentProcess;
391
392
393 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
394 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress,
395 Buffer,NumberOfBytesToRead);
396
397 Status = ObReferenceObjectByHandle(ProcessHandle,
398 PROCESS_VM_WRITE,
399 NULL,
400 UserMode,
401 (PVOID*)(&Process),
402 NULL);
403 if (!NT_SUCCESS(Status))
404 {
405 return(Status);
406 }
407
408 CurrentProcess = PsGetCurrentProcess();
409
410 if (Process == CurrentProcess)
411 {
412 memcpy(Buffer, BaseAddress, NumberOfBytesToRead);
413 }
414 else
415 {
416 Mdl = MmCreateMdl(NULL,
417 Buffer,
418 NumberOfBytesToRead);
419 if(Mdl == NULL)
420 {
421 ObDereferenceObject(Process);
422 return(STATUS_NO_MEMORY);
423 }
424 MmProbeAndLockPages(Mdl,
425 UserMode,
426 IoWriteAccess);
427
428 KeAttachProcess(Process);
429
430 SystemAddress = MmGetSystemAddressForMdl(Mdl);
431 memcpy(SystemAddress, BaseAddress, NumberOfBytesToRead);
432
433 KeDetachProcess();
434
435 if (Mdl->MappedSystemVa != NULL)
436 {
437 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
438 }
439 MmUnlockPages(Mdl);
440 ExFreePool(Mdl);
441 }
442
443 ObDereferenceObject(Process);
444
445 if (NumberOfBytesRead)
446 *NumberOfBytesRead = NumberOfBytesToRead;
447 return(STATUS_SUCCESS);
448 }
449
450 /* (tMk 2004.II.05)
451 * FUNCTION: THIS function doesn't make a sense...
452 * Called from VirtualUnlock (lib\kernel32\mem\virtual.c)
453 */
454 NTSTATUS STDCALL
455 NtUnlockVirtualMemory(HANDLE ProcessHandle,
456 PVOID BaseAddress,
457 ULONG NumberOfBytesToUnlock,
458 PULONG NumberOfBytesUnlocked OPTIONAL)
459 {
460 // AG [08-20-03] : I have *no* idea if this is correct, I just used the
461 // other functions as a template and made a few intelligent guesses...
462
463 NTSTATUS Status;
464 PMDL Mdl;
465 PEPROCESS Process;
466
467 DPRINT("NtUnlockVirtualMemory(ProcessHandle %x, BaseAddress %x, "
468 "NumberOfBytesToUnlock %d), NumberOfBytesUnlocked %x\n",ProcessHandle,BaseAddress,
469 NumberOfBytesToUnlock, NumberOfBytesUnlocked);
470
471 Status = ObReferenceObjectByHandle(ProcessHandle,
472 PROCESS_VM_WRITE,
473 NULL,
474 UserMode,
475 (PVOID*)(&Process),
476 NULL);
477 if (!NT_SUCCESS(Status))
478 {
479 return(Status);
480 }
481
482 Mdl = MmCreateMdl(NULL,
483 BaseAddress,
484 NumberOfBytesToUnlock);
485 if(Mdl == NULL)
486 {
487 ObDereferenceObject(Process);
488 return(STATUS_NO_MEMORY);
489 }
490
491 ObDereferenceObject(Process);
492
493 if (Mdl->MappedSystemVa != NULL)
494 {
495 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
496 }
497 MmUnlockPages(Mdl);
498 ExFreePool(Mdl);
499
500 *NumberOfBytesUnlocked = NumberOfBytesToUnlock;
501
502 return(STATUS_SUCCESS);
503 }
504
505
506 /* (tMk 2004.II.05)
507 * FUNCTION:
508 * Called from WriteProcessMemory (lib\kernel32\mem\procmem.c) and KlInitPeb(lib\kernel32\process\create.c)
509 *
510 * NOTE: This function will be correct if MmProbeAndLockPages() would be fully IMPLEMENTED.
511 */
512 NTSTATUS STDCALL
513 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
514 IN PVOID BaseAddress,
515 IN PVOID Buffer,
516 IN ULONG NumberOfBytesToWrite,
517 OUT PULONG NumberOfBytesWritten)
518 {
519 NTSTATUS Status;
520 PMDL Mdl;
521 PVOID SystemAddress;
522 PEPROCESS Process;
523
524 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
525 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
526 Buffer,NumberOfBytesToWrite);
527
528 Status = ObReferenceObjectByHandle(ProcessHandle,
529 PROCESS_VM_WRITE,
530 NULL,
531 UserMode,
532 (PVOID*)(&Process),
533 NULL);
534 if (!NT_SUCCESS(Status))
535 {
536 return(Status);
537 }
538
539 if (Process == PsGetCurrentProcess())
540 {
541 memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
542 }
543 else
544 {
545 Mdl = MmCreateMdl(NULL,
546 Buffer,
547 NumberOfBytesToWrite);
548 MmProbeAndLockPages(Mdl,
549 UserMode,
550 IoReadAccess);
551 if(Mdl == NULL)
552 {
553 ObDereferenceObject(Process);
554 return(STATUS_NO_MEMORY);
555 }
556 KeAttachProcess(Process);
557
558 SystemAddress = MmGetSystemAddressForMdl(Mdl);
559 memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
560
561 KeDetachProcess();
562
563 if (Mdl->MappedSystemVa != NULL)
564 {
565 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
566 }
567 MmUnlockPages(Mdl);
568 ExFreePool(Mdl);
569 }
570
571 ObDereferenceObject(Process);
572
573 *NumberOfBytesWritten = NumberOfBytesToWrite;
574
575 return(STATUS_SUCCESS);
576 }
577
578 /*
579 * @unimplemented
580 */
581
582 PVOID
583 STDCALL
584 MmGetVirtualForPhysical (
585 IN PHYSICAL_ADDRESS PhysicalAddress
586 )
587 {
588 UNIMPLEMENTED;
589 return 0;
590 }
591
592 /* FUNCTION:
593 * Called from EngSecureMem (subsys\win32k\eng\mem.c)
594 * @unimplemented
595 */
596 PVOID STDCALL
597 MmSecureVirtualMemory (PVOID Address,
598 SIZE_T Length,
599 ULONG Mode)
600 {
601 /* Only works for user space */
602 if (MmHighestUserAddress < Address)
603 {
604 return NULL;
605 }
606
607 UNIMPLEMENTED;
608
609 return 0;
610 }
611
612
613 /* FUNCTION:
614 * Called from EngUnsecureMem (subsys\win32k\eng\mem.c)
615 * @unimplemented
616 */
617 VOID STDCALL
618 MmUnsecureVirtualMemory(PVOID SecureMem)
619 {
620 if (NULL == SecureMem)
621 {
622 return;
623 }
624
625 UNIMPLEMENTED;
626 }
627
628
629 /*
630 * @implemented
631 */
632 VOID STDCALL
633 ProbeForRead (IN PVOID Address,
634 IN ULONG Length,
635 IN ULONG Alignment)
636 {
637 ASSERT(Alignment ==1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
638
639 if (Length == 0)
640 return;
641
642 if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
643 {
644 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
645 }
646 else if ((ULONG_PTR)Address + Length < (ULONG_PTR)Address ||
647 (ULONG_PTR)Address + Length > (ULONG_PTR)MmUserProbeAddress)
648 {
649 ExRaiseStatus (STATUS_ACCESS_VIOLATION);
650 }
651 }
652
653
654 /*
655 * @implemented
656 */
657 VOID STDCALL
658 ProbeForWrite (IN PVOID Address,
659 IN ULONG Length,
660 IN ULONG Alignment)
661 {
662 PULONG Ptr;
663 ULONG x;
664 ULONG i;
665
666 ASSERT(Alignment ==1 || Alignment == 2 || Alignment == 4 || Alignment == 8);
667
668 if (Length == 0)
669 return;
670
671 if (((ULONG_PTR)Address & (Alignment - 1)) != 0)
672 {
673 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
674 }
675 else if ((ULONG_PTR)Address + Length < (ULONG_PTR)Address ||
676 (ULONG_PTR)Address + Length > (ULONG_PTR)MmUserProbeAddress)
677 {
678 ExRaiseStatus (STATUS_ACCESS_VIOLATION);
679 }
680
681 /* Check for accessible pages */
682 for (i = 0; i < Length; i += PAGE_SIZE)
683 {
684 Ptr = (PULONG)(((ULONG_PTR)Address & ~(PAGE_SIZE - 1)) + i);
685 x = *Ptr;
686 *Ptr = x;
687 }
688 }
689
690 /* EOF */