dd0f818e0cae13596f8e89c3b49f8ff03cb4cc87
[reactos.git] / reactos / 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 MiQueryAddressState(IN PVOID Va,
1054 IN PMMVAD Vad,
1055 IN PEPROCESS TargetProcess,
1056 OUT PULONG ReturnedProtect,
1057 OUT PVOID *NextVa)
1058 {
1059
1060 PMMPTE PointerPte, PointerPde;
1061 MMPTE TempPte;
1062 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1063 ULONG State = MEM_RESERVE, Protect = 0, LockChange;
1064 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1065 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1066
1067 /* Only normal VADs supported */
1068 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1069
1070 /* Get the PDE and PTE for the address */
1071 PointerPde = MiAddressToPde(Va);
1072 PointerPte = MiAddressToPte(Va);
1073
1074 /* Return the next range */
1075 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1076
1077 /* Loop to make sure the PDE is valid */
1078 do
1079 {
1080 /* Try again */
1081 LockChange = 0;
1082
1083 /* Is the PDE empty? */
1084 if (!PointerPde->u.Long)
1085 {
1086 /* No address in this range used yet, move to the next PDE range */
1087 *NextVa = MiPteToAddress(MiPteToAddress(PointerPde + 1));
1088 break;
1089 }
1090
1091 /* The PDE is empty, but is it faulted in? */
1092 if (!PointerPde->u.Hard.Valid)
1093 {
1094 /* It isn't, go ahead and do the fault */
1095 LockChange = MiMakeSystemAddressValid(MiPteToAddress(PointerPde),
1096 TargetProcess);
1097 }
1098
1099 /* Check if the PDE was faulted in, making the PTE readable */
1100 if (!LockChange) ValidPte = TRUE;
1101 } while (LockChange);
1102
1103 /* Is it safe to try reading the PTE? */
1104 if (ValidPte)
1105 {
1106 /* FIXME: watch out for large pages */
1107
1108 /* Capture the PTE */
1109 TempPte = *PointerPte;
1110 if (TempPte.u.Long)
1111 {
1112 /* The PTE is valid, so it's not zeroed out */
1113 DemandZeroPte = FALSE;
1114
1115 /* Check if it's valid or has a valid protection mask */
1116 ASSERT(TempPte.u.Soft.Prototype == 0);
1117 if ((TempPte.u.Soft.Protection != MM_DECOMMIT) ||
1118 (TempPte.u.Hard.Valid == 1))
1119 {
1120 /* This means it's committed */
1121 State = MEM_COMMIT;
1122
1123 /* For now, we lie about the protection */
1124 Protect = PAGE_EXECUTE_READWRITE;
1125 }
1126 else
1127 {
1128 /* Otherwise our defaults should hold */
1129 ASSERT(Protect == 0);
1130 ASSERT(State == MEM_RESERVE);
1131 }
1132 }
1133 }
1134
1135 /* Check if this was a demand-zero PTE, since we need to find the state */
1136 if (DemandZeroPte)
1137 {
1138 /* Check if the VAD is for committed memory */
1139 if (Vad->u.VadFlags.MemCommit)
1140 {
1141 /* This is committed memory */
1142 State = MEM_COMMIT;
1143
1144 /* Convert the protection */
1145 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1146 }
1147 }
1148
1149 /* Return the protection code */
1150 *ReturnedProtect = Protect;
1151 return State;
1152 }
1153
1154 /* PUBLIC FUNCTIONS ***********************************************************/
1155
1156 /*
1157 * @unimplemented
1158 */
1159 PVOID
1160 NTAPI
1161 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
1162 {
1163 UNIMPLEMENTED;
1164 return 0;
1165 }
1166
1167 /*
1168 * @unimplemented
1169 */
1170 PVOID
1171 NTAPI
1172 MmSecureVirtualMemory(IN PVOID Address,
1173 IN SIZE_T Length,
1174 IN ULONG Mode)
1175 {
1176 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
1177 return Address;
1178 }
1179
1180 /*
1181 * @unimplemented
1182 */
1183 VOID
1184 NTAPI
1185 MmUnsecureVirtualMemory(IN PVOID SecureMem)
1186 {
1187 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
1188 }
1189
1190 /* SYSTEM CALLS ***************************************************************/
1191
1192 NTSTATUS
1193 NTAPI
1194 NtReadVirtualMemory(IN HANDLE ProcessHandle,
1195 IN PVOID BaseAddress,
1196 OUT PVOID Buffer,
1197 IN SIZE_T NumberOfBytesToRead,
1198 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
1199 {
1200 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1201 PEPROCESS Process;
1202 NTSTATUS Status = STATUS_SUCCESS;
1203 SIZE_T BytesRead = 0;
1204 PAGED_CODE();
1205
1206 //
1207 // Check if we came from user mode
1208 //
1209 if (PreviousMode != KernelMode)
1210 {
1211 //
1212 // Validate the read addresses
1213 //
1214 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
1215 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
1216 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
1217 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
1218 {
1219 //
1220 // Don't allow to write into kernel space
1221 //
1222 return STATUS_ACCESS_VIOLATION;
1223 }
1224
1225 //
1226 // Enter SEH for probe
1227 //
1228 _SEH2_TRY
1229 {
1230 //
1231 // Probe the output value
1232 //
1233 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
1234 }
1235 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1236 {
1237 //
1238 // Get exception code
1239 //
1240 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1241 }
1242 _SEH2_END;
1243 }
1244
1245 //
1246 // Don't do zero-byte transfers
1247 //
1248 if (NumberOfBytesToRead)
1249 {
1250 //
1251 // Reference the process
1252 //
1253 Status = ObReferenceObjectByHandle(ProcessHandle,
1254 PROCESS_VM_READ,
1255 PsProcessType,
1256 PreviousMode,
1257 (PVOID*)(&Process),
1258 NULL);
1259 if (NT_SUCCESS(Status))
1260 {
1261 //
1262 // Do the copy
1263 //
1264 Status = MmCopyVirtualMemory(Process,
1265 BaseAddress,
1266 PsGetCurrentProcess(),
1267 Buffer,
1268 NumberOfBytesToRead,
1269 PreviousMode,
1270 &BytesRead);
1271
1272 //
1273 // Dereference the process
1274 //
1275 ObDereferenceObject(Process);
1276 }
1277 }
1278
1279 //
1280 // Check if the caller sent this parameter
1281 //
1282 if (NumberOfBytesRead)
1283 {
1284 //
1285 // Enter SEH to guard write
1286 //
1287 _SEH2_TRY
1288 {
1289 //
1290 // Return the number of bytes read
1291 //
1292 *NumberOfBytesRead = BytesRead;
1293 }
1294 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1295 {
1296 }
1297 _SEH2_END;
1298 }
1299
1300 //
1301 // Return status
1302 //
1303 return Status;
1304 }
1305
1306 NTSTATUS
1307 NTAPI
1308 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
1309 IN PVOID BaseAddress,
1310 IN PVOID Buffer,
1311 IN SIZE_T NumberOfBytesToWrite,
1312 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
1313 {
1314 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1315 PEPROCESS Process;
1316 NTSTATUS Status = STATUS_SUCCESS;
1317 SIZE_T BytesWritten = 0;
1318 PAGED_CODE();
1319
1320 //
1321 // Check if we came from user mode
1322 //
1323 if (PreviousMode != KernelMode)
1324 {
1325 //
1326 // Validate the read addresses
1327 //
1328 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
1329 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
1330 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
1331 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
1332 {
1333 //
1334 // Don't allow to write into kernel space
1335 //
1336 return STATUS_ACCESS_VIOLATION;
1337 }
1338
1339 //
1340 // Enter SEH for probe
1341 //
1342 _SEH2_TRY
1343 {
1344 //
1345 // Probe the output value
1346 //
1347 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
1348 }
1349 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1350 {
1351 //
1352 // Get exception code
1353 //
1354 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1355 }
1356 _SEH2_END;
1357 }
1358
1359 //
1360 // Don't do zero-byte transfers
1361 //
1362 if (NumberOfBytesToWrite)
1363 {
1364 //
1365 // Reference the process
1366 //
1367 Status = ObReferenceObjectByHandle(ProcessHandle,
1368 PROCESS_VM_WRITE,
1369 PsProcessType,
1370 PreviousMode,
1371 (PVOID*)&Process,
1372 NULL);
1373 if (NT_SUCCESS(Status))
1374 {
1375 //
1376 // Do the copy
1377 //
1378 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
1379 Buffer,
1380 Process,
1381 BaseAddress,
1382 NumberOfBytesToWrite,
1383 PreviousMode,
1384 &BytesWritten);
1385
1386 //
1387 // Dereference the process
1388 //
1389 ObDereferenceObject(Process);
1390 }
1391 }
1392
1393 //
1394 // Check if the caller sent this parameter
1395 //
1396 if (NumberOfBytesWritten)
1397 {
1398 //
1399 // Enter SEH to guard write
1400 //
1401 _SEH2_TRY
1402 {
1403 //
1404 // Return the number of bytes written
1405 //
1406 *NumberOfBytesWritten = BytesWritten;
1407 }
1408 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1409 {
1410 }
1411 _SEH2_END;
1412 }
1413
1414 //
1415 // Return status
1416 //
1417 return Status;
1418 }
1419
1420 NTSTATUS
1421 NTAPI
1422 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
1423 IN OUT PVOID *UnsafeBaseAddress,
1424 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
1425 IN ULONG NewAccessProtection,
1426 OUT PULONG UnsafeOldAccessProtection)
1427 {
1428 PEPROCESS Process;
1429 ULONG OldAccessProtection;
1430 ULONG Protection;
1431 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1432 PVOID BaseAddress = NULL;
1433 SIZE_T NumberOfBytesToProtect = 0;
1434 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1435 NTSTATUS Status;
1436 BOOLEAN Attached = FALSE;
1437 KAPC_STATE ApcState;
1438 PAGED_CODE();
1439
1440 //
1441 // Check for valid protection flags
1442 //
1443 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
1444 if (Protection != PAGE_NOACCESS &&
1445 Protection != PAGE_READONLY &&
1446 Protection != PAGE_READWRITE &&
1447 Protection != PAGE_WRITECOPY &&
1448 Protection != PAGE_EXECUTE &&
1449 Protection != PAGE_EXECUTE_READ &&
1450 Protection != PAGE_EXECUTE_READWRITE &&
1451 Protection != PAGE_EXECUTE_WRITECOPY)
1452 {
1453 //
1454 // Fail
1455 //
1456 return STATUS_INVALID_PAGE_PROTECTION;
1457 }
1458
1459 //
1460 // Check if we came from user mode
1461 //
1462 if (PreviousMode != KernelMode)
1463 {
1464 //
1465 // Enter SEH for probing
1466 //
1467 _SEH2_TRY
1468 {
1469 //
1470 // Validate all outputs
1471 //
1472 ProbeForWritePointer(UnsafeBaseAddress);
1473 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
1474 ProbeForWriteUlong(UnsafeOldAccessProtection);
1475
1476 //
1477 // Capture them
1478 //
1479 BaseAddress = *UnsafeBaseAddress;
1480 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1481 }
1482 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1483 {
1484 //
1485 // Get exception code
1486 //
1487 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1488 }
1489 _SEH2_END;
1490 }
1491 else
1492 {
1493 //
1494 // Capture directly
1495 //
1496 BaseAddress = *UnsafeBaseAddress;
1497 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1498 }
1499
1500 //
1501 // Catch illegal base address
1502 //
1503 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
1504
1505 //
1506 // Catch illegal region size
1507 //
1508 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
1509 {
1510 //
1511 // Fail
1512 //
1513 return STATUS_INVALID_PARAMETER_3;
1514 }
1515
1516 //
1517 // 0 is also illegal
1518 //
1519 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
1520
1521 //
1522 // Get a reference to the process
1523 //
1524 Status = ObReferenceObjectByHandle(ProcessHandle,
1525 PROCESS_VM_OPERATION,
1526 PsProcessType,
1527 PreviousMode,
1528 (PVOID*)(&Process),
1529 NULL);
1530 if (!NT_SUCCESS(Status)) return Status;
1531
1532 //
1533 // Check if we should attach
1534 //
1535 if (CurrentProcess != Process)
1536 {
1537 //
1538 // Do it
1539 //
1540 KeStackAttachProcess(&Process->Pcb, &ApcState);
1541 Attached = TRUE;
1542 }
1543
1544 //
1545 // Do the actual work
1546 //
1547 Status = MiProtectVirtualMemory(Process,
1548 &BaseAddress,
1549 &NumberOfBytesToProtect,
1550 NewAccessProtection,
1551 &OldAccessProtection);
1552
1553 //
1554 // Detach if needed
1555 //
1556 if (Attached) KeUnstackDetachProcess(&ApcState);
1557
1558 //
1559 // Release reference
1560 //
1561 ObDereferenceObject(Process);
1562
1563 //
1564 // Enter SEH to return data
1565 //
1566 _SEH2_TRY
1567 {
1568 //
1569 // Return data to user
1570 //
1571 *UnsafeOldAccessProtection = OldAccessProtection;
1572 *UnsafeBaseAddress = BaseAddress;
1573 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
1574 }
1575 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1576 {
1577 }
1578 _SEH2_END;
1579
1580 //
1581 // Return status
1582 //
1583 return Status;
1584 }
1585
1586 NTSTATUS
1587 NTAPI
1588 NtLockVirtualMemory(IN HANDLE ProcessHandle,
1589 IN OUT PVOID *BaseAddress,
1590 IN OUT PSIZE_T NumberOfBytesToLock,
1591 IN ULONG MapType)
1592 {
1593 PEPROCESS Process;
1594 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1595 NTSTATUS Status;
1596 BOOLEAN Attached = FALSE;
1597 KAPC_STATE ApcState;
1598 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1599 PVOID CapturedBaseAddress;
1600 SIZE_T CapturedBytesToLock;
1601 PAGED_CODE();
1602
1603 //
1604 // Validate flags
1605 //
1606 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
1607 {
1608 //
1609 // Invalid set of flags
1610 //
1611 return STATUS_INVALID_PARAMETER;
1612 }
1613
1614 //
1615 // At least one flag must be specified
1616 //
1617 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
1618 {
1619 //
1620 // No flag given
1621 //
1622 return STATUS_INVALID_PARAMETER;
1623 }
1624
1625 //
1626 // Enter SEH for probing
1627 //
1628 _SEH2_TRY
1629 {
1630 //
1631 // Validate output data
1632 //
1633 ProbeForWritePointer(BaseAddress);
1634 ProbeForWriteSize_t(NumberOfBytesToLock);
1635
1636 //
1637 // Capture it
1638 //
1639 CapturedBaseAddress = *BaseAddress;
1640 CapturedBytesToLock = *NumberOfBytesToLock;
1641 }
1642 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1643 {
1644 //
1645 // Get exception code
1646 //
1647 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1648 }
1649 _SEH2_END;
1650
1651 //
1652 // Catch illegal base address
1653 //
1654 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
1655
1656 //
1657 // Catch illegal region size
1658 //
1659 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
1660 {
1661 //
1662 // Fail
1663 //
1664 return STATUS_INVALID_PARAMETER;
1665 }
1666
1667 //
1668 // 0 is also illegal
1669 //
1670 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
1671
1672 //
1673 // Get a reference to the process
1674 //
1675 Status = ObReferenceObjectByHandle(ProcessHandle,
1676 PROCESS_VM_OPERATION,
1677 PsProcessType,
1678 PreviousMode,
1679 (PVOID*)(&Process),
1680 NULL);
1681 if (!NT_SUCCESS(Status)) return Status;
1682
1683 //
1684 // Check if this is is system-mapped
1685 //
1686 if (MapType & MAP_SYSTEM)
1687 {
1688 //
1689 // Check for required privilege
1690 //
1691 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
1692 {
1693 //
1694 // Fail: Don't have it
1695 //
1696 ObDereferenceObject(Process);
1697 return STATUS_PRIVILEGE_NOT_HELD;
1698 }
1699 }
1700
1701 //
1702 // Check if we should attach
1703 //
1704 if (CurrentProcess != Process)
1705 {
1706 //
1707 // Do it
1708 //
1709 KeStackAttachProcess(&Process->Pcb, &ApcState);
1710 Attached = TRUE;
1711 }
1712
1713 //
1714 // Oops :(
1715 //
1716 UNIMPLEMENTED;
1717
1718 //
1719 // Detach if needed
1720 //
1721 if (Attached) KeUnstackDetachProcess(&ApcState);
1722
1723 //
1724 // Release reference
1725 //
1726 ObDereferenceObject(Process);
1727
1728 //
1729 // Enter SEH to return data
1730 //
1731 _SEH2_TRY
1732 {
1733 //
1734 // Return data to user
1735 //
1736 *BaseAddress = CapturedBaseAddress;
1737 *NumberOfBytesToLock = 0;
1738 }
1739 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1740 {
1741 //
1742 // Get exception code
1743 //
1744 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1745 }
1746 _SEH2_END;
1747
1748 //
1749 // Return status
1750 //
1751 return STATUS_SUCCESS;
1752 }
1753
1754 NTSTATUS
1755 NTAPI
1756 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
1757 IN OUT PVOID *BaseAddress,
1758 IN OUT PSIZE_T NumberOfBytesToUnlock,
1759 IN ULONG MapType)
1760 {
1761 PEPROCESS Process;
1762 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1763 NTSTATUS Status;
1764 BOOLEAN Attached = FALSE;
1765 KAPC_STATE ApcState;
1766 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1767 PVOID CapturedBaseAddress;
1768 SIZE_T CapturedBytesToUnlock;
1769 PAGED_CODE();
1770
1771 //
1772 // Validate flags
1773 //
1774 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
1775 {
1776 //
1777 // Invalid set of flags
1778 //
1779 return STATUS_INVALID_PARAMETER;
1780 }
1781
1782 //
1783 // At least one flag must be specified
1784 //
1785 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
1786 {
1787 //
1788 // No flag given
1789 //
1790 return STATUS_INVALID_PARAMETER;
1791 }
1792
1793 //
1794 // Enter SEH for probing
1795 //
1796 _SEH2_TRY
1797 {
1798 //
1799 // Validate output data
1800 //
1801 ProbeForWritePointer(BaseAddress);
1802 ProbeForWriteSize_t(NumberOfBytesToUnlock);
1803
1804 //
1805 // Capture it
1806 //
1807 CapturedBaseAddress = *BaseAddress;
1808 CapturedBytesToUnlock = *NumberOfBytesToUnlock;
1809 }
1810 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1811 {
1812 //
1813 // Get exception code
1814 //
1815 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1816 }
1817 _SEH2_END;
1818
1819 //
1820 // Catch illegal base address
1821 //
1822 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
1823
1824 //
1825 // Catch illegal region size
1826 //
1827 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
1828 {
1829 //
1830 // Fail
1831 //
1832 return STATUS_INVALID_PARAMETER;
1833 }
1834
1835 //
1836 // 0 is also illegal
1837 //
1838 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
1839
1840 //
1841 // Get a reference to the process
1842 //
1843 Status = ObReferenceObjectByHandle(ProcessHandle,
1844 PROCESS_VM_OPERATION,
1845 PsProcessType,
1846 PreviousMode,
1847 (PVOID*)(&Process),
1848 NULL);
1849 if (!NT_SUCCESS(Status)) return Status;
1850
1851 //
1852 // Check if this is is system-mapped
1853 //
1854 if (MapType & MAP_SYSTEM)
1855 {
1856 //
1857 // Check for required privilege
1858 //
1859 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
1860 {
1861 //
1862 // Fail: Don't have it
1863 //
1864 ObDereferenceObject(Process);
1865 return STATUS_PRIVILEGE_NOT_HELD;
1866 }
1867 }
1868
1869 //
1870 // Check if we should attach
1871 //
1872 if (CurrentProcess != Process)
1873 {
1874 //
1875 // Do it
1876 //
1877 KeStackAttachProcess(&Process->Pcb, &ApcState);
1878 Attached = TRUE;
1879 }
1880
1881 //
1882 // Oops :(
1883 //
1884 UNIMPLEMENTED;
1885
1886 //
1887 // Detach if needed
1888 //
1889 if (Attached) KeUnstackDetachProcess(&ApcState);
1890
1891 //
1892 // Release reference
1893 //
1894 ObDereferenceObject(Process);
1895
1896 //
1897 // Enter SEH to return data
1898 //
1899 _SEH2_TRY
1900 {
1901 //
1902 // Return data to user
1903 //
1904 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
1905 *NumberOfBytesToUnlock = 0;
1906 }
1907 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1908 {
1909 //
1910 // Get exception code
1911 //
1912 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1913 }
1914 _SEH2_END;
1915
1916 //
1917 // Return status
1918 //
1919 return STATUS_SUCCESS;
1920 }
1921
1922 NTSTATUS
1923 NTAPI
1924 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
1925 IN OUT PVOID *BaseAddress,
1926 IN OUT PSIZE_T NumberOfBytesToFlush,
1927 OUT PIO_STATUS_BLOCK IoStatusBlock)
1928 {
1929 PEPROCESS Process;
1930 NTSTATUS Status;
1931 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1932 PVOID CapturedBaseAddress;
1933 SIZE_T CapturedBytesToFlush;
1934 IO_STATUS_BLOCK LocalStatusBlock;
1935 PAGED_CODE();
1936
1937 //
1938 // Check if we came from user mode
1939 //
1940 if (PreviousMode != KernelMode)
1941 {
1942 //
1943 // Enter SEH for probing
1944 //
1945 _SEH2_TRY
1946 {
1947 //
1948 // Validate all outputs
1949 //
1950 ProbeForWritePointer(BaseAddress);
1951 ProbeForWriteSize_t(NumberOfBytesToFlush);
1952 ProbeForWriteIoStatusBlock(IoStatusBlock);
1953
1954 //
1955 // Capture them
1956 //
1957 CapturedBaseAddress = *BaseAddress;
1958 CapturedBytesToFlush = *NumberOfBytesToFlush;
1959 }
1960 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1961 {
1962 //
1963 // Get exception code
1964 //
1965 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1966 }
1967 _SEH2_END;
1968 }
1969 else
1970 {
1971 //
1972 // Capture directly
1973 //
1974 CapturedBaseAddress = *BaseAddress;
1975 CapturedBytesToFlush = *NumberOfBytesToFlush;
1976 }
1977
1978 //
1979 // Catch illegal base address
1980 //
1981 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
1982
1983 //
1984 // Catch illegal region size
1985 //
1986 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
1987 {
1988 //
1989 // Fail
1990 //
1991 return STATUS_INVALID_PARAMETER;
1992 }
1993
1994 //
1995 // Get a reference to the process
1996 //
1997 Status = ObReferenceObjectByHandle(ProcessHandle,
1998 PROCESS_VM_OPERATION,
1999 PsProcessType,
2000 PreviousMode,
2001 (PVOID*)(&Process),
2002 NULL);
2003 if (!NT_SUCCESS(Status)) return Status;
2004
2005 //
2006 // Do it
2007 //
2008 Status = MmFlushVirtualMemory(Process,
2009 &CapturedBaseAddress,
2010 &CapturedBytesToFlush,
2011 &LocalStatusBlock);
2012
2013 //
2014 // Release reference
2015 //
2016 ObDereferenceObject(Process);
2017
2018 //
2019 // Enter SEH to return data
2020 //
2021 _SEH2_TRY
2022 {
2023 //
2024 // Return data to user
2025 //
2026 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
2027 *NumberOfBytesToFlush = 0;
2028 *IoStatusBlock = LocalStatusBlock;
2029 }
2030 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2031 {
2032 }
2033 _SEH2_END;
2034
2035 //
2036 // Return status
2037 //
2038 return Status;
2039 }
2040
2041 /*
2042 * @unimplemented
2043 */
2044 NTSTATUS
2045 NTAPI
2046 NtGetWriteWatch(IN HANDLE ProcessHandle,
2047 IN ULONG Flags,
2048 IN PVOID BaseAddress,
2049 IN SIZE_T RegionSize,
2050 IN PVOID *UserAddressArray,
2051 OUT PULONG_PTR EntriesInUserAddressArray,
2052 OUT PULONG Granularity)
2053 {
2054 PEPROCESS Process;
2055 NTSTATUS Status;
2056 PVOID EndAddress;
2057 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2058 ULONG_PTR CapturedEntryCount;
2059 PAGED_CODE();
2060
2061 //
2062 // Check if we came from user mode
2063 //
2064 if (PreviousMode != KernelMode)
2065 {
2066 //
2067 // Enter SEH for probing
2068 //
2069 _SEH2_TRY
2070 {
2071 //
2072 // Catch illegal base address
2073 //
2074 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2075
2076 //
2077 // Catch illegal region size
2078 //
2079 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
2080 {
2081 //
2082 // Fail
2083 //
2084 return STATUS_INVALID_PARAMETER_3;
2085 }
2086
2087 //
2088 // Validate all data
2089 //
2090 ProbeForWriteSize_t(EntriesInUserAddressArray);
2091 ProbeForWriteUlong(Granularity);
2092
2093 //
2094 // Capture them
2095 //
2096 CapturedEntryCount = *EntriesInUserAddressArray;
2097
2098 //
2099 // Must have a count
2100 //
2101 if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5;
2102
2103 //
2104 // Can't be larger than the maximum
2105 //
2106 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
2107 {
2108 //
2109 // Fail
2110 //
2111 return STATUS_INVALID_PARAMETER_5;
2112 }
2113
2114 //
2115 // Probe the actual array
2116 //
2117 ProbeForWrite(UserAddressArray,
2118 CapturedEntryCount * sizeof(PVOID),
2119 sizeof(PVOID));
2120 }
2121 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2122 {
2123 //
2124 // Get exception code
2125 //
2126 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2127 }
2128 _SEH2_END;
2129 }
2130 else
2131 {
2132 //
2133 // Capture directly
2134 //
2135 CapturedEntryCount = *EntriesInUserAddressArray;
2136 ASSERT(CapturedEntryCount != 0);
2137 }
2138
2139 //
2140 // Check if this is a local request
2141 //
2142 if (ProcessHandle == NtCurrentProcess())
2143 {
2144 //
2145 // No need to reference the process
2146 //
2147 Process = PsGetCurrentProcess();
2148 }
2149 else
2150 {
2151 //
2152 // Reference the target
2153 //
2154 Status = ObReferenceObjectByHandle(ProcessHandle,
2155 PROCESS_VM_OPERATION,
2156 PsProcessType,
2157 PreviousMode,
2158 (PVOID *)&Process,
2159 NULL);
2160 if (!NT_SUCCESS(Status)) return Status;
2161 }
2162
2163 //
2164 // Compute the last address and validate it
2165 //
2166 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
2167 if (BaseAddress > EndAddress)
2168 {
2169 //
2170 // Fail
2171 //
2172 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2173 return STATUS_INVALID_PARAMETER_4;
2174 }
2175
2176 //
2177 // Oops :(
2178 //
2179 UNIMPLEMENTED;
2180
2181 //
2182 // Dereference if needed
2183 //
2184 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2185
2186 //
2187 // Enter SEH to return data
2188 //
2189 _SEH2_TRY
2190 {
2191 //
2192 // Return data to user
2193 //
2194 *EntriesInUserAddressArray = 0;
2195 *Granularity = PAGE_SIZE;
2196 }
2197 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2198 {
2199 //
2200 // Get exception code
2201 //
2202 Status = _SEH2_GetExceptionCode();
2203 }
2204 _SEH2_END;
2205
2206 //
2207 // Return success
2208 //
2209 return STATUS_SUCCESS;
2210 }
2211
2212 /*
2213 * @unimplemented
2214 */
2215 NTSTATUS
2216 NTAPI
2217 NtResetWriteWatch(IN HANDLE ProcessHandle,
2218 IN PVOID BaseAddress,
2219 IN SIZE_T RegionSize)
2220 {
2221 PVOID EndAddress;
2222 PEPROCESS Process;
2223 NTSTATUS Status;
2224 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2225 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
2226
2227 //
2228 // Catch illegal base address
2229 //
2230 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2231
2232 //
2233 // Catch illegal region size
2234 //
2235 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
2236 {
2237 //
2238 // Fail
2239 //
2240 return STATUS_INVALID_PARAMETER_3;
2241 }
2242
2243 //
2244 // Check if this is a local request
2245 //
2246 if (ProcessHandle == NtCurrentProcess())
2247 {
2248 //
2249 // No need to reference the process
2250 //
2251 Process = PsGetCurrentProcess();
2252 }
2253 else
2254 {
2255 //
2256 // Reference the target
2257 //
2258 Status = ObReferenceObjectByHandle(ProcessHandle,
2259 PROCESS_VM_OPERATION,
2260 PsProcessType,
2261 PreviousMode,
2262 (PVOID *)&Process,
2263 NULL);
2264 if (!NT_SUCCESS(Status)) return Status;
2265 }
2266
2267 //
2268 // Compute the last address and validate it
2269 //
2270 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
2271 if (BaseAddress > EndAddress)
2272 {
2273 //
2274 // Fail
2275 //
2276 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2277 return STATUS_INVALID_PARAMETER_3;
2278 }
2279
2280 //
2281 // Oops :(
2282 //
2283 UNIMPLEMENTED;
2284
2285 //
2286 // Dereference if needed
2287 //
2288 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2289
2290 //
2291 // Return success
2292 //
2293 return STATUS_SUCCESS;
2294 }
2295
2296 NTSTATUS
2297 NTAPI
2298 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
2299 IN PVOID BaseAddress,
2300 IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
2301 OUT PVOID MemoryInformation,
2302 IN SIZE_T MemoryInformationLength,
2303 OUT PSIZE_T ReturnLength)
2304 {
2305 PEPROCESS TargetProcess;
2306 NTSTATUS Status;
2307 PMMVAD Vad = NULL;
2308 PVOID Address, NextAddress;
2309 BOOLEAN Found;
2310 ULONG NewProtect, NewState, BaseVpn;
2311 MEMORY_BASIC_INFORMATION MemoryInfo;
2312 KAPC_STATE ApcState;
2313 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
2314
2315 /* Only this class is supported for now */
2316 ASSERT(MemoryInformationClass == MemoryBasicInformation);
2317
2318 /* Validate the size information of the class */
2319 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
2320 {
2321 /* The size is invalid */
2322 return STATUS_INFO_LENGTH_MISMATCH;
2323 }
2324
2325 /* Bail out if the address is invalid */
2326 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
2327
2328 /* Check for illegal addresses in user-space, or the shared memory area */
2329 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
2330 (PAGE_ALIGN(BaseAddress) == (PVOID)USER_SHARED_DATA))
2331 {
2332 /* FIXME: We should return some bogus info structure */
2333 UNIMPLEMENTED;
2334 while (TRUE);
2335 }
2336
2337 /* Check if this is for a local or remote process */
2338 if (ProcessHandle == NtCurrentProcess())
2339 {
2340 TargetProcess = PsGetCurrentProcess();
2341 }
2342 else
2343 {
2344 /* Reference the target process */
2345 Status = ObReferenceObjectByHandle(ProcessHandle,
2346 PROCESS_QUERY_INFORMATION,
2347 PsProcessType,
2348 ExGetPreviousMode(),
2349 (PVOID*)&TargetProcess,
2350 NULL);
2351 if (!NT_SUCCESS(Status)) return Status;
2352
2353 /* Attach to it now */
2354 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
2355 }
2356
2357 /* Loop the VADs */
2358 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
2359 if (TargetProcess->VadRoot.NumberGenericTableElements)
2360 {
2361 /* Scan on the right */
2362 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
2363 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
2364 while (Vad)
2365 {
2366 /* Check if this VAD covers the allocation range */
2367 if ((BaseVpn >= Vad->StartingVpn) &&
2368 (BaseVpn <= Vad->EndingVpn))
2369 {
2370 /* We're done */
2371 Found = TRUE;
2372 break;
2373 }
2374
2375 /* Check if this VAD is too high */
2376 if (BaseVpn < Vad->StartingVpn)
2377 {
2378 /* Search on the left next */
2379 Vad = Vad->LeftChild;
2380 }
2381 else
2382 {
2383 /* Then this VAD is too low, keep searching on the right */
2384 ASSERT(BaseVpn > Vad->EndingVpn);
2385 Vad = Vad->RightChild;
2386 }
2387 }
2388 }
2389
2390 /* Was a VAD found? */
2391 if (!Found)
2392 {
2393 /* We don't handle this yet */
2394 UNIMPLEMENTED;
2395 while (TRUE);
2396 }
2397
2398 /* This must be a VM VAD */
2399 ASSERT(Vad->u.VadFlags.PrivateMemory);
2400
2401 /* Build the initial information block */
2402 Address = PAGE_ALIGN(BaseAddress);
2403 MemoryInfo.BaseAddress = Address;
2404 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
2405 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2406 MemoryInfo.Type = MEM_PRIVATE;
2407
2408 /* Find the largest chunk of memory which has the same state and protection mask */
2409 MemoryInfo.State = MiQueryAddressState(Address,
2410 Vad,
2411 TargetProcess,
2412 &MemoryInfo.Protect,
2413 &NextAddress);
2414 Address = NextAddress;
2415 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
2416 {
2417 /* Keep going unless the state or protection mask changed */
2418 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
2419 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
2420 Address = NextAddress;
2421 }
2422
2423 /* Now that we know the last VA address, calculate hte region size */
2424 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
2425
2426 /* Check if we were attached */
2427 if (ProcessHandle != NtCurrentProcess())
2428 {
2429 /* Detach and derefernece the process */
2430 KeUnstackDetachProcess(&ApcState);
2431 ObDereferenceObject(TargetProcess);
2432 }
2433
2434 /* Return the data (FIXME: Use SEH) */
2435 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2436 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2437
2438 /* All went well */
2439 DPRINT("Base: %p AllocBase: %p Protect: %lx AllocProtect: %lx "
2440 "State: %lx Type: %lx Size: %lx\n",
2441 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
2442 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
2443 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
2444 return STATUS_SUCCESS;
2445 }
2446
2447 /* EOF */