Sync to trunk head (r42241)
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/virtual.c
5 * PURPOSE: Implementing operations on virtual memory.
6 *
7 * PROGRAMMERS: David Welch
8 */
9
10 /* INCLUDE ********************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define MI_MAPPED_COPY_PAGES 16
17 #define MI_POOL_COPY_BYTES 512
18 #define MI_MAX_TRANSFER_SIZE 64 * 1024
19 #define TAG_VM TAG('V', 'm', 'R', 'w')
20
21 /* PRIVATE FUNCTIONS **********************************************************/
22
23 LONG
24 MiGetExceptionInfo(EXCEPTION_POINTERS *ExceptionInfo, BOOLEAN * HaveBadAddress, ULONG_PTR * BadAddress)
25 {
26 PEXCEPTION_RECORD ExceptionRecord;
27 PAGED_CODE();
28
29 /* Assume default */
30 *HaveBadAddress = FALSE;
31
32 /* Get the exception record */
33 ExceptionRecord = ExceptionInfo->ExceptionRecord;
34
35 /* Look at the exception code */
36 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
37 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
38 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
39 {
40 /* We can tell the address if we have more than one parameter */
41 if (ExceptionRecord->NumberParameters > 1)
42 {
43 /* Return the address */
44 *HaveBadAddress = TRUE;
45 *BadAddress = ExceptionRecord->ExceptionInformation[1];
46 }
47 }
48
49 /* Continue executing the next handler */
50 return EXCEPTION_EXECUTE_HANDLER;
51 }
52
53 NTSTATUS
54 NTAPI
55 MiDoMappedCopy(IN PEPROCESS SourceProcess,
56 IN PVOID SourceAddress,
57 IN PEPROCESS TargetProcess,
58 OUT PVOID TargetAddress,
59 IN SIZE_T BufferSize,
60 IN KPROCESSOR_MODE PreviousMode,
61 OUT PSIZE_T ReturnSize)
62 {
63 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
64 PMDL Mdl = (PMDL)MdlBuffer;
65 SIZE_T TotalSize, CurrentSize, RemainingSize;
66 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
67 volatile BOOLEAN PagesLocked;
68 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
69 volatile PVOID MdlAddress;
70 KAPC_STATE ApcState;
71 BOOLEAN HaveBadAddress;
72 ULONG_PTR BadAddress;
73 NTSTATUS Status = STATUS_SUCCESS;
74 PAGED_CODE();
75
76 /* Calculate the maximum amount of data to move */
77 TotalSize = (MI_MAPPED_COPY_PAGES - 2) * PAGE_SIZE;
78 if (BufferSize <= TotalSize) TotalSize = BufferSize;
79 CurrentSize = TotalSize;
80 RemainingSize = BufferSize;
81
82 /* Loop as long as there is still data */
83 while (RemainingSize > 0)
84 {
85 /* Check if this transfer will finish everything off */
86 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
87
88 /* Attach to the source address space */
89 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
90
91 /* Reset state for this pass */
92 MdlAddress = NULL;
93 PagesLocked = FALSE;
94 FailedInMoving = FALSE;
95 ASSERT(FailedInProbe == FALSE);
96
97 /* Protect user-mode copy */
98 _SEH2_TRY
99 {
100 /* If this is our first time, probe the buffer */
101 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
102 {
103 /* Catch a failure here */
104 FailedInProbe = TRUE;
105
106 /* Do the probe */
107 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
108
109 /* Passed */
110 FailedInProbe = FALSE;
111 }
112
113 /* Initialize and probe and lock the MDL */
114 MmInitializeMdl (Mdl, CurrentAddress, CurrentSize);
115 MmProbeAndLockPages (Mdl, PreviousMode, IoReadAccess);
116 PagesLocked = TRUE;
117
118 /* Now map the pages */
119 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
120 KernelMode,
121 MmCached,
122 NULL,
123 FALSE,
124 HighPagePriority);
125 if (!MdlAddress)
126 {
127 /* Use our SEH handler to pick this up */
128 FailedInMapping = TRUE;
129 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
130 }
131
132 /* Now let go of the source and grab to the target process */
133 KeUnstackDetachProcess(&ApcState);
134 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
135
136 /* Check if this is our first time through */
137 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
138 {
139 /* Catch a failure here */
140 FailedInProbe = TRUE;
141
142 /* Do the probe */
143 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
144
145 /* Passed */
146 FailedInProbe = FALSE;
147 }
148
149 /* Now do the actual move */
150 FailedInMoving = TRUE;
151 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
152 }
153 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress))
154 {
155 /* Detach from whoever we may be attached to */
156 KeUnstackDetachProcess(&ApcState);
157
158 /* Check if we had mapped the pages */
159 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
160
161 /* Check if we had locked the pages */
162 if (PagesLocked) MmUnlockPages(Mdl);
163
164 /* Check if we failed during the probe or mapping */
165 if ((FailedInProbe) || (FailedInMapping))
166 {
167 /* Exit */
168 Status = _SEH2_GetExceptionCode();
169 _SEH2_YIELD(return Status);
170 }
171
172 /* Otherwise, we failed probably during the move */
173 *ReturnSize = BufferSize - RemainingSize;
174 if (FailedInMoving)
175 {
176 /* Check if we know exactly where we stopped copying */
177 if (HaveBadAddress)
178 {
179 /* Return the exact number of bytes copied */
180 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
181 }
182 }
183
184 /* Return partial copy */
185 Status = STATUS_PARTIAL_COPY;
186 }
187 _SEH2_END;
188
189 /* Check for SEH status */
190 if (Status != STATUS_SUCCESS) return Status;
191
192 /* Detach from target */
193 KeUnstackDetachProcess(&ApcState);
194
195 /* Unmap and unlock */
196 MmUnmapLockedPages(MdlAddress, Mdl);
197 MmUnlockPages(Mdl);
198
199 /* Update location and size */
200 RemainingSize -= CurrentSize;
201 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
202 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
203 }
204
205 /* All bytes read */
206 *ReturnSize = BufferSize;
207 return STATUS_SUCCESS;
208 }
209
210 NTSTATUS
211 NTAPI
212 MiDoPoolCopy(IN PEPROCESS SourceProcess,
213 IN PVOID SourceAddress,
214 IN PEPROCESS TargetProcess,
215 OUT PVOID TargetAddress,
216 IN SIZE_T BufferSize,
217 IN KPROCESSOR_MODE PreviousMode,
218 OUT PSIZE_T ReturnSize)
219 {
220 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
221 SIZE_T TotalSize, CurrentSize, RemainingSize;
222 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
223 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
224 PVOID PoolAddress;
225 KAPC_STATE ApcState;
226 BOOLEAN HaveBadAddress;
227 ULONG_PTR BadAddress;
228 NTSTATUS Status = STATUS_SUCCESS;
229 PAGED_CODE();
230
231 /* Calculate the maximum amount of data to move */
232 TotalSize = MI_MAX_TRANSFER_SIZE;
233 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
234 CurrentSize = TotalSize;
235 RemainingSize = BufferSize;
236
237 /* Check if we can use the stack */
238 if (BufferSize <= MI_POOL_COPY_BYTES)
239 {
240 /* Use it */
241 PoolAddress = (PVOID)StackBuffer;
242 }
243 else
244 {
245 /* Allocate pool */
246 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, TAG_VM);
247 if (!PoolAddress) ASSERT(FALSE);
248 HavePoolAddress = TRUE;
249 }
250
251 /* Loop as long as there is still data */
252 while (RemainingSize > 0)
253 {
254 /* Check if this transfer will finish everything off */
255 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
256
257 /* Attach to the source address space */
258 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
259
260 /* Reset state for this pass */
261 FailedInMoving = FALSE;
262 ASSERT(FailedInProbe == FALSE);
263
264 /* Protect user-mode copy */
265 _SEH2_TRY
266 {
267 /* If this is our first time, probe the buffer */
268 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
269 {
270 /* Catch a failure here */
271 FailedInProbe = TRUE;
272
273 /* Do the probe */
274 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
275
276 /* Passed */
277 FailedInProbe = FALSE;
278 }
279
280 /* Do the copy */
281 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
282
283 /* Now let go of the source and grab to the target process */
284 KeUnstackDetachProcess(&ApcState);
285 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
286
287 /* Check if this is our first time through */
288 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
289 {
290 /* Catch a failure here */
291 FailedInProbe = TRUE;
292
293 /* Do the probe */
294 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
295
296 /* Passed */
297 FailedInProbe = FALSE;
298 }
299
300 /* Now do the actual move */
301 FailedInMoving = TRUE;
302 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
303 }
304 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), &HaveBadAddress, &BadAddress))
305 {
306 /* Detach from whoever we may be attached to */
307 KeUnstackDetachProcess(&ApcState);
308
309 /* Check if we had allocated pool */
310 if (HavePoolAddress) ExFreePool(PoolAddress);
311
312 /* Check if we failed during the probe */
313 if (FailedInProbe)
314 {
315 /* Exit */
316 Status = _SEH2_GetExceptionCode();
317 _SEH2_YIELD(return Status);
318 }
319
320 /* Otherwise, we failed probably during the move */
321 *ReturnSize = BufferSize - RemainingSize;
322 if (FailedInMoving)
323 {
324 /* Check if we know exactly where we stopped copying */
325 if (HaveBadAddress)
326 {
327 /* Return the exact number of bytes copied */
328 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
329 }
330 }
331
332 /* Return partial copy */
333 Status = STATUS_PARTIAL_COPY;
334 }
335 _SEH2_END;
336
337 /* Check for SEH status */
338 if (Status != STATUS_SUCCESS) return Status;
339
340 /* Detach from target */
341 KeUnstackDetachProcess(&ApcState);
342
343 /* Update location and size */
344 RemainingSize -= CurrentSize;
345 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
346 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
347 }
348
349 /* Check if we had allocated pool */
350 if (HavePoolAddress) ExFreePool(PoolAddress);
351
352 /* All bytes read */
353 *ReturnSize = BufferSize;
354 return STATUS_SUCCESS;
355 }
356
357 NTSTATUS
358 NTAPI
359 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
360 IN PVOID SourceAddress,
361 IN PEPROCESS TargetProcess,
362 OUT PVOID TargetAddress,
363 IN SIZE_T BufferSize,
364 IN KPROCESSOR_MODE PreviousMode,
365 OUT PSIZE_T ReturnSize)
366 {
367 NTSTATUS Status;
368 PEPROCESS Process = SourceProcess;
369
370 /* Don't accept zero-sized buffers */
371 if (!BufferSize) return STATUS_SUCCESS;
372
373 /* If we are copying from ourselves, lock the target instead */
374 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
375
376 /* Acquire rundown protection */
377 if (!ExAcquireRundownProtection(&Process->RundownProtect))
378 {
379 /* Fail */
380 return STATUS_PROCESS_IS_TERMINATING;
381 }
382
383 /* See if we should use the pool copy */
384 if (BufferSize > MI_POOL_COPY_BYTES)
385 {
386 /* Use MDL-copy */
387 Status = MiDoMappedCopy(SourceProcess,
388 SourceAddress,
389 TargetProcess,
390 TargetAddress,
391 BufferSize,
392 PreviousMode,
393 ReturnSize);
394 }
395 else
396 {
397 /* Do pool copy */
398 Status = MiDoPoolCopy(SourceProcess,
399 SourceAddress,
400 TargetProcess,
401 TargetAddress,
402 BufferSize,
403 PreviousMode,
404 ReturnSize);
405 }
406
407 /* Release the lock */
408 ExReleaseRundownProtection(&Process->RundownProtect);
409 return Status;
410 }
411
412 NTSTATUS FASTCALL
413 MiQueryVirtualMemory(IN HANDLE ProcessHandle,
414 IN PVOID Address,
415 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
416 OUT PVOID VirtualMemoryInformation,
417 IN SIZE_T Length,
418 OUT PSIZE_T ResultLength)
419 {
420 NTSTATUS Status;
421 PEPROCESS Process;
422 MEMORY_AREA* MemoryArea;
423 PMMSUPPORT AddressSpace;
424
425 Status = ObReferenceObjectByHandle(ProcessHandle,
426 PROCESS_QUERY_INFORMATION,
427 NULL,
428 UserMode,
429 (PVOID*)(&Process),
430 NULL);
431
432 if (!NT_SUCCESS(Status))
433 {
434 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
435 return(Status);
436 }
437
438 AddressSpace = &Process->Vm;
439
440 MmLockAddressSpace(AddressSpace);
441 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
442 switch(VirtualMemoryInformationClass)
443 {
444 case MemoryBasicInformation:
445 {
446 PMEMORY_BASIC_INFORMATION Info =
447 (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
448 if (Length != sizeof(MEMORY_BASIC_INFORMATION))
449 {
450 MmUnlockAddressSpace(AddressSpace);
451 ObDereferenceObject(Process);
452 return(STATUS_INFO_LENGTH_MISMATCH);
453 }
454
455 if (MemoryArea == NULL)
456 {
457 Info->Type = 0;
458 Info->State = MEM_FREE;
459 Info->Protect = PAGE_NOACCESS;
460 Info->AllocationProtect = 0;
461 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
462 Info->AllocationBase = NULL;
463 Info->RegionSize = MmFindGapAtAddress(AddressSpace, Info->BaseAddress);
464 Status = STATUS_SUCCESS;
465 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
466 }
467 else
468 {
469 switch(MemoryArea->Type)
470 {
471 case MEMORY_AREA_VIRTUAL_MEMORY:
472 case MEMORY_AREA_PEB_OR_TEB:
473 Status = MmQueryAnonMem(MemoryArea, Address, Info,
474 ResultLength);
475 break;
476
477 case MEMORY_AREA_SECTION_VIEW:
478 Status = MmQuerySectionView(MemoryArea, Address, Info,
479 ResultLength);
480 break;
481
482 case MEMORY_AREA_NO_ACCESS:
483 Info->Type = MEM_PRIVATE;
484 Info->State = MEM_RESERVE;
485 Info->Protect = MemoryArea->Protect;
486 Info->AllocationProtect = MemoryArea->Protect;
487 Info->BaseAddress = MemoryArea->StartingAddress;
488 Info->AllocationBase = MemoryArea->StartingAddress;
489 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
490 (ULONG_PTR)MemoryArea->StartingAddress;
491 Status = STATUS_SUCCESS;
492 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
493 break;
494
495 case MEMORY_AREA_SHARED_DATA:
496 Info->Type = MEM_PRIVATE;
497 Info->State = MEM_COMMIT;
498 Info->Protect = MemoryArea->Protect;
499 Info->AllocationProtect = MemoryArea->Protect;
500 Info->BaseAddress = MemoryArea->StartingAddress;
501 Info->AllocationBase = MemoryArea->StartingAddress;
502 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
503 (ULONG_PTR)MemoryArea->StartingAddress;
504 Status = STATUS_SUCCESS;
505 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
506 break;
507
508 case MEMORY_AREA_SYSTEM:
509 Info->Type = 0;
510 Info->State = MEM_COMMIT;
511 Info->Protect = MemoryArea->Protect;
512 Info->AllocationProtect = MemoryArea->Protect;
513 Info->BaseAddress = MemoryArea->StartingAddress;
514 Info->AllocationBase = MemoryArea->StartingAddress;
515 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
516 (ULONG_PTR)MemoryArea->StartingAddress;
517 Status = STATUS_SUCCESS;
518 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
519 break;
520
521 case MEMORY_AREA_KERNEL_STACK:
522 Info->Type = 0;
523 Info->State = MEM_COMMIT;
524 Info->Protect = MemoryArea->Protect;
525 Info->AllocationProtect = MemoryArea->Protect;
526 Info->BaseAddress = MemoryArea->StartingAddress;
527 Info->AllocationBase = MemoryArea->StartingAddress;
528 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
529 (ULONG_PTR)MemoryArea->StartingAddress;
530 Status = STATUS_SUCCESS;
531 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
532 break;
533
534 case MEMORY_AREA_PAGED_POOL:
535 Info->Type = 0;
536 Info->State = MEM_COMMIT;
537 Info->Protect = MemoryArea->Protect;
538 Info->AllocationProtect = MemoryArea->Protect;
539 Info->BaseAddress = MemoryArea->StartingAddress;
540 Info->AllocationBase = MemoryArea->StartingAddress;
541 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
542 (ULONG_PTR)MemoryArea->StartingAddress;
543 Status = STATUS_SUCCESS;
544 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
545 break;
546
547 default:
548 DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type);
549 Status = STATUS_UNSUCCESSFUL;
550 *ResultLength = 0;
551 }
552 }
553 break;
554 }
555
556 default:
557 {
558 DPRINT1("Unsupported or unimplemented class: %lx\n", VirtualMemoryInformationClass);
559 Status = STATUS_INVALID_INFO_CLASS;
560 *ResultLength = 0;
561 break;
562 }
563 }
564
565 MmUnlockAddressSpace(AddressSpace);
566 ObDereferenceObject(Process);
567
568 return Status;
569 }
570
571 NTSTATUS NTAPI
572 MiProtectVirtualMemory(IN PEPROCESS Process,
573 IN OUT PVOID *BaseAddress,
574 IN OUT PSIZE_T NumberOfBytesToProtect,
575 IN ULONG NewAccessProtection,
576 OUT PULONG OldAccessProtection OPTIONAL)
577 {
578 PMEMORY_AREA MemoryArea;
579 PMMSUPPORT AddressSpace;
580 ULONG OldAccessProtection_;
581 NTSTATUS Status;
582
583 *NumberOfBytesToProtect =
584 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
585 PAGE_ROUND_DOWN(*BaseAddress);
586 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
587
588 AddressSpace = &Process->Vm;
589
590 MmLockAddressSpace(AddressSpace);
591 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
592 if (MemoryArea == NULL)
593 {
594 MmUnlockAddressSpace(AddressSpace);
595 return STATUS_UNSUCCESSFUL;
596 }
597
598 if (OldAccessProtection == NULL)
599 OldAccessProtection = &OldAccessProtection_;
600
601 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
602 {
603 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
604 *NumberOfBytesToProtect, NewAccessProtection,
605 OldAccessProtection);
606 }
607 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
608 {
609 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
610 *NumberOfBytesToProtect,
611 NewAccessProtection,
612 OldAccessProtection);
613 }
614 else
615 {
616 /* FIXME: Should we return failure or success in this case? */
617 Status = STATUS_CONFLICTING_ADDRESSES;
618 }
619
620 MmUnlockAddressSpace(AddressSpace);
621
622 return Status;
623 }
624
625 PVOID
626 NTAPI
627 MiMapLockedPagesInUserSpace(IN PMDL Mdl,
628 IN PVOID BaseVa,
629 IN MEMORY_CACHING_TYPE CacheType,
630 IN PVOID BaseAddress)
631 {
632 PVOID Base;
633 PPFN_NUMBER MdlPages;
634 ULONG PageCount;
635 PEPROCESS CurrentProcess;
636 NTSTATUS Status;
637 ULONG Protect;
638 MEMORY_AREA *Result;
639 LARGE_INTEGER BoundaryAddressMultiple;
640
641 /* Calculate the number of pages required. */
642 MdlPages = (PPFN_NUMBER)(Mdl + 1);
643 PageCount = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
644
645 /* Set default page protection */
646 Protect = PAGE_READWRITE;
647 if (CacheType == MmNonCached) Protect |= PAGE_NOCACHE;
648
649 BoundaryAddressMultiple.QuadPart = 0;
650 Base = BaseAddress;
651
652 CurrentProcess = PsGetCurrentProcess();
653
654 MmLockAddressSpace(&CurrentProcess->Vm);
655 Status = MmCreateMemoryArea(&CurrentProcess->Vm,
656 MEMORY_AREA_MDL_MAPPING,
657 &Base,
658 PageCount * PAGE_SIZE,
659 Protect,
660 &Result,
661 (Base != NULL),
662 0,
663 BoundaryAddressMultiple);
664 MmUnlockAddressSpace(&CurrentProcess->Vm);
665 if (!NT_SUCCESS(Status))
666 {
667 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL)
668 {
669 return NULL;
670 }
671
672 /* Throw exception */
673 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
674 ASSERT(0);
675 }
676
677 /* Set the virtual mappings for the MDL pages. */
678 if (Mdl->MdlFlags & MDL_IO_SPACE)
679 {
680 /* Map the pages */
681 Status = MmCreateVirtualMappingUnsafe(CurrentProcess,
682 Base,
683 Protect,
684 MdlPages,
685 PageCount);
686 }
687 else
688 {
689 /* Map the pages */
690 Status = MmCreateVirtualMapping(CurrentProcess,
691 Base,
692 Protect,
693 MdlPages,
694 PageCount);
695 }
696
697 /* Check if the mapping suceeded */
698 if (!NT_SUCCESS(Status))
699 {
700 /* If it can fail, return NULL */
701 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) return NULL;
702
703 /* Throw exception */
704 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
705 }
706
707 /* Return the base */
708 Base = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
709 return Base;
710 }
711
712 VOID
713 NTAPI
714 MiUnmapLockedPagesInUserSpace(IN PVOID BaseAddress,
715 IN PMDL Mdl)
716 {
717 PMEMORY_AREA MemoryArea;
718
719 /* Sanity check */
720 ASSERT(Mdl->Process == PsGetCurrentProcess());
721
722 /* Find the memory area */
723 MemoryArea = MmLocateMemoryAreaByAddress(&Mdl->Process->Vm,
724 BaseAddress);
725 ASSERT(MemoryArea);
726
727 /* Free it */
728 MmFreeMemoryArea(&Mdl->Process->Vm,
729 MemoryArea,
730 NULL,
731 NULL);
732 }
733
734 /* PUBLIC FUNCTIONS ***********************************************************/
735
736 /*
737 * @unimplemented
738 */
739 PVOID
740 NTAPI
741 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
742 {
743 UNIMPLEMENTED;
744 return 0;
745 }
746
747 /*
748 * @unimplemented
749 */
750 PVOID
751 NTAPI
752 MmSecureVirtualMemory(IN PVOID Address,
753 IN SIZE_T Length,
754 IN ULONG Mode)
755 {
756 UNIMPLEMENTED;
757 return NULL;
758 }
759
760 /*
761 * @unimplemented
762 */
763 VOID
764 NTAPI
765 MmUnsecureVirtualMemory(IN PVOID SecureMem)
766 {
767 UNIMPLEMENTED;
768 }
769
770 /* SYSTEM CALLS ***************************************************************/
771
772 NTSTATUS
773 NTAPI
774 NtReadVirtualMemory(IN HANDLE ProcessHandle,
775 IN PVOID BaseAddress,
776 OUT PVOID Buffer,
777 IN SIZE_T NumberOfBytesToRead,
778 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
779 {
780 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
781 PEPROCESS Process;
782 NTSTATUS Status = STATUS_SUCCESS;
783 SIZE_T BytesRead = 0;
784 PAGED_CODE();
785
786 /* Check if we came from user mode */
787 if (PreviousMode != KernelMode)
788 {
789 /* Validate the read addresses */
790 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
791 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
792 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
793 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
794 {
795 /* Don't allow to write into kernel space */
796 return STATUS_ACCESS_VIOLATION;
797 }
798
799 /* Enter SEH for probe */
800 _SEH2_TRY
801 {
802 /* Probe the output value */
803 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
804 }
805 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
806 {
807 /* Get exception code */
808 Status = _SEH2_GetExceptionCode();
809 }
810 _SEH2_END;
811
812 /* Return if we failed */
813 if (!NT_SUCCESS(Status)) return Status;
814 }
815
816 /* Reference the process */
817 Status = ObReferenceObjectByHandle(ProcessHandle,
818 PROCESS_VM_READ,
819 PsProcessType,
820 PreviousMode,
821 (PVOID*)(&Process),
822 NULL);
823 if (NT_SUCCESS(Status))
824 {
825 /* Do the copy */
826 Status = MmCopyVirtualMemory(Process,
827 BaseAddress,
828 PsGetCurrentProcess(),
829 Buffer,
830 NumberOfBytesToRead,
831 PreviousMode,
832 &BytesRead);
833
834 /* Dereference the process */
835 ObDereferenceObject(Process);
836 }
837
838 /* Check if the caller sent this parameter */
839 if (NumberOfBytesRead)
840 {
841 /* Enter SEH to guard write */
842 _SEH2_TRY
843 {
844 /* Return the number of bytes read */
845 *NumberOfBytesRead = BytesRead;
846 }
847 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
848 {
849 /* Handle exception */
850 Status = _SEH2_GetExceptionCode();
851 }
852 _SEH2_END;
853 }
854
855 /* Return status */
856 return Status;
857 }
858
859 NTSTATUS
860 NTAPI
861 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
862 IN PVOID BaseAddress,
863 IN PVOID Buffer,
864 IN SIZE_T NumberOfBytesToWrite,
865 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
866 {
867 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
868 PEPROCESS Process;
869 NTSTATUS Status = STATUS_SUCCESS;
870 SIZE_T BytesWritten = 0;
871 PAGED_CODE();
872
873 /* Check if we came from user mode */
874 if (PreviousMode != KernelMode)
875 {
876 /* Validate the read addresses */
877 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
878 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
879 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
880 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
881 {
882 /* Don't allow to write into kernel space */
883 return STATUS_ACCESS_VIOLATION;
884 }
885
886 /* Enter SEH for probe */
887 _SEH2_TRY
888 {
889 /* Probe the output value */
890 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
891 }
892 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
893 {
894 /* Get exception code */
895 Status = _SEH2_GetExceptionCode();
896 }
897 _SEH2_END;
898
899 /* Return if we failed */
900 if (!NT_SUCCESS(Status)) return Status;
901 }
902
903 /* Reference the process */
904 Status = ObReferenceObjectByHandle(ProcessHandle,
905 PROCESS_VM_WRITE,
906 PsProcessType,
907 PreviousMode,
908 (PVOID*)&Process,
909 NULL);
910 if (NT_SUCCESS(Status))
911 {
912 /* Do the copy */
913 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
914 Buffer,
915 Process,
916 BaseAddress,
917 NumberOfBytesToWrite,
918 PreviousMode,
919 &BytesWritten);
920
921 /* Dereference the process */
922 ObDereferenceObject(Process);
923 }
924
925 /* Check if the caller sent this parameter */
926 if (NumberOfBytesWritten)
927 {
928 /* Enter SEH to guard write */
929 _SEH2_TRY
930 {
931 /* Return the number of bytes read */
932 *NumberOfBytesWritten = BytesWritten;
933 }
934 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
935 {
936 /* Handle exception */
937 Status = _SEH2_GetExceptionCode();
938 }
939 _SEH2_END;
940 }
941
942 /* Return status */
943 return Status;
944 }
945
946 NTSTATUS
947 NTAPI
948 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
949 IN OUT PVOID *UnsafeBaseAddress,
950 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
951 IN ULONG NewAccessProtection,
952 OUT PULONG UnsafeOldAccessProtection)
953 {
954 PEPROCESS Process;
955 ULONG OldAccessProtection;
956 ULONG Protection;
957 PVOID BaseAddress = NULL;
958 SIZE_T NumberOfBytesToProtect = 0;
959 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
960 NTSTATUS Status = STATUS_SUCCESS;
961 PAGED_CODE();
962
963 /* Check for valid protection flags */
964 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
965 if (Protection != PAGE_NOACCESS &&
966 Protection != PAGE_READONLY &&
967 Protection != PAGE_READWRITE &&
968 Protection != PAGE_WRITECOPY &&
969 Protection != PAGE_EXECUTE &&
970 Protection != PAGE_EXECUTE_READ &&
971 Protection != PAGE_EXECUTE_READWRITE &&
972 Protection != PAGE_EXECUTE_WRITECOPY)
973 {
974 return STATUS_INVALID_PAGE_PROTECTION;
975 }
976
977 /* Check if we came from user mode */
978 if (PreviousMode != KernelMode)
979 {
980 /* Enter SEH for probing */
981 _SEH2_TRY
982 {
983 /* Validate all outputs */
984 ProbeForWritePointer(UnsafeBaseAddress);
985 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
986 ProbeForWriteUlong(UnsafeOldAccessProtection);
987
988 /* Capture them */
989 BaseAddress = *UnsafeBaseAddress;
990 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
991 }
992 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
993 {
994 /* Get exception code */
995 Status = _SEH2_GetExceptionCode();
996 }
997 _SEH2_END;
998
999 /* Return on exception */
1000 if (!NT_SUCCESS(Status)) return Status;
1001 }
1002 else
1003 {
1004 /* Capture directly */
1005 BaseAddress = *UnsafeBaseAddress;
1006 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1007 }
1008
1009 /* Catch illegal base address */
1010 if (BaseAddress > (PVOID)MmUserProbeAddress) return STATUS_INVALID_PARAMETER_2;
1011
1012 /* Catch illegal region size */
1013 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
1014 {
1015 /* Fail */
1016 return STATUS_INVALID_PARAMETER_3;
1017 }
1018
1019 /* 0 is also illegal */
1020 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
1021
1022 /* Get a reference to the process */
1023 Status = ObReferenceObjectByHandle(ProcessHandle,
1024 PROCESS_VM_OPERATION,
1025 PsProcessType,
1026 PreviousMode,
1027 (PVOID*)(&Process),
1028 NULL);
1029 if (!NT_SUCCESS(Status)) return Status;
1030
1031 /* Do the actual work */
1032 Status = MiProtectVirtualMemory(Process,
1033 &BaseAddress,
1034 &NumberOfBytesToProtect,
1035 NewAccessProtection,
1036 &OldAccessProtection);
1037
1038 /* Release reference */
1039 ObDereferenceObject(Process);
1040
1041 /* Enter SEH to return data */
1042 _SEH2_TRY
1043 {
1044 /* Return data to user */
1045 *UnsafeOldAccessProtection = OldAccessProtection;
1046 *UnsafeBaseAddress = BaseAddress;
1047 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
1048 }
1049 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1050 {
1051 /* Catch exception */
1052 Status = _SEH2_GetExceptionCode();
1053 }
1054 _SEH2_END;
1055
1056 /* Return status */
1057 return Status;
1058 }
1059
1060 NTSTATUS NTAPI
1061 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
1062 IN PVOID Address,
1063 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
1064 OUT PVOID VirtualMemoryInformation,
1065 IN SIZE_T Length,
1066 OUT PSIZE_T UnsafeResultLength)
1067 {
1068 NTSTATUS Status = STATUS_SUCCESS;
1069 SIZE_T ResultLength = 0;
1070 KPROCESSOR_MODE PreviousMode;
1071 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
1072 UNICODE_STRING ModuleFileName;
1073 PMEMORY_SECTION_NAME SectionName = NULL;
1074 PEPROCESS Process;
1075 union
1076 {
1077 MEMORY_BASIC_INFORMATION BasicInfo;
1078 }
1079 VirtualMemoryInfo;
1080
1081 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
1082 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
1083 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
1084 VirtualMemoryInformationClass,VirtualMemoryInformation,
1085 Length,ResultLength);
1086
1087 PreviousMode = ExGetPreviousMode();
1088
1089 if (PreviousMode != KernelMode)
1090 {
1091 _SEH2_TRY
1092 {
1093 ProbeForWrite(VirtualMemoryInformation,
1094 Length,
1095 sizeof(ULONG_PTR));
1096
1097 if (UnsafeResultLength) ProbeForWriteSize_t(UnsafeResultLength);
1098 }
1099 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1100 {
1101 Status = _SEH2_GetExceptionCode();
1102 }
1103 _SEH2_END;
1104
1105 if (!NT_SUCCESS(Status))
1106 {
1107 return Status;
1108 }
1109 }
1110
1111 if (Address >= MmSystemRangeStart)
1112 {
1113 DPRINT1("Invalid parameter\n");
1114 return STATUS_INVALID_PARAMETER;
1115 }
1116
1117 /* FIXME: Move this inside MiQueryVirtualMemory */
1118 if (VirtualMemoryInformationClass == MemorySectionName)
1119 {
1120 Status = ObReferenceObjectByHandle(ProcessHandle,
1121 PROCESS_QUERY_INFORMATION,
1122 NULL,
1123 PreviousMode,
1124 (PVOID*)(&Process),
1125 NULL);
1126
1127 if (!NT_SUCCESS(Status))
1128 {
1129 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
1130 return(Status);
1131 }
1132
1133 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
1134 Status = MmGetFileNameForAddress(Address, &ModuleFileName);
1135
1136 if (NT_SUCCESS(Status))
1137 {
1138 SectionName = VirtualMemoryInformation;
1139 if (PreviousMode != KernelMode)
1140 {
1141 _SEH2_TRY
1142 {
1143 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1144 SectionName->SectionFileName.MaximumLength = Length;
1145 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1146
1147 if (UnsafeResultLength != NULL)
1148 {
1149 *UnsafeResultLength = ModuleFileName.Length;
1150 }
1151 }
1152 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1153 {
1154 Status = _SEH2_GetExceptionCode();
1155 }
1156 _SEH2_END;
1157 }
1158 else
1159 {
1160 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1161 SectionName->SectionFileName.MaximumLength = Length;
1162 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1163
1164 if (UnsafeResultLength != NULL)
1165 {
1166 *UnsafeResultLength = ModuleFileName.Length;
1167 }
1168 }
1169 }
1170 ObDereferenceObject(Process);
1171 return Status;
1172 }
1173 else
1174 {
1175 Status = MiQueryVirtualMemory(ProcessHandle,
1176 Address,
1177 VirtualMemoryInformationClass,
1178 &VirtualMemoryInfo,
1179 Length,
1180 &ResultLength);
1181 }
1182
1183 if (NT_SUCCESS(Status))
1184 {
1185 if (PreviousMode != KernelMode)
1186 {
1187 _SEH2_TRY
1188 {
1189 if (ResultLength > 0)
1190 {
1191 ProbeForWrite(VirtualMemoryInformation,
1192 ResultLength,
1193 1);
1194 RtlCopyMemory(VirtualMemoryInformation,
1195 &VirtualMemoryInfo,
1196 ResultLength);
1197 }
1198 if (UnsafeResultLength != NULL)
1199 {
1200 *UnsafeResultLength = ResultLength;
1201 }
1202 }
1203 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1204 {
1205 Status = _SEH2_GetExceptionCode();
1206 }
1207 _SEH2_END;
1208 }
1209 else
1210 {
1211 if (ResultLength > 0)
1212 {
1213 RtlCopyMemory(VirtualMemoryInformation,
1214 &VirtualMemoryInfo,
1215 ResultLength);
1216 }
1217
1218 if (UnsafeResultLength != NULL)
1219 {
1220 *UnsafeResultLength = ResultLength;
1221 }
1222 }
1223 }
1224
1225 return(Status);
1226 }
1227
1228 NTSTATUS
1229 NTAPI
1230 NtLockVirtualMemory(IN HANDLE ProcessHandle,
1231 IN PVOID BaseAddress,
1232 IN SIZE_T NumberOfBytesToLock,
1233 OUT PSIZE_T NumberOfBytesLocked OPTIONAL)
1234 {
1235 UNIMPLEMENTED;
1236 if (NumberOfBytesLocked) *NumberOfBytesLocked = 0;
1237 return STATUS_SUCCESS;
1238 }
1239
1240 NTSTATUS
1241 NTAPI
1242 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
1243 IN PVOID BaseAddress,
1244 IN SIZE_T NumberOfBytesToUnlock,
1245 OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL)
1246 {
1247 UNIMPLEMENTED;
1248 if (NumberOfBytesUnlocked) *NumberOfBytesUnlocked = 0;
1249 return STATUS_SUCCESS;
1250 }
1251
1252 NTSTATUS
1253 NTAPI
1254 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
1255 IN OUT PVOID *BaseAddress,
1256 IN OUT PSIZE_T NumberOfBytesToFlush,
1257 OUT PIO_STATUS_BLOCK IoStatusBlock)
1258 {
1259 UNIMPLEMENTED;
1260 return STATUS_SUCCESS;
1261 }
1262
1263 /*
1264 * @unimplemented
1265 */
1266 NTSTATUS
1267 NTAPI
1268 NtGetWriteWatch(IN HANDLE ProcessHandle,
1269 IN ULONG Flags,
1270 IN PVOID BaseAddress,
1271 IN ULONG RegionSize,
1272 IN PVOID *UserAddressArray,
1273 OUT PULONG EntriesInUserAddressArray,
1274 OUT PULONG Granularity)
1275 {
1276 if (!EntriesInUserAddressArray || !Granularity)
1277 {
1278 return STATUS_ACCESS_VIOLATION;
1279 }
1280
1281 if (!*EntriesInUserAddressArray || !RegionSize)
1282 {
1283 return STATUS_INVALID_PARAMETER;
1284 }
1285
1286 if (!UserAddressArray)
1287 {
1288 return STATUS_ACCESS_VIOLATION;
1289 }
1290
1291 /* HACK: Set granularity to PAGE_SIZE */
1292 *Granularity = PAGE_SIZE;
1293
1294 UNIMPLEMENTED;
1295 return STATUS_NOT_IMPLEMENTED;
1296 }
1297
1298 /*
1299 * @unimplemented
1300 */
1301 NTSTATUS
1302 NTAPI
1303 NtResetWriteWatch(IN HANDLE ProcessHandle,
1304 IN PVOID BaseAddress,
1305 IN ULONG RegionSize)
1306 {
1307 if (!RegionSize)
1308 {
1309 return STATUS_INVALID_PARAMETER;
1310 }
1311
1312 UNIMPLEMENTED;
1313 return STATUS_NOT_IMPLEMENTED;
1314 }
1315
1316 /* EOF */