- Don't use "static" in the kernel.
[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 Status = STATUS_INVALID_INFO_CLASS;
559 *ResultLength = 0;
560 break;
561 }
562 }
563
564 MmUnlockAddressSpace(AddressSpace);
565 ObDereferenceObject(Process);
566
567 return Status;
568 }
569
570 NTSTATUS NTAPI
571 MiProtectVirtualMemory(IN PEPROCESS Process,
572 IN OUT PVOID *BaseAddress,
573 IN OUT PSIZE_T NumberOfBytesToProtect,
574 IN ULONG NewAccessProtection,
575 OUT PULONG OldAccessProtection OPTIONAL)
576 {
577 PMEMORY_AREA MemoryArea;
578 PMMSUPPORT AddressSpace;
579 ULONG OldAccessProtection_;
580 NTSTATUS Status;
581
582 *NumberOfBytesToProtect =
583 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
584 PAGE_ROUND_DOWN(*BaseAddress);
585 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
586
587 AddressSpace = &Process->Vm;
588
589 MmLockAddressSpace(AddressSpace);
590 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
591 if (MemoryArea == NULL)
592 {
593 MmUnlockAddressSpace(AddressSpace);
594 return STATUS_UNSUCCESSFUL;
595 }
596
597 if (OldAccessProtection == NULL)
598 OldAccessProtection = &OldAccessProtection_;
599
600 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
601 {
602 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
603 *NumberOfBytesToProtect, NewAccessProtection,
604 OldAccessProtection);
605 }
606 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
607 {
608 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
609 *NumberOfBytesToProtect,
610 NewAccessProtection,
611 OldAccessProtection);
612 }
613 else
614 {
615 /* FIXME: Should we return failure or success in this case? */
616 Status = STATUS_CONFLICTING_ADDRESSES;
617 }
618
619 MmUnlockAddressSpace(AddressSpace);
620
621 return Status;
622 }
623
624 PVOID
625 NTAPI
626 MiMapLockedPagesInUserSpace(IN PMDL Mdl,
627 IN PVOID BaseVa,
628 IN MEMORY_CACHING_TYPE CacheType,
629 IN PVOID BaseAddress)
630 {
631 PVOID Base;
632 PPFN_NUMBER MdlPages;
633 ULONG PageCount;
634 PEPROCESS CurrentProcess;
635 NTSTATUS Status;
636 ULONG Protect;
637 MEMORY_AREA *Result;
638 LARGE_INTEGER BoundaryAddressMultiple;
639
640 /* Calculate the number of pages required. */
641 MdlPages = (PPFN_NUMBER)(Mdl + 1);
642 PageCount = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
643
644 /* Set default page protection */
645 Protect = PAGE_READWRITE;
646 if (CacheType == MmNonCached) Protect |= PAGE_NOCACHE;
647
648 BoundaryAddressMultiple.QuadPart = 0;
649 Base = BaseAddress;
650
651 CurrentProcess = PsGetCurrentProcess();
652
653 MmLockAddressSpace(&CurrentProcess->Vm);
654 Status = MmCreateMemoryArea(&CurrentProcess->Vm,
655 MEMORY_AREA_MDL_MAPPING,
656 &Base,
657 PageCount * PAGE_SIZE,
658 Protect,
659 &Result,
660 (Base != NULL),
661 0,
662 BoundaryAddressMultiple);
663 MmUnlockAddressSpace(&CurrentProcess->Vm);
664 if (!NT_SUCCESS(Status))
665 {
666 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL)
667 {
668 return NULL;
669 }
670
671 /* Throw exception */
672 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
673 ASSERT(0);
674 }
675
676 /* Set the virtual mappings for the MDL pages. */
677 if (Mdl->MdlFlags & MDL_IO_SPACE)
678 {
679 /* Map the pages */
680 Status = MmCreateVirtualMappingUnsafe(CurrentProcess,
681 Base,
682 Protect,
683 MdlPages,
684 PageCount);
685 }
686 else
687 {
688 /* Map the pages */
689 Status = MmCreateVirtualMapping(CurrentProcess,
690 Base,
691 Protect,
692 MdlPages,
693 PageCount);
694 }
695
696 /* Check if the mapping suceeded */
697 if (!NT_SUCCESS(Status))
698 {
699 /* If it can fail, return NULL */
700 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) return NULL;
701
702 /* Throw exception */
703 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
704 }
705
706 /* Return the base */
707 Base = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
708 return Base;
709 }
710
711 VOID
712 NTAPI
713 MiUnmapLockedPagesInUserSpace(IN PVOID BaseAddress,
714 IN PMDL Mdl)
715 {
716 PMEMORY_AREA MemoryArea;
717
718 /* Sanity check */
719 ASSERT(Mdl->Process == PsGetCurrentProcess());
720
721 /* Find the memory area */
722 MemoryArea = MmLocateMemoryAreaByAddress(&Mdl->Process->Vm,
723 BaseAddress);
724 ASSERT(MemoryArea);
725
726 /* Free it */
727 MmFreeMemoryArea(&Mdl->Process->Vm,
728 MemoryArea,
729 NULL,
730 NULL);
731 }
732
733 /* PUBLIC FUNCTIONS ***********************************************************/
734
735 /*
736 * @unimplemented
737 */
738 PVOID
739 NTAPI
740 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
741 {
742 UNIMPLEMENTED;
743 return 0;
744 }
745
746 /*
747 * @unimplemented
748 */
749 PVOID
750 NTAPI
751 MmSecureVirtualMemory(IN PVOID Address,
752 IN SIZE_T Length,
753 IN ULONG Mode)
754 {
755 UNIMPLEMENTED;
756 return NULL;
757 }
758
759 /*
760 * @unimplemented
761 */
762 VOID
763 NTAPI
764 MmUnsecureVirtualMemory(IN PVOID SecureMem)
765 {
766 UNIMPLEMENTED;
767 }
768
769 /* SYSTEM CALLS ***************************************************************/
770
771 NTSTATUS
772 NTAPI
773 NtReadVirtualMemory(IN HANDLE ProcessHandle,
774 IN PVOID BaseAddress,
775 OUT PVOID Buffer,
776 IN SIZE_T NumberOfBytesToRead,
777 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
778 {
779 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
780 PEPROCESS Process;
781 NTSTATUS Status = STATUS_SUCCESS;
782 SIZE_T BytesRead = 0;
783 PAGED_CODE();
784
785 /* Check if we came from user mode */
786 if (PreviousMode != KernelMode)
787 {
788 /* Validate the read addresses */
789 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
790 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
791 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
792 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
793 {
794 /* Don't allow to write into kernel space */
795 return STATUS_ACCESS_VIOLATION;
796 }
797
798 /* Enter SEH for probe */
799 _SEH2_TRY
800 {
801 /* Probe the output value */
802 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
803 }
804 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
805 {
806 /* Get exception code */
807 Status = _SEH2_GetExceptionCode();
808 }
809 _SEH2_END;
810
811 /* Return if we failed */
812 if (!NT_SUCCESS(Status)) return Status;
813 }
814
815 /* Reference the process */
816 Status = ObReferenceObjectByHandle(ProcessHandle,
817 PROCESS_VM_READ,
818 PsProcessType,
819 PreviousMode,
820 (PVOID*)(&Process),
821 NULL);
822 if (NT_SUCCESS(Status))
823 {
824 /* Do the copy */
825 Status = MmCopyVirtualMemory(Process,
826 BaseAddress,
827 PsGetCurrentProcess(),
828 Buffer,
829 NumberOfBytesToRead,
830 PreviousMode,
831 &BytesRead);
832
833 /* Dereference the process */
834 ObDereferenceObject(Process);
835 }
836
837 /* Check if the caller sent this parameter */
838 if (NumberOfBytesRead)
839 {
840 /* Enter SEH to guard write */
841 _SEH2_TRY
842 {
843 /* Return the number of bytes read */
844 *NumberOfBytesRead = BytesRead;
845 }
846 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
847 {
848 /* Handle exception */
849 Status = _SEH2_GetExceptionCode();
850 }
851 _SEH2_END;
852 }
853
854 /* Return status */
855 return Status;
856 }
857
858 NTSTATUS
859 NTAPI
860 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
861 IN PVOID BaseAddress,
862 IN PVOID Buffer,
863 IN SIZE_T NumberOfBytesToWrite,
864 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
865 {
866 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
867 PEPROCESS Process;
868 NTSTATUS Status = STATUS_SUCCESS;
869 ULONG BytesWritten = 0;
870 PAGED_CODE();
871
872 /* Check if we came from user mode */
873 if (PreviousMode != KernelMode)
874 {
875 /* Validate the read addresses */
876 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
877 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
878 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
879 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
880 {
881 /* Don't allow to write into kernel space */
882 return STATUS_ACCESS_VIOLATION;
883 }
884
885 /* Enter SEH for probe */
886 _SEH2_TRY
887 {
888 /* Probe the output value */
889 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
890 }
891 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
892 {
893 /* Get exception code */
894 Status = _SEH2_GetExceptionCode();
895 }
896 _SEH2_END;
897
898 /* Return if we failed */
899 if (!NT_SUCCESS(Status)) return Status;
900 }
901
902 /* Reference the process */
903 Status = ObReferenceObjectByHandle(ProcessHandle,
904 PROCESS_VM_WRITE,
905 PsProcessType,
906 PreviousMode,
907 (PVOID*)&Process,
908 NULL);
909 if (NT_SUCCESS(Status))
910 {
911 /* Do the copy */
912 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
913 Buffer,
914 Process,
915 BaseAddress,
916 NumberOfBytesToWrite,
917 PreviousMode,
918 &BytesWritten);
919
920 /* Dereference the process */
921 ObDereferenceObject(Process);
922 }
923
924 /* Check if the caller sent this parameter */
925 if (NumberOfBytesWritten)
926 {
927 /* Enter SEH to guard write */
928 _SEH2_TRY
929 {
930 /* Return the number of bytes read */
931 *NumberOfBytesWritten = BytesWritten;
932 }
933 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
934 {
935 /* Handle exception */
936 Status = _SEH2_GetExceptionCode();
937 }
938 _SEH2_END;
939 }
940
941 /* Return status */
942 return Status;
943 }
944
945 NTSTATUS
946 NTAPI
947 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
948 IN OUT PVOID *UnsafeBaseAddress,
949 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
950 IN ULONG NewAccessProtection,
951 OUT PULONG UnsafeOldAccessProtection)
952 {
953 PEPROCESS Process;
954 ULONG OldAccessProtection;
955 ULONG Protection;
956 PVOID BaseAddress = NULL;
957 SIZE_T NumberOfBytesToProtect = 0;
958 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
959 NTSTATUS Status = STATUS_SUCCESS;
960 PAGED_CODE();
961
962 /* Check for valid protection flags */
963 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
964 if (Protection != PAGE_NOACCESS &&
965 Protection != PAGE_READONLY &&
966 Protection != PAGE_READWRITE &&
967 Protection != PAGE_WRITECOPY &&
968 Protection != PAGE_EXECUTE &&
969 Protection != PAGE_EXECUTE_READ &&
970 Protection != PAGE_EXECUTE_READWRITE &&
971 Protection != PAGE_EXECUTE_WRITECOPY)
972 {
973 return STATUS_INVALID_PAGE_PROTECTION;
974 }
975
976 /* Check if we came from user mode */
977 if (PreviousMode != KernelMode)
978 {
979 /* Enter SEH for probing */
980 _SEH2_TRY
981 {
982 /* Validate all outputs */
983 ProbeForWritePointer(UnsafeBaseAddress);
984 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
985 ProbeForWriteUlong(UnsafeOldAccessProtection);
986
987 /* Capture them */
988 BaseAddress = *UnsafeBaseAddress;
989 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
990 }
991 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
992 {
993 /* Get exception code */
994 Status = _SEH2_GetExceptionCode();
995 }
996 _SEH2_END;
997
998 /* Return on exception */
999 if (!NT_SUCCESS(Status)) return Status;
1000 }
1001 else
1002 {
1003 /* Capture directly */
1004 BaseAddress = *UnsafeBaseAddress;
1005 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1006 }
1007
1008 /* Catch illegal base address */
1009 if (BaseAddress > (PVOID)MmUserProbeAddress) return STATUS_INVALID_PARAMETER_2;
1010
1011 /* Catch illegal region size */
1012 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
1013 {
1014 /* Fail */
1015 return STATUS_INVALID_PARAMETER_3;
1016 }
1017
1018 /* 0 is also illegal */
1019 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
1020
1021 /* Get a reference to the process */
1022 Status = ObReferenceObjectByHandle(ProcessHandle,
1023 PROCESS_VM_OPERATION,
1024 PsProcessType,
1025 PreviousMode,
1026 (PVOID*)(&Process),
1027 NULL);
1028 if (!NT_SUCCESS(Status)) return Status;
1029
1030 /* Do the actual work */
1031 Status = MiProtectVirtualMemory(Process,
1032 &BaseAddress,
1033 &NumberOfBytesToProtect,
1034 NewAccessProtection,
1035 &OldAccessProtection);
1036
1037 /* Release reference */
1038 ObDereferenceObject(Process);
1039
1040 /* Enter SEH to return data */
1041 _SEH2_TRY
1042 {
1043 /* Return data to user */
1044 *UnsafeOldAccessProtection = OldAccessProtection;
1045 *UnsafeBaseAddress = BaseAddress;
1046 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
1047 }
1048 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1049 {
1050 /* Catch exception */
1051 Status = _SEH2_GetExceptionCode();
1052 }
1053 _SEH2_END;
1054
1055 /* Return status */
1056 return Status;
1057 }
1058
1059 NTSTATUS NTAPI
1060 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
1061 IN PVOID Address,
1062 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
1063 OUT PVOID VirtualMemoryInformation,
1064 IN SIZE_T Length,
1065 OUT PSIZE_T UnsafeResultLength)
1066 {
1067 NTSTATUS Status = STATUS_SUCCESS;
1068 SIZE_T ResultLength = 0;
1069 KPROCESSOR_MODE PreviousMode;
1070 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
1071 UNICODE_STRING ModuleFileName;
1072 PMEMORY_SECTION_NAME SectionName = NULL;
1073 PEPROCESS Process;
1074 union
1075 {
1076 MEMORY_BASIC_INFORMATION BasicInfo;
1077 }
1078 VirtualMemoryInfo;
1079
1080 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
1081 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
1082 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
1083 VirtualMemoryInformationClass,VirtualMemoryInformation,
1084 Length,ResultLength);
1085
1086 PreviousMode = ExGetPreviousMode();
1087
1088 if (PreviousMode != KernelMode)
1089 {
1090 _SEH2_TRY
1091 {
1092 ProbeForWrite(VirtualMemoryInformation,
1093 Length,
1094 sizeof(ULONG_PTR));
1095
1096 if (UnsafeResultLength) ProbeForWriteSize_t(UnsafeResultLength);
1097 }
1098 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1099 {
1100 Status = _SEH2_GetExceptionCode();
1101 }
1102 _SEH2_END;
1103
1104 if (!NT_SUCCESS(Status))
1105 {
1106 return Status;
1107 }
1108 }
1109
1110 if (Address >= MmSystemRangeStart)
1111 {
1112 DPRINT1("Invalid parameter\n");
1113 return STATUS_INVALID_PARAMETER;
1114 }
1115
1116 /* FIXME: Move this inside MiQueryVirtualMemory */
1117 if (VirtualMemoryInformationClass == MemorySectionName)
1118 {
1119 Status = ObReferenceObjectByHandle(ProcessHandle,
1120 PROCESS_QUERY_INFORMATION,
1121 NULL,
1122 PreviousMode,
1123 (PVOID*)(&Process),
1124 NULL);
1125
1126 if (!NT_SUCCESS(Status))
1127 {
1128 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
1129 return(Status);
1130 }
1131
1132 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
1133 Status = MmGetFileNameForAddress(Address, &ModuleFileName);
1134
1135 if (NT_SUCCESS(Status))
1136 {
1137 SectionName = VirtualMemoryInformation;
1138 if (PreviousMode != KernelMode)
1139 {
1140 _SEH2_TRY
1141 {
1142 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1143 SectionName->SectionFileName.MaximumLength = Length;
1144 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1145
1146 if (UnsafeResultLength != NULL)
1147 {
1148 *UnsafeResultLength = ModuleFileName.Length;
1149 }
1150 }
1151 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1152 {
1153 Status = _SEH2_GetExceptionCode();
1154 }
1155 _SEH2_END;
1156 }
1157 else
1158 {
1159 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1160 SectionName->SectionFileName.MaximumLength = Length;
1161 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1162
1163 if (UnsafeResultLength != NULL)
1164 {
1165 *UnsafeResultLength = ModuleFileName.Length;
1166 }
1167 }
1168 }
1169 ObDereferenceObject(Process);
1170 return Status;
1171 }
1172 else
1173 {
1174 Status = MiQueryVirtualMemory(ProcessHandle,
1175 Address,
1176 VirtualMemoryInformationClass,
1177 &VirtualMemoryInfo,
1178 Length,
1179 &ResultLength);
1180 }
1181
1182 if (NT_SUCCESS(Status))
1183 {
1184 if (PreviousMode != KernelMode)
1185 {
1186 _SEH2_TRY
1187 {
1188 if (ResultLength > 0)
1189 {
1190 ProbeForWrite(VirtualMemoryInformation,
1191 ResultLength,
1192 1);
1193 RtlCopyMemory(VirtualMemoryInformation,
1194 &VirtualMemoryInfo,
1195 ResultLength);
1196 }
1197 if (UnsafeResultLength != NULL)
1198 {
1199 *UnsafeResultLength = ResultLength;
1200 }
1201 }
1202 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1203 {
1204 Status = _SEH2_GetExceptionCode();
1205 }
1206 _SEH2_END;
1207 }
1208 else
1209 {
1210 if (ResultLength > 0)
1211 {
1212 RtlCopyMemory(VirtualMemoryInformation,
1213 &VirtualMemoryInfo,
1214 ResultLength);
1215 }
1216
1217 if (UnsafeResultLength != NULL)
1218 {
1219 *UnsafeResultLength = ResultLength;
1220 }
1221 }
1222 }
1223
1224 return(Status);
1225 }
1226
1227 NTSTATUS
1228 NTAPI
1229 NtLockVirtualMemory(IN HANDLE ProcessHandle,
1230 IN PVOID BaseAddress,
1231 IN SIZE_T NumberOfBytesToLock,
1232 OUT PSIZE_T NumberOfBytesLocked OPTIONAL)
1233 {
1234 UNIMPLEMENTED;
1235 if (NumberOfBytesLocked) *NumberOfBytesLocked = 0;
1236 return STATUS_SUCCESS;
1237 }
1238
1239 NTSTATUS
1240 NTAPI
1241 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
1242 IN PVOID BaseAddress,
1243 IN SIZE_T NumberOfBytesToUnlock,
1244 OUT PSIZE_T NumberOfBytesUnlocked OPTIONAL)
1245 {
1246 UNIMPLEMENTED;
1247 if (NumberOfBytesUnlocked) *NumberOfBytesUnlocked = 0;
1248 return STATUS_SUCCESS;
1249 }
1250
1251 NTSTATUS
1252 NTAPI
1253 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
1254 IN OUT PVOID *BaseAddress,
1255 IN OUT PSIZE_T NumberOfBytesToFlush,
1256 OUT PIO_STATUS_BLOCK IoStatusBlock)
1257 {
1258 UNIMPLEMENTED;
1259 return STATUS_SUCCESS;
1260 }
1261
1262 /*
1263 * @unimplemented
1264 */
1265 NTSTATUS
1266 NTAPI
1267 NtGetWriteWatch(IN HANDLE ProcessHandle,
1268 IN ULONG Flags,
1269 IN PVOID BaseAddress,
1270 IN ULONG RegionSize,
1271 IN PVOID *UserAddressArray,
1272 OUT PULONG EntriesInUserAddressArray,
1273 OUT PULONG Granularity)
1274 {
1275 if (!EntriesInUserAddressArray || !Granularity)
1276 {
1277 return STATUS_ACCESS_VIOLATION;
1278 }
1279
1280 if (!*EntriesInUserAddressArray || !RegionSize)
1281 {
1282 return STATUS_INVALID_PARAMETER;
1283 }
1284
1285 if (!UserAddressArray)
1286 {
1287 return STATUS_ACCESS_VIOLATION;
1288 }
1289
1290 /* HACK: Set granularity to PAGE_SIZE */
1291 *Granularity = PAGE_SIZE;
1292
1293 UNIMPLEMENTED;
1294 return STATUS_NOT_IMPLEMENTED;
1295 }
1296
1297 /*
1298 * @unimplemented
1299 */
1300 NTSTATUS
1301 NTAPI
1302 NtResetWriteWatch(IN HANDLE ProcessHandle,
1303 IN PVOID BaseAddress,
1304 IN ULONG RegionSize)
1305 {
1306 if (!RegionSize)
1307 {
1308 return STATUS_INVALID_PARAMETER;
1309 }
1310
1311 UNIMPLEMENTED;
1312 return STATUS_NOT_IMPLEMENTED;
1313 }
1314
1315 /* EOF */