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