sync with trunk r49322
[reactos.git] / ntoskrnl / mm / ARM3 / virtual.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/virtual.c
5 * PURPOSE: ARM Memory Manager Virtual Memory Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #line 15 "ARMĀ³::VIRTUAL"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
18
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
22
23 NTSTATUS NTAPI
24 MiProtectVirtualMemory(IN PEPROCESS Process,
25 IN OUT PVOID *BaseAddress,
26 IN OUT PSIZE_T NumberOfBytesToProtect,
27 IN ULONG NewAccessProtection,
28 OUT PULONG OldAccessProtection OPTIONAL);
29
30 /* PRIVATE FUNCTIONS **********************************************************/
31
32 ULONG
33 NTAPI
34 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
35 IN PEPROCESS CurrentProcess)
36 {
37 NTSTATUS Status;
38 BOOLEAN LockChange = FALSE;
39
40 /* Must be a non-pool page table, since those are double-mapped already */
41 ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
42 ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
43 (PageTableVirtualAddress > MmPagedPoolEnd));
44
45 /* Working set lock or PFN lock should be held */
46 ASSERT(KeAreAllApcsDisabled() == TRUE);
47
48 /* Check if the page table is valid */
49 while (!MmIsAddressValid(PageTableVirtualAddress))
50 {
51 /* Fault it in */
52 Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
53 if (!NT_SUCCESS(Status))
54 {
55 /* This should not fail */
56 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
57 1,
58 Status,
59 (ULONG_PTR)CurrentProcess,
60 (ULONG_PTR)PageTableVirtualAddress);
61 }
62
63 /* This flag will be useful later when we do better locking */
64 LockChange = TRUE;
65 }
66
67 /* Let caller know what the lock state is */
68 return LockChange;
69 }
70
71 ULONG
72 NTAPI
73 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
74 IN KIRQL OldIrql)
75 {
76 NTSTATUS Status;
77 BOOLEAN LockChange = FALSE;
78
79 /* Must be e kernel address */
80 ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS);
81
82 /* Check if the page is valid */
83 while (!MmIsAddressValid(VirtualAddress))
84 {
85 /* Release the PFN database */
86 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
87
88 /* Fault it in */
89 Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL);
90 if (!NT_SUCCESS(Status))
91 {
92 /* This should not fail */
93 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
94 3,
95 Status,
96 0,
97 (ULONG_PTR)VirtualAddress);
98 }
99
100 /* This flag will be useful later when we do better locking */
101 LockChange = TRUE;
102
103 /* Lock the PFN database */
104 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
105 }
106
107 /* Let caller know what the lock state is */
108 return LockChange;
109 }
110
111 PFN_NUMBER
112 NTAPI
113 MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
114 IN PFN_NUMBER PageCount,
115 IN ULONG Flags,
116 OUT PPFN_NUMBER ValidPages)
117 {
118 PFN_NUMBER ActualPages = 0;
119 PETHREAD CurrentThread = PsGetCurrentThread();
120 PMMPFN Pfn1, Pfn2;
121 PFN_NUMBER PageFrameIndex, PageTableIndex;
122 KIRQL OldIrql;
123 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
124
125 /* Lock the system working set */
126 MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
127
128 /* Loop all pages */
129 while (PageCount)
130 {
131 /* Make sure there's some data about the page */
132 if (PointerPte->u.Long)
133 {
134 /* As always, only handle current ARM3 scenarios */
135 ASSERT(PointerPte->u.Soft.Prototype == 0);
136 ASSERT(PointerPte->u.Soft.Transition == 0);
137
138 /* Normally this is one possibility -- freeing a valid page */
139 if (PointerPte->u.Hard.Valid)
140 {
141 /* Get the page PFN */
142 PageFrameIndex = PFN_FROM_PTE(PointerPte);
143 Pfn1 = MiGetPfnEntry(PageFrameIndex);
144
145 /* Should not have any working set data yet */
146 ASSERT(Pfn1->u1.WsIndex == 0);
147
148 /* Actual valid, legitimate, pages */
149 if (ValidPages) *ValidPages++;
150
151 /* Get the page table entry */
152 PageTableIndex = Pfn1->u4.PteFrame;
153 Pfn2 = MiGetPfnEntry(PageTableIndex);
154
155 /* Lock the PFN database */
156 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
157
158 /* Delete it the page */
159 MI_SET_PFN_DELETED(Pfn1);
160 MiDecrementShareCount(Pfn1, PageFrameIndex);
161
162 /* Decrement the page table too */
163 DPRINT("FIXME: ARM3 should decrement the PT refcount for: %p\n", Pfn2);
164 #if 0 // ARM3: Dont't trust this yet
165 MiDecrementShareCount(Pfn2, PageTableIndex);
166 #endif
167
168 /* Release the PFN database */
169 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
170
171 /* Destroy the PTE */
172 PointerPte->u.Long = 0;
173 }
174
175 /* Actual legitimate pages */
176 ActualPages++;
177 }
178 else
179 {
180 /*
181 * The only other ARM3 possibility is a demand zero page, which would
182 * mean freeing some of the paged pool pages that haven't even been
183 * touched yet, as part of a larger allocation.
184 *
185 * Right now, we shouldn't expect any page file information in the PTE
186 */
187 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
188
189 /* Destroy the PTE */
190 PointerPte->u.Long = 0;
191 }
192
193 /* Keep going */
194 PointerPte++;
195 PageCount--;
196 }
197
198 /* Release the working set */
199 MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
200
201 /* Flush the entire TLB */
202 KeFlushEntireTb(TRUE, TRUE);
203
204 /* Done */
205 return ActualPages;
206 }
207
208 VOID
209 NTAPI
210 MiDeletePte(IN PMMPTE PointerPte,
211 IN PVOID VirtualAddress,
212 IN PEPROCESS CurrentProcess,
213 IN PMMPTE PrototypePte)
214 {
215 PMMPFN Pfn1;
216 MMPTE TempPte;
217 PFN_NUMBER PageFrameIndex;
218 PMMPDE PointerPde;
219
220 /* PFN lock must be held */
221 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
222
223 /* Capture the PTE */
224 TempPte = *PointerPte;
225
226 /* We only support valid PTEs for now */
227 ASSERT(TempPte.u.Hard.Valid == 1);
228 if (TempPte.u.Hard.Valid == 0)
229 {
230 /* Invalid PTEs not supported yet */
231 ASSERT(TempPte.u.Soft.Prototype == 0);
232 ASSERT(TempPte.u.Soft.Transition == 0);
233 }
234
235 /* Get the PFN entry */
236 PageFrameIndex = PFN_FROM_PTE(&TempPte);
237 Pfn1 = MiGetPfnEntry(PageFrameIndex);
238
239 /* Check if this is a valid, prototype PTE */
240 if (Pfn1->u3.e1.PrototypePte == 1)
241 {
242 /* Get the PDE and make sure it's faulted in */
243 PointerPde = MiAddressToPde(PointerPte);
244 if (PointerPde->u.Hard.Valid == 0)
245 {
246 #if (_MI_PAGING_LEVELS == 2)
247 /* Could be paged pool access from a new process -- synchronize the page directories */
248 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
249 {
250 #endif
251 /* The PDE must be valid at this point */
252 KeBugCheckEx(MEMORY_MANAGEMENT,
253 0x61940,
254 (ULONG_PTR)PointerPte,
255 PointerPte->u.Long,
256 (ULONG_PTR)VirtualAddress);
257 }
258 #if (_MI_PAGING_LEVELS == 2)
259 }
260 #endif
261 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
262 //MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde)), PFN_FROM_PDE(PointerPde));
263
264 /* Drop the share count */
265 MiDecrementShareCount(Pfn1, PageFrameIndex);
266
267 /* No fork yet */
268 if (PointerPte <= MiHighestUserPte) ASSERT(PrototypePte == Pfn1->PteAddress);
269 }
270 else
271 {
272 /* Make sure the saved PTE address is valid */
273 if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
274 {
275 /* The PFN entry is illegal, or invalid */
276 KeBugCheckEx(MEMORY_MANAGEMENT,
277 0x401,
278 (ULONG_PTR)PointerPte,
279 PointerPte->u.Long,
280 (ULONG_PTR)Pfn1->PteAddress);
281 }
282
283 /* There should only be 1 shared reference count */
284 ASSERT(Pfn1->u2.ShareCount == 1);
285
286 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
287 //MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
288
289 /* Mark the PFN for deletion and dereference what should be the last ref */
290 MI_SET_PFN_DELETED(Pfn1);
291 MiDecrementShareCount(Pfn1, PageFrameIndex);
292
293 /* We should eventually do this */
294 //CurrentProcess->NumberOfPrivatePages--;
295 }
296
297 /* Destroy the PTE and flush the TLB */
298 PointerPte->u.Long = 0;
299 KeFlushCurrentTb();
300 }
301
302 VOID
303 NTAPI
304 MiDeleteVirtualAddresses(IN ULONG_PTR Va,
305 IN ULONG_PTR EndingAddress,
306 IN PMMVAD Vad)
307 {
308 PMMPTE PointerPte, PointerPde, PrototypePte, LastPrototypePte;
309 MMPTE TempPte;
310 PEPROCESS CurrentProcess;
311 KIRQL OldIrql;
312 BOOLEAN AddressGap = FALSE;
313 PSUBSECTION Subsection;
314
315 /* Get out if this is a fake VAD, RosMm will free the marea pages */
316 if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
317
318 /* Grab the process and PTE/PDE for the address being deleted */
319 CurrentProcess = PsGetCurrentProcess();
320 PointerPde = MiAddressToPde(Va);
321 PointerPte = MiAddressToPte(Va);
322
323 /* Check if this is a section VAD or a VM VAD */
324 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
325 {
326 /* Don't worry about prototypes */
327 PrototypePte = LastPrototypePte = NULL;
328 }
329 else
330 {
331 /* Get the prototype PTE */
332 PrototypePte = Vad->FirstPrototypePte;
333 LastPrototypePte = Vad->FirstPrototypePte + 1;
334 }
335
336 /* In all cases, we don't support fork() yet */
337 ASSERT(CurrentProcess->CloneRoot == NULL);
338
339 /* Loop the PTE for each VA */
340 while (TRUE)
341 {
342 /* First keep going until we find a valid PDE */
343 while (!PointerPde->u.Long)
344 {
345 /* There are gaps in the address space */
346 AddressGap = TRUE;
347
348 /* Still no valid PDE, try the next 4MB (or whatever) */
349 PointerPde++;
350
351 /* Update the PTE on this new boundary */
352 PointerPte = MiPteToAddress(PointerPde);
353
354 /* Check if all the PDEs are invalid, so there's nothing to free */
355 Va = (ULONG_PTR)MiPteToAddress(PointerPte);
356 if (Va > EndingAddress) return;
357 }
358
359 /* Now check if the PDE is mapped in */
360 if (!PointerPde->u.Hard.Valid)
361 {
362 /* It isn't, so map it in */
363 PointerPte = MiPteToAddress(PointerPde);
364 MiMakeSystemAddressValid(PointerPte, CurrentProcess);
365 }
366
367 /* Now we should have a valid PDE, mapped in, and still have some VA */
368 ASSERT(PointerPde->u.Hard.Valid == 1);
369 ASSERT(Va <= EndingAddress);
370
371 /* Check if this is a section VAD with gaps in it */
372 if ((AddressGap) && (LastPrototypePte))
373 {
374 /* We need to skip to the next correct prototype PTE */
375 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
376
377 /* And we need the subsection to skip to the next last prototype PTE */
378 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
379 if (Subsection)
380 {
381 /* Found it! */
382 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
383 }
384 else
385 {
386 /* No more subsections, we are done with prototype PTEs */
387 PrototypePte = NULL;
388 }
389 }
390
391 /* Lock the PFN Database while we delete the PTEs */
392 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
393 do
394 {
395 /* Capture the PDE and make sure it exists */
396 TempPte = *PointerPte;
397 if (TempPte.u.Long)
398 {
399 /* Check if the PTE is actually mapped in */
400 if (TempPte.u.Long & 0xFFFFFC01)
401 {
402 /* Are we dealing with section VAD? */
403 if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
404 {
405 /* We need to skip to the next correct prototype PTE */
406 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
407
408 /* And we need the subsection to skip to the next last prototype PTE */
409 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
410 if (Subsection)
411 {
412 /* Found it! */
413 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
414 }
415 else
416 {
417 /* No more subsections, we are done with prototype PTEs */
418 PrototypePte = NULL;
419 }
420 }
421
422 /* Check for prototype PTE */
423 if ((TempPte.u.Hard.Valid == 0) &&
424 (TempPte.u.Soft.Prototype == 1))
425 {
426 /* Just nuke it */
427 PointerPte->u.Long = 0;
428 }
429 else
430 {
431 /* Delete the PTE proper */
432 MiDeletePte(PointerPte,
433 (PVOID)Va,
434 CurrentProcess,
435 PrototypePte);
436 }
437 }
438 else
439 {
440 /* The PTE was never mapped, just nuke it here */
441 PointerPte->u.Long = 0;
442 }
443 }
444
445 /* Update the address and PTE for it */
446 Va += PAGE_SIZE;
447 PointerPte++;
448 PrototypePte++;
449
450 /* Making sure the PDE is still valid */
451 ASSERT(PointerPde->u.Hard.Valid == 1);
452 }
453 while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
454
455 /* The PDE should still be valid at this point */
456 ASSERT(PointerPde->u.Hard.Valid == 1);
457
458 /* Release the lock and get out if we're done */
459 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
460 if (Va > EndingAddress) return;
461
462 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
463 PointerPde = MiAddressToPde(Va);
464 AddressGap = FALSE;
465 }
466 }
467
468 LONG
469 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
470 OUT PBOOLEAN HaveBadAddress,
471 OUT PULONG_PTR BadAddress)
472 {
473 PEXCEPTION_RECORD ExceptionRecord;
474 PAGED_CODE();
475
476 //
477 // Assume default
478 //
479 *HaveBadAddress = FALSE;
480
481 //
482 // Get the exception record
483 //
484 ExceptionRecord = ExceptionInfo->ExceptionRecord;
485
486 //
487 // Look at the exception code
488 //
489 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
490 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
491 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
492 {
493 //
494 // We can tell the address if we have more than one parameter
495 //
496 if (ExceptionRecord->NumberParameters > 1)
497 {
498 //
499 // Return the address
500 //
501 *HaveBadAddress = TRUE;
502 *BadAddress = ExceptionRecord->ExceptionInformation[1];
503 }
504 }
505
506 //
507 // Continue executing the next handler
508 //
509 return EXCEPTION_EXECUTE_HANDLER;
510 }
511
512 NTSTATUS
513 NTAPI
514 MiDoMappedCopy(IN PEPROCESS SourceProcess,
515 IN PVOID SourceAddress,
516 IN PEPROCESS TargetProcess,
517 OUT PVOID TargetAddress,
518 IN SIZE_T BufferSize,
519 IN KPROCESSOR_MODE PreviousMode,
520 OUT PSIZE_T ReturnSize)
521 {
522 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
523 PMDL Mdl = (PMDL)MdlBuffer;
524 SIZE_T TotalSize, CurrentSize, RemainingSize;
525 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
526 volatile BOOLEAN PagesLocked;
527 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
528 volatile PVOID MdlAddress;
529 KAPC_STATE ApcState;
530 BOOLEAN HaveBadAddress;
531 ULONG_PTR BadAddress;
532 NTSTATUS Status = STATUS_SUCCESS;
533 PAGED_CODE();
534
535 //
536 // Calculate the maximum amount of data to move
537 //
538 TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
539 if (BufferSize <= TotalSize) TotalSize = BufferSize;
540 CurrentSize = TotalSize;
541 RemainingSize = BufferSize;
542
543 //
544 // Loop as long as there is still data
545 //
546 while (RemainingSize > 0)
547 {
548 //
549 // Check if this transfer will finish everything off
550 //
551 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
552
553 //
554 // Attach to the source address space
555 //
556 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
557
558 //
559 // Reset state for this pass
560 //
561 MdlAddress = NULL;
562 PagesLocked = FALSE;
563 FailedInMoving = FALSE;
564 ASSERT(FailedInProbe == FALSE);
565
566 //
567 // Protect user-mode copy
568 //
569 _SEH2_TRY
570 {
571 //
572 // If this is our first time, probe the buffer
573 //
574 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
575 {
576 //
577 // Catch a failure here
578 //
579 FailedInProbe = TRUE;
580
581 //
582 // Do the probe
583 //
584 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
585
586 //
587 // Passed
588 //
589 FailedInProbe = FALSE;
590 }
591
592 //
593 // Initialize and probe and lock the MDL
594 //
595 MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
596 MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
597 PagesLocked = TRUE;
598
599 //
600 // Now map the pages
601 //
602 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
603 KernelMode,
604 MmCached,
605 NULL,
606 FALSE,
607 HighPagePriority);
608 if (!MdlAddress)
609 {
610 //
611 // Use our SEH handler to pick this up
612 //
613 FailedInMapping = TRUE;
614 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
615 }
616
617 //
618 // Now let go of the source and grab to the target process
619 //
620 KeUnstackDetachProcess(&ApcState);
621 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
622
623 //
624 // Check if this is our first time through
625 //
626 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
627 {
628 //
629 // Catch a failure here
630 //
631 FailedInProbe = TRUE;
632
633 //
634 // Do the probe
635 //
636 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
637
638 //
639 // Passed
640 //
641 FailedInProbe = FALSE;
642 }
643
644 //
645 // Now do the actual move
646 //
647 FailedInMoving = TRUE;
648 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
649 }
650 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
651 &HaveBadAddress,
652 &BadAddress))
653 {
654 //
655 // Detach from whoever we may be attached to
656 //
657 KeUnstackDetachProcess(&ApcState);
658
659 //
660 // Check if we had mapped the pages
661 //
662 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
663
664 //
665 // Check if we had locked the pages
666 //
667 if (PagesLocked) MmUnlockPages(Mdl);
668
669 //
670 // Check if we hit working set quota
671 //
672 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA)
673 {
674 //
675 // Return the error
676 //
677 return STATUS_WORKING_SET_QUOTA;
678 }
679
680 //
681 // Check if we failed during the probe or mapping
682 //
683 if ((FailedInProbe) || (FailedInMapping))
684 {
685 //
686 // Exit
687 //
688 Status = _SEH2_GetExceptionCode();
689 _SEH2_YIELD(return Status);
690 }
691
692 //
693 // Otherwise, we failed probably during the move
694 //
695 *ReturnSize = BufferSize - RemainingSize;
696 if (FailedInMoving)
697 {
698 //
699 // Check if we know exactly where we stopped copying
700 //
701 if (HaveBadAddress)
702 {
703 //
704 // Return the exact number of bytes copied
705 //
706 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
707 }
708 }
709
710 //
711 // Return partial copy
712 //
713 Status = STATUS_PARTIAL_COPY;
714 }
715 _SEH2_END;
716
717 //
718 // Check for SEH status
719 //
720 if (Status != STATUS_SUCCESS) return Status;
721
722 //
723 // Detach from target
724 //
725 KeUnstackDetachProcess(&ApcState);
726
727 //
728 // Unmap and unlock
729 //
730 MmUnmapLockedPages(MdlAddress, Mdl);
731 MmUnlockPages(Mdl);
732
733 //
734 // Update location and size
735 //
736 RemainingSize -= CurrentSize;
737 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
738 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
739 }
740
741 //
742 // All bytes read
743 //
744 *ReturnSize = BufferSize;
745 return STATUS_SUCCESS;
746 }
747
748 NTSTATUS
749 NTAPI
750 MiDoPoolCopy(IN PEPROCESS SourceProcess,
751 IN PVOID SourceAddress,
752 IN PEPROCESS TargetProcess,
753 OUT PVOID TargetAddress,
754 IN SIZE_T BufferSize,
755 IN KPROCESSOR_MODE PreviousMode,
756 OUT PSIZE_T ReturnSize)
757 {
758 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
759 SIZE_T TotalSize, CurrentSize, RemainingSize;
760 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
761 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
762 PVOID PoolAddress;
763 KAPC_STATE ApcState;
764 BOOLEAN HaveBadAddress;
765 ULONG_PTR BadAddress;
766 NTSTATUS Status = STATUS_SUCCESS;
767 PAGED_CODE();
768
769 //
770 // Calculate the maximum amount of data to move
771 //
772 TotalSize = MI_MAX_TRANSFER_SIZE;
773 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
774 CurrentSize = TotalSize;
775 RemainingSize = BufferSize;
776
777 //
778 // Check if we can use the stack
779 //
780 if (BufferSize <= MI_POOL_COPY_BYTES)
781 {
782 //
783 // Use it
784 //
785 PoolAddress = (PVOID)StackBuffer;
786 }
787 else
788 {
789 //
790 // Allocate pool
791 //
792 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
793 if (!PoolAddress) ASSERT(FALSE);
794 HavePoolAddress = TRUE;
795 }
796
797 //
798 // Loop as long as there is still data
799 //
800 while (RemainingSize > 0)
801 {
802 //
803 // Check if this transfer will finish everything off
804 //
805 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
806
807 //
808 // Attach to the source address space
809 //
810 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
811
812 //
813 // Reset state for this pass
814 //
815 FailedInMoving = FALSE;
816 ASSERT(FailedInProbe == FALSE);
817
818 //
819 // Protect user-mode copy
820 //
821 _SEH2_TRY
822 {
823 //
824 // If this is our first time, probe the buffer
825 //
826 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
827 {
828 //
829 // Catch a failure here
830 //
831 FailedInProbe = TRUE;
832
833 //
834 // Do the probe
835 //
836 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
837
838 //
839 // Passed
840 //
841 FailedInProbe = FALSE;
842 }
843
844 //
845 // Do the copy
846 //
847 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
848
849 //
850 // Now let go of the source and grab to the target process
851 //
852 KeUnstackDetachProcess(&ApcState);
853 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
854
855 //
856 // Check if this is our first time through
857 //
858 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
859 {
860 //
861 // Catch a failure here
862 //
863 FailedInProbe = TRUE;
864
865 //
866 // Do the probe
867 //
868 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
869
870 //
871 // Passed
872 //
873 FailedInProbe = FALSE;
874 }
875
876 //
877 // Now do the actual move
878 //
879 FailedInMoving = TRUE;
880 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
881 }
882 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
883 &HaveBadAddress,
884 &BadAddress))
885 {
886 //
887 // Detach from whoever we may be attached to
888 //
889 KeUnstackDetachProcess(&ApcState);
890
891 //
892 // Check if we had allocated pool
893 //
894 if (HavePoolAddress) ExFreePool(PoolAddress);
895
896 //
897 // Check if we failed during the probe
898 //
899 if (FailedInProbe)
900 {
901 //
902 // Exit
903 //
904 Status = _SEH2_GetExceptionCode();
905 _SEH2_YIELD(return Status);
906 }
907
908 //
909 // Otherwise, we failed, probably during the move
910 //
911 *ReturnSize = BufferSize - RemainingSize;
912 if (FailedInMoving)
913 {
914 //
915 // Check if we know exactly where we stopped copying
916 //
917 if (HaveBadAddress)
918 {
919 //
920 // Return the exact number of bytes copied
921 //
922 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
923 }
924 }
925
926 //
927 // Return partial copy
928 //
929 Status = STATUS_PARTIAL_COPY;
930 }
931 _SEH2_END;
932
933 //
934 // Check for SEH status
935 //
936 if (Status != STATUS_SUCCESS) return Status;
937
938 //
939 // Detach from target
940 //
941 KeUnstackDetachProcess(&ApcState);
942
943 //
944 // Update location and size
945 //
946 RemainingSize -= CurrentSize;
947 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
948 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
949 CurrentSize);
950 }
951
952 //
953 // Check if we had allocated pool
954 //
955 if (HavePoolAddress) ExFreePool(PoolAddress);
956
957 //
958 // All bytes read
959 //
960 *ReturnSize = BufferSize;
961 return STATUS_SUCCESS;
962 }
963
964 NTSTATUS
965 NTAPI
966 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
967 IN PVOID SourceAddress,
968 IN PEPROCESS TargetProcess,
969 OUT PVOID TargetAddress,
970 IN SIZE_T BufferSize,
971 IN KPROCESSOR_MODE PreviousMode,
972 OUT PSIZE_T ReturnSize)
973 {
974 NTSTATUS Status;
975 PEPROCESS Process = SourceProcess;
976
977 //
978 // Don't accept zero-sized buffers
979 //
980 if (!BufferSize) return STATUS_SUCCESS;
981
982 //
983 // If we are copying from ourselves, lock the target instead
984 //
985 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
986
987 //
988 // Acquire rundown protection
989 //
990 if (!ExAcquireRundownProtection(&Process->RundownProtect))
991 {
992 //
993 // Fail
994 //
995 return STATUS_PROCESS_IS_TERMINATING;
996 }
997
998 //
999 // See if we should use the pool copy
1000 //
1001 if (BufferSize > MI_POOL_COPY_BYTES)
1002 {
1003 //
1004 // Use MDL-copy
1005 //
1006 Status = MiDoMappedCopy(SourceProcess,
1007 SourceAddress,
1008 TargetProcess,
1009 TargetAddress,
1010 BufferSize,
1011 PreviousMode,
1012 ReturnSize);
1013 }
1014 else
1015 {
1016 //
1017 // Do pool copy
1018 //
1019 Status = MiDoPoolCopy(SourceProcess,
1020 SourceAddress,
1021 TargetProcess,
1022 TargetAddress,
1023 BufferSize,
1024 PreviousMode,
1025 ReturnSize);
1026 }
1027
1028 //
1029 // Release the lock
1030 //
1031 ExReleaseRundownProtection(&Process->RundownProtect);
1032 return Status;
1033 }
1034
1035 NTSTATUS
1036 NTAPI
1037 MmFlushVirtualMemory(IN PEPROCESS Process,
1038 IN OUT PVOID *BaseAddress,
1039 IN OUT PSIZE_T RegionSize,
1040 OUT PIO_STATUS_BLOCK IoStatusBlock)
1041 {
1042 PAGED_CODE();
1043 UNIMPLEMENTED;
1044
1045 //
1046 // Fake success
1047 //
1048 return STATUS_SUCCESS;
1049 }
1050
1051 ULONG
1052 NTAPI
1053 MiGetPageProtection(IN PMMPTE PointerPte)
1054 {
1055 MMPTE TempPte;
1056 PMMPFN Pfn;
1057 PAGED_CODE();
1058
1059 /* Copy this PTE's contents */
1060 TempPte = *PointerPte;
1061
1062 /* Assure it's not totally zero */
1063 ASSERT(TempPte.u.Long);
1064
1065 /* Check for a special prototype format */
1066 if (TempPte.u.Soft.Valid == 0 &&
1067 TempPte.u.Soft.Prototype == 1)
1068 {
1069 /* Unsupported now */
1070 UNIMPLEMENTED;
1071 ASSERT(FALSE);
1072 }
1073
1074 /* In the easy case of transition or demand zero PTE just return its protection */
1075 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1076
1077 /* If we get here, the PTE is valid, so look up the page in PFN database */
1078 Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1079
1080 if (!Pfn->u3.e1.PrototypePte)
1081 {
1082 /* Return protection of the original pte */
1083 return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1084 }
1085
1086 /* This is hardware PTE */
1087 UNIMPLEMENTED;
1088 ASSERT(FALSE);
1089
1090 return PAGE_NOACCESS;
1091 }
1092
1093 ULONG
1094 NTAPI
1095 MiQueryAddressState(IN PVOID Va,
1096 IN PMMVAD Vad,
1097 IN PEPROCESS TargetProcess,
1098 OUT PULONG ReturnedProtect,
1099 OUT PVOID *NextVa)
1100 {
1101
1102 PMMPTE PointerPte, PointerPde;
1103 MMPTE TempPte;
1104 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1105 ULONG State = MEM_RESERVE, Protect = 0, LockChange;
1106 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1107 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1108
1109 /* Only normal VADs supported */
1110 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1111
1112 /* Get the PDE and PTE for the address */
1113 PointerPde = MiAddressToPde(Va);
1114 PointerPte = MiAddressToPte(Va);
1115
1116 /* Return the next range */
1117 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1118
1119 /* Loop to make sure the PDE is valid */
1120 do
1121 {
1122 /* Try again */
1123 LockChange = 0;
1124
1125 /* Is the PDE empty? */
1126 if (!PointerPde->u.Long)
1127 {
1128 /* No address in this range used yet, move to the next PDE range */
1129 *NextVa = MiPteToAddress(MiPteToAddress(PointerPde + 1));
1130 break;
1131 }
1132
1133 /* The PDE is empty, but is it faulted in? */
1134 if (!PointerPde->u.Hard.Valid)
1135 {
1136 /* It isn't, go ahead and do the fault */
1137 LockChange = MiMakeSystemAddressValid(MiPteToAddress(PointerPde),
1138 TargetProcess);
1139 }
1140
1141 /* Check if the PDE was faulted in, making the PTE readable */
1142 if (!LockChange) ValidPte = TRUE;
1143 } while (LockChange);
1144
1145 /* Is it safe to try reading the PTE? */
1146 if (ValidPte)
1147 {
1148 /* FIXME: watch out for large pages */
1149
1150 /* Capture the PTE */
1151 TempPte = *PointerPte;
1152 if (TempPte.u.Long)
1153 {
1154 /* The PTE is valid, so it's not zeroed out */
1155 DemandZeroPte = FALSE;
1156
1157 /* Check if it's valid or has a valid protection mask */
1158 ASSERT(TempPte.u.Soft.Prototype == 0);
1159 if ((TempPte.u.Soft.Protection != MM_DECOMMIT) ||
1160 (TempPte.u.Hard.Valid == 1))
1161 {
1162 /* This means it's committed */
1163 State = MEM_COMMIT;
1164
1165 /* Get protection state of this page */
1166 Protect = MiGetPageProtection(PointerPte);
1167 }
1168 else
1169 {
1170 /* Otherwise our defaults should hold */
1171 ASSERT(Protect == 0);
1172 ASSERT(State == MEM_RESERVE);
1173 }
1174 }
1175 }
1176
1177 /* Check if this was a demand-zero PTE, since we need to find the state */
1178 if (DemandZeroPte)
1179 {
1180 /* Check if the VAD is for committed memory */
1181 if (Vad->u.VadFlags.MemCommit)
1182 {
1183 /* This is committed memory */
1184 State = MEM_COMMIT;
1185
1186 /* Convert the protection */
1187 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1188 }
1189 }
1190
1191 /* Return the protection code */
1192 *ReturnedProtect = Protect;
1193 return State;
1194 }
1195
1196 /* PUBLIC FUNCTIONS ***********************************************************/
1197
1198 /*
1199 * @unimplemented
1200 */
1201 PVOID
1202 NTAPI
1203 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
1204 {
1205 UNIMPLEMENTED;
1206 return 0;
1207 }
1208
1209 /*
1210 * @unimplemented
1211 */
1212 PVOID
1213 NTAPI
1214 MmSecureVirtualMemory(IN PVOID Address,
1215 IN SIZE_T Length,
1216 IN ULONG Mode)
1217 {
1218 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
1219 return Address;
1220 }
1221
1222 /*
1223 * @unimplemented
1224 */
1225 VOID
1226 NTAPI
1227 MmUnsecureVirtualMemory(IN PVOID SecureMem)
1228 {
1229 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
1230 }
1231
1232 /* SYSTEM CALLS ***************************************************************/
1233
1234 NTSTATUS
1235 NTAPI
1236 NtReadVirtualMemory(IN HANDLE ProcessHandle,
1237 IN PVOID BaseAddress,
1238 OUT PVOID Buffer,
1239 IN SIZE_T NumberOfBytesToRead,
1240 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
1241 {
1242 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1243 PEPROCESS Process;
1244 NTSTATUS Status = STATUS_SUCCESS;
1245 SIZE_T BytesRead = 0;
1246 PAGED_CODE();
1247
1248 //
1249 // Check if we came from user mode
1250 //
1251 if (PreviousMode != KernelMode)
1252 {
1253 //
1254 // Validate the read addresses
1255 //
1256 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
1257 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
1258 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
1259 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
1260 {
1261 //
1262 // Don't allow to write into kernel space
1263 //
1264 return STATUS_ACCESS_VIOLATION;
1265 }
1266
1267 //
1268 // Enter SEH for probe
1269 //
1270 _SEH2_TRY
1271 {
1272 //
1273 // Probe the output value
1274 //
1275 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
1276 }
1277 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1278 {
1279 //
1280 // Get exception code
1281 //
1282 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1283 }
1284 _SEH2_END;
1285 }
1286
1287 //
1288 // Don't do zero-byte transfers
1289 //
1290 if (NumberOfBytesToRead)
1291 {
1292 //
1293 // Reference the process
1294 //
1295 Status = ObReferenceObjectByHandle(ProcessHandle,
1296 PROCESS_VM_READ,
1297 PsProcessType,
1298 PreviousMode,
1299 (PVOID*)(&Process),
1300 NULL);
1301 if (NT_SUCCESS(Status))
1302 {
1303 //
1304 // Do the copy
1305 //
1306 Status = MmCopyVirtualMemory(Process,
1307 BaseAddress,
1308 PsGetCurrentProcess(),
1309 Buffer,
1310 NumberOfBytesToRead,
1311 PreviousMode,
1312 &BytesRead);
1313
1314 //
1315 // Dereference the process
1316 //
1317 ObDereferenceObject(Process);
1318 }
1319 }
1320
1321 //
1322 // Check if the caller sent this parameter
1323 //
1324 if (NumberOfBytesRead)
1325 {
1326 //
1327 // Enter SEH to guard write
1328 //
1329 _SEH2_TRY
1330 {
1331 //
1332 // Return the number of bytes read
1333 //
1334 *NumberOfBytesRead = BytesRead;
1335 }
1336 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1337 {
1338 }
1339 _SEH2_END;
1340 }
1341
1342 //
1343 // Return status
1344 //
1345 return Status;
1346 }
1347
1348 NTSTATUS
1349 NTAPI
1350 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
1351 IN PVOID BaseAddress,
1352 IN PVOID Buffer,
1353 IN SIZE_T NumberOfBytesToWrite,
1354 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
1355 {
1356 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1357 PEPROCESS Process;
1358 NTSTATUS Status = STATUS_SUCCESS;
1359 SIZE_T BytesWritten = 0;
1360 PAGED_CODE();
1361
1362 //
1363 // Check if we came from user mode
1364 //
1365 if (PreviousMode != KernelMode)
1366 {
1367 //
1368 // Validate the read addresses
1369 //
1370 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
1371 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
1372 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
1373 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
1374 {
1375 //
1376 // Don't allow to write into kernel space
1377 //
1378 return STATUS_ACCESS_VIOLATION;
1379 }
1380
1381 //
1382 // Enter SEH for probe
1383 //
1384 _SEH2_TRY
1385 {
1386 //
1387 // Probe the output value
1388 //
1389 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
1390 }
1391 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1392 {
1393 //
1394 // Get exception code
1395 //
1396 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1397 }
1398 _SEH2_END;
1399 }
1400
1401 //
1402 // Don't do zero-byte transfers
1403 //
1404 if (NumberOfBytesToWrite)
1405 {
1406 //
1407 // Reference the process
1408 //
1409 Status = ObReferenceObjectByHandle(ProcessHandle,
1410 PROCESS_VM_WRITE,
1411 PsProcessType,
1412 PreviousMode,
1413 (PVOID*)&Process,
1414 NULL);
1415 if (NT_SUCCESS(Status))
1416 {
1417 //
1418 // Do the copy
1419 //
1420 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
1421 Buffer,
1422 Process,
1423 BaseAddress,
1424 NumberOfBytesToWrite,
1425 PreviousMode,
1426 &BytesWritten);
1427
1428 //
1429 // Dereference the process
1430 //
1431 ObDereferenceObject(Process);
1432 }
1433 }
1434
1435 //
1436 // Check if the caller sent this parameter
1437 //
1438 if (NumberOfBytesWritten)
1439 {
1440 //
1441 // Enter SEH to guard write
1442 //
1443 _SEH2_TRY
1444 {
1445 //
1446 // Return the number of bytes written
1447 //
1448 *NumberOfBytesWritten = BytesWritten;
1449 }
1450 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1451 {
1452 }
1453 _SEH2_END;
1454 }
1455
1456 //
1457 // Return status
1458 //
1459 return Status;
1460 }
1461
1462 NTSTATUS
1463 NTAPI
1464 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
1465 IN OUT PVOID *UnsafeBaseAddress,
1466 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
1467 IN ULONG NewAccessProtection,
1468 OUT PULONG UnsafeOldAccessProtection)
1469 {
1470 PEPROCESS Process;
1471 ULONG OldAccessProtection;
1472 ULONG Protection;
1473 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1474 PVOID BaseAddress = NULL;
1475 SIZE_T NumberOfBytesToProtect = 0;
1476 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1477 NTSTATUS Status;
1478 BOOLEAN Attached = FALSE;
1479 KAPC_STATE ApcState;
1480 PAGED_CODE();
1481
1482 //
1483 // Check for valid protection flags
1484 //
1485 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
1486 if (Protection != PAGE_NOACCESS &&
1487 Protection != PAGE_READONLY &&
1488 Protection != PAGE_READWRITE &&
1489 Protection != PAGE_WRITECOPY &&
1490 Protection != PAGE_EXECUTE &&
1491 Protection != PAGE_EXECUTE_READ &&
1492 Protection != PAGE_EXECUTE_READWRITE &&
1493 Protection != PAGE_EXECUTE_WRITECOPY)
1494 {
1495 //
1496 // Fail
1497 //
1498 return STATUS_INVALID_PAGE_PROTECTION;
1499 }
1500
1501 //
1502 // Check if we came from user mode
1503 //
1504 if (PreviousMode != KernelMode)
1505 {
1506 //
1507 // Enter SEH for probing
1508 //
1509 _SEH2_TRY
1510 {
1511 //
1512 // Validate all outputs
1513 //
1514 ProbeForWritePointer(UnsafeBaseAddress);
1515 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
1516 ProbeForWriteUlong(UnsafeOldAccessProtection);
1517
1518 //
1519 // Capture them
1520 //
1521 BaseAddress = *UnsafeBaseAddress;
1522 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1523 }
1524 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1525 {
1526 //
1527 // Get exception code
1528 //
1529 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1530 }
1531 _SEH2_END;
1532 }
1533 else
1534 {
1535 //
1536 // Capture directly
1537 //
1538 BaseAddress = *UnsafeBaseAddress;
1539 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1540 }
1541
1542 //
1543 // Catch illegal base address
1544 //
1545 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
1546
1547 //
1548 // Catch illegal region size
1549 //
1550 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
1551 {
1552 //
1553 // Fail
1554 //
1555 return STATUS_INVALID_PARAMETER_3;
1556 }
1557
1558 //
1559 // 0 is also illegal
1560 //
1561 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
1562
1563 //
1564 // Get a reference to the process
1565 //
1566 Status = ObReferenceObjectByHandle(ProcessHandle,
1567 PROCESS_VM_OPERATION,
1568 PsProcessType,
1569 PreviousMode,
1570 (PVOID*)(&Process),
1571 NULL);
1572 if (!NT_SUCCESS(Status)) return Status;
1573
1574 //
1575 // Check if we should attach
1576 //
1577 if (CurrentProcess != Process)
1578 {
1579 //
1580 // Do it
1581 //
1582 KeStackAttachProcess(&Process->Pcb, &ApcState);
1583 Attached = TRUE;
1584 }
1585
1586 //
1587 // Do the actual work
1588 //
1589 Status = MiProtectVirtualMemory(Process,
1590 &BaseAddress,
1591 &NumberOfBytesToProtect,
1592 NewAccessProtection,
1593 &OldAccessProtection);
1594
1595 //
1596 // Detach if needed
1597 //
1598 if (Attached) KeUnstackDetachProcess(&ApcState);
1599
1600 //
1601 // Release reference
1602 //
1603 ObDereferenceObject(Process);
1604
1605 //
1606 // Enter SEH to return data
1607 //
1608 _SEH2_TRY
1609 {
1610 //
1611 // Return data to user
1612 //
1613 *UnsafeOldAccessProtection = OldAccessProtection;
1614 *UnsafeBaseAddress = BaseAddress;
1615 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
1616 }
1617 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1618 {
1619 }
1620 _SEH2_END;
1621
1622 //
1623 // Return status
1624 //
1625 return Status;
1626 }
1627
1628 NTSTATUS
1629 NTAPI
1630 NtLockVirtualMemory(IN HANDLE ProcessHandle,
1631 IN OUT PVOID *BaseAddress,
1632 IN OUT PSIZE_T NumberOfBytesToLock,
1633 IN ULONG MapType)
1634 {
1635 PEPROCESS Process;
1636 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1637 NTSTATUS Status;
1638 BOOLEAN Attached = FALSE;
1639 KAPC_STATE ApcState;
1640 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1641 PVOID CapturedBaseAddress;
1642 SIZE_T CapturedBytesToLock;
1643 PAGED_CODE();
1644
1645 //
1646 // Validate flags
1647 //
1648 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
1649 {
1650 //
1651 // Invalid set of flags
1652 //
1653 return STATUS_INVALID_PARAMETER;
1654 }
1655
1656 //
1657 // At least one flag must be specified
1658 //
1659 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
1660 {
1661 //
1662 // No flag given
1663 //
1664 return STATUS_INVALID_PARAMETER;
1665 }
1666
1667 //
1668 // Enter SEH for probing
1669 //
1670 _SEH2_TRY
1671 {
1672 //
1673 // Validate output data
1674 //
1675 ProbeForWritePointer(BaseAddress);
1676 ProbeForWriteSize_t(NumberOfBytesToLock);
1677
1678 //
1679 // Capture it
1680 //
1681 CapturedBaseAddress = *BaseAddress;
1682 CapturedBytesToLock = *NumberOfBytesToLock;
1683 }
1684 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1685 {
1686 //
1687 // Get exception code
1688 //
1689 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1690 }
1691 _SEH2_END;
1692
1693 //
1694 // Catch illegal base address
1695 //
1696 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
1697
1698 //
1699 // Catch illegal region size
1700 //
1701 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
1702 {
1703 //
1704 // Fail
1705 //
1706 return STATUS_INVALID_PARAMETER;
1707 }
1708
1709 //
1710 // 0 is also illegal
1711 //
1712 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
1713
1714 //
1715 // Get a reference to the process
1716 //
1717 Status = ObReferenceObjectByHandle(ProcessHandle,
1718 PROCESS_VM_OPERATION,
1719 PsProcessType,
1720 PreviousMode,
1721 (PVOID*)(&Process),
1722 NULL);
1723 if (!NT_SUCCESS(Status)) return Status;
1724
1725 //
1726 // Check if this is is system-mapped
1727 //
1728 if (MapType & MAP_SYSTEM)
1729 {
1730 //
1731 // Check for required privilege
1732 //
1733 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
1734 {
1735 //
1736 // Fail: Don't have it
1737 //
1738 ObDereferenceObject(Process);
1739 return STATUS_PRIVILEGE_NOT_HELD;
1740 }
1741 }
1742
1743 //
1744 // Check if we should attach
1745 //
1746 if (CurrentProcess != Process)
1747 {
1748 //
1749 // Do it
1750 //
1751 KeStackAttachProcess(&Process->Pcb, &ApcState);
1752 Attached = TRUE;
1753 }
1754
1755 //
1756 // Oops :(
1757 //
1758 UNIMPLEMENTED;
1759
1760 //
1761 // Detach if needed
1762 //
1763 if (Attached) KeUnstackDetachProcess(&ApcState);
1764
1765 //
1766 // Release reference
1767 //
1768 ObDereferenceObject(Process);
1769
1770 //
1771 // Enter SEH to return data
1772 //
1773 _SEH2_TRY
1774 {
1775 //
1776 // Return data to user
1777 //
1778 *BaseAddress = CapturedBaseAddress;
1779 *NumberOfBytesToLock = 0;
1780 }
1781 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1782 {
1783 //
1784 // Get exception code
1785 //
1786 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1787 }
1788 _SEH2_END;
1789
1790 //
1791 // Return status
1792 //
1793 return STATUS_SUCCESS;
1794 }
1795
1796 NTSTATUS
1797 NTAPI
1798 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
1799 IN OUT PVOID *BaseAddress,
1800 IN OUT PSIZE_T NumberOfBytesToUnlock,
1801 IN ULONG MapType)
1802 {
1803 PEPROCESS Process;
1804 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1805 NTSTATUS Status;
1806 BOOLEAN Attached = FALSE;
1807 KAPC_STATE ApcState;
1808 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1809 PVOID CapturedBaseAddress;
1810 SIZE_T CapturedBytesToUnlock;
1811 PAGED_CODE();
1812
1813 //
1814 // Validate flags
1815 //
1816 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
1817 {
1818 //
1819 // Invalid set of flags
1820 //
1821 return STATUS_INVALID_PARAMETER;
1822 }
1823
1824 //
1825 // At least one flag must be specified
1826 //
1827 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
1828 {
1829 //
1830 // No flag given
1831 //
1832 return STATUS_INVALID_PARAMETER;
1833 }
1834
1835 //
1836 // Enter SEH for probing
1837 //
1838 _SEH2_TRY
1839 {
1840 //
1841 // Validate output data
1842 //
1843 ProbeForWritePointer(BaseAddress);
1844 ProbeForWriteSize_t(NumberOfBytesToUnlock);
1845
1846 //
1847 // Capture it
1848 //
1849 CapturedBaseAddress = *BaseAddress;
1850 CapturedBytesToUnlock = *NumberOfBytesToUnlock;
1851 }
1852 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1853 {
1854 //
1855 // Get exception code
1856 //
1857 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1858 }
1859 _SEH2_END;
1860
1861 //
1862 // Catch illegal base address
1863 //
1864 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
1865
1866 //
1867 // Catch illegal region size
1868 //
1869 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
1870 {
1871 //
1872 // Fail
1873 //
1874 return STATUS_INVALID_PARAMETER;
1875 }
1876
1877 //
1878 // 0 is also illegal
1879 //
1880 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
1881
1882 //
1883 // Get a reference to the process
1884 //
1885 Status = ObReferenceObjectByHandle(ProcessHandle,
1886 PROCESS_VM_OPERATION,
1887 PsProcessType,
1888 PreviousMode,
1889 (PVOID*)(&Process),
1890 NULL);
1891 if (!NT_SUCCESS(Status)) return Status;
1892
1893 //
1894 // Check if this is is system-mapped
1895 //
1896 if (MapType & MAP_SYSTEM)
1897 {
1898 //
1899 // Check for required privilege
1900 //
1901 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
1902 {
1903 //
1904 // Fail: Don't have it
1905 //
1906 ObDereferenceObject(Process);
1907 return STATUS_PRIVILEGE_NOT_HELD;
1908 }
1909 }
1910
1911 //
1912 // Check if we should attach
1913 //
1914 if (CurrentProcess != Process)
1915 {
1916 //
1917 // Do it
1918 //
1919 KeStackAttachProcess(&Process->Pcb, &ApcState);
1920 Attached = TRUE;
1921 }
1922
1923 //
1924 // Oops :(
1925 //
1926 UNIMPLEMENTED;
1927
1928 //
1929 // Detach if needed
1930 //
1931 if (Attached) KeUnstackDetachProcess(&ApcState);
1932
1933 //
1934 // Release reference
1935 //
1936 ObDereferenceObject(Process);
1937
1938 //
1939 // Enter SEH to return data
1940 //
1941 _SEH2_TRY
1942 {
1943 //
1944 // Return data to user
1945 //
1946 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
1947 *NumberOfBytesToUnlock = 0;
1948 }
1949 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1950 {
1951 //
1952 // Get exception code
1953 //
1954 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1955 }
1956 _SEH2_END;
1957
1958 //
1959 // Return status
1960 //
1961 return STATUS_SUCCESS;
1962 }
1963
1964 NTSTATUS
1965 NTAPI
1966 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
1967 IN OUT PVOID *BaseAddress,
1968 IN OUT PSIZE_T NumberOfBytesToFlush,
1969 OUT PIO_STATUS_BLOCK IoStatusBlock)
1970 {
1971 PEPROCESS Process;
1972 NTSTATUS Status;
1973 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1974 PVOID CapturedBaseAddress;
1975 SIZE_T CapturedBytesToFlush;
1976 IO_STATUS_BLOCK LocalStatusBlock;
1977 PAGED_CODE();
1978
1979 //
1980 // Check if we came from user mode
1981 //
1982 if (PreviousMode != KernelMode)
1983 {
1984 //
1985 // Enter SEH for probing
1986 //
1987 _SEH2_TRY
1988 {
1989 //
1990 // Validate all outputs
1991 //
1992 ProbeForWritePointer(BaseAddress);
1993 ProbeForWriteSize_t(NumberOfBytesToFlush);
1994 ProbeForWriteIoStatusBlock(IoStatusBlock);
1995
1996 //
1997 // Capture them
1998 //
1999 CapturedBaseAddress = *BaseAddress;
2000 CapturedBytesToFlush = *NumberOfBytesToFlush;
2001 }
2002 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2003 {
2004 //
2005 // Get exception code
2006 //
2007 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2008 }
2009 _SEH2_END;
2010 }
2011 else
2012 {
2013 //
2014 // Capture directly
2015 //
2016 CapturedBaseAddress = *BaseAddress;
2017 CapturedBytesToFlush = *NumberOfBytesToFlush;
2018 }
2019
2020 //
2021 // Catch illegal base address
2022 //
2023 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
2024
2025 //
2026 // Catch illegal region size
2027 //
2028 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
2029 {
2030 //
2031 // Fail
2032 //
2033 return STATUS_INVALID_PARAMETER;
2034 }
2035
2036 //
2037 // Get a reference to the process
2038 //
2039 Status = ObReferenceObjectByHandle(ProcessHandle,
2040 PROCESS_VM_OPERATION,
2041 PsProcessType,
2042 PreviousMode,
2043 (PVOID*)(&Process),
2044 NULL);
2045 if (!NT_SUCCESS(Status)) return Status;
2046
2047 //
2048 // Do it
2049 //
2050 Status = MmFlushVirtualMemory(Process,
2051 &CapturedBaseAddress,
2052 &CapturedBytesToFlush,
2053 &LocalStatusBlock);
2054
2055 //
2056 // Release reference
2057 //
2058 ObDereferenceObject(Process);
2059
2060 //
2061 // Enter SEH to return data
2062 //
2063 _SEH2_TRY
2064 {
2065 //
2066 // Return data to user
2067 //
2068 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
2069 *NumberOfBytesToFlush = 0;
2070 *IoStatusBlock = LocalStatusBlock;
2071 }
2072 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2073 {
2074 }
2075 _SEH2_END;
2076
2077 //
2078 // Return status
2079 //
2080 return Status;
2081 }
2082
2083 /*
2084 * @unimplemented
2085 */
2086 NTSTATUS
2087 NTAPI
2088 NtGetWriteWatch(IN HANDLE ProcessHandle,
2089 IN ULONG Flags,
2090 IN PVOID BaseAddress,
2091 IN SIZE_T RegionSize,
2092 IN PVOID *UserAddressArray,
2093 OUT PULONG_PTR EntriesInUserAddressArray,
2094 OUT PULONG Granularity)
2095 {
2096 PEPROCESS Process;
2097 NTSTATUS Status;
2098 PVOID EndAddress;
2099 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2100 ULONG_PTR CapturedEntryCount;
2101 PAGED_CODE();
2102
2103 //
2104 // Check if we came from user mode
2105 //
2106 if (PreviousMode != KernelMode)
2107 {
2108 //
2109 // Enter SEH for probing
2110 //
2111 _SEH2_TRY
2112 {
2113 //
2114 // Catch illegal base address
2115 //
2116 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2117
2118 //
2119 // Catch illegal region size
2120 //
2121 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
2122 {
2123 //
2124 // Fail
2125 //
2126 return STATUS_INVALID_PARAMETER_3;
2127 }
2128
2129 //
2130 // Validate all data
2131 //
2132 ProbeForWriteSize_t(EntriesInUserAddressArray);
2133 ProbeForWriteUlong(Granularity);
2134
2135 //
2136 // Capture them
2137 //
2138 CapturedEntryCount = *EntriesInUserAddressArray;
2139
2140 //
2141 // Must have a count
2142 //
2143 if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5;
2144
2145 //
2146 // Can't be larger than the maximum
2147 //
2148 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
2149 {
2150 //
2151 // Fail
2152 //
2153 return STATUS_INVALID_PARAMETER_5;
2154 }
2155
2156 //
2157 // Probe the actual array
2158 //
2159 ProbeForWrite(UserAddressArray,
2160 CapturedEntryCount * sizeof(PVOID),
2161 sizeof(PVOID));
2162 }
2163 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2164 {
2165 //
2166 // Get exception code
2167 //
2168 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2169 }
2170 _SEH2_END;
2171 }
2172 else
2173 {
2174 //
2175 // Capture directly
2176 //
2177 CapturedEntryCount = *EntriesInUserAddressArray;
2178 ASSERT(CapturedEntryCount != 0);
2179 }
2180
2181 //
2182 // Check if this is a local request
2183 //
2184 if (ProcessHandle == NtCurrentProcess())
2185 {
2186 //
2187 // No need to reference the process
2188 //
2189 Process = PsGetCurrentProcess();
2190 }
2191 else
2192 {
2193 //
2194 // Reference the target
2195 //
2196 Status = ObReferenceObjectByHandle(ProcessHandle,
2197 PROCESS_VM_OPERATION,
2198 PsProcessType,
2199 PreviousMode,
2200 (PVOID *)&Process,
2201 NULL);
2202 if (!NT_SUCCESS(Status)) return Status;
2203 }
2204
2205 //
2206 // Compute the last address and validate it
2207 //
2208 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
2209 if (BaseAddress > EndAddress)
2210 {
2211 //
2212 // Fail
2213 //
2214 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2215 return STATUS_INVALID_PARAMETER_4;
2216 }
2217
2218 //
2219 // Oops :(
2220 //
2221 UNIMPLEMENTED;
2222
2223 //
2224 // Dereference if needed
2225 //
2226 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2227
2228 //
2229 // Enter SEH to return data
2230 //
2231 _SEH2_TRY
2232 {
2233 //
2234 // Return data to user
2235 //
2236 *EntriesInUserAddressArray = 0;
2237 *Granularity = PAGE_SIZE;
2238 }
2239 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2240 {
2241 //
2242 // Get exception code
2243 //
2244 Status = _SEH2_GetExceptionCode();
2245 }
2246 _SEH2_END;
2247
2248 //
2249 // Return success
2250 //
2251 return STATUS_SUCCESS;
2252 }
2253
2254 /*
2255 * @unimplemented
2256 */
2257 NTSTATUS
2258 NTAPI
2259 NtResetWriteWatch(IN HANDLE ProcessHandle,
2260 IN PVOID BaseAddress,
2261 IN SIZE_T RegionSize)
2262 {
2263 PVOID EndAddress;
2264 PEPROCESS Process;
2265 NTSTATUS Status;
2266 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2267 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
2268
2269 //
2270 // Catch illegal base address
2271 //
2272 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2273
2274 //
2275 // Catch illegal region size
2276 //
2277 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
2278 {
2279 //
2280 // Fail
2281 //
2282 return STATUS_INVALID_PARAMETER_3;
2283 }
2284
2285 //
2286 // Check if this is a local request
2287 //
2288 if (ProcessHandle == NtCurrentProcess())
2289 {
2290 //
2291 // No need to reference the process
2292 //
2293 Process = PsGetCurrentProcess();
2294 }
2295 else
2296 {
2297 //
2298 // Reference the target
2299 //
2300 Status = ObReferenceObjectByHandle(ProcessHandle,
2301 PROCESS_VM_OPERATION,
2302 PsProcessType,
2303 PreviousMode,
2304 (PVOID *)&Process,
2305 NULL);
2306 if (!NT_SUCCESS(Status)) return Status;
2307 }
2308
2309 //
2310 // Compute the last address and validate it
2311 //
2312 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
2313 if (BaseAddress > EndAddress)
2314 {
2315 //
2316 // Fail
2317 //
2318 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2319 return STATUS_INVALID_PARAMETER_3;
2320 }
2321
2322 //
2323 // Oops :(
2324 //
2325 UNIMPLEMENTED;
2326
2327 //
2328 // Dereference if needed
2329 //
2330 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2331
2332 //
2333 // Return success
2334 //
2335 return STATUS_SUCCESS;
2336 }
2337
2338 NTSTATUS
2339 NTAPI
2340 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
2341 IN PVOID BaseAddress,
2342 IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
2343 OUT PVOID MemoryInformation,
2344 IN SIZE_T MemoryInformationLength,
2345 OUT PSIZE_T ReturnLength)
2346 {
2347 PEPROCESS TargetProcess;
2348 NTSTATUS Status;
2349 PMMVAD Vad = NULL;
2350 PVOID Address, NextAddress;
2351 BOOLEAN Found = FALSE;
2352 ULONG NewProtect, NewState, BaseVpn;
2353 MEMORY_BASIC_INFORMATION MemoryInfo;
2354 KAPC_STATE ApcState;
2355 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
2356
2357 /* Only this class is supported for now */
2358 ASSERT(MemoryInformationClass == MemoryBasicInformation);
2359
2360 /* Validate the size information of the class */
2361 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
2362 {
2363 /* The size is invalid */
2364 return STATUS_INFO_LENGTH_MISMATCH;
2365 }
2366
2367 /* Bail out if the address is invalid */
2368 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
2369
2370 /* Check for illegal addresses in user-space, or the shared memory area */
2371 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
2372 (PAGE_ALIGN(BaseAddress) == (PVOID)USER_SHARED_DATA))
2373 {
2374 Address = PAGE_ALIGN(BaseAddress);
2375
2376 /* Make up an info structure describing this range */
2377 MemoryInfo.BaseAddress = Address;
2378 MemoryInfo.AllocationProtect = PAGE_READONLY;
2379 MemoryInfo.Type = MEM_PRIVATE;
2380
2381 /* Special case for shared data */
2382 if (Address == (PVOID)USER_SHARED_DATA)
2383 {
2384 MemoryInfo.AllocationBase = (PVOID)USER_SHARED_DATA;
2385 MemoryInfo.State = MEM_COMMIT;
2386 MemoryInfo.Protect = PAGE_READONLY;
2387 MemoryInfo.RegionSize = PAGE_SIZE;
2388 }
2389 else
2390 {
2391 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
2392 MemoryInfo.State = MEM_RESERVE;
2393 MemoryInfo.Protect = PAGE_NOACCESS;
2394 MemoryInfo.RegionSize = (ULONG_PTR)MemoryInfo.AllocationBase - (ULONG_PTR)Address;
2395 }
2396
2397 /* Return the data (FIXME: Use SEH) */
2398 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2399 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2400
2401 return STATUS_SUCCESS;
2402 }
2403
2404 /* Check if this is for a local or remote process */
2405 if (ProcessHandle == NtCurrentProcess())
2406 {
2407 TargetProcess = PsGetCurrentProcess();
2408 }
2409 else
2410 {
2411 /* Reference the target process */
2412 Status = ObReferenceObjectByHandle(ProcessHandle,
2413 PROCESS_QUERY_INFORMATION,
2414 PsProcessType,
2415 ExGetPreviousMode(),
2416 (PVOID*)&TargetProcess,
2417 NULL);
2418 if (!NT_SUCCESS(Status)) return Status;
2419
2420 /* Attach to it now */
2421 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
2422 }
2423
2424 /* Loop the VADs */
2425 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
2426 if (TargetProcess->VadRoot.NumberGenericTableElements)
2427 {
2428 /* Scan on the right */
2429 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
2430 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
2431 while (Vad)
2432 {
2433 /* Check if this VAD covers the allocation range */
2434 if ((BaseVpn >= Vad->StartingVpn) &&
2435 (BaseVpn <= Vad->EndingVpn))
2436 {
2437 /* We're done */
2438 Found = TRUE;
2439 break;
2440 }
2441
2442 /* Check if this VAD is too high */
2443 if (BaseVpn < Vad->StartingVpn)
2444 {
2445 /* Search on the left next */
2446 Vad = Vad->LeftChild;
2447 }
2448 else
2449 {
2450 /* Then this VAD is too low, keep searching on the right */
2451 ASSERT(BaseVpn > Vad->EndingVpn);
2452 Vad = Vad->RightChild;
2453 }
2454 }
2455 }
2456
2457 /* Was a VAD found? */
2458 if (!Found)
2459 {
2460 Address = PAGE_ALIGN(BaseAddress);
2461
2462 /* Calculate region size */
2463 if (Vad)
2464 {
2465 if (Vad->StartingVpn >= BaseVpn)
2466 {
2467 /* Region size is the free space till the start of that VAD */
2468 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
2469 }
2470 else
2471 {
2472 /* Get the next VAD */
2473 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
2474 if (Vad)
2475 {
2476 /* Region size is the free space till the start of that VAD */
2477 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
2478 }
2479 else
2480 {
2481 /* Maximum possible region size with that base address */
2482 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
2483 }
2484 }
2485 }
2486 else
2487 {
2488 /* Maximum possible region size with that base address */
2489 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
2490 }
2491
2492 /* Check if we were attached */
2493 if (ProcessHandle != NtCurrentProcess())
2494 {
2495 /* Detach and derefernece the process */
2496 KeUnstackDetachProcess(&ApcState);
2497 ObDereferenceObject(TargetProcess);
2498 }
2499
2500 /* Build the rest of the initial information block */
2501 MemoryInfo.BaseAddress = Address;
2502 MemoryInfo.AllocationBase = NULL;
2503 MemoryInfo.AllocationProtect = 0;
2504 MemoryInfo.State = MEM_FREE;
2505 MemoryInfo.Protect = PAGE_NOACCESS;
2506 MemoryInfo.Type = 0;
2507
2508 /* Return the data (FIXME: Use SEH) */
2509 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2510 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2511
2512 return STATUS_SUCCESS;
2513 }
2514
2515 /* This must be a VM VAD */
2516 ASSERT(Vad->u.VadFlags.PrivateMemory);
2517
2518 /* Build the initial information block */
2519 Address = PAGE_ALIGN(BaseAddress);
2520 MemoryInfo.BaseAddress = Address;
2521 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
2522 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2523 MemoryInfo.Type = MEM_PRIVATE;
2524
2525 /* Find the largest chunk of memory which has the same state and protection mask */
2526 MemoryInfo.State = MiQueryAddressState(Address,
2527 Vad,
2528 TargetProcess,
2529 &MemoryInfo.Protect,
2530 &NextAddress);
2531 Address = NextAddress;
2532 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
2533 {
2534 /* Keep going unless the state or protection mask changed */
2535 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
2536 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
2537 Address = NextAddress;
2538 }
2539
2540 /* Now that we know the last VA address, calculate hte region size */
2541 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
2542
2543 /* Check if we were attached */
2544 if (ProcessHandle != NtCurrentProcess())
2545 {
2546 /* Detach and derefernece the process */
2547 KeUnstackDetachProcess(&ApcState);
2548 ObDereferenceObject(TargetProcess);
2549 }
2550
2551 /* Return the data (FIXME: Use SEH) */
2552 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2553 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2554
2555 /* All went well */
2556 DPRINT("Base: %p AllocBase: %p Protect: %lx AllocProtect: %lx "
2557 "State: %lx Type: %lx Size: %lx\n",
2558 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
2559 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
2560 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
2561 return STATUS_SUCCESS;
2562 }
2563
2564 /* EOF */