5fd8d048c80671d0a027ff2d1db86dffa8eb9f53
[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 /* So long, and Thanks for All the Fish */
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
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 VOID
31 NTAPI
32 MiFlushTbAndCapture(IN PMMVAD FoundVad,
33 IN PMMPTE PointerPte,
34 IN ULONG ProtectionMask,
35 IN PMMPFN Pfn1,
36 IN BOOLEAN CaptureDirtyBit);
37
38
39 /* PRIVATE FUNCTIONS **********************************************************/
40
41 ULONG
42 NTAPI
43 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress,
44 IN ULONG_PTR EndingAddress,
45 IN PMMVAD Vad,
46 IN PEPROCESS Process)
47 {
48 PMMPTE PointerPte, LastPte, PointerPde;
49 ULONG CommittedPages;
50
51 /* Compute starting and ending PTE and PDE addresses */
52 PointerPde = MiAddressToPde(StartingAddress);
53 PointerPte = MiAddressToPte(StartingAddress);
54 LastPte = MiAddressToPte(EndingAddress);
55
56 /* Handle commited pages first */
57 if (Vad->u.VadFlags.MemCommit == 1)
58 {
59 /* This is a committed VAD, so Assume the whole range is committed */
60 CommittedPages = BYTES_TO_PAGES(EndingAddress - StartingAddress);
61
62 /* Is the PDE demand-zero? */
63 PointerPde = MiAddressToPte(PointerPte);
64 if (PointerPde->u.Long != 0)
65 {
66 /* It is not. Is it valid? */
67 if (PointerPde->u.Hard.Valid == 0)
68 {
69 /* Fault it in */
70 PointerPte = MiPteToAddress(PointerPde);
71 MiMakeSystemAddressValid(PointerPte, Process);
72 }
73 }
74 else
75 {
76 /* It is, skip it and move to the next PDE, unless we're done */
77 PointerPde++;
78 PointerPte = MiPteToAddress(PointerPde);
79 if (PointerPte > LastPte) return CommittedPages;
80 }
81
82 /* Now loop all the PTEs in the range */
83 while (PointerPte <= LastPte)
84 {
85 /* Have we crossed a PDE boundary? */
86 if (MiIsPteOnPdeBoundary(PointerPte))
87 {
88 /* Is this PDE demand zero? */
89 PointerPde = MiAddressToPte(PointerPte);
90 if (PointerPde->u.Long != 0)
91 {
92 /* It isn't -- is it valid? */
93 if (PointerPde->u.Hard.Valid == 0)
94 {
95 /* Nope, fault it in */
96 PointerPte = MiPteToAddress(PointerPde);
97 MiMakeSystemAddressValid(PointerPte, Process);
98 }
99 }
100 else
101 {
102 /* It is, skip it and move to the next PDE */
103 PointerPde++;
104 PointerPte = MiPteToAddress(PointerPde);
105 continue;
106 }
107 }
108
109 /* Is this PTE demand zero? */
110 if (PointerPte->u.Long != 0)
111 {
112 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
113 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
114 (PointerPte->u.Hard.Valid == 0) &&
115 ((PointerPte->u.Soft.Prototype == 0) ||
116 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
117 {
118 /* It is, so remove it from the count of commited pages */
119 CommittedPages--;
120 }
121 }
122
123 /* Move to the next PTE */
124 PointerPte++;
125 }
126
127 /* Return how many committed pages there still are */
128 return CommittedPages;
129 }
130
131 /* This is a non-commited VAD, so assume none of it is committed */
132 CommittedPages = 0;
133
134 /* Is the PDE demand-zero? */
135 PointerPde = MiAddressToPte(PointerPte);
136 if (PointerPde->u.Long != 0)
137 {
138 /* It isn't -- is it invalid? */
139 if (PointerPde->u.Hard.Valid == 0)
140 {
141 /* It is, so page it in */
142 PointerPte = MiPteToAddress(PointerPde);
143 MiMakeSystemAddressValid(PointerPte, Process);
144 }
145 }
146 else
147 {
148 /* It is, so skip it and move to the next PDE */
149 PointerPde++;
150 PointerPte = MiPteToAddress(PointerPde);
151 if (PointerPte > LastPte) return CommittedPages;
152 }
153
154 /* Loop all the PTEs in this PDE */
155 while (PointerPte <= LastPte)
156 {
157 /* Have we crossed a PDE boundary? */
158 if (MiIsPteOnPdeBoundary(PointerPte))
159 {
160 /* Is this new PDE demand-zero? */
161 PointerPde = MiAddressToPte(PointerPte);
162 if (PointerPde->u.Long != 0)
163 {
164 /* It isn't. Is it valid? */
165 if (PointerPde->u.Hard.Valid == 0)
166 {
167 /* It isn't, so make it valid */
168 PointerPte = MiPteToAddress(PointerPde);
169 MiMakeSystemAddressValid(PointerPte, Process);
170 }
171 }
172 else
173 {
174 /* It is, so skip it and move to the next one */
175 PointerPde++;
176 PointerPte = MiPteToAddress(PointerPde);
177 continue;
178 }
179 }
180
181 /* Is this PTE demand-zero? */
182 if (PointerPte->u.Long != 0)
183 {
184 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
185 if ((PointerPte->u.Soft.Protection != MM_DECOMMIT) ||
186 (PointerPte->u.Hard.Valid == 1) ||
187 ((PointerPte->u.Soft.Prototype == 1) &&
188 (PointerPte->u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)))
189 {
190 /* It is! So we'll treat this as a committed page */
191 CommittedPages++;
192 }
193 }
194
195 /* Move to the next PTE */
196 PointerPte++;
197 }
198
199 /* Return how many committed pages we found in this VAD */
200 return CommittedPages;
201 }
202
203 ULONG
204 NTAPI
205 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
206 IN PEPROCESS CurrentProcess)
207 {
208 NTSTATUS Status;
209 BOOLEAN WsShared = FALSE, WsSafe = FALSE, LockChange = FALSE;
210 PETHREAD CurrentThread = PsGetCurrentThread();
211
212 /* Must be a non-pool page table, since those are double-mapped already */
213 ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
214 ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
215 (PageTableVirtualAddress > MmPagedPoolEnd));
216
217 /* Working set lock or PFN lock should be held */
218 ASSERT(KeAreAllApcsDisabled() == TRUE);
219
220 /* Check if the page table is valid */
221 while (!MmIsAddressValid(PageTableVirtualAddress))
222 {
223 /* Release the working set lock */
224 MiUnlockProcessWorkingSetForFault(CurrentProcess,
225 CurrentThread,
226 &WsSafe,
227 &WsShared);
228
229 /* Fault it in */
230 Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
231 if (!NT_SUCCESS(Status))
232 {
233 /* This should not fail */
234 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
235 1,
236 Status,
237 (ULONG_PTR)CurrentProcess,
238 (ULONG_PTR)PageTableVirtualAddress);
239 }
240
241 /* Lock the working set again */
242 MiLockProcessWorkingSetForFault(CurrentProcess,
243 CurrentThread,
244 WsSafe,
245 WsShared);
246
247 /* This flag will be useful later when we do better locking */
248 LockChange = TRUE;
249 }
250
251 /* Let caller know what the lock state is */
252 return LockChange;
253 }
254
255 ULONG
256 NTAPI
257 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
258 IN KIRQL OldIrql)
259 {
260 NTSTATUS Status;
261 BOOLEAN LockChange = FALSE;
262
263 /* Must be e kernel address */
264 ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS);
265
266 /* Check if the page is valid */
267 while (!MmIsAddressValid(VirtualAddress))
268 {
269 /* Release the PFN database */
270 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
271
272 /* Fault it in */
273 Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL);
274 if (!NT_SUCCESS(Status))
275 {
276 /* This should not fail */
277 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
278 3,
279 Status,
280 0,
281 (ULONG_PTR)VirtualAddress);
282 }
283
284 /* This flag will be useful later when we do better locking */
285 LockChange = TRUE;
286
287 /* Lock the PFN database */
288 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
289 }
290
291 /* Let caller know what the lock state is */
292 return LockChange;
293 }
294
295 PFN_COUNT
296 NTAPI
297 MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
298 IN PFN_NUMBER PageCount,
299 IN ULONG Flags,
300 OUT PPFN_NUMBER ValidPages)
301 {
302 PFN_COUNT ActualPages = 0;
303 PETHREAD CurrentThread = PsGetCurrentThread();
304 PMMPFN Pfn1, Pfn2;
305 PFN_NUMBER PageFrameIndex, PageTableIndex;
306 KIRQL OldIrql;
307 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
308
309 /* Lock the system working set */
310 MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
311
312 /* Loop all pages */
313 while (PageCount)
314 {
315 /* Make sure there's some data about the page */
316 if (PointerPte->u.Long)
317 {
318 /* As always, only handle current ARM3 scenarios */
319 ASSERT(PointerPte->u.Soft.Prototype == 0);
320 ASSERT(PointerPte->u.Soft.Transition == 0);
321
322 /* Normally this is one possibility -- freeing a valid page */
323 if (PointerPte->u.Hard.Valid)
324 {
325 /* Get the page PFN */
326 PageFrameIndex = PFN_FROM_PTE(PointerPte);
327 Pfn1 = MiGetPfnEntry(PageFrameIndex);
328
329 /* Should not have any working set data yet */
330 ASSERT(Pfn1->u1.WsIndex == 0);
331
332 /* Actual valid, legitimate, pages */
333 if (ValidPages) (*ValidPages)++;
334
335 /* Get the page table entry */
336 PageTableIndex = Pfn1->u4.PteFrame;
337 Pfn2 = MiGetPfnEntry(PageTableIndex);
338
339 /* Lock the PFN database */
340 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
341
342 /* Delete it the page */
343 MI_SET_PFN_DELETED(Pfn1);
344 MiDecrementShareCount(Pfn1, PageFrameIndex);
345
346 /* Decrement the page table too */
347 MiDecrementShareCount(Pfn2, PageTableIndex);
348
349 /* Release the PFN database */
350 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
351
352 /* Destroy the PTE */
353 MI_ERASE_PTE(PointerPte);
354 }
355 else
356 {
357 /*
358 * The only other ARM3 possibility is a demand zero page, which would
359 * mean freeing some of the paged pool pages that haven't even been
360 * touched yet, as part of a larger allocation.
361 *
362 * Right now, we shouldn't expect any page file information in the PTE
363 */
364 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
365
366 /* Destroy the PTE */
367 MI_ERASE_PTE(PointerPte);
368 }
369
370 /* Actual legitimate pages */
371 ActualPages++;
372 }
373
374 /* Keep going */
375 PointerPte++;
376 PageCount--;
377 }
378
379 /* Release the working set */
380 MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
381
382 /* Flush the entire TLB */
383 KeFlushEntireTb(TRUE, TRUE);
384
385 /* Done */
386 return ActualPages;
387 }
388
389 VOID
390 NTAPI
391 MiDeletePte(IN PMMPTE PointerPte,
392 IN PVOID VirtualAddress,
393 IN PEPROCESS CurrentProcess,
394 IN PMMPTE PrototypePte)
395 {
396 PMMPFN Pfn1;
397 MMPTE TempPte;
398 PFN_NUMBER PageFrameIndex;
399 PMMPDE PointerPde;
400
401 /* PFN lock must be held */
402 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
403
404 /* Capture the PTE */
405 TempPte = *PointerPte;
406
407 /* We only support valid PTEs for now */
408 ASSERT(TempPte.u.Hard.Valid == 1);
409 if (TempPte.u.Hard.Valid == 0)
410 {
411 /* Invalid PTEs not supported yet */
412 ASSERT(TempPte.u.Soft.Prototype == 0);
413 ASSERT(TempPte.u.Soft.Transition == 0);
414 }
415
416 /* Get the PFN entry */
417 PageFrameIndex = PFN_FROM_PTE(&TempPte);
418 Pfn1 = MiGetPfnEntry(PageFrameIndex);
419
420 /* Check if this is a valid, prototype PTE */
421 if (Pfn1->u3.e1.PrototypePte == 1)
422 {
423 /* Get the PDE and make sure it's faulted in */
424 PointerPde = MiPteToPde(PointerPte);
425 if (PointerPde->u.Hard.Valid == 0)
426 {
427 #if (_MI_PAGING_LEVELS == 2)
428 /* Could be paged pool access from a new process -- synchronize the page directories */
429 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
430 {
431 #endif
432 /* The PDE must be valid at this point */
433 KeBugCheckEx(MEMORY_MANAGEMENT,
434 0x61940,
435 (ULONG_PTR)PointerPte,
436 PointerPte->u.Long,
437 (ULONG_PTR)VirtualAddress);
438 }
439 #if (_MI_PAGING_LEVELS == 2)
440 }
441 #endif
442 /* Drop the share count */
443 MiDecrementShareCount(Pfn1, PageFrameIndex);
444
445 /* Either a fork, or this is the shared user data page */
446 if ((PointerPte <= MiHighestUserPte) && (PrototypePte != Pfn1->PteAddress))
447 {
448 /* If it's not the shared user page, then crash, since there's no fork() yet */
449 if ((PAGE_ALIGN(VirtualAddress) != (PVOID)USER_SHARED_DATA) ||
450 (MmHighestUserAddress <= (PVOID)USER_SHARED_DATA))
451 {
452 /* Must be some sort of memory corruption */
453 KeBugCheckEx(MEMORY_MANAGEMENT,
454 0x400,
455 (ULONG_PTR)PointerPte,
456 (ULONG_PTR)PrototypePte,
457 (ULONG_PTR)Pfn1->PteAddress);
458 }
459 }
460 }
461 else
462 {
463 /* Make sure the saved PTE address is valid */
464 if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
465 {
466 /* The PFN entry is illegal, or invalid */
467 KeBugCheckEx(MEMORY_MANAGEMENT,
468 0x401,
469 (ULONG_PTR)PointerPte,
470 PointerPte->u.Long,
471 (ULONG_PTR)Pfn1->PteAddress);
472 }
473
474 /* There should only be 1 shared reference count */
475 ASSERT(Pfn1->u2.ShareCount == 1);
476
477 /* Drop the reference on the page table. */
478 MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
479
480 /* Mark the PFN for deletion and dereference what should be the last ref */
481 MI_SET_PFN_DELETED(Pfn1);
482 MiDecrementShareCount(Pfn1, PageFrameIndex);
483
484 /* We should eventually do this */
485 //CurrentProcess->NumberOfPrivatePages--;
486 }
487
488 /* Destroy the PTE and flush the TLB */
489 MI_ERASE_PTE(PointerPte);
490 KeFlushCurrentTb();
491 }
492
493 VOID
494 NTAPI
495 MiDeleteVirtualAddresses(IN ULONG_PTR Va,
496 IN ULONG_PTR EndingAddress,
497 IN PMMVAD Vad)
498 {
499 PMMPTE PointerPte, PrototypePte, LastPrototypePte;
500 PMMPDE PointerPde;
501 MMPTE TempPte;
502 PEPROCESS CurrentProcess;
503 KIRQL OldIrql;
504 BOOLEAN AddressGap = FALSE;
505 PSUBSECTION Subsection;
506
507 /* Get out if this is a fake VAD, RosMm will free the marea pages */
508 if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
509
510 /* Grab the process and PTE/PDE for the address being deleted */
511 CurrentProcess = PsGetCurrentProcess();
512 PointerPde = MiAddressToPde(Va);
513 PointerPte = MiAddressToPte(Va);
514
515 /* Check if this is a section VAD or a VM VAD */
516 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
517 {
518 /* Don't worry about prototypes */
519 PrototypePte = LastPrototypePte = NULL;
520 }
521 else
522 {
523 /* Get the prototype PTE */
524 PrototypePte = Vad->FirstPrototypePte;
525 LastPrototypePte = Vad->FirstPrototypePte + 1;
526 }
527
528 /* In all cases, we don't support fork() yet */
529 ASSERT(CurrentProcess->CloneRoot == NULL);
530
531 /* Loop the PTE for each VA */
532 while (TRUE)
533 {
534 /* First keep going until we find a valid PDE */
535 while (!PointerPde->u.Long)
536 {
537 /* There are gaps in the address space */
538 AddressGap = TRUE;
539
540 /* Still no valid PDE, try the next 4MB (or whatever) */
541 PointerPde++;
542
543 /* Update the PTE on this new boundary */
544 PointerPte = MiPteToAddress(PointerPde);
545
546 /* Check if all the PDEs are invalid, so there's nothing to free */
547 Va = (ULONG_PTR)MiPteToAddress(PointerPte);
548 if (Va > EndingAddress) return;
549 }
550
551 /* Now check if the PDE is mapped in */
552 if (!PointerPde->u.Hard.Valid)
553 {
554 /* It isn't, so map it in */
555 PointerPte = MiPteToAddress(PointerPde);
556 MiMakeSystemAddressValid(PointerPte, CurrentProcess);
557 }
558
559 /* Now we should have a valid PDE, mapped in, and still have some VA */
560 ASSERT(PointerPde->u.Hard.Valid == 1);
561 ASSERT(Va <= EndingAddress);
562
563 /* Check if this is a section VAD with gaps in it */
564 if ((AddressGap) && (LastPrototypePte))
565 {
566 /* We need to skip to the next correct prototype PTE */
567 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
568
569 /* And we need the subsection to skip to the next last prototype PTE */
570 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
571 if (Subsection)
572 {
573 /* Found it! */
574 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
575 }
576 else
577 {
578 /* No more subsections, we are done with prototype PTEs */
579 PrototypePte = NULL;
580 }
581 }
582
583 /* Lock the PFN Database while we delete the PTEs */
584 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
585 do
586 {
587 /* Capture the PDE and make sure it exists */
588 TempPte = *PointerPte;
589 if (TempPte.u.Long)
590 {
591 MiDecrementPageTableReferences((PVOID)Va);
592
593 /* Check if the PTE is actually mapped in */
594 if (MI_IS_MAPPED_PTE(&TempPte))
595 {
596 /* Are we dealing with section VAD? */
597 if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
598 {
599 /* We need to skip to the next correct prototype PTE */
600 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
601
602 /* And we need the subsection to skip to the next last prototype PTE */
603 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
604 if (Subsection)
605 {
606 /* Found it! */
607 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
608 }
609 else
610 {
611 /* No more subsections, we are done with prototype PTEs */
612 PrototypePte = NULL;
613 }
614 }
615
616 /* Check for prototype PTE */
617 if ((TempPte.u.Hard.Valid == 0) &&
618 (TempPte.u.Soft.Prototype == 1))
619 {
620 /* Just nuke it */
621 MI_ERASE_PTE(PointerPte);
622 }
623 else
624 {
625 /* Delete the PTE proper */
626 MiDeletePte(PointerPte,
627 (PVOID)Va,
628 CurrentProcess,
629 PrototypePte);
630 }
631 }
632 else
633 {
634 /* The PTE was never mapped, just nuke it here */
635 MI_ERASE_PTE(PointerPte);
636 }
637 }
638
639 /* Update the address and PTE for it */
640 Va += PAGE_SIZE;
641 PointerPte++;
642 PrototypePte++;
643
644 /* Making sure the PDE is still valid */
645 ASSERT(PointerPde->u.Hard.Valid == 1);
646 }
647 while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
648
649 /* The PDE should still be valid at this point */
650 ASSERT(PointerPde->u.Hard.Valid == 1);
651
652 /* Check remaining PTE count (go back 1 page due to above loop) */
653 if (MiQueryPageTableReferences((PVOID)(Va - PAGE_SIZE)) == 0)
654 {
655 if (PointerPde->u.Long != 0)
656 {
657 /* Delete the PTE proper */
658 MiDeletePte(PointerPde,
659 MiPteToAddress(PointerPde),
660 CurrentProcess,
661 NULL);
662 }
663 }
664
665 /* Release the lock and get out if we're done */
666 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
667 if (Va > EndingAddress) return;
668
669 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
670 PointerPde = MiAddressToPde(Va);
671 AddressGap = FALSE;
672 }
673 }
674
675 LONG
676 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
677 OUT PBOOLEAN HaveBadAddress,
678 OUT PULONG_PTR BadAddress)
679 {
680 PEXCEPTION_RECORD ExceptionRecord;
681 PAGED_CODE();
682
683 //
684 // Assume default
685 //
686 *HaveBadAddress = FALSE;
687
688 //
689 // Get the exception record
690 //
691 ExceptionRecord = ExceptionInfo->ExceptionRecord;
692
693 //
694 // Look at the exception code
695 //
696 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
697 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
698 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
699 {
700 //
701 // We can tell the address if we have more than one parameter
702 //
703 if (ExceptionRecord->NumberParameters > 1)
704 {
705 //
706 // Return the address
707 //
708 *HaveBadAddress = TRUE;
709 *BadAddress = ExceptionRecord->ExceptionInformation[1];
710 }
711 }
712
713 //
714 // Continue executing the next handler
715 //
716 return EXCEPTION_EXECUTE_HANDLER;
717 }
718
719 NTSTATUS
720 NTAPI
721 MiDoMappedCopy(IN PEPROCESS SourceProcess,
722 IN PVOID SourceAddress,
723 IN PEPROCESS TargetProcess,
724 OUT PVOID TargetAddress,
725 IN SIZE_T BufferSize,
726 IN KPROCESSOR_MODE PreviousMode,
727 OUT PSIZE_T ReturnSize)
728 {
729 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
730 PMDL Mdl = (PMDL)MdlBuffer;
731 SIZE_T TotalSize, CurrentSize, RemainingSize;
732 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
733 volatile BOOLEAN PagesLocked;
734 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
735 volatile PVOID MdlAddress;
736 KAPC_STATE ApcState;
737 BOOLEAN HaveBadAddress;
738 ULONG_PTR BadAddress;
739 NTSTATUS Status = STATUS_SUCCESS;
740 PAGED_CODE();
741
742 //
743 // Calculate the maximum amount of data to move
744 //
745 TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
746 if (BufferSize <= TotalSize) TotalSize = BufferSize;
747 CurrentSize = TotalSize;
748 RemainingSize = BufferSize;
749
750 //
751 // Loop as long as there is still data
752 //
753 while (RemainingSize > 0)
754 {
755 //
756 // Check if this transfer will finish everything off
757 //
758 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
759
760 //
761 // Attach to the source address space
762 //
763 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
764
765 //
766 // Reset state for this pass
767 //
768 MdlAddress = NULL;
769 PagesLocked = FALSE;
770 FailedInMoving = FALSE;
771 ASSERT(FailedInProbe == FALSE);
772
773 //
774 // Protect user-mode copy
775 //
776 _SEH2_TRY
777 {
778 //
779 // If this is our first time, probe the buffer
780 //
781 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
782 {
783 //
784 // Catch a failure here
785 //
786 FailedInProbe = TRUE;
787
788 //
789 // Do the probe
790 //
791 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
792
793 //
794 // Passed
795 //
796 FailedInProbe = FALSE;
797 }
798
799 //
800 // Initialize and probe and lock the MDL
801 //
802 MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
803 MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
804 PagesLocked = TRUE;
805
806 //
807 // Now map the pages
808 //
809 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
810 KernelMode,
811 MmCached,
812 NULL,
813 FALSE,
814 HighPagePriority);
815 if (!MdlAddress)
816 {
817 //
818 // Use our SEH handler to pick this up
819 //
820 FailedInMapping = TRUE;
821 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
822 }
823
824 //
825 // Now let go of the source and grab to the target process
826 //
827 KeUnstackDetachProcess(&ApcState);
828 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
829
830 //
831 // Check if this is our first time through
832 //
833 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
834 {
835 //
836 // Catch a failure here
837 //
838 FailedInProbe = TRUE;
839
840 //
841 // Do the probe
842 //
843 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
844
845 //
846 // Passed
847 //
848 FailedInProbe = FALSE;
849 }
850
851 //
852 // Now do the actual move
853 //
854 FailedInMoving = TRUE;
855 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
856 }
857 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
858 &HaveBadAddress,
859 &BadAddress))
860 {
861 //
862 // Detach from whoever we may be attached to
863 //
864 KeUnstackDetachProcess(&ApcState);
865
866 //
867 // Check if we had mapped the pages
868 //
869 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
870
871 //
872 // Check if we had locked the pages
873 //
874 if (PagesLocked) MmUnlockPages(Mdl);
875
876 //
877 // Check if we hit working set quota
878 //
879 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA)
880 {
881 //
882 // Return the error
883 //
884 _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA);
885 }
886
887 //
888 // Check if we failed during the probe or mapping
889 //
890 if ((FailedInProbe) || (FailedInMapping))
891 {
892 //
893 // Exit
894 //
895 Status = _SEH2_GetExceptionCode();
896 _SEH2_YIELD(return Status);
897 }
898
899 //
900 // Otherwise, we failed probably during the move
901 //
902 *ReturnSize = BufferSize - RemainingSize;
903 if (FailedInMoving)
904 {
905 //
906 // Check if we know exactly where we stopped copying
907 //
908 if (HaveBadAddress)
909 {
910 //
911 // Return the exact number of bytes copied
912 //
913 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
914 }
915 }
916
917 //
918 // Return partial copy
919 //
920 Status = STATUS_PARTIAL_COPY;
921 }
922 _SEH2_END;
923
924 //
925 // Check for SEH status
926 //
927 if (Status != STATUS_SUCCESS) return Status;
928
929 //
930 // Detach from target
931 //
932 KeUnstackDetachProcess(&ApcState);
933
934 //
935 // Unmap and unlock
936 //
937 MmUnmapLockedPages(MdlAddress, Mdl);
938 MmUnlockPages(Mdl);
939
940 //
941 // Update location and size
942 //
943 RemainingSize -= CurrentSize;
944 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
945 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
946 }
947
948 //
949 // All bytes read
950 //
951 *ReturnSize = BufferSize;
952 return STATUS_SUCCESS;
953 }
954
955 NTSTATUS
956 NTAPI
957 MiDoPoolCopy(IN PEPROCESS SourceProcess,
958 IN PVOID SourceAddress,
959 IN PEPROCESS TargetProcess,
960 OUT PVOID TargetAddress,
961 IN SIZE_T BufferSize,
962 IN KPROCESSOR_MODE PreviousMode,
963 OUT PSIZE_T ReturnSize)
964 {
965 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
966 SIZE_T TotalSize, CurrentSize, RemainingSize;
967 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
968 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
969 PVOID PoolAddress;
970 KAPC_STATE ApcState;
971 BOOLEAN HaveBadAddress;
972 ULONG_PTR BadAddress;
973 NTSTATUS Status = STATUS_SUCCESS;
974 PAGED_CODE();
975
976 //
977 // Calculate the maximum amount of data to move
978 //
979 TotalSize = MI_MAX_TRANSFER_SIZE;
980 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
981 CurrentSize = TotalSize;
982 RemainingSize = BufferSize;
983
984 //
985 // Check if we can use the stack
986 //
987 if (BufferSize <= MI_POOL_COPY_BYTES)
988 {
989 //
990 // Use it
991 //
992 PoolAddress = (PVOID)StackBuffer;
993 }
994 else
995 {
996 //
997 // Allocate pool
998 //
999 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
1000 if (!PoolAddress) ASSERT(FALSE);
1001 HavePoolAddress = TRUE;
1002 }
1003
1004 //
1005 // Loop as long as there is still data
1006 //
1007 while (RemainingSize > 0)
1008 {
1009 //
1010 // Check if this transfer will finish everything off
1011 //
1012 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
1013
1014 //
1015 // Attach to the source address space
1016 //
1017 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
1018
1019 //
1020 // Reset state for this pass
1021 //
1022 FailedInMoving = FALSE;
1023 ASSERT(FailedInProbe == FALSE);
1024
1025 //
1026 // Protect user-mode copy
1027 //
1028 _SEH2_TRY
1029 {
1030 //
1031 // If this is our first time, probe the buffer
1032 //
1033 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
1034 {
1035 //
1036 // Catch a failure here
1037 //
1038 FailedInProbe = TRUE;
1039
1040 //
1041 // Do the probe
1042 //
1043 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
1044
1045 //
1046 // Passed
1047 //
1048 FailedInProbe = FALSE;
1049 }
1050
1051 //
1052 // Do the copy
1053 //
1054 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
1055
1056 //
1057 // Now let go of the source and grab to the target process
1058 //
1059 KeUnstackDetachProcess(&ApcState);
1060 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1061
1062 //
1063 // Check if this is our first time through
1064 //
1065 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
1066 {
1067 //
1068 // Catch a failure here
1069 //
1070 FailedInProbe = TRUE;
1071
1072 //
1073 // Do the probe
1074 //
1075 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
1076
1077 //
1078 // Passed
1079 //
1080 FailedInProbe = FALSE;
1081 }
1082
1083 //
1084 // Now do the actual move
1085 //
1086 FailedInMoving = TRUE;
1087 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
1088 }
1089 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1090 &HaveBadAddress,
1091 &BadAddress))
1092 {
1093 //
1094 // Detach from whoever we may be attached to
1095 //
1096 KeUnstackDetachProcess(&ApcState);
1097
1098 //
1099 // Check if we had allocated pool
1100 //
1101 if (HavePoolAddress) ExFreePoolWithTag(PoolAddress, 'VmRw');
1102
1103 //
1104 // Check if we failed during the probe
1105 //
1106 if (FailedInProbe)
1107 {
1108 //
1109 // Exit
1110 //
1111 Status = _SEH2_GetExceptionCode();
1112 _SEH2_YIELD(return Status);
1113 }
1114
1115 //
1116 // Otherwise, we failed, probably during the move
1117 //
1118 *ReturnSize = BufferSize - RemainingSize;
1119 if (FailedInMoving)
1120 {
1121 //
1122 // Check if we know exactly where we stopped copying
1123 //
1124 if (HaveBadAddress)
1125 {
1126 //
1127 // Return the exact number of bytes copied
1128 //
1129 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
1130 }
1131 }
1132
1133 //
1134 // Return partial copy
1135 //
1136 Status = STATUS_PARTIAL_COPY;
1137 }
1138 _SEH2_END;
1139
1140 //
1141 // Check for SEH status
1142 //
1143 if (Status != STATUS_SUCCESS) return Status;
1144
1145 //
1146 // Detach from target
1147 //
1148 KeUnstackDetachProcess(&ApcState);
1149
1150 //
1151 // Update location and size
1152 //
1153 RemainingSize -= CurrentSize;
1154 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
1155 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
1156 CurrentSize);
1157 }
1158
1159 //
1160 // Check if we had allocated pool
1161 //
1162 if (HavePoolAddress) ExFreePoolWithTag(PoolAddress, 'VmRw');
1163
1164 //
1165 // All bytes read
1166 //
1167 *ReturnSize = BufferSize;
1168 return STATUS_SUCCESS;
1169 }
1170
1171 NTSTATUS
1172 NTAPI
1173 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
1174 IN PVOID SourceAddress,
1175 IN PEPROCESS TargetProcess,
1176 OUT PVOID TargetAddress,
1177 IN SIZE_T BufferSize,
1178 IN KPROCESSOR_MODE PreviousMode,
1179 OUT PSIZE_T ReturnSize)
1180 {
1181 NTSTATUS Status;
1182 PEPROCESS Process = SourceProcess;
1183
1184 //
1185 // Don't accept zero-sized buffers
1186 //
1187 if (!BufferSize) return STATUS_SUCCESS;
1188
1189 //
1190 // If we are copying from ourselves, lock the target instead
1191 //
1192 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
1193
1194 //
1195 // Acquire rundown protection
1196 //
1197 if (!ExAcquireRundownProtection(&Process->RundownProtect))
1198 {
1199 //
1200 // Fail
1201 //
1202 return STATUS_PROCESS_IS_TERMINATING;
1203 }
1204
1205 //
1206 // See if we should use the pool copy
1207 //
1208 if (BufferSize > MI_POOL_COPY_BYTES)
1209 {
1210 //
1211 // Use MDL-copy
1212 //
1213 Status = MiDoMappedCopy(SourceProcess,
1214 SourceAddress,
1215 TargetProcess,
1216 TargetAddress,
1217 BufferSize,
1218 PreviousMode,
1219 ReturnSize);
1220 }
1221 else
1222 {
1223 //
1224 // Do pool copy
1225 //
1226 Status = MiDoPoolCopy(SourceProcess,
1227 SourceAddress,
1228 TargetProcess,
1229 TargetAddress,
1230 BufferSize,
1231 PreviousMode,
1232 ReturnSize);
1233 }
1234
1235 //
1236 // Release the lock
1237 //
1238 ExReleaseRundownProtection(&Process->RundownProtect);
1239 return Status;
1240 }
1241
1242 NTSTATUS
1243 NTAPI
1244 MmFlushVirtualMemory(IN PEPROCESS Process,
1245 IN OUT PVOID *BaseAddress,
1246 IN OUT PSIZE_T RegionSize,
1247 OUT PIO_STATUS_BLOCK IoStatusBlock)
1248 {
1249 PAGED_CODE();
1250 UNIMPLEMENTED;
1251
1252 //
1253 // Fake success
1254 //
1255 return STATUS_SUCCESS;
1256 }
1257
1258 ULONG
1259 NTAPI
1260 MiGetPageProtection(IN PMMPTE PointerPte)
1261 {
1262 MMPTE TempPte;
1263 PMMPFN Pfn;
1264 PAGED_CODE();
1265
1266 /* Copy this PTE's contents */
1267 TempPte = *PointerPte;
1268
1269 /* Assure it's not totally zero */
1270 ASSERT(TempPte.u.Long);
1271
1272 /* Check for a special prototype format */
1273 if (TempPte.u.Soft.Valid == 0 &&
1274 TempPte.u.Soft.Prototype == 1)
1275 {
1276 /* Unsupported now */
1277 UNIMPLEMENTED;
1278 ASSERT(FALSE);
1279 }
1280
1281 /* In the easy case of transition or demand zero PTE just return its protection */
1282 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1283
1284 /* If we get here, the PTE is valid, so look up the page in PFN database */
1285 Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1286 if (!Pfn->u3.e1.PrototypePte)
1287 {
1288 /* Return protection of the original pte */
1289 ASSERT(Pfn->u4.AweAllocation == 0);
1290 return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1291 }
1292
1293 /* This is software PTE */
1294 DPRINT1("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
1295 DPRINT1("VA: %p\n", MiPteToAddress(&TempPte));
1296 DPRINT1("Mask: %lx\n", TempPte.u.Soft.Protection);
1297 DPRINT1("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
1298 return MmProtectToValue[TempPte.u.Soft.Protection];
1299 }
1300
1301 ULONG
1302 NTAPI
1303 MiQueryAddressState(IN PVOID Va,
1304 IN PMMVAD Vad,
1305 IN PEPROCESS TargetProcess,
1306 OUT PULONG ReturnedProtect,
1307 OUT PVOID *NextVa)
1308 {
1309
1310 PMMPTE PointerPte, ProtoPte;
1311 PMMPDE PointerPde;
1312 MMPTE TempPte, TempProtoPte;
1313 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1314 ULONG State = MEM_RESERVE, Protect = 0;
1315 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1316 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1317
1318 /* Only normal VADs supported */
1319 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1320
1321 /* Get the PDE and PTE for the address */
1322 PointerPde = MiAddressToPde(Va);
1323 PointerPte = MiAddressToPte(Va);
1324
1325 /* Return the next range */
1326 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1327
1328 /* Is the PDE demand-zero? */
1329 if (PointerPde->u.Long != 0)
1330 {
1331 /* It is not. Is it valid? */
1332 if (PointerPde->u.Hard.Valid == 0)
1333 {
1334 /* Is isn't, fault it in */
1335 PointerPte = MiPteToAddress(PointerPde);
1336 MiMakeSystemAddressValid(PointerPte, TargetProcess);
1337 ValidPte = TRUE;
1338 }
1339 }
1340 else
1341 {
1342 /* It is, skip it and move to the next PDE */
1343 *NextVa = MiPdeToAddress(PointerPde + 1);
1344 }
1345
1346 /* Is it safe to try reading the PTE? */
1347 if (ValidPte)
1348 {
1349 /* FIXME: watch out for large pages */
1350 ASSERT(PointerPde->u.Hard.LargePage == FALSE);
1351
1352 /* Capture the PTE */
1353 TempPte = *PointerPte;
1354 if (TempPte.u.Long != 0)
1355 {
1356 /* The PTE is valid, so it's not zeroed out */
1357 DemandZeroPte = FALSE;
1358
1359 /* Is it a decommited, invalid, or faulted PTE? */
1360 if ((TempPte.u.Soft.Protection == MM_DECOMMIT) &&
1361 (TempPte.u.Hard.Valid == 0) &&
1362 ((TempPte.u.Soft.Prototype == 0) ||
1363 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1364 {
1365 /* Otherwise our defaults should hold */
1366 ASSERT(Protect == 0);
1367 ASSERT(State == MEM_RESERVE);
1368 }
1369 else
1370 {
1371 /* This means it's committed */
1372 State = MEM_COMMIT;
1373
1374 /* We don't support these */
1375 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1376 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
1377 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1378
1379 /* Get protection state of this page */
1380 Protect = MiGetPageProtection(PointerPte);
1381
1382 /* Check if this is an image-backed VAD */
1383 if ((TempPte.u.Soft.Valid == 0) &&
1384 (TempPte.u.Soft.Prototype == 1) &&
1385 (Vad->u.VadFlags.PrivateMemory == 0) &&
1386 (Vad->ControlArea))
1387 {
1388 DPRINT1("Not supported\n");
1389 ASSERT(FALSE);
1390 }
1391 }
1392 }
1393 }
1394
1395 /* Check if this was a demand-zero PTE, since we need to find the state */
1396 if (DemandZeroPte)
1397 {
1398 /* Not yet handled */
1399 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1400 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1401
1402 /* Check if this is private commited memory, or an section-backed VAD */
1403 if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea))
1404 {
1405 /* Tell caller about the next range */
1406 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1407
1408 /* Get the prototype PTE for this VAD */
1409 ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad,
1410 (ULONG_PTR)Va >> PAGE_SHIFT);
1411 if (ProtoPte)
1412 {
1413 /* We should unlock the working set, but it's not being held! */
1414
1415 /* Is the prototype PTE actually valid (committed)? */
1416 TempProtoPte = *ProtoPte;
1417 if (TempProtoPte.u.Long)
1418 {
1419 /* Unless this is a memory-mapped file, handle it like private VAD */
1420 State = MEM_COMMIT;
1421 ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
1422 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1423 }
1424
1425 /* We should re-lock the working set */
1426 }
1427 }
1428 else if (Vad->u.VadFlags.MemCommit)
1429 {
1430 /* This is committed memory */
1431 State = MEM_COMMIT;
1432
1433 /* Convert the protection */
1434 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1435 }
1436 }
1437
1438 /* Return the protection code */
1439 *ReturnedProtect = Protect;
1440 return State;
1441 }
1442
1443 NTSTATUS
1444 NTAPI
1445 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
1446 IN PVOID BaseAddress,
1447 OUT PVOID MemoryInformation,
1448 IN SIZE_T MemoryInformationLength,
1449 OUT PSIZE_T ReturnLength)
1450 {
1451 PEPROCESS TargetProcess;
1452 NTSTATUS Status = STATUS_SUCCESS;
1453 PMMVAD Vad = NULL;
1454 PVOID Address, NextAddress;
1455 BOOLEAN Found = FALSE;
1456 ULONG NewProtect, NewState;
1457 ULONG_PTR BaseVpn;
1458 MEMORY_BASIC_INFORMATION MemoryInfo;
1459 KAPC_STATE ApcState;
1460 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1461 PMEMORY_AREA MemoryArea;
1462 SIZE_T ResultLength;
1463
1464 /* Check for illegal addresses in user-space, or the shared memory area */
1465 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
1466 (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA))
1467 {
1468 Address = PAGE_ALIGN(BaseAddress);
1469
1470 /* Make up an info structure describing this range */
1471 MemoryInfo.BaseAddress = Address;
1472 MemoryInfo.AllocationProtect = PAGE_READONLY;
1473 MemoryInfo.Type = MEM_PRIVATE;
1474
1475 /* Special case for shared data */
1476 if (Address == (PVOID)MM_SHARED_USER_DATA_VA)
1477 {
1478 MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA;
1479 MemoryInfo.State = MEM_COMMIT;
1480 MemoryInfo.Protect = PAGE_READONLY;
1481 MemoryInfo.RegionSize = PAGE_SIZE;
1482 }
1483 else
1484 {
1485 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
1486 MemoryInfo.State = MEM_RESERVE;
1487 MemoryInfo.Protect = PAGE_NOACCESS;
1488 MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address;
1489 }
1490
1491 /* Return the data, NtQueryInformation already probed it*/
1492 if (PreviousMode != KernelMode)
1493 {
1494 _SEH2_TRY
1495 {
1496 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1497 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1498 }
1499 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1500 {
1501 Status = _SEH2_GetExceptionCode();
1502 }
1503 _SEH2_END;
1504 }
1505 else
1506 {
1507 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1508 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1509 }
1510
1511 return Status;
1512 }
1513
1514 /* Check if this is for a local or remote process */
1515 if (ProcessHandle == NtCurrentProcess())
1516 {
1517 TargetProcess = PsGetCurrentProcess();
1518 }
1519 else
1520 {
1521 /* Reference the target process */
1522 Status = ObReferenceObjectByHandle(ProcessHandle,
1523 PROCESS_QUERY_INFORMATION,
1524 PsProcessType,
1525 ExGetPreviousMode(),
1526 (PVOID*)&TargetProcess,
1527 NULL);
1528 if (!NT_SUCCESS(Status)) return Status;
1529
1530 /* Attach to it now */
1531 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1532 }
1533
1534 /* Lock the address space and make sure the process isn't already dead */
1535 MmLockAddressSpace(&TargetProcess->Vm);
1536 if (TargetProcess->VmDeleted)
1537 {
1538 /* Unlock the address space of the process */
1539 MmUnlockAddressSpace(&TargetProcess->Vm);
1540
1541 /* Check if we were attached */
1542 if (ProcessHandle != NtCurrentProcess())
1543 {
1544 /* Detach and dereference the process */
1545 KeUnstackDetachProcess(&ApcState);
1546 ObDereferenceObject(TargetProcess);
1547 }
1548
1549 /* Bail out */
1550 DPRINT1("Process is dying\n");
1551 return STATUS_PROCESS_IS_TERMINATING;
1552 }
1553
1554 /* Loop the VADs */
1555 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
1556 if (TargetProcess->VadRoot.NumberGenericTableElements)
1557 {
1558 /* Scan on the right */
1559 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
1560 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
1561 while (Vad)
1562 {
1563 /* Check if this VAD covers the allocation range */
1564 if ((BaseVpn >= Vad->StartingVpn) &&
1565 (BaseVpn <= Vad->EndingVpn))
1566 {
1567 /* We're done */
1568 Found = TRUE;
1569 break;
1570 }
1571
1572 /* Check if this VAD is too high */
1573 if (BaseVpn < Vad->StartingVpn)
1574 {
1575 /* Stop if there is no left child */
1576 if (!Vad->LeftChild) break;
1577
1578 /* Search on the left next */
1579 Vad = Vad->LeftChild;
1580 }
1581 else
1582 {
1583 /* Then this VAD is too low, keep searching on the right */
1584 ASSERT(BaseVpn > Vad->EndingVpn);
1585
1586 /* Stop if there is no right child */
1587 if (!Vad->RightChild) break;
1588
1589 /* Search on the right next */
1590 Vad = Vad->RightChild;
1591 }
1592 }
1593 }
1594
1595 /* Was a VAD found? */
1596 if (!Found)
1597 {
1598 Address = PAGE_ALIGN(BaseAddress);
1599
1600 /* Calculate region size */
1601 if (Vad)
1602 {
1603 if (Vad->StartingVpn >= BaseVpn)
1604 {
1605 /* Region size is the free space till the start of that VAD */
1606 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1607 }
1608 else
1609 {
1610 /* Get the next VAD */
1611 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
1612 if (Vad)
1613 {
1614 /* Region size is the free space till the start of that VAD */
1615 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1616 }
1617 else
1618 {
1619 /* Maximum possible region size with that base address */
1620 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1621 }
1622 }
1623 }
1624 else
1625 {
1626 /* Maximum possible region size with that base address */
1627 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1628 }
1629
1630 /* Unlock the address space of the process */
1631 MmUnlockAddressSpace(&TargetProcess->Vm);
1632
1633 /* Check if we were attached */
1634 if (ProcessHandle != NtCurrentProcess())
1635 {
1636 /* Detach and derefernece the process */
1637 KeUnstackDetachProcess(&ApcState);
1638 ObDereferenceObject(TargetProcess);
1639 }
1640
1641 /* Build the rest of the initial information block */
1642 MemoryInfo.BaseAddress = Address;
1643 MemoryInfo.AllocationBase = NULL;
1644 MemoryInfo.AllocationProtect = 0;
1645 MemoryInfo.State = MEM_FREE;
1646 MemoryInfo.Protect = PAGE_NOACCESS;
1647 MemoryInfo.Type = 0;
1648
1649 /* Return the data, NtQueryInformation already probed it*/
1650 if (PreviousMode != KernelMode)
1651 {
1652 _SEH2_TRY
1653 {
1654 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1655 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1656 }
1657 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1658 {
1659 Status = _SEH2_GetExceptionCode();
1660 }
1661 _SEH2_END;
1662 }
1663 else
1664 {
1665 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1666 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1667 }
1668
1669 return Status;
1670 }
1671
1672 /* Set the correct memory type based on what kind of VAD this is */
1673 if ((Vad->u.VadFlags.PrivateMemory) ||
1674 (Vad->u.VadFlags.VadType == VadRotatePhysical))
1675 {
1676 MemoryInfo.Type = MEM_PRIVATE;
1677 }
1678 else if (Vad->u.VadFlags.VadType == VadImageMap)
1679 {
1680 MemoryInfo.Type = MEM_IMAGE;
1681 }
1682 else
1683 {
1684 MemoryInfo.Type = MEM_MAPPED;
1685 }
1686
1687 /* Find the memory area the specified address belongs to */
1688 MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
1689 ASSERT(MemoryArea != NULL);
1690
1691 /* Determine information dependent on the memory area type */
1692 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1693 {
1694 Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
1695 if (!NT_SUCCESS(Status))
1696 {
1697 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1698 MemoryArea, MemoryArea->StartingAddress, MemoryArea->EndingAddress, BaseAddress);
1699 NT_ASSERT(NT_SUCCESS(Status));
1700 }
1701 }
1702 else
1703 {
1704 /* Build the initial information block */
1705 Address = PAGE_ALIGN(BaseAddress);
1706 MemoryInfo.BaseAddress = Address;
1707 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1708 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
1709 MemoryInfo.Type = MEM_PRIVATE;
1710
1711 /* Find the largest chunk of memory which has the same state and protection mask */
1712 MemoryInfo.State = MiQueryAddressState(Address,
1713 Vad,
1714 TargetProcess,
1715 &MemoryInfo.Protect,
1716 &NextAddress);
1717 Address = NextAddress;
1718 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1719 {
1720 /* Keep going unless the state or protection mask changed */
1721 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1722 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1723 Address = NextAddress;
1724 }
1725
1726 /* Now that we know the last VA address, calculate the region size */
1727 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1728 }
1729
1730 /* Unlock the address space of the process */
1731 MmUnlockAddressSpace(&TargetProcess->Vm);
1732
1733 /* Check if we were attached */
1734 if (ProcessHandle != NtCurrentProcess())
1735 {
1736 /* Detach and derefernece the process */
1737 KeUnstackDetachProcess(&ApcState);
1738 ObDereferenceObject(TargetProcess);
1739 }
1740
1741 /* Return the data, NtQueryInformation already probed it */
1742 if (PreviousMode != KernelMode)
1743 {
1744 _SEH2_TRY
1745 {
1746 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1747 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1748 }
1749 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1750 {
1751 Status = _SEH2_GetExceptionCode();
1752 }
1753 _SEH2_END;
1754 }
1755 else
1756 {
1757 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1758 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1759 }
1760
1761 /* All went well */
1762 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1763 "State: %lx Type: %lx Size: %lx\n",
1764 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
1765 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
1766 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
1767
1768 return Status;
1769 }
1770
1771 BOOLEAN
1772 NTAPI
1773 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
1774 IN ULONG_PTR EndingAddress,
1775 IN PMMVAD Vad,
1776 IN PEPROCESS Process)
1777 {
1778 PMMPTE PointerPte, LastPte, PointerPde;
1779 BOOLEAN OnBoundary = TRUE;
1780 PAGED_CODE();
1781
1782 /* Get the PDE and PTE addresses */
1783 PointerPde = MiAddressToPde(StartingAddress);
1784 PointerPte = MiAddressToPte(StartingAddress);
1785 LastPte = MiAddressToPte(EndingAddress);
1786
1787 /* Loop all the PTEs */
1788 while (PointerPte <= LastPte)
1789 {
1790 /* Check if we've hit an new PDE boundary */
1791 if (OnBoundary)
1792 {
1793 /* Is this PDE demand zero? */
1794 PointerPde = MiAddressToPte(PointerPte);
1795 if (PointerPde->u.Long != 0)
1796 {
1797 /* It isn't -- is it valid? */
1798 if (PointerPde->u.Hard.Valid == 0)
1799 {
1800 /* Nope, fault it in */
1801 PointerPte = MiPteToAddress(PointerPde);
1802 MiMakeSystemAddressValid(PointerPte, Process);
1803 }
1804 }
1805 else
1806 {
1807 /* The PTE was already valid, so move to the next one */
1808 PointerPde++;
1809 PointerPte = MiPteToAddress(PointerPde);
1810
1811 /* Is the entire VAD committed? If not, fail */
1812 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1813
1814 /* Everything is committed so far past the range, return true */
1815 if (PointerPte > LastPte) return TRUE;
1816 }
1817 }
1818
1819 /* Is the PTE demand zero? */
1820 if (PointerPte->u.Long == 0)
1821 {
1822 /* Is the entire VAD committed? If not, fail */
1823 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1824 }
1825 else
1826 {
1827 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1828 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
1829 (PointerPte->u.Hard.Valid == 0) &&
1830 ((PointerPte->u.Soft.Prototype == 0) ||
1831 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1832 {
1833 /* Then part of the range is decommitted, so fail */
1834 return FALSE;
1835 }
1836 }
1837
1838 /* Move to the next PTE */
1839 PointerPte++;
1840 OnBoundary = MiIsPteOnPdeBoundary(PointerPte);
1841 }
1842
1843 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1844 return TRUE;
1845 }
1846
1847 NTSTATUS
1848 NTAPI
1849 MiRosProtectVirtualMemory(IN PEPROCESS Process,
1850 IN OUT PVOID *BaseAddress,
1851 IN OUT PSIZE_T NumberOfBytesToProtect,
1852 IN ULONG NewAccessProtection,
1853 OUT PULONG OldAccessProtection OPTIONAL)
1854 {
1855 PMEMORY_AREA MemoryArea;
1856 PMMSUPPORT AddressSpace;
1857 ULONG OldAccessProtection_;
1858 NTSTATUS Status;
1859
1860 *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
1861 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
1862
1863 AddressSpace = &Process->Vm;
1864 MmLockAddressSpace(AddressSpace);
1865 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
1866 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
1867 {
1868 MmUnlockAddressSpace(AddressSpace);
1869 return STATUS_UNSUCCESSFUL;
1870 }
1871
1872 if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
1873
1874 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
1875 Status = MmProtectSectionView(AddressSpace,
1876 MemoryArea,
1877 *BaseAddress,
1878 *NumberOfBytesToProtect,
1879 NewAccessProtection,
1880 OldAccessProtection);
1881
1882 MmUnlockAddressSpace(AddressSpace);
1883
1884 return Status;
1885 }
1886
1887 NTSTATUS
1888 NTAPI
1889 MiProtectVirtualMemory(IN PEPROCESS Process,
1890 IN OUT PVOID *BaseAddress,
1891 IN OUT PSIZE_T NumberOfBytesToProtect,
1892 IN ULONG NewAccessProtection,
1893 OUT PULONG OldAccessProtection OPTIONAL)
1894 {
1895 PMEMORY_AREA MemoryArea;
1896 PMMVAD Vad;
1897 PMMSUPPORT AddressSpace;
1898 ULONG_PTR StartingAddress, EndingAddress;
1899 PMMPTE PointerPde, PointerPte, LastPte;
1900 MMPTE PteContents;
1901 PMMPFN Pfn1;
1902 ULONG ProtectionMask, OldProtect;
1903 BOOLEAN Committed;
1904 NTSTATUS Status = STATUS_SUCCESS;
1905 PETHREAD Thread = PsGetCurrentThread();
1906
1907 /* Calculate base address for the VAD */
1908 StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
1909 EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
1910
1911 /* Calculate the protection mask and make sure it's valid */
1912 ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
1913 if (ProtectionMask == MM_INVALID_PROTECTION)
1914 {
1915 DPRINT1("Invalid protection mask\n");
1916 return STATUS_INVALID_PAGE_PROTECTION;
1917 }
1918
1919 /* Check for ROS specific memory area */
1920 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
1921 if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW))
1922 {
1923 /* Evil hack */
1924 return MiRosProtectVirtualMemory(Process,
1925 BaseAddress,
1926 NumberOfBytesToProtect,
1927 NewAccessProtection,
1928 OldAccessProtection);
1929 }
1930
1931 /* Lock the address space and make sure the process isn't already dead */
1932 AddressSpace = MmGetCurrentAddressSpace();
1933 MmLockAddressSpace(AddressSpace);
1934 if (Process->VmDeleted)
1935 {
1936 DPRINT1("Process is dying\n");
1937 Status = STATUS_PROCESS_IS_TERMINATING;
1938 goto FailPath;
1939 }
1940
1941 /* Get the VAD for this address range, and make sure it exists */
1942 Vad = (PMMVAD)MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
1943 EndingAddress >> PAGE_SHIFT,
1944 &Process->VadRoot);
1945 if (!Vad)
1946 {
1947 DPRINT("Could not find a VAD for this allocation\n");
1948 Status = STATUS_CONFLICTING_ADDRESSES;
1949 goto FailPath;
1950 }
1951
1952 /* Make sure the address is within this VAD's boundaries */
1953 if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
1954 (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
1955 {
1956 Status = STATUS_CONFLICTING_ADDRESSES;
1957 goto FailPath;
1958 }
1959
1960 /* These kinds of VADs are not supported atm */
1961 if ((Vad->u.VadFlags.VadType == VadAwe) ||
1962 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
1963 (Vad->u.VadFlags.VadType == VadLargePages))
1964 {
1965 DPRINT1("Illegal VAD for attempting to set protection\n");
1966 Status = STATUS_CONFLICTING_ADDRESSES;
1967 goto FailPath;
1968 }
1969
1970 /* Check for a VAD whose protection can't be changed */
1971 if (Vad->u.VadFlags.NoChange == 1)
1972 {
1973 DPRINT1("Trying to change protection of a NoChange VAD\n");
1974 Status = STATUS_INVALID_PAGE_PROTECTION;
1975 goto FailPath;
1976 }
1977
1978 /* Is this section, or private memory? */
1979 if (Vad->u.VadFlags.PrivateMemory == 0)
1980 {
1981 /* Not yet supported */
1982 if (Vad->u.VadFlags.VadType == VadLargePageSection)
1983 {
1984 DPRINT1("Illegal VAD for attempting to set protection\n");
1985 Status = STATUS_CONFLICTING_ADDRESSES;
1986 goto FailPath;
1987 }
1988
1989 /* Rotate VADs are not yet supported */
1990 if (Vad->u.VadFlags.VadType == VadRotatePhysical)
1991 {
1992 DPRINT1("Illegal VAD for attempting to set protection\n");
1993 Status = STATUS_CONFLICTING_ADDRESSES;
1994 goto FailPath;
1995 }
1996
1997 /* Not valid on section files */
1998 if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
1999 {
2000 /* Fail */
2001 DPRINT1("Invalid protection flags for section\n");
2002 Status = STATUS_INVALID_PARAMETER_4;
2003 goto FailPath;
2004 }
2005
2006 /* Check if data or page file mapping protection PTE is compatible */
2007 if (!Vad->ControlArea->u.Flags.Image)
2008 {
2009 /* Not yet */
2010 DPRINT1("Fixme: Not checking for valid protection\n");
2011 }
2012
2013 /* This is a section, and this is not yet supported */
2014 DPRINT1("Section protection not yet supported\n");
2015 OldProtect = 0;
2016 }
2017 else
2018 {
2019 /* Private memory, check protection flags */
2020 if ((NewAccessProtection & PAGE_WRITECOPY) ||
2021 (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2022 {
2023 DPRINT1("Invalid protection flags for private memory\n");
2024 Status = STATUS_INVALID_PARAMETER_4;
2025 goto FailPath;
2026 }
2027
2028 /* Lock the working set */
2029 MiLockProcessWorkingSetUnsafe(Process, Thread);
2030
2031 /* Check if all pages in this range are committed */
2032 Committed = MiIsEntireRangeCommitted(StartingAddress,
2033 EndingAddress,
2034 Vad,
2035 Process);
2036 if (!Committed)
2037 {
2038 /* Fail */
2039 DPRINT1("The entire range is not committed\n");
2040 Status = STATUS_NOT_COMMITTED;
2041 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2042 goto FailPath;
2043 }
2044
2045 /* Compute starting and ending PTE and PDE addresses */
2046 PointerPde = MiAddressToPde(StartingAddress);
2047 PointerPte = MiAddressToPte(StartingAddress);
2048 LastPte = MiAddressToPte(EndingAddress);
2049
2050 /* Make this PDE valid */
2051 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2052
2053 /* Save protection of the first page */
2054 if (PointerPte->u.Long != 0)
2055 {
2056 /* Capture the page protection and make the PDE valid */
2057 OldProtect = MiGetPageProtection(PointerPte);
2058 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2059 }
2060 else
2061 {
2062 /* Grab the old protection from the VAD itself */
2063 OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2064 }
2065
2066 /* Loop all the PTEs now */
2067 while (PointerPte <= LastPte)
2068 {
2069 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2070 if (MiIsPteOnPdeBoundary(PointerPte))
2071 {
2072 PointerPde = MiAddressToPte(PointerPte);
2073 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2074 }
2075
2076 /* Capture the PTE and check if it was empty */
2077 PteContents = *PointerPte;
2078 if (PteContents.u.Long == 0)
2079 {
2080 /* This used to be a zero PTE and it no longer is, so we must add a
2081 reference to the pagetable. */
2082 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2083 }
2084
2085 /* Check what kind of PTE we are dealing with */
2086 if (PteContents.u.Hard.Valid == 1)
2087 {
2088 /* Get the PFN entry */
2089 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2090
2091 /* We don't support this yet */
2092 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2093
2094 /* Check if the page should not be accessible at all */
2095 if ((NewAccessProtection & PAGE_NOACCESS) ||
2096 (NewAccessProtection & PAGE_GUARD))
2097 {
2098 /* The page should be in the WS and we should make it transition now */
2099 DPRINT1("Making valid page invalid is not yet supported!\n");
2100 Status = STATUS_NOT_IMPLEMENTED;
2101 /* Unlock the working set */
2102 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2103 goto FailPath;
2104 }
2105
2106 /* Write the protection mask and write it with a TLB flush */
2107 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2108 MiFlushTbAndCapture(Vad,
2109 PointerPte,
2110 ProtectionMask,
2111 Pfn1,
2112 TRUE);
2113 }
2114 else
2115 {
2116 /* We don't support these cases yet */
2117 ASSERT(PteContents.u.Soft.Prototype == 0);
2118 ASSERT(PteContents.u.Soft.Transition == 0);
2119
2120 /* The PTE is already demand-zero, just update the protection mask */
2121 PteContents.u.Soft.Protection = ProtectionMask;
2122 MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2123 ASSERT(PointerPte->u.Long != 0);
2124 }
2125
2126 /* Move to the next PTE */
2127 PointerPte++;
2128 }
2129
2130 /* Unlock the working set */
2131 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2132 }
2133
2134 /* Unlock the address space */
2135 MmUnlockAddressSpace(AddressSpace);
2136
2137 /* Return parameters and success */
2138 *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2139 *BaseAddress = (PVOID)StartingAddress;
2140 *OldAccessProtection = OldProtect;
2141 return STATUS_SUCCESS;
2142
2143 FailPath:
2144 /* Unlock the address space and return the failure code */
2145 MmUnlockAddressSpace(AddressSpace);
2146 return Status;
2147 }
2148
2149 VOID
2150 NTAPI
2151 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde,
2152 IN PEPROCESS TargetProcess,
2153 IN KIRQL OldIrql)
2154 {
2155 PMMPTE PointerPte, PointerPpe, PointerPxe;
2156
2157 //
2158 // Sanity checks. The latter is because we only use this function with the
2159 // PFN lock not held, so it may go away in the future.
2160 //
2161 ASSERT(KeAreAllApcsDisabled() == TRUE);
2162 ASSERT(OldIrql == MM_NOIRQL);
2163
2164 //
2165 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2166 // return the same address as the PDE on 2-level page table systems.
2167 //
2168 // If everything is already valid, there is nothing to do.
2169 //
2170 PointerPpe = MiAddressToPte(PointerPde);
2171 PointerPxe = MiAddressToPde(PointerPde);
2172 if ((PointerPxe->u.Hard.Valid) &&
2173 (PointerPpe->u.Hard.Valid) &&
2174 (PointerPde->u.Hard.Valid))
2175 {
2176 return;
2177 }
2178
2179 //
2180 // At least something is invalid, so begin by getting the PTE for the PDE itself
2181 // and then lookup each additional level. We must do it in this precise order
2182 // because the pagfault.c code (as well as in Windows) depends that the next
2183 // level up (higher) must be valid when faulting a lower level
2184 //
2185 PointerPte = MiPteToAddress(PointerPde);
2186 do
2187 {
2188 //
2189 // Make sure APCs continued to be disabled
2190 //
2191 ASSERT(KeAreAllApcsDisabled() == TRUE);
2192
2193 //
2194 // First, make the PXE valid if needed
2195 //
2196 if (!PointerPxe->u.Hard.Valid)
2197 {
2198 MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2199 ASSERT(PointerPxe->u.Hard.Valid == 1);
2200 }
2201
2202 //
2203 // Next, the PPE
2204 //
2205 if (!PointerPpe->u.Hard.Valid)
2206 {
2207 MiMakeSystemAddressValid(PointerPde, TargetProcess);
2208 ASSERT(PointerPpe->u.Hard.Valid == 1);
2209 }
2210
2211 //
2212 // And finally, make the PDE itself valid.
2213 //
2214 MiMakeSystemAddressValid(PointerPte, TargetProcess);
2215
2216 //
2217 // This should've worked the first time so the loop is really just for
2218 // show -- ASSERT that we're actually NOT going to be looping.
2219 //
2220 ASSERT(PointerPxe->u.Hard.Valid == 1);
2221 ASSERT(PointerPpe->u.Hard.Valid == 1);
2222 ASSERT(PointerPde->u.Hard.Valid == 1);
2223 } while (!(PointerPxe->u.Hard.Valid) ||
2224 !(PointerPpe->u.Hard.Valid) ||
2225 !(PointerPde->u.Hard.Valid));
2226 }
2227
2228 VOID
2229 NTAPI
2230 MiProcessValidPteList(IN PMMPTE *ValidPteList,
2231 IN ULONG Count)
2232 {
2233 KIRQL OldIrql;
2234 ULONG i;
2235 MMPTE TempPte;
2236 PFN_NUMBER PageFrameIndex;
2237 PMMPFN Pfn1, Pfn2;
2238
2239 //
2240 // Acquire the PFN lock and loop all the PTEs in the list
2241 //
2242 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2243 for (i = 0; i != Count; i++)
2244 {
2245 //
2246 // The PTE must currently be valid
2247 //
2248 TempPte = *ValidPteList[i];
2249 ASSERT(TempPte.u.Hard.Valid == 1);
2250
2251 //
2252 // Get the PFN entry for the page itself, and then for its page table
2253 //
2254 PageFrameIndex = PFN_FROM_PTE(&TempPte);
2255 Pfn1 = MiGetPfnEntry(PageFrameIndex);
2256 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2257
2258 //
2259 // Decrement the share count on the page table, and then on the page
2260 // itself
2261 //
2262 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2263 MI_SET_PFN_DELETED(Pfn1);
2264 MiDecrementShareCount(Pfn1, PageFrameIndex);
2265
2266 //
2267 // Make the page decommitted
2268 //
2269 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2270 }
2271
2272 //
2273 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2274 // and then release the PFN lock
2275 //
2276 KeFlushCurrentTb();
2277 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2278 }
2279
2280 ULONG
2281 NTAPI
2282 MiDecommitPages(IN PVOID StartingAddress,
2283 IN PMMPTE EndingPte,
2284 IN PEPROCESS Process,
2285 IN PMMVAD Vad)
2286 {
2287 PMMPTE PointerPde, PointerPte, CommitPte = NULL;
2288 ULONG CommitReduction = 0;
2289 PMMPTE ValidPteList[256];
2290 ULONG PteCount = 0;
2291 PMMPFN Pfn1;
2292 MMPTE PteContents;
2293 PETHREAD CurrentThread = PsGetCurrentThread();
2294
2295 //
2296 // Get the PTE and PTE for the address, and lock the working set
2297 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2298 // commited range ends so that we can do the right accounting.
2299 //
2300 PointerPde = MiAddressToPde(StartingAddress);
2301 PointerPte = MiAddressToPte(StartingAddress);
2302 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2303 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2304
2305 //
2306 // Make the PDE valid, and now loop through each page's worth of data
2307 //
2308 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2309 while (PointerPte <= EndingPte)
2310 {
2311 //
2312 // Check if we've crossed a PDE boundary
2313 //
2314 if (MiIsPteOnPdeBoundary(PointerPte))
2315 {
2316 //
2317 // Get the new PDE and flush the valid PTEs we had built up until
2318 // now. This helps reduce the amount of TLB flushing we have to do.
2319 // Note that Windows does a much better job using timestamps and
2320 // such, and does not flush the entire TLB all the time, but right
2321 // now we have bigger problems to worry about than TLB flushing.
2322 //
2323 PointerPde = MiAddressToPde(StartingAddress);
2324 if (PteCount)
2325 {
2326 MiProcessValidPteList(ValidPteList, PteCount);
2327 PteCount = 0;
2328 }
2329
2330 //
2331 // Make this PDE valid
2332 //
2333 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2334 }
2335
2336 //
2337 // Read this PTE. It might be active or still demand-zero.
2338 //
2339 PteContents = *PointerPte;
2340 if (PteContents.u.Long)
2341 {
2342 //
2343 // The PTE is active. It might be valid and in a working set, or
2344 // it might be a prototype PTE or paged out or even in transition.
2345 //
2346 if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2347 {
2348 //
2349 // It's already decommited, so there's nothing for us to do here
2350 //
2351 CommitReduction++;
2352 }
2353 else
2354 {
2355 //
2356 // Remove it from the counters, and check if it was valid or not
2357 //
2358 //Process->NumberOfPrivatePages--;
2359 if (PteContents.u.Hard.Valid)
2360 {
2361 //
2362 // It's valid. At this point make sure that it is not a ROS
2363 // PFN. Also, we don't support ProtoPTEs in this code path.
2364 //
2365 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2366 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2367 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2368
2369 //
2370 // Flush any pending PTEs that we had not yet flushed, if our
2371 // list has gotten too big, then add this PTE to the flush list.
2372 //
2373 if (PteCount == 256)
2374 {
2375 MiProcessValidPteList(ValidPteList, PteCount);
2376 PteCount = 0;
2377 }
2378 ValidPteList[PteCount++] = PointerPte;
2379 }
2380 else
2381 {
2382 //
2383 // We do not support any of these other scenarios at the moment
2384 //
2385 ASSERT(PteContents.u.Soft.Prototype == 0);
2386 ASSERT(PteContents.u.Soft.Transition == 0);
2387 ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2388
2389 //
2390 // So the only other possibility is that it is still a demand
2391 // zero PTE, in which case we undo the accounting we did
2392 // earlier and simply make the page decommitted.
2393 //
2394 //Process->NumberOfPrivatePages++;
2395 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2396 }
2397 }
2398 }
2399 else
2400 {
2401 //
2402 // This used to be a zero PTE and it no longer is, so we must add a
2403 // reference to the pagetable.
2404 //
2405 MiIncrementPageTableReferences(StartingAddress);
2406
2407 //
2408 // Next, we account for decommitted PTEs and make the PTE as such
2409 //
2410 if (PointerPte > CommitPte) CommitReduction++;
2411 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2412 }
2413
2414 //
2415 // Move to the next PTE and the next address
2416 //
2417 PointerPte++;
2418 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2419 }
2420
2421 //
2422 // Flush any dangling PTEs from the loop in the last page table, and then
2423 // release the working set and return the commit reduction accounting.
2424 //
2425 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2426 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
2427 return CommitReduction;
2428 }
2429
2430 /* PUBLIC FUNCTIONS ***********************************************************/
2431
2432 /*
2433 * @unimplemented
2434 */
2435 PVOID
2436 NTAPI
2437 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
2438 {
2439 UNIMPLEMENTED;
2440 return 0;
2441 }
2442
2443 /*
2444 * @unimplemented
2445 */
2446 PVOID
2447 NTAPI
2448 MmSecureVirtualMemory(IN PVOID Address,
2449 IN SIZE_T Length,
2450 IN ULONG Mode)
2451 {
2452 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2453 return Address;
2454 }
2455
2456 /*
2457 * @unimplemented
2458 */
2459 VOID
2460 NTAPI
2461 MmUnsecureVirtualMemory(IN PVOID SecureMem)
2462 {
2463 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2464 }
2465
2466 /* SYSTEM CALLS ***************************************************************/
2467
2468 NTSTATUS
2469 NTAPI
2470 NtReadVirtualMemory(IN HANDLE ProcessHandle,
2471 IN PVOID BaseAddress,
2472 OUT PVOID Buffer,
2473 IN SIZE_T NumberOfBytesToRead,
2474 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2475 {
2476 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2477 PEPROCESS Process;
2478 NTSTATUS Status = STATUS_SUCCESS;
2479 SIZE_T BytesRead = 0;
2480 PAGED_CODE();
2481
2482 //
2483 // Check if we came from user mode
2484 //
2485 if (PreviousMode != KernelMode)
2486 {
2487 //
2488 // Validate the read addresses
2489 //
2490 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2491 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2492 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2493 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2494 {
2495 //
2496 // Don't allow to write into kernel space
2497 //
2498 return STATUS_ACCESS_VIOLATION;
2499 }
2500
2501 //
2502 // Enter SEH for probe
2503 //
2504 _SEH2_TRY
2505 {
2506 //
2507 // Probe the output value
2508 //
2509 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2510 }
2511 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2512 {
2513 //
2514 // Get exception code
2515 //
2516 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2517 }
2518 _SEH2_END;
2519 }
2520
2521 //
2522 // Don't do zero-byte transfers
2523 //
2524 if (NumberOfBytesToRead)
2525 {
2526 //
2527 // Reference the process
2528 //
2529 Status = ObReferenceObjectByHandle(ProcessHandle,
2530 PROCESS_VM_READ,
2531 PsProcessType,
2532 PreviousMode,
2533 (PVOID*)(&Process),
2534 NULL);
2535 if (NT_SUCCESS(Status))
2536 {
2537 //
2538 // Do the copy
2539 //
2540 Status = MmCopyVirtualMemory(Process,
2541 BaseAddress,
2542 PsGetCurrentProcess(),
2543 Buffer,
2544 NumberOfBytesToRead,
2545 PreviousMode,
2546 &BytesRead);
2547
2548 //
2549 // Dereference the process
2550 //
2551 ObDereferenceObject(Process);
2552 }
2553 }
2554
2555 //
2556 // Check if the caller sent this parameter
2557 //
2558 if (NumberOfBytesRead)
2559 {
2560 //
2561 // Enter SEH to guard write
2562 //
2563 _SEH2_TRY
2564 {
2565 //
2566 // Return the number of bytes read
2567 //
2568 *NumberOfBytesRead = BytesRead;
2569 }
2570 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2571 {
2572 }
2573 _SEH2_END;
2574 }
2575
2576 //
2577 // Return status
2578 //
2579 return Status;
2580 }
2581
2582 NTSTATUS
2583 NTAPI
2584 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
2585 IN PVOID BaseAddress,
2586 IN PVOID Buffer,
2587 IN SIZE_T NumberOfBytesToWrite,
2588 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2589 {
2590 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2591 PEPROCESS Process;
2592 NTSTATUS Status = STATUS_SUCCESS;
2593 SIZE_T BytesWritten = 0;
2594 PAGED_CODE();
2595
2596 //
2597 // Check if we came from user mode
2598 //
2599 if (PreviousMode != KernelMode)
2600 {
2601 //
2602 // Validate the read addresses
2603 //
2604 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2605 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2606 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2607 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2608 {
2609 //
2610 // Don't allow to write into kernel space
2611 //
2612 return STATUS_ACCESS_VIOLATION;
2613 }
2614
2615 //
2616 // Enter SEH for probe
2617 //
2618 _SEH2_TRY
2619 {
2620 //
2621 // Probe the output value
2622 //
2623 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2624 }
2625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2626 {
2627 //
2628 // Get exception code
2629 //
2630 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2631 }
2632 _SEH2_END;
2633 }
2634
2635 //
2636 // Don't do zero-byte transfers
2637 //
2638 if (NumberOfBytesToWrite)
2639 {
2640 //
2641 // Reference the process
2642 //
2643 Status = ObReferenceObjectByHandle(ProcessHandle,
2644 PROCESS_VM_WRITE,
2645 PsProcessType,
2646 PreviousMode,
2647 (PVOID*)&Process,
2648 NULL);
2649 if (NT_SUCCESS(Status))
2650 {
2651 //
2652 // Do the copy
2653 //
2654 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
2655 Buffer,
2656 Process,
2657 BaseAddress,
2658 NumberOfBytesToWrite,
2659 PreviousMode,
2660 &BytesWritten);
2661
2662 //
2663 // Dereference the process
2664 //
2665 ObDereferenceObject(Process);
2666 }
2667 }
2668
2669 //
2670 // Check if the caller sent this parameter
2671 //
2672 if (NumberOfBytesWritten)
2673 {
2674 //
2675 // Enter SEH to guard write
2676 //
2677 _SEH2_TRY
2678 {
2679 //
2680 // Return the number of bytes written
2681 //
2682 *NumberOfBytesWritten = BytesWritten;
2683 }
2684 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2685 {
2686 }
2687 _SEH2_END;
2688 }
2689
2690 //
2691 // Return status
2692 //
2693 return Status;
2694 }
2695
2696 NTSTATUS
2697 NTAPI
2698 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
2699 IN OUT PVOID *UnsafeBaseAddress,
2700 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
2701 IN ULONG NewAccessProtection,
2702 OUT PULONG UnsafeOldAccessProtection)
2703 {
2704 PEPROCESS Process;
2705 ULONG OldAccessProtection;
2706 ULONG Protection;
2707 PEPROCESS CurrentProcess = PsGetCurrentProcess();
2708 PVOID BaseAddress = NULL;
2709 SIZE_T NumberOfBytesToProtect = 0;
2710 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2711 NTSTATUS Status;
2712 BOOLEAN Attached = FALSE;
2713 KAPC_STATE ApcState;
2714 PAGED_CODE();
2715
2716 //
2717 // Check for valid protection flags
2718 //
2719 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
2720 if (Protection != PAGE_NOACCESS &&
2721 Protection != PAGE_READONLY &&
2722 Protection != PAGE_READWRITE &&
2723 Protection != PAGE_WRITECOPY &&
2724 Protection != PAGE_EXECUTE &&
2725 Protection != PAGE_EXECUTE_READ &&
2726 Protection != PAGE_EXECUTE_READWRITE &&
2727 Protection != PAGE_EXECUTE_WRITECOPY)
2728 {
2729 //
2730 // Fail
2731 //
2732 return STATUS_INVALID_PAGE_PROTECTION;
2733 }
2734
2735 //
2736 // Check if we came from user mode
2737 //
2738 if (PreviousMode != KernelMode)
2739 {
2740 //
2741 // Enter SEH for probing
2742 //
2743 _SEH2_TRY
2744 {
2745 //
2746 // Validate all outputs
2747 //
2748 ProbeForWritePointer(UnsafeBaseAddress);
2749 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
2750 ProbeForWriteUlong(UnsafeOldAccessProtection);
2751
2752 //
2753 // Capture them
2754 //
2755 BaseAddress = *UnsafeBaseAddress;
2756 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2757 }
2758 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2759 {
2760 //
2761 // Get exception code
2762 //
2763 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2764 }
2765 _SEH2_END;
2766 }
2767 else
2768 {
2769 //
2770 // Capture directly
2771 //
2772 BaseAddress = *UnsafeBaseAddress;
2773 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2774 }
2775
2776 //
2777 // Catch illegal base address
2778 //
2779 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2780
2781 //
2782 // Catch illegal region size
2783 //
2784 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
2785 {
2786 //
2787 // Fail
2788 //
2789 return STATUS_INVALID_PARAMETER_3;
2790 }
2791
2792 //
2793 // 0 is also illegal
2794 //
2795 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
2796
2797 //
2798 // Get a reference to the process
2799 //
2800 Status = ObReferenceObjectByHandle(ProcessHandle,
2801 PROCESS_VM_OPERATION,
2802 PsProcessType,
2803 PreviousMode,
2804 (PVOID*)(&Process),
2805 NULL);
2806 if (!NT_SUCCESS(Status)) return Status;
2807
2808 //
2809 // Check if we should attach
2810 //
2811 if (CurrentProcess != Process)
2812 {
2813 //
2814 // Do it
2815 //
2816 KeStackAttachProcess(&Process->Pcb, &ApcState);
2817 Attached = TRUE;
2818 }
2819
2820 //
2821 // Do the actual work
2822 //
2823 Status = MiProtectVirtualMemory(Process,
2824 &BaseAddress,
2825 &NumberOfBytesToProtect,
2826 NewAccessProtection,
2827 &OldAccessProtection);
2828
2829 //
2830 // Detach if needed
2831 //
2832 if (Attached) KeUnstackDetachProcess(&ApcState);
2833
2834 //
2835 // Release reference
2836 //
2837 ObDereferenceObject(Process);
2838
2839 //
2840 // Enter SEH to return data
2841 //
2842 _SEH2_TRY
2843 {
2844 //
2845 // Return data to user
2846 //
2847 *UnsafeOldAccessProtection = OldAccessProtection;
2848 *UnsafeBaseAddress = BaseAddress;
2849 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
2850 }
2851 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2852 {
2853 }
2854 _SEH2_END;
2855
2856 //
2857 // Return status
2858 //
2859 return Status;
2860 }
2861
2862 FORCEINLINE
2863 BOOLEAN
2864 MI_IS_LOCKED_VA(
2865 PMMPFN Pfn1,
2866 ULONG LockType)
2867 {
2868 // HACK until we have proper WSLIST support
2869 PMMWSLE Wsle = &Pfn1->Wsle;
2870
2871 if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
2872 return TRUE;
2873 if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
2874 return TRUE;
2875
2876 return FALSE;
2877 }
2878
2879 FORCEINLINE
2880 VOID
2881 MI_LOCK_VA(
2882 PMMPFN Pfn1,
2883 ULONG LockType)
2884 {
2885 // HACK until we have proper WSLIST support
2886 PMMWSLE Wsle = &Pfn1->Wsle;
2887
2888 if (!Wsle->u1.e1.LockedInWs &&
2889 !Wsle->u1.e1.LockedInMemory)
2890 {
2891 MiReferenceProbedPageAndBumpLockCount(Pfn1);
2892 }
2893
2894 if (LockType & MAP_PROCESS)
2895 Wsle->u1.e1.LockedInWs = 1;
2896 if (LockType & MAP_SYSTEM)
2897 Wsle->u1.e1.LockedInMemory = 1;
2898 }
2899
2900 FORCEINLINE
2901 VOID
2902 MI_UNLOCK_VA(
2903 PMMPFN Pfn1,
2904 ULONG LockType)
2905 {
2906 // HACK until we have proper WSLIST support
2907 PMMWSLE Wsle = &Pfn1->Wsle;
2908
2909 if (LockType & MAP_PROCESS)
2910 Wsle->u1.e1.LockedInWs = 0;
2911 if (LockType & MAP_SYSTEM)
2912 Wsle->u1.e1.LockedInMemory = 0;
2913
2914 if (!Wsle->u1.e1.LockedInWs &&
2915 !Wsle->u1.e1.LockedInMemory)
2916 {
2917 MiDereferencePfnAndDropLockCount(Pfn1);
2918 }
2919 }
2920
2921 static
2922 NTSTATUS
2923 MiCheckVadsForLockOperation(
2924 _Inout_ PVOID *BaseAddress,
2925 _Inout_ PSIZE_T RegionSize,
2926 _Inout_ PVOID *EndAddress)
2927
2928 {
2929 PMMVAD Vad;
2930 PVOID CurrentVa;
2931
2932 /* Get the base address and align the start address */
2933 *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
2934 *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
2935 *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE);
2936
2937 /* First loop and check all VADs */
2938 CurrentVa = *BaseAddress;
2939 while (CurrentVa < *EndAddress)
2940 {
2941 /* Get VAD */
2942 Vad = MiLocateAddress(CurrentVa);
2943 if (Vad == NULL)
2944 {
2945 /// FIXME: this might be a memory area for a section view...
2946 return STATUS_ACCESS_VIOLATION;
2947 }
2948
2949 /* Check VAD type */
2950 if ((Vad->u.VadFlags.VadType != VadNone) &&
2951 (Vad->u.VadFlags.VadType != VadImageMap) &&
2952 (Vad->u.VadFlags.VadType != VadWriteWatch))
2953 {
2954 *EndAddress = CurrentVa;
2955 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
2956 return STATUS_INCOMPATIBLE_FILE_MAP;
2957 }
2958
2959 CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
2960 }
2961
2962 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
2963 return STATUS_SUCCESS;
2964 }
2965
2966 static
2967 NTSTATUS
2968 MiLockVirtualMemory(
2969 IN OUT PVOID *BaseAddress,
2970 IN OUT PSIZE_T RegionSize,
2971 IN ULONG MapType)
2972 {
2973 PEPROCESS CurrentProcess;
2974 PMMSUPPORT AddressSpace;
2975 PVOID CurrentVa, EndAddress;
2976 PMMPTE PointerPte, LastPte;
2977 PMMPDE PointerPde;
2978 #if (_MI_PAGING_LEVELS >= 3)
2979 PMMPDE PointerPpe;
2980 #endif
2981 #if (_MI_PAGING_LEVELS == 4)
2982 PMMPDE PointerPxe;
2983 #endif
2984 PMMPFN Pfn1;
2985 NTSTATUS Status, TempStatus;
2986
2987 /* Lock the address space */
2988 AddressSpace = MmGetCurrentAddressSpace();
2989 MmLockAddressSpace(AddressSpace);
2990
2991 /* Make sure we still have an address space */
2992 CurrentProcess = PsGetCurrentProcess();
2993 if (CurrentProcess->VmDeleted)
2994 {
2995 Status = STATUS_PROCESS_IS_TERMINATING;
2996 goto Cleanup;
2997 }
2998
2999 /* Check the VADs in the requested range */
3000 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3001 if (!NT_SUCCESS(Status))
3002 {
3003 goto Cleanup;
3004 }
3005
3006 /* Enter SEH for probing */
3007 _SEH2_TRY
3008 {
3009 /* Loop all pages and probe them */
3010 CurrentVa = *BaseAddress;
3011 while (CurrentVa < EndAddress)
3012 {
3013 (void)(*(volatile CHAR*)CurrentVa);
3014 CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
3015 }
3016 }
3017 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3018 {
3019 Status = _SEH2_GetExceptionCode();
3020 goto Cleanup;
3021 }
3022 _SEH2_END;
3023
3024 /* All pages were accessible, since we hold the address space lock, nothing
3025 can be de-committed. Assume success for now. */
3026 Status = STATUS_SUCCESS;
3027
3028 /* Get the PTE and PDE */
3029 PointerPte = MiAddressToPte(*BaseAddress);
3030 PointerPde = MiAddressToPde(*BaseAddress);
3031 #if (_MI_PAGING_LEVELS >= 3)
3032 PointerPpe = MiAddressToPpe(*BaseAddress);
3033 #endif
3034 #if (_MI_PAGING_LEVELS == 4)
3035 PointerPxe = MiAddressToPxe(*BaseAddress);
3036 #endif
3037
3038 /* Get the last PTE */
3039 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3040
3041 /* Lock the process working set */
3042 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3043
3044 /* Loop the pages */
3045 do
3046 {
3047 /* Check for a page that is not accessible */
3048 while (
3049 #if (_MI_PAGING_LEVELS == 4)
3050 (PointerPxe->u.Hard.Valid == 0) ||
3051 #endif
3052 #if (_MI_PAGING_LEVELS >= 3)
3053 (PointerPpe->u.Hard.Valid == 0) ||
3054 #endif
3055 (PointerPde->u.Hard.Valid == 0) ||
3056 (PointerPte->u.Hard.Valid == 0))
3057 {
3058 /* Release process working set */
3059 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3060
3061 /* Access the page */
3062 CurrentVa = MiPteToAddress(PointerPte);
3063
3064 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3065 TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)0xBADBADA3);
3066 if (!NT_SUCCESS(TempStatus))
3067 {
3068 // This should only happen, when remote backing storage is not accessible
3069 ASSERT(FALSE);
3070 Status = TempStatus;
3071 goto Cleanup;
3072 }
3073
3074 /* Lock the process working set */
3075 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3076 }
3077
3078 /* Get the PFN */
3079 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3080 ASSERT(Pfn1 != NULL);
3081
3082 /* Check the previous lock status */
3083 if (MI_IS_LOCKED_VA(Pfn1, MapType))
3084 {
3085 Status = STATUS_WAS_LOCKED;
3086 }
3087
3088 /* Lock it */
3089 MI_LOCK_VA(Pfn1, MapType);
3090
3091 /* Go to the next PTE */
3092 PointerPte++;
3093
3094 /* Check if we're on a PDE boundary */
3095 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3096 #if (_MI_PAGING_LEVELS >= 3)
3097 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3098 #endif
3099 #if (_MI_PAGING_LEVELS == 4)
3100 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3101 #endif
3102 } while (PointerPte <= LastPte);
3103
3104 /* Release process working set */
3105 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3106
3107 Cleanup:
3108 /* Unlock address space */
3109 MmUnlockAddressSpace(AddressSpace);
3110
3111 return Status;
3112 }
3113
3114 NTSTATUS
3115 NTAPI
3116 NtLockVirtualMemory(IN HANDLE ProcessHandle,
3117 IN OUT PVOID *BaseAddress,
3118 IN OUT PSIZE_T NumberOfBytesToLock,
3119 IN ULONG MapType)
3120 {
3121 PEPROCESS Process;
3122 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3123 NTSTATUS Status;
3124 BOOLEAN Attached = FALSE;
3125 KAPC_STATE ApcState;
3126 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3127 PVOID CapturedBaseAddress;
3128 SIZE_T CapturedBytesToLock;
3129 PAGED_CODE();
3130
3131 //
3132 // Validate flags
3133 //
3134 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3135 {
3136 //
3137 // Invalid set of flags
3138 //
3139 return STATUS_INVALID_PARAMETER;
3140 }
3141
3142 //
3143 // At least one flag must be specified
3144 //
3145 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3146 {
3147 //
3148 // No flag given
3149 //
3150 return STATUS_INVALID_PARAMETER;
3151 }
3152
3153 //
3154 // Enter SEH for probing
3155 //
3156 _SEH2_TRY
3157 {
3158 //
3159 // Validate output data
3160 //
3161 ProbeForWritePointer(BaseAddress);
3162 ProbeForWriteSize_t(NumberOfBytesToLock);
3163
3164 //
3165 // Capture it
3166 //
3167 CapturedBaseAddress = *BaseAddress;
3168 CapturedBytesToLock = *NumberOfBytesToLock;
3169 }
3170 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3171 {
3172 //
3173 // Get exception code
3174 //
3175 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3176 }
3177 _SEH2_END;
3178
3179 //
3180 // Catch illegal base address
3181 //
3182 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3183
3184 //
3185 // Catch illegal region size
3186 //
3187 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
3188 {
3189 //
3190 // Fail
3191 //
3192 return STATUS_INVALID_PARAMETER;
3193 }
3194
3195 //
3196 // 0 is also illegal
3197 //
3198 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
3199
3200 //
3201 // Get a reference to the process
3202 //
3203 Status = ObReferenceObjectByHandle(ProcessHandle,
3204 PROCESS_VM_OPERATION,
3205 PsProcessType,
3206 PreviousMode,
3207 (PVOID*)(&Process),
3208 NULL);
3209 if (!NT_SUCCESS(Status)) return Status;
3210
3211 //
3212 // Check if this is is system-mapped
3213 //
3214 if (MapType & MAP_SYSTEM)
3215 {
3216 //
3217 // Check for required privilege
3218 //
3219 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3220 {
3221 //
3222 // Fail: Don't have it
3223 //
3224 ObDereferenceObject(Process);
3225 return STATUS_PRIVILEGE_NOT_HELD;
3226 }
3227 }
3228
3229 //
3230 // Check if we should attach
3231 //
3232 if (CurrentProcess != Process)
3233 {
3234 //
3235 // Do it
3236 //
3237 KeStackAttachProcess(&Process->Pcb, &ApcState);
3238 Attached = TRUE;
3239 }
3240
3241 //
3242 // Call the internal function
3243 //
3244 Status = MiLockVirtualMemory(&CapturedBaseAddress,
3245 &CapturedBytesToLock,
3246 MapType);
3247
3248 //
3249 // Detach if needed
3250 //
3251 if (Attached) KeUnstackDetachProcess(&ApcState);
3252
3253 //
3254 // Release reference
3255 //
3256 ObDereferenceObject(Process);
3257
3258 //
3259 // Enter SEH to return data
3260 //
3261 _SEH2_TRY
3262 {
3263 //
3264 // Return data to user
3265 //
3266 *BaseAddress = CapturedBaseAddress;
3267 *NumberOfBytesToLock = CapturedBytesToLock;
3268 }
3269 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3270 {
3271 //
3272 // Get exception code
3273 //
3274 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3275 }
3276 _SEH2_END;
3277
3278 //
3279 // Return status
3280 //
3281 return Status;
3282 }
3283
3284
3285 static
3286 NTSTATUS
3287 MiUnlockVirtualMemory(
3288 IN OUT PVOID *BaseAddress,
3289 IN OUT PSIZE_T RegionSize,
3290 IN ULONG MapType)
3291 {
3292 PEPROCESS CurrentProcess;
3293 PMMSUPPORT AddressSpace;
3294 PVOID EndAddress;
3295 PMMPTE PointerPte, LastPte;
3296 PMMPDE PointerPde;
3297 #if (_MI_PAGING_LEVELS >= 3)
3298 PMMPDE PointerPpe;
3299 #endif
3300 #if (_MI_PAGING_LEVELS == 4)
3301 PMMPDE PointerPxe;
3302 #endif
3303 PMMPFN Pfn1;
3304 NTSTATUS Status;
3305
3306 /* Lock the address space */
3307 AddressSpace = MmGetCurrentAddressSpace();
3308 MmLockAddressSpace(AddressSpace);
3309
3310 /* Make sure we still have an address space */
3311 CurrentProcess = PsGetCurrentProcess();
3312 if (CurrentProcess->VmDeleted)
3313 {
3314 Status = STATUS_PROCESS_IS_TERMINATING;
3315 goto Cleanup;
3316 }
3317
3318 /* Check the VADs in the requested range */
3319 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3320
3321 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3322 incompatible VAD we continue, like Windows does */
3323 if (Status == STATUS_ACCESS_VIOLATION)
3324 {
3325 Status = STATUS_NOT_LOCKED;
3326 goto Cleanup;
3327 }
3328
3329 /* Get the PTE and PDE */
3330 PointerPte = MiAddressToPte(*BaseAddress);
3331 PointerPde = MiAddressToPde(*BaseAddress);
3332 #if (_MI_PAGING_LEVELS >= 3)
3333 PointerPpe = MiAddressToPpe(*BaseAddress);
3334 #endif
3335 #if (_MI_PAGING_LEVELS == 4)
3336 PointerPxe = MiAddressToPxe(*BaseAddress);
3337 #endif
3338
3339 /* Get the last PTE */
3340 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3341
3342 /* Lock the process working set */
3343 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3344
3345 /* Loop the pages */
3346 do
3347 {
3348 /* Check for a page that is not present */
3349 if (
3350 #if (_MI_PAGING_LEVELS == 4)
3351 (PointerPxe->u.Hard.Valid == 0) ||
3352 #endif
3353 #if (_MI_PAGING_LEVELS >= 3)
3354 (PointerPpe->u.Hard.Valid == 0) ||
3355 #endif
3356 (PointerPde->u.Hard.Valid == 0) ||
3357 (PointerPte->u.Hard.Valid == 0))
3358 {
3359 /* Remember it, but keep going */
3360 Status = STATUS_NOT_LOCKED;
3361 }
3362 else
3363 {
3364 /* Get the PFN */
3365 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3366 ASSERT(Pfn1 != NULL);
3367
3368 /* Check if all of the requested locks are present */
3369 if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
3370 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
3371 {
3372 /* Remember it, but keep going */
3373 Status = STATUS_NOT_LOCKED;
3374
3375 /* Check if no lock is present */
3376 if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM))
3377 {
3378 DPRINT1("FIXME: Should remove the page from WS\n");
3379 }
3380 }
3381 }
3382
3383 /* Go to the next PTE */
3384 PointerPte++;
3385
3386 /* Check if we're on a PDE boundary */
3387 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3388 #if (_MI_PAGING_LEVELS >= 3)
3389 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3390 #endif
3391 #if (_MI_PAGING_LEVELS == 4)
3392 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3393 #endif
3394 } while (PointerPte <= LastPte);
3395
3396 /* Check if we hit a page that was not locked */
3397 if (Status == STATUS_NOT_LOCKED)
3398 {
3399 goto CleanupWithWsLock;
3400 }
3401
3402 /* All pages in the region were locked, so unlock them all */
3403
3404 /* Get the PTE and PDE */
3405 PointerPte = MiAddressToPte(*BaseAddress);
3406 PointerPde = MiAddressToPde(*BaseAddress);
3407 #if (_MI_PAGING_LEVELS >= 3)
3408 PointerPpe = MiAddressToPpe(*BaseAddress);
3409 #endif
3410 #if (_MI_PAGING_LEVELS == 4)
3411 PointerPxe = MiAddressToPxe(*BaseAddress);
3412 #endif
3413
3414 /* Loop the pages */
3415 do
3416 {
3417 /* Unlock it */
3418 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3419 MI_UNLOCK_VA(Pfn1, MapType);
3420
3421 /* Go to the next PTE */
3422 PointerPte++;
3423
3424 /* Check if we're on a PDE boundary */
3425 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3426 #if (_MI_PAGING_LEVELS >= 3)
3427 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3428 #endif
3429 #if (_MI_PAGING_LEVELS == 4)