f82aedb9c9fbaa7e9bfb83e0f4945275de41a428
[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 = (ULONG)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 DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
1295 DPRINT("VA: %p\n", MiPteToAddress(&TempPte));
1296 DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection);
1297 DPRINT("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 #if (_MI_PAGING_LEVELS >= 3)
1313 PMMPPE PointerPpe;
1314 #endif
1315 #if (_MI_PAGING_LEVELS >= 4)
1316 PMMPXE PointerPxe;
1317 #endif
1318 MMPTE TempPte, TempProtoPte;
1319 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1320 ULONG State = MEM_RESERVE, Protect = 0;
1321 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1322 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1323
1324 /* Only normal VADs supported */
1325 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1326
1327 /* Get the PDE and PTE for the address */
1328 PointerPde = MiAddressToPde(Va);
1329 PointerPte = MiAddressToPte(Va);
1330 #if (_MI_PAGING_LEVELS >= 3)
1331 PointerPpe = MiAddressToPpe(Va);
1332 #endif
1333 #if (_MI_PAGING_LEVELS >= 4)
1334 PointerPxe = MiAddressToPxe(Va);
1335 #endif
1336
1337 /* Return the next range */
1338 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1339
1340 do
1341 {
1342 #if (_MI_PAGING_LEVELS >= 4)
1343 /* Does the PXE exist? */
1344 if (PointerPxe->u.Long == 0)
1345 {
1346 /* It does not, next range starts at the next PXE */
1347 *NextVa = MiPxeToAddress(PointerPxe + 1);
1348 break;
1349 }
1350
1351 /* Is the PXE valid? */
1352 if (PointerPxe->u.Hard.Valid == 0)
1353 {
1354 /* Is isn't, fault it in (make the PPE accessible) */
1355 MiMakeSystemAddressValid(PointerPpe, TargetProcess);
1356 }
1357 #endif
1358 #if (_MI_PAGING_LEVELS >= 3)
1359 /* Does the PPE exist? */
1360 if (PointerPpe->u.Long == 0)
1361 {
1362 /* It does not, next range starts at the next PPE */
1363 *NextVa = MiPpeToAddress(PointerPpe + 1);
1364 break;
1365 }
1366
1367 /* Is the PPE valid? */
1368 if (PointerPpe->u.Hard.Valid == 0)
1369 {
1370 /* Is isn't, fault it in (make the PDE accessible) */
1371 MiMakeSystemAddressValid(PointerPde, TargetProcess);
1372 }
1373 #endif
1374
1375 /* Does the PDE exist? */
1376 if (PointerPde->u.Long == 0)
1377 {
1378 /* It does not, next range starts at the next PDE */
1379 *NextVa = MiPdeToAddress(PointerPde + 1);
1380 break;
1381 }
1382
1383 /* Is the PDE valid? */
1384 if (PointerPde->u.Hard.Valid == 0)
1385 {
1386 /* Is isn't, fault it in (make the PTE accessible) */
1387 MiMakeSystemAddressValid(PointerPte, TargetProcess);
1388 }
1389
1390 /* We have a PTE that we can access now! */
1391 ValidPte = TRUE;
1392
1393 } while (FALSE);
1394
1395 /* Is it safe to try reading the PTE? */
1396 if (ValidPte)
1397 {
1398 /* FIXME: watch out for large pages */
1399 ASSERT(PointerPde->u.Hard.LargePage == FALSE);
1400
1401 /* Capture the PTE */
1402 TempPte = *PointerPte;
1403 if (TempPte.u.Long != 0)
1404 {
1405 /* The PTE is valid, so it's not zeroed out */
1406 DemandZeroPte = FALSE;
1407
1408 /* Is it a decommited, invalid, or faulted PTE? */
1409 if ((TempPte.u.Soft.Protection == MM_DECOMMIT) &&
1410 (TempPte.u.Hard.Valid == 0) &&
1411 ((TempPte.u.Soft.Prototype == 0) ||
1412 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1413 {
1414 /* Otherwise our defaults should hold */
1415 ASSERT(Protect == 0);
1416 ASSERT(State == MEM_RESERVE);
1417 }
1418 else
1419 {
1420 /* This means it's committed */
1421 State = MEM_COMMIT;
1422
1423 /* We don't support these */
1424 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1425 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
1426 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1427
1428 /* Get protection state of this page */
1429 Protect = MiGetPageProtection(PointerPte);
1430
1431 /* Check if this is an image-backed VAD */
1432 if ((TempPte.u.Soft.Valid == 0) &&
1433 (TempPte.u.Soft.Prototype == 1) &&
1434 (Vad->u.VadFlags.PrivateMemory == 0) &&
1435 (Vad->ControlArea))
1436 {
1437 DPRINT1("Not supported\n");
1438 ASSERT(FALSE);
1439 }
1440 }
1441 }
1442 }
1443
1444 /* Check if this was a demand-zero PTE, since we need to find the state */
1445 if (DemandZeroPte)
1446 {
1447 /* Not yet handled */
1448 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1449 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1450
1451 /* Check if this is private commited memory, or an section-backed VAD */
1452 if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea))
1453 {
1454 /* Tell caller about the next range */
1455 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1456
1457 /* Get the prototype PTE for this VAD */
1458 ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad,
1459 (ULONG_PTR)Va >> PAGE_SHIFT);
1460 if (ProtoPte)
1461 {
1462 /* We should unlock the working set, but it's not being held! */
1463
1464 /* Is the prototype PTE actually valid (committed)? */
1465 TempProtoPte = *ProtoPte;
1466 if (TempProtoPte.u.Long)
1467 {
1468 /* Unless this is a memory-mapped file, handle it like private VAD */
1469 State = MEM_COMMIT;
1470 ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
1471 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1472 }
1473
1474 /* We should re-lock the working set */
1475 }
1476 }
1477 else if (Vad->u.VadFlags.MemCommit)
1478 {
1479 /* This is committed memory */
1480 State = MEM_COMMIT;
1481
1482 /* Convert the protection */
1483 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1484 }
1485 }
1486
1487 /* Return the protection code */
1488 *ReturnedProtect = Protect;
1489 return State;
1490 }
1491
1492 NTSTATUS
1493 NTAPI
1494 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
1495 IN PVOID BaseAddress,
1496 OUT PVOID MemoryInformation,
1497 IN SIZE_T MemoryInformationLength,
1498 OUT PSIZE_T ReturnLength)
1499 {
1500 PEPROCESS TargetProcess;
1501 NTSTATUS Status = STATUS_SUCCESS;
1502 PMMVAD Vad = NULL;
1503 PVOID Address, NextAddress;
1504 BOOLEAN Found = FALSE;
1505 ULONG NewProtect, NewState;
1506 ULONG_PTR BaseVpn;
1507 MEMORY_BASIC_INFORMATION MemoryInfo;
1508 KAPC_STATE ApcState;
1509 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1510 PMEMORY_AREA MemoryArea;
1511 SIZE_T ResultLength;
1512
1513 /* Check for illegal addresses in user-space, or the shared memory area */
1514 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
1515 (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA))
1516 {
1517 Address = PAGE_ALIGN(BaseAddress);
1518
1519 /* Make up an info structure describing this range */
1520 MemoryInfo.BaseAddress = Address;
1521 MemoryInfo.AllocationProtect = PAGE_READONLY;
1522 MemoryInfo.Type = MEM_PRIVATE;
1523
1524 /* Special case for shared data */
1525 if (Address == (PVOID)MM_SHARED_USER_DATA_VA)
1526 {
1527 MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA;
1528 MemoryInfo.State = MEM_COMMIT;
1529 MemoryInfo.Protect = PAGE_READONLY;
1530 MemoryInfo.RegionSize = PAGE_SIZE;
1531 }
1532 else
1533 {
1534 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
1535 MemoryInfo.State = MEM_RESERVE;
1536 MemoryInfo.Protect = PAGE_NOACCESS;
1537 MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address;
1538 }
1539
1540 /* Return the data, NtQueryInformation already probed it*/
1541 if (PreviousMode != KernelMode)
1542 {
1543 _SEH2_TRY
1544 {
1545 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1546 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1547 }
1548 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1549 {
1550 Status = _SEH2_GetExceptionCode();
1551 }
1552 _SEH2_END;
1553 }
1554 else
1555 {
1556 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1557 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1558 }
1559
1560 return Status;
1561 }
1562
1563 /* Check if this is for a local or remote process */
1564 if (ProcessHandle == NtCurrentProcess())
1565 {
1566 TargetProcess = PsGetCurrentProcess();
1567 }
1568 else
1569 {
1570 /* Reference the target process */
1571 Status = ObReferenceObjectByHandle(ProcessHandle,
1572 PROCESS_QUERY_INFORMATION,
1573 PsProcessType,
1574 ExGetPreviousMode(),
1575 (PVOID*)&TargetProcess,
1576 NULL);
1577 if (!NT_SUCCESS(Status)) return Status;
1578
1579 /* Attach to it now */
1580 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1581 }
1582
1583 /* Lock the address space and make sure the process isn't already dead */
1584 MmLockAddressSpace(&TargetProcess->Vm);
1585 if (TargetProcess->VmDeleted)
1586 {
1587 /* Unlock the address space of the process */
1588 MmUnlockAddressSpace(&TargetProcess->Vm);
1589
1590 /* Check if we were attached */
1591 if (ProcessHandle != NtCurrentProcess())
1592 {
1593 /* Detach and dereference the process */
1594 KeUnstackDetachProcess(&ApcState);
1595 ObDereferenceObject(TargetProcess);
1596 }
1597
1598 /* Bail out */
1599 DPRINT1("Process is dying\n");
1600 return STATUS_PROCESS_IS_TERMINATING;
1601 }
1602
1603 /* Loop the VADs */
1604 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
1605 if (TargetProcess->VadRoot.NumberGenericTableElements)
1606 {
1607 /* Scan on the right */
1608 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
1609 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
1610 while (Vad)
1611 {
1612 /* Check if this VAD covers the allocation range */
1613 if ((BaseVpn >= Vad->StartingVpn) &&
1614 (BaseVpn <= Vad->EndingVpn))
1615 {
1616 /* We're done */
1617 Found = TRUE;
1618 break;
1619 }
1620
1621 /* Check if this VAD is too high */
1622 if (BaseVpn < Vad->StartingVpn)
1623 {
1624 /* Stop if there is no left child */
1625 if (!Vad->LeftChild) break;
1626
1627 /* Search on the left next */
1628 Vad = Vad->LeftChild;
1629 }
1630 else
1631 {
1632 /* Then this VAD is too low, keep searching on the right */
1633 ASSERT(BaseVpn > Vad->EndingVpn);
1634
1635 /* Stop if there is no right child */
1636 if (!Vad->RightChild) break;
1637
1638 /* Search on the right next */
1639 Vad = Vad->RightChild;
1640 }
1641 }
1642 }
1643
1644 /* Was a VAD found? */
1645 if (!Found)
1646 {
1647 Address = PAGE_ALIGN(BaseAddress);
1648
1649 /* Calculate region size */
1650 if (Vad)
1651 {
1652 if (Vad->StartingVpn >= BaseVpn)
1653 {
1654 /* Region size is the free space till the start of that VAD */
1655 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1656 }
1657 else
1658 {
1659 /* Get the next VAD */
1660 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
1661 if (Vad)
1662 {
1663 /* Region size is the free space till the start of that VAD */
1664 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1665 }
1666 else
1667 {
1668 /* Maximum possible region size with that base address */
1669 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1670 }
1671 }
1672 }
1673 else
1674 {
1675 /* Maximum possible region size with that base address */
1676 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1677 }
1678
1679 /* Unlock the address space of the process */
1680 MmUnlockAddressSpace(&TargetProcess->Vm);
1681
1682 /* Check if we were attached */
1683 if (ProcessHandle != NtCurrentProcess())
1684 {
1685 /* Detach and derefernece the process */
1686 KeUnstackDetachProcess(&ApcState);
1687 ObDereferenceObject(TargetProcess);
1688 }
1689
1690 /* Build the rest of the initial information block */
1691 MemoryInfo.BaseAddress = Address;
1692 MemoryInfo.AllocationBase = NULL;
1693 MemoryInfo.AllocationProtect = 0;
1694 MemoryInfo.State = MEM_FREE;
1695 MemoryInfo.Protect = PAGE_NOACCESS;
1696 MemoryInfo.Type = 0;
1697
1698 /* Return the data, NtQueryInformation already probed it*/
1699 if (PreviousMode != KernelMode)
1700 {
1701 _SEH2_TRY
1702 {
1703 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1704 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1705 }
1706 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1707 {
1708 Status = _SEH2_GetExceptionCode();
1709 }
1710 _SEH2_END;
1711 }
1712 else
1713 {
1714 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1715 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1716 }
1717
1718 return Status;
1719 }
1720
1721 /* Set the correct memory type based on what kind of VAD this is */
1722 if ((Vad->u.VadFlags.PrivateMemory) ||
1723 (Vad->u.VadFlags.VadType == VadRotatePhysical))
1724 {
1725 MemoryInfo.Type = MEM_PRIVATE;
1726 }
1727 else if (Vad->u.VadFlags.VadType == VadImageMap)
1728 {
1729 MemoryInfo.Type = MEM_IMAGE;
1730 }
1731 else
1732 {
1733 MemoryInfo.Type = MEM_MAPPED;
1734 }
1735
1736 /* Find the memory area the specified address belongs to */
1737 MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
1738 ASSERT(MemoryArea != NULL);
1739
1740 /* Determine information dependent on the memory area type */
1741 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1742 {
1743 Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
1744 if (!NT_SUCCESS(Status))
1745 {
1746 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1747 MemoryArea, MemoryArea->StartingAddress, MemoryArea->EndingAddress, BaseAddress);
1748 NT_ASSERT(NT_SUCCESS(Status));
1749 }
1750 }
1751 else
1752 {
1753 /* Build the initial information block */
1754 Address = PAGE_ALIGN(BaseAddress);
1755 MemoryInfo.BaseAddress = Address;
1756 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1757 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
1758 MemoryInfo.Type = MEM_PRIVATE;
1759
1760 /* Acquire the working set lock (shared is enough) */
1761 MiLockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
1762
1763 /* Find the largest chunk of memory which has the same state and protection mask */
1764 MemoryInfo.State = MiQueryAddressState(Address,
1765 Vad,
1766 TargetProcess,
1767 &MemoryInfo.Protect,
1768 &NextAddress);
1769 Address = NextAddress;
1770 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1771 {
1772 /* Keep going unless the state or protection mask changed */
1773 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1774 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1775 Address = NextAddress;
1776 }
1777
1778 /* Release the working set lock */
1779 MiUnlockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
1780
1781 /* Check if we went outside of the VAD */
1782 if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn)
1783 {
1784 /* Set the end of the VAD as the end address */
1785 Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
1786 }
1787
1788 /* Now that we know the last VA address, calculate the region size */
1789 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1790 }
1791
1792 /* Unlock the address space of the process */
1793 MmUnlockAddressSpace(&TargetProcess->Vm);
1794
1795 /* Check if we were attached */
1796 if (ProcessHandle != NtCurrentProcess())
1797 {
1798 /* Detach and derefernece the process */
1799 KeUnstackDetachProcess(&ApcState);
1800 ObDereferenceObject(TargetProcess);
1801 }
1802
1803 /* Return the data, NtQueryInformation already probed it */
1804 if (PreviousMode != KernelMode)
1805 {
1806 _SEH2_TRY
1807 {
1808 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1809 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1810 }
1811 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1812 {
1813 Status = _SEH2_GetExceptionCode();
1814 }
1815 _SEH2_END;
1816 }
1817 else
1818 {
1819 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1820 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1821 }
1822
1823 /* All went well */
1824 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1825 "State: %lx Type: %lx Size: %lx\n",
1826 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
1827 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
1828 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
1829
1830 return Status;
1831 }
1832
1833 BOOLEAN
1834 NTAPI
1835 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
1836 IN ULONG_PTR EndingAddress,
1837 IN PMMVAD Vad,
1838 IN PEPROCESS Process)
1839 {
1840 PMMPTE PointerPte, LastPte, PointerPde;
1841 BOOLEAN OnBoundary = TRUE;
1842 PAGED_CODE();
1843
1844 /* Get the PDE and PTE addresses */
1845 PointerPde = MiAddressToPde(StartingAddress);
1846 PointerPte = MiAddressToPte(StartingAddress);
1847 LastPte = MiAddressToPte(EndingAddress);
1848
1849 /* Loop all the PTEs */
1850 while (PointerPte <= LastPte)
1851 {
1852 /* Check if we've hit an new PDE boundary */
1853 if (OnBoundary)
1854 {
1855 /* Is this PDE demand zero? */
1856 PointerPde = MiAddressToPte(PointerPte);
1857 if (PointerPde->u.Long != 0)
1858 {
1859 /* It isn't -- is it valid? */
1860 if (PointerPde->u.Hard.Valid == 0)
1861 {
1862 /* Nope, fault it in */
1863 PointerPte = MiPteToAddress(PointerPde);
1864 MiMakeSystemAddressValid(PointerPte, Process);
1865 }
1866 }
1867 else
1868 {
1869 /* The PTE was already valid, so move to the next one */
1870 PointerPde++;
1871 PointerPte = MiPteToAddress(PointerPde);
1872
1873 /* Is the entire VAD committed? If not, fail */
1874 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1875
1876 /* Everything is committed so far past the range, return true */
1877 if (PointerPte > LastPte) return TRUE;
1878 }
1879 }
1880
1881 /* Is the PTE demand zero? */
1882 if (PointerPte->u.Long == 0)
1883 {
1884 /* Is the entire VAD committed? If not, fail */
1885 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1886 }
1887 else
1888 {
1889 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1890 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
1891 (PointerPte->u.Hard.Valid == 0) &&
1892 ((PointerPte->u.Soft.Prototype == 0) ||
1893 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1894 {
1895 /* Then part of the range is decommitted, so fail */
1896 return FALSE;
1897 }
1898 }
1899
1900 /* Move to the next PTE */
1901 PointerPte++;
1902 OnBoundary = MiIsPteOnPdeBoundary(PointerPte);
1903 }
1904
1905 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1906 return TRUE;
1907 }
1908
1909 NTSTATUS
1910 NTAPI
1911 MiRosProtectVirtualMemory(IN PEPROCESS Process,
1912 IN OUT PVOID *BaseAddress,
1913 IN OUT PSIZE_T NumberOfBytesToProtect,
1914 IN ULONG NewAccessProtection,
1915 OUT PULONG OldAccessProtection OPTIONAL)
1916 {
1917 PMEMORY_AREA MemoryArea;
1918 PMMSUPPORT AddressSpace;
1919 ULONG OldAccessProtection_;
1920 NTSTATUS Status;
1921
1922 *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
1923 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
1924
1925 AddressSpace = &Process->Vm;
1926 MmLockAddressSpace(AddressSpace);
1927 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
1928 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
1929 {
1930 MmUnlockAddressSpace(AddressSpace);
1931 return STATUS_UNSUCCESSFUL;
1932 }
1933
1934 if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
1935
1936 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
1937 Status = MmProtectSectionView(AddressSpace,
1938 MemoryArea,
1939 *BaseAddress,
1940 *NumberOfBytesToProtect,
1941 NewAccessProtection,
1942 OldAccessProtection);
1943
1944 MmUnlockAddressSpace(AddressSpace);
1945
1946 return Status;
1947 }
1948
1949 NTSTATUS
1950 NTAPI
1951 MiProtectVirtualMemory(IN PEPROCESS Process,
1952 IN OUT PVOID *BaseAddress,
1953 IN OUT PSIZE_T NumberOfBytesToProtect,
1954 IN ULONG NewAccessProtection,
1955 OUT PULONG OldAccessProtection OPTIONAL)
1956 {
1957 PMEMORY_AREA MemoryArea;
1958 PMMVAD Vad;
1959 PMMSUPPORT AddressSpace;
1960 ULONG_PTR StartingAddress, EndingAddress;
1961 PMMPTE PointerPde, PointerPte, LastPte;
1962 MMPTE PteContents;
1963 PMMPFN Pfn1;
1964 ULONG ProtectionMask, OldProtect;
1965 BOOLEAN Committed;
1966 NTSTATUS Status = STATUS_SUCCESS;
1967 PETHREAD Thread = PsGetCurrentThread();
1968 TABLE_SEARCH_RESULT Result;
1969
1970 /* Calculate base address for the VAD */
1971 StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
1972 EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
1973
1974 /* Calculate the protection mask and make sure it's valid */
1975 ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
1976 if (ProtectionMask == MM_INVALID_PROTECTION)
1977 {
1978 DPRINT1("Invalid protection mask\n");
1979 return STATUS_INVALID_PAGE_PROTECTION;
1980 }
1981
1982 /* Check for ROS specific memory area */
1983 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
1984 if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW))
1985 {
1986 /* Evil hack */
1987 return MiRosProtectVirtualMemory(Process,
1988 BaseAddress,
1989 NumberOfBytesToProtect,
1990 NewAccessProtection,
1991 OldAccessProtection);
1992 }
1993
1994 /* Lock the address space and make sure the process isn't already dead */
1995 AddressSpace = MmGetCurrentAddressSpace();
1996 MmLockAddressSpace(AddressSpace);
1997 if (Process->VmDeleted)
1998 {
1999 DPRINT1("Process is dying\n");
2000 Status = STATUS_PROCESS_IS_TERMINATING;
2001 goto FailPath;
2002 }
2003
2004 /* Get the VAD for this address range, and make sure it exists */
2005 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
2006 EndingAddress >> PAGE_SHIFT,
2007 &Process->VadRoot,
2008 (PMMADDRESS_NODE*)&Vad);
2009 if (Result != TableFoundNode)
2010 {
2011 DPRINT("Could not find a VAD for this allocation\n");
2012 Status = STATUS_CONFLICTING_ADDRESSES;
2013 goto FailPath;
2014 }
2015
2016 /* Make sure the address is within this VAD's boundaries */
2017 if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
2018 (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
2019 {
2020 Status = STATUS_CONFLICTING_ADDRESSES;
2021 goto FailPath;
2022 }
2023
2024 /* These kinds of VADs are not supported atm */
2025 if ((Vad->u.VadFlags.VadType == VadAwe) ||
2026 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
2027 (Vad->u.VadFlags.VadType == VadLargePages))
2028 {
2029 DPRINT1("Illegal VAD for attempting to set protection\n");
2030 Status = STATUS_CONFLICTING_ADDRESSES;
2031 goto FailPath;
2032 }
2033
2034 /* Check for a VAD whose protection can't be changed */
2035 if (Vad->u.VadFlags.NoChange == 1)
2036 {
2037 DPRINT1("Trying to change protection of a NoChange VAD\n");
2038 Status = STATUS_INVALID_PAGE_PROTECTION;
2039 goto FailPath;
2040 }
2041
2042 /* Is this section, or private memory? */
2043 if (Vad->u.VadFlags.PrivateMemory == 0)
2044 {
2045 /* Not yet supported */
2046 if (Vad->u.VadFlags.VadType == VadLargePageSection)
2047 {
2048 DPRINT1("Illegal VAD for attempting to set protection\n");
2049 Status = STATUS_CONFLICTING_ADDRESSES;
2050 goto FailPath;
2051 }
2052
2053 /* Rotate VADs are not yet supported */
2054 if (Vad->u.VadFlags.VadType == VadRotatePhysical)
2055 {
2056 DPRINT1("Illegal VAD for attempting to set protection\n");
2057 Status = STATUS_CONFLICTING_ADDRESSES;
2058 goto FailPath;
2059 }
2060
2061 /* Not valid on section files */
2062 if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
2063 {
2064 /* Fail */
2065 DPRINT1("Invalid protection flags for section\n");
2066 Status = STATUS_INVALID_PARAMETER_4;
2067 goto FailPath;
2068 }
2069
2070 /* Check if data or page file mapping protection PTE is compatible */
2071 if (!Vad->ControlArea->u.Flags.Image)
2072 {
2073 /* Not yet */
2074 DPRINT1("Fixme: Not checking for valid protection\n");
2075 }
2076
2077 /* This is a section, and this is not yet supported */
2078 DPRINT1("Section protection not yet supported\n");
2079 OldProtect = 0;
2080 }
2081 else
2082 {
2083 /* Private memory, check protection flags */
2084 if ((NewAccessProtection & PAGE_WRITECOPY) ||
2085 (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2086 {
2087 DPRINT1("Invalid protection flags for private memory\n");
2088 Status = STATUS_INVALID_PARAMETER_4;
2089 goto FailPath;
2090 }
2091
2092 /* Lock the working set */
2093 MiLockProcessWorkingSetUnsafe(Process, Thread);
2094
2095 /* Check if all pages in this range are committed */
2096 Committed = MiIsEntireRangeCommitted(StartingAddress,
2097 EndingAddress,
2098 Vad,
2099 Process);
2100 if (!Committed)
2101 {
2102 /* Fail */
2103 DPRINT1("The entire range is not committed\n");
2104 Status = STATUS_NOT_COMMITTED;
2105 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2106 goto FailPath;
2107 }
2108
2109 /* Compute starting and ending PTE and PDE addresses */
2110 PointerPde = MiAddressToPde(StartingAddress);
2111 PointerPte = MiAddressToPte(StartingAddress);
2112 LastPte = MiAddressToPte(EndingAddress);
2113
2114 /* Make this PDE valid */
2115 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2116
2117 /* Save protection of the first page */
2118 if (PointerPte->u.Long != 0)
2119 {
2120 /* Capture the page protection and make the PDE valid */
2121 OldProtect = MiGetPageProtection(PointerPte);
2122 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2123 }
2124 else
2125 {
2126 /* Grab the old protection from the VAD itself */
2127 OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2128 }
2129
2130 /* Loop all the PTEs now */
2131 while (PointerPte <= LastPte)
2132 {
2133 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2134 if (MiIsPteOnPdeBoundary(PointerPte))
2135 {
2136 PointerPde = MiAddressToPte(PointerPte);
2137 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2138 }
2139
2140 /* Capture the PTE and check if it was empty */
2141 PteContents = *PointerPte;
2142 if (PteContents.u.Long == 0)
2143 {
2144 /* This used to be a zero PTE and it no longer is, so we must add a
2145 reference to the pagetable. */
2146 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2147 }
2148
2149 /* Check what kind of PTE we are dealing with */
2150 if (PteContents.u.Hard.Valid == 1)
2151 {
2152 /* Get the PFN entry */
2153 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2154
2155 /* We don't support this yet */
2156 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2157
2158 /* Check if the page should not be accessible at all */
2159 if ((NewAccessProtection & PAGE_NOACCESS) ||
2160 (NewAccessProtection & PAGE_GUARD))
2161 {
2162 /* The page should be in the WS and we should make it transition now */
2163 DPRINT1("Making valid page invalid is not yet supported!\n");
2164 Status = STATUS_NOT_IMPLEMENTED;
2165 /* Unlock the working set */
2166 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2167 goto FailPath;
2168 }
2169
2170 /* Write the protection mask and write it with a TLB flush */
2171 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2172 MiFlushTbAndCapture(Vad,
2173 PointerPte,
2174 ProtectionMask,
2175 Pfn1,
2176 TRUE);
2177 }
2178 else
2179 {
2180 /* We don't support these cases yet */
2181 ASSERT(PteContents.u.Soft.Prototype == 0);
2182 ASSERT(PteContents.u.Soft.Transition == 0);
2183
2184 /* The PTE is already demand-zero, just update the protection mask */
2185 PteContents.u.Soft.Protection = ProtectionMask;
2186 MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2187 ASSERT(PointerPte->u.Long != 0);
2188 }
2189
2190 /* Move to the next PTE */
2191 PointerPte++;
2192 }
2193
2194 /* Unlock the working set */
2195 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2196 }
2197
2198 /* Unlock the address space */
2199 MmUnlockAddressSpace(AddressSpace);
2200
2201 /* Return parameters and success */
2202 *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2203 *BaseAddress = (PVOID)StartingAddress;
2204 *OldAccessProtection = OldProtect;
2205 return STATUS_SUCCESS;
2206
2207 FailPath:
2208 /* Unlock the address space and return the failure code */
2209 MmUnlockAddressSpace(AddressSpace);
2210 return Status;
2211 }
2212
2213 VOID
2214 NTAPI
2215 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde,
2216 IN PEPROCESS TargetProcess,
2217 IN KIRQL OldIrql)
2218 {
2219 PMMPTE PointerPte, PointerPpe, PointerPxe;
2220
2221 //
2222 // Sanity checks. The latter is because we only use this function with the
2223 // PFN lock not held, so it may go away in the future.
2224 //
2225 ASSERT(KeAreAllApcsDisabled() == TRUE);
2226 ASSERT(OldIrql == MM_NOIRQL);
2227
2228 //
2229 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2230 // return the same address as the PDE on 2-level page table systems.
2231 //
2232 // If everything is already valid, there is nothing to do.
2233 //
2234 PointerPpe = MiAddressToPte(PointerPde);
2235 PointerPxe = MiAddressToPde(PointerPde);
2236 if ((PointerPxe->u.Hard.Valid) &&
2237 (PointerPpe->u.Hard.Valid) &&
2238 (PointerPde->u.Hard.Valid))
2239 {
2240 return;
2241 }
2242
2243 //
2244 // At least something is invalid, so begin by getting the PTE for the PDE itself
2245 // and then lookup each additional level. We must do it in this precise order
2246 // because the pagfault.c code (as well as in Windows) depends that the next
2247 // level up (higher) must be valid when faulting a lower level
2248 //
2249 PointerPte = MiPteToAddress(PointerPde);
2250 do
2251 {
2252 //
2253 // Make sure APCs continued to be disabled
2254 //
2255 ASSERT(KeAreAllApcsDisabled() == TRUE);
2256
2257 //
2258 // First, make the PXE valid if needed
2259 //
2260 if (!PointerPxe->u.Hard.Valid)
2261 {
2262 MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2263 ASSERT(PointerPxe->u.Hard.Valid == 1);
2264 }
2265
2266 //
2267 // Next, the PPE
2268 //
2269 if (!PointerPpe->u.Hard.Valid)
2270 {
2271 MiMakeSystemAddressValid(PointerPde, TargetProcess);
2272 ASSERT(PointerPpe->u.Hard.Valid == 1);
2273 }
2274
2275 //
2276 // And finally, make the PDE itself valid.
2277 //
2278 MiMakeSystemAddressValid(PointerPte, TargetProcess);
2279
2280 //
2281 // This should've worked the first time so the loop is really just for
2282 // show -- ASSERT that we're actually NOT going to be looping.
2283 //
2284 ASSERT(PointerPxe->u.Hard.Valid == 1);
2285 ASSERT(PointerPpe->u.Hard.Valid == 1);
2286 ASSERT(PointerPde->u.Hard.Valid == 1);
2287 } while (!(PointerPxe->u.Hard.Valid) ||
2288 !(PointerPpe->u.Hard.Valid) ||
2289 !(PointerPde->u.Hard.Valid));
2290 }
2291
2292 VOID
2293 NTAPI
2294 MiProcessValidPteList(IN PMMPTE *ValidPteList,
2295 IN ULONG Count)
2296 {
2297 KIRQL OldIrql;
2298 ULONG i;
2299 MMPTE TempPte;
2300 PFN_NUMBER PageFrameIndex;
2301 PMMPFN Pfn1, Pfn2;
2302
2303 //
2304 // Acquire the PFN lock and loop all the PTEs in the list
2305 //
2306 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2307 for (i = 0; i != Count; i++)
2308 {
2309 //
2310 // The PTE must currently be valid
2311 //
2312 TempPte = *ValidPteList[i];
2313 ASSERT(TempPte.u.Hard.Valid == 1);
2314
2315 //
2316 // Get the PFN entry for the page itself, and then for its page table
2317 //
2318 PageFrameIndex = PFN_FROM_PTE(&TempPte);
2319 Pfn1 = MiGetPfnEntry(PageFrameIndex);
2320 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2321
2322 //
2323 // Decrement the share count on the page table, and then on the page
2324 // itself
2325 //
2326 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2327 MI_SET_PFN_DELETED(Pfn1);
2328 MiDecrementShareCount(Pfn1, PageFrameIndex);
2329
2330 //
2331 // Make the page decommitted
2332 //
2333 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2334 }
2335
2336 //
2337 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2338 // and then release the PFN lock
2339 //
2340 KeFlushCurrentTb();
2341 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2342 }
2343
2344 ULONG
2345 NTAPI
2346 MiDecommitPages(IN PVOID StartingAddress,
2347 IN PMMPTE EndingPte,
2348 IN PEPROCESS Process,
2349 IN PMMVAD Vad)
2350 {
2351 PMMPTE PointerPde, PointerPte, CommitPte = NULL;
2352 ULONG CommitReduction = 0;
2353 PMMPTE ValidPteList[256];
2354 ULONG PteCount = 0;
2355 PMMPFN Pfn1;
2356 MMPTE PteContents;
2357 PETHREAD CurrentThread = PsGetCurrentThread();
2358
2359 //
2360 // Get the PTE and PTE for the address, and lock the working set
2361 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2362 // commited range ends so that we can do the right accounting.
2363 //
2364 PointerPde = MiAddressToPde(StartingAddress);
2365 PointerPte = MiAddressToPte(StartingAddress);
2366 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2367 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2368
2369 //
2370 // Make the PDE valid, and now loop through each page's worth of data
2371 //
2372 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2373 while (PointerPte <= EndingPte)
2374 {
2375 //
2376 // Check if we've crossed a PDE boundary
2377 //
2378 if (MiIsPteOnPdeBoundary(PointerPte))
2379 {
2380 //
2381 // Get the new PDE and flush the valid PTEs we had built up until
2382 // now. This helps reduce the amount of TLB flushing we have to do.
2383 // Note that Windows does a much better job using timestamps and
2384 // such, and does not flush the entire TLB all the time, but right
2385 // now we have bigger problems to worry about than TLB flushing.
2386 //
2387 PointerPde = MiAddressToPde(StartingAddress);
2388 if (PteCount)
2389 {
2390 MiProcessValidPteList(ValidPteList, PteCount);
2391 PteCount = 0;
2392 }
2393
2394 //
2395 // Make this PDE valid
2396 //
2397 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2398 }
2399
2400 //
2401 // Read this PTE. It might be active or still demand-zero.
2402 //
2403 PteContents = *PointerPte;
2404 if (PteContents.u.Long)
2405 {
2406 //
2407 // The PTE is active. It might be valid and in a working set, or
2408 // it might be a prototype PTE or paged out or even in transition.
2409 //
2410 if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2411 {
2412 //
2413 // It's already decommited, so there's nothing for us to do here
2414 //
2415 CommitReduction++;
2416 }
2417 else
2418 {
2419 //
2420 // Remove it from the counters, and check if it was valid or not
2421 //
2422 //Process->NumberOfPrivatePages--;
2423 if (PteContents.u.Hard.Valid)
2424 {
2425 //
2426 // It's valid. At this point make sure that it is not a ROS
2427 // PFN. Also, we don't support ProtoPTEs in this code path.
2428 //
2429 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2430 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2431 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2432
2433 //
2434 // Flush any pending PTEs that we had not yet flushed, if our
2435 // list has gotten too big, then add this PTE to the flush list.
2436 //
2437 if (PteCount == 256)
2438 {
2439 MiProcessValidPteList(ValidPteList, PteCount);
2440 PteCount = 0;
2441 }
2442 ValidPteList[PteCount++] = PointerPte;
2443 }
2444 else
2445 {
2446 //
2447 // We do not support any of these other scenarios at the moment
2448 //
2449 ASSERT(PteContents.u.Soft.Prototype == 0);
2450 ASSERT(PteContents.u.Soft.Transition == 0);
2451 ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2452
2453 //
2454 // So the only other possibility is that it is still a demand
2455 // zero PTE, in which case we undo the accounting we did
2456 // earlier and simply make the page decommitted.
2457 //
2458 //Process->NumberOfPrivatePages++;
2459 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2460 }
2461 }
2462 }
2463 else
2464 {
2465 //
2466 // This used to be a zero PTE and it no longer is, so we must add a
2467 // reference to the pagetable.
2468 //
2469 MiIncrementPageTableReferences(StartingAddress);
2470
2471 //
2472 // Next, we account for decommitted PTEs and make the PTE as such
2473 //
2474 if (PointerPte > CommitPte) CommitReduction++;
2475 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2476 }
2477
2478 //
2479 // Move to the next PTE and the next address
2480 //
2481 PointerPte++;
2482 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2483 }
2484
2485 //
2486 // Flush any dangling PTEs from the loop in the last page table, and then
2487 // release the working set and return the commit reduction accounting.
2488 //
2489 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2490 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
2491 return CommitReduction;
2492 }
2493
2494 /* PUBLIC FUNCTIONS ***********************************************************/
2495
2496 /*
2497 * @unimplemented
2498 */
2499 PVOID
2500 NTAPI
2501 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
2502 {
2503 UNIMPLEMENTED;
2504 return 0;
2505 }
2506
2507 /*
2508 * @unimplemented
2509 */
2510 PVOID
2511 NTAPI
2512 MmSecureVirtualMemory(IN PVOID Address,
2513 IN SIZE_T Length,
2514 IN ULONG Mode)
2515 {
2516 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2517 return Address;
2518 }
2519
2520 /*
2521 * @unimplemented
2522 */
2523 VOID
2524 NTAPI
2525 MmUnsecureVirtualMemory(IN PVOID SecureMem)
2526 {
2527 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2528 }
2529
2530 /* SYSTEM CALLS ***************************************************************/
2531
2532 NTSTATUS
2533 NTAPI
2534 NtReadVirtualMemory(IN HANDLE ProcessHandle,
2535 IN PVOID BaseAddress,
2536 OUT PVOID Buffer,
2537 IN SIZE_T NumberOfBytesToRead,
2538 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2539 {
2540 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2541 PEPROCESS Process;
2542 NTSTATUS Status = STATUS_SUCCESS;
2543 SIZE_T BytesRead = 0;
2544 PAGED_CODE();
2545
2546 //
2547 // Check if we came from user mode
2548 //
2549 if (PreviousMode != KernelMode)
2550 {
2551 //
2552 // Validate the read addresses
2553 //
2554 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2555 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2556 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2557 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2558 {
2559 //
2560 // Don't allow to write into kernel space
2561 //
2562 return STATUS_ACCESS_VIOLATION;
2563 }
2564
2565 //
2566 // Enter SEH for probe
2567 //
2568 _SEH2_TRY
2569 {
2570 //
2571 // Probe the output value
2572 //
2573 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2574 }
2575 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2576 {
2577 //
2578 // Get exception code
2579 //
2580 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2581 }
2582 _SEH2_END;
2583 }
2584
2585 //
2586 // Don't do zero-byte transfers
2587 //
2588 if (NumberOfBytesToRead)
2589 {
2590 //
2591 // Reference the process
2592 //
2593 Status = ObReferenceObjectByHandle(ProcessHandle,
2594 PROCESS_VM_READ,
2595 PsProcessType,
2596 PreviousMode,
2597 (PVOID*)(&Process),
2598 NULL);
2599 if (NT_SUCCESS(Status))
2600 {
2601 //
2602 // Do the copy
2603 //
2604 Status = MmCopyVirtualMemory(Process,
2605 BaseAddress,
2606 PsGetCurrentProcess(),
2607 Buffer,
2608 NumberOfBytesToRead,
2609 PreviousMode,
2610 &BytesRead);
2611
2612 //
2613 // Dereference the process
2614 //
2615 ObDereferenceObject(Process);
2616 }
2617 }
2618
2619 //
2620 // Check if the caller sent this parameter
2621 //
2622 if (NumberOfBytesRead)
2623 {
2624 //
2625 // Enter SEH to guard write
2626 //
2627 _SEH2_TRY
2628 {
2629 //
2630 // Return the number of bytes read
2631 //
2632 *NumberOfBytesRead = BytesRead;
2633 }
2634 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2635 {
2636 }
2637 _SEH2_END;
2638 }
2639
2640 //
2641 // Return status
2642 //
2643 return Status;
2644 }
2645
2646 NTSTATUS
2647 NTAPI
2648 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
2649 IN PVOID BaseAddress,
2650 IN PVOID Buffer,
2651 IN SIZE_T NumberOfBytesToWrite,
2652 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2653 {
2654 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2655 PEPROCESS Process;
2656 NTSTATUS Status = STATUS_SUCCESS;
2657 SIZE_T BytesWritten = 0;
2658 PAGED_CODE();
2659
2660 //
2661 // Check if we came from user mode
2662 //
2663 if (PreviousMode != KernelMode)
2664 {
2665 //
2666 // Validate the read addresses
2667 //
2668 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2669 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2670 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2671 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2672 {
2673 //
2674 // Don't allow to write into kernel space
2675 //
2676 return STATUS_ACCESS_VIOLATION;
2677 }
2678
2679 //
2680 // Enter SEH for probe
2681 //
2682 _SEH2_TRY
2683 {
2684 //
2685 // Probe the output value
2686 //
2687 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2688 }
2689 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2690 {
2691 //
2692 // Get exception code
2693 //
2694 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2695 }
2696 _SEH2_END;
2697 }
2698
2699 //
2700 // Don't do zero-byte transfers
2701 //
2702 if (NumberOfBytesToWrite)
2703 {
2704 //
2705 // Reference the process
2706 //
2707 Status = ObReferenceObjectByHandle(ProcessHandle,
2708 PROCESS_VM_WRITE,
2709 PsProcessType,
2710 PreviousMode,
2711 (PVOID*)&Process,
2712 NULL);
2713 if (NT_SUCCESS(Status))
2714 {
2715 //
2716 // Do the copy
2717 //
2718 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
2719 Buffer,
2720 Process,
2721 BaseAddress,
2722 NumberOfBytesToWrite,
2723 PreviousMode,
2724 &BytesWritten);
2725
2726 //
2727 // Dereference the process
2728 //
2729 ObDereferenceObject(Process);
2730 }
2731 }
2732
2733 //
2734 // Check if the caller sent this parameter
2735 //
2736 if (NumberOfBytesWritten)
2737 {
2738 //
2739 // Enter SEH to guard write
2740 //
2741 _SEH2_TRY
2742 {
2743 //
2744 // Return the number of bytes written
2745 //
2746 *NumberOfBytesWritten = BytesWritten;
2747 }
2748 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2749 {
2750 }
2751 _SEH2_END;
2752 }
2753
2754 //
2755 // Return status
2756 //
2757 return Status;
2758 }
2759
2760 NTSTATUS
2761 NTAPI
2762 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
2763 IN OUT PVOID *UnsafeBaseAddress,
2764 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
2765 IN ULONG NewAccessProtection,
2766 OUT PULONG UnsafeOldAccessProtection)
2767 {
2768 PEPROCESS Process;
2769 ULONG OldAccessProtection;
2770 ULONG Protection;
2771 PEPROCESS CurrentProcess = PsGetCurrentProcess();
2772 PVOID BaseAddress = NULL;
2773 SIZE_T NumberOfBytesToProtect = 0;
2774 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2775 NTSTATUS Status;
2776 BOOLEAN Attached = FALSE;
2777 KAPC_STATE ApcState;
2778 PAGED_CODE();
2779
2780 //
2781 // Check for valid protection flags
2782 //
2783 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
2784 if (Protection != PAGE_NOACCESS &&
2785 Protection != PAGE_READONLY &&
2786 Protection != PAGE_READWRITE &&
2787 Protection != PAGE_WRITECOPY &&
2788 Protection != PAGE_EXECUTE &&
2789 Protection != PAGE_EXECUTE_READ &&
2790 Protection != PAGE_EXECUTE_READWRITE &&
2791 Protection != PAGE_EXECUTE_WRITECOPY)
2792 {
2793 //
2794 // Fail
2795 //
2796 return STATUS_INVALID_PAGE_PROTECTION;
2797 }
2798
2799 //
2800 // Check if we came from user mode
2801 //
2802 if (PreviousMode != KernelMode)
2803 {
2804 //
2805 // Enter SEH for probing
2806 //
2807 _SEH2_TRY
2808 {
2809 //
2810 // Validate all outputs
2811 //
2812 ProbeForWritePointer(UnsafeBaseAddress);
2813 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
2814 ProbeForWriteUlong(UnsafeOldAccessProtection);
2815
2816 //
2817 // Capture them
2818 //
2819 BaseAddress = *UnsafeBaseAddress;
2820 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2821 }
2822 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2823 {
2824 //
2825 // Get exception code
2826 //
2827 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2828 }
2829 _SEH2_END;
2830 }
2831 else
2832 {
2833 //
2834 // Capture directly
2835 //
2836 BaseAddress = *UnsafeBaseAddress;
2837 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2838 }
2839
2840 //
2841 // Catch illegal base address
2842 //
2843 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2844
2845 //
2846 // Catch illegal region size
2847 //
2848 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
2849 {
2850 //
2851 // Fail
2852 //
2853 return STATUS_INVALID_PARAMETER_3;
2854 }
2855
2856 //
2857 // 0 is also illegal
2858 //
2859 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
2860
2861 //
2862 // Get a reference to the process
2863 //
2864 Status = ObReferenceObjectByHandle(ProcessHandle,
2865 PROCESS_VM_OPERATION,
2866 PsProcessType,
2867 PreviousMode,
2868 (PVOID*)(&Process),
2869 NULL);
2870 if (!NT_SUCCESS(Status)) return Status;
2871
2872 //
2873 // Check if we should attach
2874 //
2875 if (CurrentProcess != Process)
2876 {
2877 //
2878 // Do it
2879 //
2880 KeStackAttachProcess(&Process->Pcb, &ApcState);
2881 Attached = TRUE;
2882 }
2883
2884 //
2885 // Do the actual work
2886 //
2887 Status = MiProtectVirtualMemory(Process,
2888 &BaseAddress,
2889 &NumberOfBytesToProtect,
2890 NewAccessProtection,
2891 &OldAccessProtection);
2892
2893 //
2894 // Detach if needed
2895 //
2896 if (Attached) KeUnstackDetachProcess(&ApcState);
2897
2898 //
2899 // Release reference
2900 //
2901 ObDereferenceObject(Process);
2902
2903 //
2904 // Enter SEH to return data
2905 //
2906 _SEH2_TRY
2907 {
2908 //
2909 // Return data to user
2910 //
2911 *UnsafeOldAccessProtection = OldAccessProtection;
2912 *UnsafeBaseAddress = BaseAddress;
2913 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
2914 }
2915 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2916 {
2917 }
2918 _SEH2_END;
2919
2920 //
2921 // Return status
2922 //
2923 return Status;
2924 }
2925
2926 FORCEINLINE
2927 BOOLEAN
2928 MI_IS_LOCKED_VA(
2929 PMMPFN Pfn1,
2930 ULONG LockType)
2931 {
2932 // HACK until we have proper WSLIST support
2933 PMMWSLE Wsle = &Pfn1->Wsle;
2934
2935 if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
2936 return TRUE;
2937 if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
2938 return TRUE;
2939
2940 return FALSE;
2941 }
2942
2943 FORCEINLINE
2944 VOID
2945 MI_LOCK_VA(
2946 PMMPFN Pfn1,
2947 ULONG LockType)
2948 {
2949 // HACK until we have proper WSLIST support
2950 PMMWSLE Wsle = &Pfn1->Wsle;
2951
2952 if (!Wsle->u1.e1.LockedInWs &&
2953 !Wsle->u1.e1.LockedInMemory)
2954 {
2955 MiReferenceProbedPageAndBumpLockCount(Pfn1);
2956 }
2957
2958 if (LockType & MAP_PROCESS)
2959 Wsle->u1.e1.LockedInWs = 1;
2960 if (LockType & MAP_SYSTEM)
2961 Wsle->u1.e1.LockedInMemory = 1;
2962 }
2963
2964 FORCEINLINE
2965 VOID
2966 MI_UNLOCK_VA(
2967 PMMPFN Pfn1,
2968 ULONG LockType)
2969 {
2970 // HACK until we have proper WSLIST support
2971 PMMWSLE Wsle = &Pfn1->Wsle;
2972
2973 if (LockType & MAP_PROCESS)
2974 Wsle->u1.e1.LockedInWs = 0;
2975 if (LockType & MAP_SYSTEM)
2976 Wsle->u1.e1.LockedInMemory = 0;
2977
2978 if (!Wsle->u1.e1.LockedInWs &&
2979 !Wsle->u1.e1.LockedInMemory)
2980 {
2981 MiDereferencePfnAndDropLockCount(Pfn1);
2982 }
2983 }
2984
2985 static
2986 NTSTATUS
2987 MiCheckVadsForLockOperation(
2988 _Inout_ PVOID *BaseAddress,
2989 _Inout_ PSIZE_T RegionSize,
2990 _Inout_ PVOID *EndAddress)
2991
2992 {
2993 PMMVAD Vad;
2994 PVOID CurrentVa;
2995
2996 /* Get the base address and align the start address */
2997 *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
2998 *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
2999 *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE);
3000
3001 /* First loop and check all VADs */
3002 CurrentVa = *BaseAddress;
3003 while (CurrentVa < *EndAddress)
3004 {
3005 /* Get VAD */
3006 Vad = MiLocateAddress(CurrentVa);
3007 if (Vad == NULL)
3008 {
3009 /// FIXME: this might be a memory area for a section view...
3010 return STATUS_ACCESS_VIOLATION;
3011 }
3012
3013 /* Check VAD type */
3014 if ((Vad->u.VadFlags.VadType != VadNone) &&
3015 (Vad->u.VadFlags.VadType != VadImageMap) &&
3016 (Vad->u.VadFlags.VadType != VadWriteWatch))
3017 {
3018 *EndAddress = CurrentVa;
3019 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3020 return STATUS_INCOMPATIBLE_FILE_MAP;
3021 }
3022
3023 CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
3024 }
3025
3026 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
3027 return STATUS_SUCCESS;
3028 }
3029
3030 static
3031 NTSTATUS
3032 MiLockVirtualMemory(
3033 IN OUT PVOID *BaseAddress,
3034 IN OUT PSIZE_T RegionSize,
3035 IN ULONG MapType)
3036 {
3037 PEPROCESS CurrentProcess;
3038 PMMSUPPORT AddressSpace;
3039 PVOID CurrentVa, EndAddress;
3040 PMMPTE PointerPte, LastPte;
3041 PMMPDE PointerPde;
3042 #if (_MI_PAGING_LEVELS >= 3)
3043 PMMPDE PointerPpe;
3044 #endif
3045 #if (_MI_PAGING_LEVELS == 4)
3046 PMMPDE PointerPxe;
3047 #endif
3048 PMMPFN Pfn1;
3049 NTSTATUS Status, TempStatus;
3050
3051 /* Lock the address space */
3052 AddressSpace = MmGetCurrentAddressSpace();
3053 MmLockAddressSpace(AddressSpace);
3054
3055 /* Make sure we still have an address space */
3056 CurrentProcess = PsGetCurrentProcess();
3057 if (CurrentProcess->VmDeleted)
3058 {
3059 Status = STATUS_PROCESS_IS_TERMINATING;
3060 goto Cleanup;
3061 }
3062
3063 /* Check the VADs in the requested range */
3064 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3065 if (!NT_SUCCESS(Status))
3066 {
3067 goto Cleanup;
3068 }
3069
3070 /* Enter SEH for probing */
3071 _SEH2_TRY
3072 {
3073 /* Loop all pages and probe them */
3074 CurrentVa = *BaseAddress;
3075 while (CurrentVa < EndAddress)
3076 {
3077 (void)(*(volatile CHAR*)CurrentVa);
3078 CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
3079 }
3080 }
3081 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3082 {
3083 Status = _SEH2_GetExceptionCode();
3084 goto Cleanup;
3085 }
3086 _SEH2_END;
3087
3088 /* All pages were accessible, since we hold the address space lock, nothing
3089 can be de-committed. Assume success for now. */
3090 Status = STATUS_SUCCESS;
3091
3092 /* Get the PTE and PDE */
3093 PointerPte = MiAddressToPte(*BaseAddress);
3094 PointerPde = MiAddressToPde(*BaseAddress);
3095 #if (_MI_PAGING_LEVELS >= 3)
3096 PointerPpe = MiAddressToPpe(*BaseAddress);
3097 #endif
3098 #if (_MI_PAGING_LEVELS == 4)
3099 PointerPxe = MiAddressToPxe(*BaseAddress);
3100 #endif
3101
3102 /* Get the last PTE */
3103 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3104
3105 /* Lock the process working set */
3106 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3107
3108 /* Loop the pages */
3109 do
3110 {
3111 /* Check for a page that is not accessible */
3112 while (
3113 #if (_MI_PAGING_LEVELS == 4)
3114 (PointerPxe->u.Hard.Valid == 0) ||
3115 #endif
3116 #if (_MI_PAGING_LEVELS >= 3)
3117 (PointerPpe->u.Hard.Valid == 0) ||
3118 #endif
3119 (PointerPde->u.Hard.Valid == 0) ||
3120 (PointerPte->u.Hard.Valid == 0))
3121 {
3122 /* Release process working set */
3123 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3124
3125 /* Access the page */
3126 CurrentVa = MiPteToAddress(PointerPte);
3127
3128 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3129 TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)0xBADBADA3);
3130 if (!NT_SUCCESS(TempStatus))
3131 {
3132 // This should only happen, when remote backing storage is not accessible
3133 ASSERT(FALSE);
3134 Status = TempStatus;
3135 goto Cleanup;
3136 }
3137
3138 /* Lock the process working set */
3139 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3140 }
3141
3142 /* Get the PFN */
3143 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3144 ASSERT(Pfn1 != NULL);
3145
3146 /* Check the previous lock status */
3147 if (MI_IS_LOCKED_VA(Pfn1, MapType))
3148 {
3149 Status = STATUS_WAS_LOCKED;
3150 }
3151
3152 /* Lock it */
3153 MI_LOCK_VA(Pfn1, MapType);
3154
3155 /* Go to the next PTE */
3156 PointerPte++;
3157
3158 /* Check if we're on a PDE boundary */
3159 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3160 #if (_MI_PAGING_LEVELS >= 3)
3161 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3162 #endif
3163 #if (_MI_PAGING_LEVELS == 4)
3164 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3165 #endif
3166 } while (PointerPte <= LastPte);
3167
3168 /* Release process working set */
3169 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3170
3171 Cleanup:
3172 /* Unlock address space */
3173 MmUnlockAddressSpace(AddressSpace);
3174
3175 return Status;
3176 }
3177
3178 NTSTATUS
3179 NTAPI
3180 NtLockVirtualMemory(IN HANDLE ProcessHandle,
3181 IN OUT PVOID *BaseAddress,
3182 IN OUT PSIZE_T NumberOfBytesToLock,
3183 IN ULONG MapType)
3184 {
3185 PEPROCESS Process;
3186 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3187 NTSTATUS Status;
3188 BOOLEAN Attached = FALSE;
3189 KAPC_STATE ApcState;
3190 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3191 PVOID CapturedBaseAddress;
3192 SIZE_T CapturedBytesToLock;
3193 PAGED_CODE();
3194
3195 //
3196 // Validate flags
3197 //
3198 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3199 {
3200 //
3201 // Invalid set of flags
3202 //
3203 return STATUS_INVALID_PARAMETER;
3204 }
3205
3206 //
3207 // At least one flag must be specified
3208 //
3209 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3210 {
3211 //
3212 // No flag given
3213 //
3214 return STATUS_INVALID_PARAMETER;
3215 }
3216
3217 //
3218 // Enter SEH for probing
3219 //
3220 _SEH2_TRY
3221 {
3222 //
3223 // Validate output data
3224 //
3225 ProbeForWritePointer(BaseAddress);
3226 ProbeForWriteSize_t(NumberOfBytesToLock);
3227
3228 //
3229 // Capture it
3230 //
3231 CapturedBaseAddress = *BaseAddress;
3232 CapturedBytesToLock = *NumberOfBytesToLock;
3233 }
3234 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3235 {
3236 //
3237 // Get exception code
3238 //
3239 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3240 }
3241 _SEH2_END;
3242
3243 //
3244 // Catch illegal base address
3245 //
3246 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3247
3248 //
3249 // Catch illegal region size
3250 //
3251 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
3252 {
3253 //
3254 // Fail
3255 //
3256 return STATUS_INVALID_PARAMETER;
3257 }
3258
3259 //
3260 // 0 is also illegal
3261 //
3262 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
3263
3264 //
3265 // Get a reference to the process
3266 //
3267 Status = ObReferenceObjectByHandle(ProcessHandle,
3268 PROCESS_VM_OPERATION,
3269 PsProcessType,
3270 PreviousMode,
3271 (PVOID*)(&Process),
3272 NULL);
3273 if (!NT_SUCCESS(Status)) return Status;
3274
3275 //
3276 // Check if this is is system-mapped
3277 //
3278 if (MapType & MAP_SYSTEM)
3279 {
3280 //
3281 // Check for required privilege
3282 //
3283 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3284 {
3285 //
3286 // Fail: Don't have it
3287 //
3288 ObDereferenceObject(Process);
3289 return STATUS_PRIVILEGE_NOT_HELD;
3290 }
3291 }
3292
3293 //
3294 // Check if we should attach
3295 //
3296 if (CurrentProcess != Process)
3297 {
3298 //
3299 // Do it
3300 //
3301 KeStackAttachProcess(&Process->Pcb, &ApcState);
3302 Attached = TRUE;
3303 }
3304
3305 //
3306 // Call the internal function
3307 //
3308 Status = MiLockVirtualMemory(&CapturedBaseAddress,
3309 &CapturedBytesToLock,
3310 MapType);
3311
3312 //
3313 // Detach if needed
3314 //
3315 if (Attached) KeUnstackDetachProcess(&ApcState);
3316
3317 //
3318 // Release reference
3319 //
3320 ObDereferenceObject(Process);
3321
3322 //
3323 // Enter SEH to return data
3324 //
3325 _SEH2_TRY
3326 {
3327 //
3328 // Return data to user
3329 //
3330 *BaseAddress = CapturedBaseAddress;
3331 *NumberOfBytesToLock = CapturedBytesToLock;
3332 }
3333 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3334 {
3335 //
3336 // Get exception code
3337 //
3338 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3339 }
3340 _SEH2_END;
3341
3342 //
3343 // Return status
3344 //
3345 return Status;
3346 }
3347
3348
3349 static
3350 NTSTATUS
3351 MiUnlockVirtualMemory(
3352 IN OUT PVOID *BaseAddress,
3353 IN OUT PSIZE_T RegionSize,
3354 IN ULONG MapType)
3355 {
3356 PEPROCESS CurrentProcess;
3357 PMMSUPPORT AddressSpace;
3358 PVOID EndAddress;
3359 PMMPTE PointerPte, LastPte;
3360 PMMPDE PointerPde;
3361 #if (_MI_PAGING_LEVELS >= 3)
3362 PMMPDE PointerPpe;
3363 #endif
3364 #if (_MI_PAGING_LEVELS == 4)
3365 PMMPDE PointerPxe;
3366 #endif
3367 PMMPFN Pfn1;
3368 NTSTATUS Status;
3369
3370 /* Lock the address space */
3371 AddressSpace = MmGetCurrentAddressSpace();
3372 MmLockAddressSpace(AddressSpace);
3373
3374 /* Make sure we still have an address space */
3375 CurrentProcess = PsGetCurrentProcess();
3376 if (CurrentProcess->VmDeleted)
3377 {
3378 Status = STATUS_PROCESS_IS_TERMINATING;
3379 goto Cleanup;
3380 }
3381
3382 /* Check the VADs in the requested range */
3383 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3384
3385 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3386 incompatible VAD we continue, like Windows does */
3387 if (Status == STATUS_ACCESS_VIOLATION)
3388 {
3389 Status = STATUS_NOT_LOCKED;
3390 goto Cleanup;
3391 }
3392
3393 /* Get the PTE and PDE */
3394 PointerPte = MiAddressToPte(*BaseAddress);
3395 PointerPde = MiAddressToPde(*BaseAddress);
3396 #if (_MI_PAGING_LEVELS >= 3)
3397 PointerPpe = MiAddressToPpe(*BaseAddress);
3398 #endif
3399 #if (_MI_PAGING_LEVELS == 4)
3400 PointerPxe = MiAddressToPxe(*BaseAddress);
3401 #endif
3402
3403 /* Get the last PTE */
3404 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3405
3406 /* Lock the process working set */
3407 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3408
3409 /* Loop the pages */
3410 do
3411 {
3412 /* Check for a page that is not present */
3413 if (
3414 #if (_MI_PAGING_LEVELS == 4)
3415 (PointerPxe->u.Hard.Valid == 0) ||
3416 #endif
3417 #if (_MI_PAGING_LEVELS >= 3)
3418 (PointerPpe->u.Hard.Valid == 0) ||
3419 #endif
3420 (PointerPde->u.Hard.Valid == 0) ||
3421 (PointerPte->u.Hard.Valid == 0))
3422 {
3423 /* Remember it, but keep going */
3424 Status = STATUS_NOT_LOCKED;
3425 }
3426 else
3427 {
3428 /* Get the PFN */
3429 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3430 ASSERT(Pfn1 != NULL);
3431
3432 /* Check if all of the requested locks are present */
3433 if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
3434 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
3435 {
3436 /* Remember it, but keep going */
3437 Status = STATUS_NOT_LOCKED;
3438
3439 /* Check if no lock is present */
3440 if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM))
3441 {
3442 DPRINT1("FIXME: Should remove the page from WS\n");
3443 }
3444 }
3445 }
3446
3447 /* Go to the next PTE */
3448 PointerPte++;
3449
3450 /* Check if we're on a PDE boundary */
3451 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3452 #if (_MI_PAGING_LEVELS >= 3)
3453 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3454 #endif
3455 #if (_MI_PAGING_LEVELS == 4)
3456 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3457 #endif
3458 } while (PointerPte <= LastPte);
3459
3460 /* Check if we hit a page that was not locked */
3461 if (Status == STATUS_NOT_LOCKED)
3462 {
3463 goto CleanupWithWsLock;
3464 }
3465
3466 /* All pages in the region were locked, so unlock them all */
3467
3468 /* Get the PTE and PDE */
3469 PointerPte = MiAddressToPte(*BaseAddress);
3470 PointerPde = MiAddressToPde(*BaseAddress);
3471 #if (_MI_PAGING_LEVELS >= 3)
3472 PointerPpe = MiAddressToPpe(*BaseAddress);
3473 #endif
3474 #if (_MI_PAGING_LEVELS == 4)
3475 PointerPxe = MiAddressToPxe(*BaseAddress);
3476 #endif
3477
3478 /* Loop the pages */
3479 do
3480 {
3481 /* Unlock it */
3482 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3483 MI_UNLOCK_VA(Pfn1, MapType);
3484
3485 /* Go to the next PTE */
3486 PointerPte++;
3487
3488 /* Check if we're on a PDE boundary */
3489 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3490 #if (_MI_PAGING_LEVELS >= 3)
3491 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3492 #endif
3493 #if (_MI_PAGING_LEVELS == 4)
3494 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3495 #endif
3496 } while (PointerPte <= LastPte);
3497
3498 /* Everything is done */
3499 Status = STATUS_SUCCESS;
3500
3501 CleanupWithWsLock:
3502
3503 /* Release process working set */
3504 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3505
3506 Cleanup:
3507 /* Unlock address space */
3508 MmUnlockAddressSpace(AddressSpace);
3509
3510 return Status;
3511 }
3512
3513
3514 NTSTATUS
3515 NTAPI
3516 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
3517 IN OUT PVOID *BaseAddress,
3518 IN OUT PSIZE_T NumberOfBytesToUnlock,
3519 IN ULONG MapType)
3520 {
3521 PEPROCESS Process;
3522 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3523 NTSTATUS Status;
3524 BOOLEAN Attached = FALSE;
3525 KAPC_STATE ApcState;
3526 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3527 PVOID CapturedBaseAddress;
3528 SIZE_T CapturedBytesToUnlock;
3529 PAGED_CODE();
3530
3531 //
3532 // Validate flags
3533 //
3534 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3535 {
3536 //
3537 // Invalid set of flags
3538 //
3539 return STATUS_INVALID_PARAMETER;
3540 }
3541
3542 //
3543 // At least one flag must be specified
3544 //
3545 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3546 {
3547 //
3548 // No flag given
3549 //
3550 return STATUS_INVALID_PARAMETER;
3551 }
3552
3553 //
3554 // Enter SEH for probing
3555 //
3556 _SEH2_TRY
3557 {
3558 //
3559 // Validate output data
3560 //
3561 ProbeForWritePointer(BaseAddress);
3562 ProbeForWriteSize_t(NumberOfBytesToUnlock);
3563
3564 //
3565 // Capture it
3566 //
3567 CapturedBaseAddress = *BaseAddress;
3568 CapturedBytesToUnlock = *NumberOfBytesToUnlock;
3569 }
3570 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3571 {
3572 //
3573 // Get exception code
3574 //
3575 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3576 }
3577 _SEH2_END;
3578
3579 //
3580 // Catch illegal base address
3581 //
3582 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3583
3584 //
3585 // Catch illegal region size
3586 //
3587 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
3588 {
3589 //
3590 // Fail
3591 //
3592 return STATUS_INVALID_PARAMETER;
3593 }
3594
3595 //
3596 // 0 is also illegal
3597 //
3598 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
3599
3600 //
3601 // Get a reference to the process
3602 //
3603 Status = ObReferenceObjectByHandle(ProcessHandle,
3604 PROCESS_VM_OPERATION,
3605 PsProcessType,
3606 PreviousMode,
3607 (PVOID*)(&Process),
3608 NULL);
3609 if (!NT_SUCCESS(Status)) return Status;
3610
3611 //
3612 // Check if this is is system-mapped
3613 //
3614 if (MapType & MAP_SYSTEM)
3615 {
3616 //
3617 // Check for required privilege
3618 //
3619 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3620 {
3621 //
3622 // Fail: Don't have it
3623 //
3624 ObDereferenceObject(Process);
3625 return STATUS_PRIVILEGE_NOT_HELD;
3626 }
3627 }
3628
3629 //
3630 // Check if we should attach
3631 //
3632 if (CurrentProcess != Process)
3633 {
3634 //
3635 // Do it
3636 //
3637 KeStackAttachProcess(&Process->Pcb, &ApcState);
3638 Attached = TRUE;
3639 }
3640
3641 //
3642 // Call the internal function
3643 //
3644 Status = MiUnlockVirtualMemory(&CapturedBaseAddress,
3645 &CapturedBytesToUnlock,
3646 MapType);
3647
3648 //
3649 // Detach if needed
3650 //
3651 if (Attached) KeUnstackDetachProcess(&ApcState);
3652
3653 //
3654 // Release reference
3655 //
3656 ObDereferenceObject(Process);
3657
3658 //
3659 // Enter SEH to return data
3660 //
3661 _SEH2_TRY
3662 {
3663 //
3664 // Return data to user
3665 //
3666 *BaseAddress = CapturedBaseAddress;
3667 *NumberOfBytesToUnlock = CapturedBytesToUnlock;
3668 }
3669 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3670 {
3671 //
3672 // Get exception code
3673 //
3674 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3675 }
3676 _SEH2_END;
3677
3678 //
3679 // Return status
3680 //
3681 return STATUS_SUCCESS;
3682 }
3683
3684 NTSTATUS
3685 NTAPI
3686 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
3687 IN OUT PVOID *BaseAddress,
3688 IN OUT PSIZE_T NumberOfBytesToFlush,
3689 OUT PIO_STATUS_BLOCK IoStatusBlock)
3690 {
3691 PEPROCESS Process;
3692 NTSTATUS Status;
3693 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3694 PVOID CapturedBaseAddress;
3695 SIZE_T CapturedBytesToFlush;
3696 IO_STATUS_BLOCK LocalStatusBlock;
3697 PAGED_CODE();
3698
3699 //
3700 // Check if we came from user mode
3701 //
3702 if (PreviousMode != KernelMode)
3703 {
3704 //
3705 // Enter SEH for probing
3706 //
3707 _SEH2_TRY
3708 {
3709 //
3710 // Validate all outputs
3711 //
3712 ProbeForWritePointer(BaseAddress);
3713 ProbeForWriteSize_t(NumberOfBytesToFlush);
3714 ProbeForWriteIoStatusBlock(IoStatusBlock);
3715
3716 //
3717 // Capture them
3718 //
3719 CapturedBaseAddress = *BaseAddress;
3720 CapturedBytesToFlush = *NumberOfBytesToFlush;
3721 }
3722 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3723 {
3724 //
3725 // Get exception code
3726 //
3727 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3728 }
3729 _SEH2_END;
3730 }
3731 else
3732 {
3733 //
3734 // Capture directly
3735 //
3736 CapturedBaseAddress = *BaseAddress;
3737 CapturedBytesToFlush = *NumberOfBytesToFlush;
3738 }
3739
3740 //
3741 // Catch illegal base address
3742 //
3743 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3744
3745 //
3746 // Catch illegal region size
3747 //
3748 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
3749 {
3750 //
3751 // Fail
3752 //
3753 return STATUS_INVALID_PARAMETER;
3754 }
3755
3756 //
3757 // Get a reference to the process
3758 //
3759 Status = ObReferenceObjectByHandle(ProcessHandle,
3760 PROCESS_VM_OPERATION,
3761 PsProcessType,
3762 PreviousMode,
3763 (PVOID*)(&Process),
3764 NULL);
3765 if (!NT_SUCCESS(Status)) return Status;
3766
3767 //
3768 // Do it
3769 //
3770 Status = MmFlushVirtualMemory(Process,
3771 &CapturedBaseAddress,
3772 &CapturedBytesToFlush,
3773 &LocalStatusBlock);
3774
3775 //
3776 // Release reference
3777 //
3778 ObDereferenceObject(Process);
3779
3780 //
3781 // Enter SEH to return data
3782 //
3783 _SEH2_TRY
3784 {
3785 //
3786 // Return data to user
3787 //
3788 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
3789 *NumberOfBytesToFlush = 0;
3790 *IoStatusBlock = LocalStatusBlock;
3791 }
3792 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3793 {
3794 }
3795 _SEH2_END;
3796
3797 //
3798 // Return status
3799 //
3800 return Status;
3801 }
3802
3803 /*
3804 * @unimplemented
3805 */
3806 NTSTATUS
3807 NTAPI
3808 NtGetWriteWatch(IN HANDLE ProcessHandle,
3809 IN ULONG Flags,
3810 IN PVOID BaseAddress,
3811 IN SIZE_T RegionSize,
3812 IN PVOID *UserAddressArray,
3813 OUT PULONG_PTR EntriesInUserAddressArray,
3814 OUT PULONG Granularity)
3815 {
3816 PEPROCESS Process;
3817 NTSTATUS Status;
3818 PVOID EndAddress;
3819 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3820 ULONG_PTR CapturedEntryCount;
3821 PAGED_CODE();
3822
3823 //
3824 // Check if we came from user mode
3825 //
3826 if (PreviousMode != KernelMode)
3827 {
3828 //
3829 // Enter SEH for probing
3830 //
3831 _SEH2_TRY
3832 {
3833 //
3834 // Catch illegal base address
3835 //
3836 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2);
3837
3838 //
3839 // Catch illegal region size
3840 //
3841 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
3842 {
3843 //
3844 // Fail
3845 //
3846 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3);
3847 }
3848
3849 //
3850 // Validate all data
3851 //
3852 ProbeForWriteSize_t(EntriesInUserAddressArray);
3853 ProbeForWriteUlong(Granularity);
3854
3855 //
3856 // Capture them
3857 //
3858 CapturedEntryCount = *EntriesInUserAddressArray;
3859
3860 //
3861 // Must have a count
3862 //
3863 if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
3864
3865 //
3866 // Can't be larger than the maximum
3867 //
3868 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
3869 {
3870 //
3871 // Fail
3872 //
3873 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
3874 }
3875
3876 //
3877 // Probe the actual array
3878 //
3879 ProbeForWrite(UserAddressArray,
3880 CapturedEntryCount * sizeof(PVOID),
3881 sizeof(PVOID));
3882 }
3883 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3884 {
3885 //
3886 // Get exception code
3887 //
3888 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3889 }
3890 _SEH2_END;
3891 }
3892 else
3893 {
3894 //
3895 // Capture directly
3896 //
3897 CapturedEntryCount = *EntriesInUserAddressArray;
3898 ASSERT(CapturedEntryCount != 0);
3899 }
3900
3901 //
3902 // Check if this is a local request
3903 //
3904 if (ProcessHandle == NtCurrentProcess())
3905 {
3906 //
3907 // No need to reference the process
3908 //
3909 Process = PsGetCurrentProcess();
3910 }
3911 else
3912 {
3913 //
3914 // Reference the target
3915 //
3916 Status = ObReferenceObjectByHandle(ProcessHandle,
3917 PROCESS_VM_OPERATION,
3918 PsProcessType,
3919 PreviousMode,
3920 (PVOID *)&Process,
3921 NULL);
3922 if (!NT_SUCCESS(Status)) return Status;
3923 }
3924
3925 //
3926 // Compute the last address and validate it
3927 //
3928 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
3929 if (BaseAddress > EndAddress)
3930 {
3931 //
3932 // Fail
3933 //
3934 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3935 return STATUS_INVALID_PARAMETER_4;
3936 }
3937
3938 //
3939 // Oops :(
3940 //
3941 UNIMPLEMENTED;
3942
3943 //
3944 // Dereference if needed
3945 //
3946 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3947
3948 //
3949 // Enter SEH to return data
3950 //
3951 _SEH2_TRY
3952 {
3953 //
3954 // Return data to user
3955 //
3956 *EntriesInUserAddressArray = 0;
3957 *Granularity = PAGE_SIZE;
3958 }
3959 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3960 {
3961 //
3962 // Get exception code
3963 //
3964 Status = _SEH2_GetExceptionCode();
3965 }
3966 _SEH2_END;
3967
3968 //
3969 // Return success
3970 //
3971 return STATUS_SUCCESS;
3972 }
3973
3974 /*
3975 * @unimplemented
3976 */
3977 NTSTATUS
3978 NTAPI
3979 NtResetWriteWatch(IN HANDLE ProcessHandle,
3980 IN PVOID BaseAddress,
3981 IN SIZE_T RegionSize)
3982 {
3983 PVOID EndAddress;
3984 PEPROCESS Process;
3985 NTSTATUS Status;
3986 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3987 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
3988
3989 //
3990 // Catch illegal base address
3991 //
3992 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
3993
3994 //
3995 // Catch illegal region size
3996 //
3997 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
3998 {
3999 //
4000 // Fail
4001 //
4002 return STATUS_INVALID_PARAMETER_3;
4003 }
4004
4005 //
4006 // Check if this is a local request
4007 //
4008 if (ProcessHandle == NtCurrentProcess())
4009 {
4010 //
4011 // No need to reference the process
4012 //
4013 Process = PsGetCurrentProcess();
4014 }
4015 else
4016 {
4017 //
4018 // Reference the target
4019 //
4020 Status = ObReferenceObjectByHandle(ProcessHandle,
4021 PROCESS_VM_OPERATION,
4022 PsProcessType,
4023 PreviousMode,
4024 (PVOID *)&Process,
4025 NULL);
4026 if (!NT_SUCCESS(Status)) return Status;
4027 }
4028
4029 //
4030 // Compute the last address and validate it
4031 //
4032 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
4033 if (BaseAddress > EndAddress)
4034 {
4035 //
4036 // Fail
4037 //
4038 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4039 return STATUS_INVALID_PARAMETER_3;
4040 }
4041
4042 //
4043 // Oops :(
4044 //
4045 UNIMPLEMENTED;
4046
4047 //
4048 // Dereference if needed
4049 //
4050 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4051
4052 //
4053 // Return success
4054 //
4055 return STATUS_SUCCESS;
4056 }
4057
4058 NTSTATUS
4059 NTAPI
4060 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
4061 IN PVOID BaseAddress,
4062 IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
4063 OUT PVOID MemoryInformation,
4064 IN SIZE_T MemoryInformationLength,
4065 OUT PSIZE_T ReturnLength)
4066 {
4067 NTSTATUS Status = STATUS_SUCCESS;
4068 KPROCESSOR_MODE PreviousMode;
4069
4070 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
4071
4072 /* Bail out if the address is invalid */
4073 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4074
4075 /* Probe return buffer */
4076 PreviousMode = ExGetPreviousMode();
4077 if (PreviousMode != KernelMode)
4078 {
4079 _SEH2_TRY
4080 {
4081 ProbeForWrite(MemoryInformation,
4082 MemoryInformationLength,
4083 sizeof(ULONG_PTR));
4084
4085 if (ReturnLength) ProbeForWriteSize_t(ReturnLength);
4086 }
4087 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4088 {
4089 Status = _SEH2_GetExceptionCode();
4090 }
4091 _SEH2_END;
4092
4093 if (!NT_SUCCESS(Status))
4094 {
4095 return Status;
4096 }
4097 }
4098
4099 switch(MemoryInformationClass)
4100 {
4101 case MemoryBasicInformation:
4102 /* Validate the size information of the class */
4103 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
4104 {
4105 /* The size is invalid */
4106 return STATUS_INFO_LENGTH_MISMATCH;
4107 }
4108 Status = MiQueryMemoryBasicInformation(ProcessHandle,
4109 BaseAddress,
4110 MemoryInformation,
4111 MemoryInformationLength,
4112 ReturnLength);
4113 break;
4114
4115 case MemorySectionName:
4116 /* Validate the size information of the class */
4117 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
4118 {
4119 /* The size is invalid */
4120 return STATUS_INFO_LENGTH_MISMATCH;
4121 }
4122 Status = MiQueryMemorySectionName(ProcessHandle,
4123 BaseAddress,
4124 MemoryInformation,
4125 MemoryInformationLength,
4126 ReturnLength);
4127 break;
4128 case MemoryWorkingSetList:
4129 case MemoryBasicVlmInformation:
4130 default:
4131 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
4132 break;
4133 }
4134
4135 return Status;
4136 }
4137
4138 /*
4139 * @implemented
4140 */
4141 NTSTATUS
4142 NTAPI
4143 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
4144 IN OUT PVOID* UBaseAddress,
4145 IN ULONG_PTR ZeroBits,
4146 IN OUT PSIZE_T URegionSize,
4147 IN ULONG AllocationType,
4148 IN ULONG Protect)
4149 {
4150 PEPROCESS Process;
4151 PMEMORY_AREA MemoryArea;
4152 PFN_NUMBER PageCount;
4153 PMMVAD Vad = NULL, FoundVad;
4154 NTSTATUS Status;
4155 PMMSUPPORT AddressSpace;
4156 PVOID PBaseAddress;
4157 ULONG_PTR PRegionSize, StartingAddress, EndingAddress, HighestAddress;
4158 PEPROCESS CurrentProcess = PsGetCurrentProcess();
4159 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
4160 PETHREAD CurrentThread = PsGetCurrentThread();
4161 KAPC_STATE ApcState;
4162 ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0;
4163 BOOLEAN Attached = FALSE, ChangeProtection = FALSE;
4164 MMPTE TempPte;
4165 PMMPTE PointerPte, PointerPde, LastPte;
4166 TABLE_SEARCH_RESULT Result;
4167 PMMADDRESS_NODE Parent;
4168 PAGED_CODE();
4169
4170 /* Check for valid Zero bits */
4171 if (ZeroBits > MI_MAX_ZERO_BITS)
4172 {
4173 DPRINT1("Too many zero bits\n");
4174 return STATUS_INVALID_PARAMETER_3;
4175 }
4176
4177 /* Check for valid Allocation Types */
4178 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
4179 MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES)))
4180 {
4181 DPRINT1("Invalid Allocation Type\n");
4182 return STATUS_INVALID_PARAMETER_5;
4183 }
4184
4185 /* Check for at least one of these Allocation Types to be set */
4186 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
4187 {
4188 DPRINT1("No memory allocation base type\n");
4189 return STATUS_INVALID_PARAMETER_5;
4190 }
4191
4192 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4193 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
4194 {
4195 DPRINT1("Invalid use of MEM_RESET\n");
4196 return STATUS_INVALID_PARAMETER_5;
4197 }
4198
4199 /* Check if large pages are being used */
4200 if (AllocationType & MEM_LARGE_PAGES)
4201 {
4202 /* Large page allocations MUST be committed */
4203 if (!(AllocationType & MEM_COMMIT))
4204 {
4205 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4206 return STATUS_INVALID_PARAMETER_5;
4207 }
4208
4209 /* These flags are not allowed with large page allocations */
4210 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
4211 {
4212 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4213 return STATUS_INVALID_PARAMETER_5;
4214 }
4215 }
4216
4217 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4218 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
4219 {
4220 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4221 return STATUS_INVALID_PARAMETER_5;
4222 }
4223
4224 /* Check for valid MEM_PHYSICAL usage */
4225 if (AllocationType & MEM_PHYSICAL)
4226 {
4227 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4228 if (!(AllocationType & MEM_RESERVE))
4229 {
4230 DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
4231 return STATUS_INVALID_PARAMETER_5;
4232 }
4233
4234 /* Only these flags are allowed with MEM_PHYSIAL */
4235 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
4236 {
4237 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4238 return STATUS_INVALID_PARAMETER_5;
4239 }
4240
4241 /* Then make sure PAGE_READWRITE is used */
4242 if (Protect != PAGE_READWRITE)
4243 {
4244 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4245 return STATUS_INVALID_PARAMETER_6;
4246 }
4247 }
4248
4249 /* Calculate the protection mask and make sure it's valid */
4250 ProtectionMask = MiMakeProtectionMask(Protect);
4251 if (ProtectionMask == MM_INVALID_PROTECTION)
4252 {
4253 DPRINT1("Invalid protection mask\n");
4254 return STATUS_INVALID_PAGE_PROTECTION;
4255 }
4256
4257 /* Enter SEH */
4258 _SEH2_TRY
4259 {
4260 /* Check for user-mode parameters */
4261 if (PreviousMode != KernelMode)
4262 {
4263 /* Make sure they are writable */
4264 ProbeForWritePointer(UBaseAddress);
4265 ProbeForWriteSize_t(URegionSize);
4266 }
4267
4268 /* Capture their values */
4269 PBaseAddress = *UBaseAddress;
4270 PRegionSize = *URegionSize;
4271 }
4272 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4273 {
4274 /* Return the exception code */
4275 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4276 }
4277 _SEH2_END;
4278
4279 /* Make sure the allocation isn't past the VAD area */
4280 if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS)
4281 {
4282 DPRINT1("Virtual allocation base above User Space\n");
4283 return STATUS_INVALID_PARAMETER_2;
4284 }
4285
4286 /* Make sure the allocation wouldn't overflow past the VAD area */
4287 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
4288 {
4289 DPRINT1("Region size would overflow into kernel-memory\n");
4290 return STATUS_INVALID_PARAMETER_4;
4291 }
4292
4293 /* Make sure there's a size specified */
4294 if (!PRegionSize)
4295 {
4296 DPRINT1("Region size is invalid (zero)\n");
4297 return STATUS_INVALID_PARAMETER_4;
4298 }
4299
4300 //
4301 // If this is for the current process, just use PsGetCurrentProcess
4302 //
4303 if (ProcessHandle == NtCurrentProcess())
4304 {
4305 Process = CurrentProcess;
4306 }
4307 else
4308 {
4309 //
4310 // Otherwise, reference the process with VM rights and attach to it if
4311 // this isn't the current process. We must attach because we'll be touching
4312 // PTEs and PDEs that belong to user-mode memory, and also touching the
4313 // Working Set which is stored in Hyperspace.
4314 //
4315 Status = ObReferenceObjectByHandle(ProcessHandle,
4316 PROCESS_VM_OPERATION,
4317 PsProcessType,
4318 PreviousMode,
4319 (PVOID*)&Process,
4320 NULL);
4321 if (!NT_SUCCESS(Status)) return Status;
4322 if (CurrentProcess != Process)
4323 {
4324 KeStackAttachProcess(&Process->Pcb, &ApcState);
4325 Attached = TRUE;
4326 }
4327 }
4328
4329 //
4330 // Check for large page allocations and make sure that the required privilege
4331 // is being held, before attempting to handle them.
4332 //
4333 if ((AllocationType & MEM_LARGE_PAGES) &&
4334 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)))
4335 {
4336 /* Fail without it */
4337 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4338 Status = STATUS_PRIVILEGE_NOT_HELD;
4339 goto FailPathNoLock;
4340 }
4341
4342 //
4343 // Fail on the things we don't yet support
4344 //
4345 if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES)
4346 {
4347 DPRINT1("MEM_LARGE_PAGES not supported\n");
4348 Status = STATUS_INVALID_PARAMETER;
4349 goto FailPathNoLock;
4350 }
4351 if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL)
4352 {
4353 DPRINT1("MEM_PHYSICAL not supported\n");
4354 Status = STATUS_INVALID_PARAMETER;
4355 goto FailPathNoLock;
4356 }
4357 if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH)
4358 {
4359 DPRINT1("MEM_WRITE_WATCH not supported\n");
4360 Status = STATUS_INVALID_PARAMETER;
4361 goto FailPathNoLock;
4362 }
4363
4364 //
4365 // Check if the caller is reserving memory, or committing memory and letting
4366 // us pick the base address
4367 //
4368 if (!(PBaseAddress) || (AllocationType & MEM_RESERVE))
4369 {
4370 //
4371 // Do not allow COPY_ON_WRITE through this API
4372 //
4373 if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
4374 {
4375 DPRINT1("Copy on write not allowed through this path\n");
4376 Status = STATUS_INVALID_PAGE_PROTECTION;
4377 goto FailPathNoLock;
4378 }
4379
4380 //
4381 // Does the caller have an address in mind, or is this a blind commit?
4382 //
4383 if (!PBaseAddress)
4384 {
4385 //
4386 // This is a blind commit, all we need is the region size
4387 //
4388 PRegionSize = ROUND_TO_PAGES(PRegionSize);
4389 PageCount = BYTES_TO_PAGES(PRegionSize);
4390 EndingAddress = 0;
4391 StartingAddress = 0;
4392
4393 //
4394 // Check if ZeroBits were specified
4395 //
4396 if (ZeroBits != 0)
4397 {
4398 //
4399 // Calculate the highest address and check if it's valid
4400 //
4401 HighestAddress = MAXULONG_PTR >> ZeroBits;
4402 if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS)
4403 {
4404 Status = STATUS_INVALID_PARAMETER_3;
4405 goto FailPathNoLock;
4406 }
4407 }
4408 else
4409 {
4410 //
4411 // Use the highest VAD address as maximum
4412 //
4413 HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
4414 }
4415 }
4416 else
4417 {
4418 //
4419 // This is a reservation, so compute the starting address on the
4420 // expected 64KB granularity, and see where the ending address will
4421 // fall based on the aligned address and the passed in region size
4422 //
4423 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4424 StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
4425 PageCount = BYTES_TO_PAGES(EndingAddress - StartingAddress);
4426 }
4427
4428 //
4429 // Allocate and initialize the VAD
4430 //
4431 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
4432 if (Vad == NULL)
4433 {
4434 DPRINT1("Failed to allocate a VAD!\n");
4435 Status = STATUS_INSUFFICIENT_RESOURCES;
4436 goto FailPathNoLock;
4437 }
4438
4439 Vad->u.LongFlags = 0;
4440 if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
4441 Vad->u.VadFlags.Protection = ProtectionMask;
4442 Vad->u.VadFlags.PrivateMemory = 1;
4443 Vad->u.VadFlags.CommitCharge = AllocationType & MEM_COMMIT ? PageCount : 0;
4444
4445 //
4446 // Lock the address space and make sure the process isn't already dead
4447 //
4448 AddressSpace = MmGetCurrentAddressSpace();
4449 MmLockAddressSpace(AddressSpace);
4450 if (Process->VmDeleted)
4451 {
4452 Status = STATUS_PROCESS_IS_TERMINATING;
4453 goto FailPath;
4454 }
4455
4456 //
4457 // Did we have a base address? If no, find a valid address that is 64KB
4458 // aligned in the VAD tree. Otherwise, make sure that the address range
4459 // which was passed in isn't already conflicting with an existing address
4460 // range.
4461 //
4462 if (!PBaseAddress)
4463 {
4464 /* Which way should we search? */
4465 if ((AllocationType & MEM_TOP_DOWN) || Process->VmTopDown)
4466 {
4467 /* Find an address top-down */
4468 Result = MiFindEmptyAddressRangeDownTree(PRegionSize,
4469 HighestAddress,
4470 _64K,
4471 &Process->VadRoot,
4472 &StartingAddress,
4473 &Parent);
4474 }
4475 else
4476 {
4477 /* Find an address bottom-up */
4478 Result = MiFindEmptyAddressRangeInTree(PRegionSize,
4479 _64K,
4480 &Process->VadRoot,
4481 &Parent,
4482 &StartingAddress);
4483 }
4484
4485 if (Result == TableFoundNode)
4486 {
4487 Status = STATUS_NO_MEMORY;
4488 goto FailPath;
4489 }
4490
4491 //
4492 // Now we know where the allocation ends. Make sure it doesn't end up
4493 // somewhere in kernel mode.
4494 //
4495 ASSERT(StartingAddress != 0);
4496 ASSERT(StartingAddress < (ULONG_PTR)MM_HIGHEST_USER_ADDRESS);
4497 EndingAddress = (StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4498 ASSERT(EndingAddress > StartingAddress);
4499 if (EndingAddress > HighestAddress)
4500 {
4501 Status = STATUS_NO_MEMORY;
4502 goto FailPath;
4503 }
4504 }
4505 else
4506 {
4507 /* Make sure it doesn't conflict with an existing allocation */
4508 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4509 EndingAddress >> PAGE_SHIFT,
4510 &Process->VadRoot,
4511 &Parent);
4512 if (Result == TableFoundNode)
4513 {
4514 //
4515 // The address specified is in conflict!
4516 //
4517 Status = STATUS_CONFLICTING_ADDRESSES;
4518 goto FailPath;
4519 }
4520 }
4521
4522 //
4523 // Write out the VAD fields for this allocation
4524 //
4525 Vad->StartingVpn = StartingAddress >> PAGE_SHIFT;
4526 Vad->EndingVpn = EndingAddress >> PAGE_SHIFT;
4527
4528 //
4529 // FIXME: Should setup VAD bitmap
4530 //
4531 Status = STATUS_SUCCESS;
4532
4533 //
4534 // Lock the working set and insert the VAD into the process VAD tree
4535 //
4536 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4537 Vad->ControlArea = NULL; // For Memory-Area hack
4538 Process->VadRoot.NodeHint = Vad;
4539 MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
4540 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
4541
4542 //
4543 // Make sure the actual region size is at least as big as the
4544 // requested region size and update the value
4545 //
4546 ASSERT(PRegionSize <= (EndingAddress + 1 - StartingAddress));
4547 PRegionSize = (EndingAddress + 1 - StartingAddress);
4548
4549 //
4550 // Update the virtual size of the process, and if this is now the highest
4551 // virtual size we have ever seen, update the peak virtual size to reflect
4552 // this.
4553 //
4554 Process->VirtualSize += PRegionSize;
4555 if (Process->VirtualSize > Process->PeakVirtualSize)
4556 {
4557 Process->PeakVirtualSize = Process->VirtualSize;
4558 }
4559
4560 //
4561 // Release address space and detach and dereference the target process if
4562 // it was different from the current process
4563 //
4564 MmUnlockAddressSpace(AddressSpace);
4565 if (Attached) KeUnstackDetachProcess(&ApcState);
4566 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4567
4568 //
4569 // Use SEH to write back the base address and the region size. In the case
4570 // of an exception, we do not return back the exception code, as the memory
4571 // *has* been allocated. The caller would now have to call VirtualQuery
4572 // or do some other similar trick to actually find out where its memory
4573 // allocation ended up
4574 //
4575 _SEH2_TRY
4576 {
4577 *URegionSize = PRegionSize;
4578 *UBaseAddress = (PVOID)StartingAddress;
4579 }
4580 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4581 {
4582 //
4583 // Ignore exception!
4584 //
4585 }
4586 _SEH2_END;
4587 return STATUS_SUCCESS;
4588 }
4589
4590 //
4591 // This is a MEM_COMMIT on top of an existing address which must have been
4592 // MEM_RESERVED already. Compute the start and ending base addresses based
4593 // on the user input, and then compute the actual region size once all the
4594 // alignments have been done.
4595 //
4596 EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
4597 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4598 PRegionSize = EndingAddress - StartingAddress + 1;
4599
4600 //
4601 // Lock the address space and make sure the process isn't already dead
4602 //
4603 AddressSpace = MmGetCurrentAddressSpace();
4604 MmLockAddressSpace(AddressSpace);
4605 if (Process->VmDeleted)
4606 {
4607 DPRINT1("Process is dying\n");
4608 Status = STATUS_PROCESS_IS_TERMINATING;
4609 goto FailPath;
4610 }
4611
4612 //
4613 // Get the VAD for this address range, and make sure it exists
4614 //
4615 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4616 EndingAddress >> PAGE_SHIFT,
4617 &Process->VadRoot,
4618 (PMMADDRESS_NODE*)&FoundVad);
4619 if (Result != TableFoundNode)
4620 {
4621 DPRINT1("Could not find a VAD for this allocation\n");
4622 Status = STATUS_CONFLICTING_ADDRESSES;
4623 goto FailPath;
4624 }
4625
4626 if ((AllocationType & MEM_RESET) == MEM_RESET)
4627 {
4628 /// @todo HACK: pretend success
4629 DPRINT("MEM_RESET not supported\n");
4630 Status = STATUS_SUCCESS;
4631 goto FailPath;
4632 }
4633
4634 //
4635 // These kinds of VADs are illegal for this Windows function when trying to
4636 // commit an existing range
4637 //
4638 if ((FoundVad->u.VadFlags.VadType == VadAwe) ||
4639 (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
4640 (FoundVad->u.VadFlags.VadType == VadLargePages))
4641 {
4642 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4643 Status = STATUS_CONFLICTING_ADDRESSES;
4644 goto FailPath;
4645 }
4646
4647 //
4648 // Make sure that this address range actually fits within the VAD for it
4649 //
4650 if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
4651 ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
4652 {
4653 DPRINT1("Address range does not fit into the VAD\n");
4654 Status = STATUS_CONFLICTING_ADDRESSES;
4655 goto FailPath;
4656 }
4657
4658 //
4659 // Make sure this is an ARM3 section
4660 //
4661 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress));
4662 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
4663 {
4664 DPRINT1("Illegal commit of non-ARM3 section!\n");
4665 Status = STATUS_ALREADY_COMMITTED;
4666 goto FailPath;
4667 }
4668
4669 // Is this a previously reserved section being committed? If so, enter the
4670 // special section path
4671 //
4672 if (FoundVad->u.VadFlags.PrivateMemory == FALSE)
4673 {
4674 //
4675 // You cannot commit large page sections through this API
4676 //
4677 if (FoundVad->u.VadFlags.VadType == VadLargePageSection)
4678 {
4679 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4680 Status = STATUS_INVALID_PAGE_PROTECTION;
4681 goto FailPath;
4682 }
4683
4684 //
4685 // You can only use caching flags on a rotate VAD
4686 //
4687 if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) &&
4688 (FoundVad->u.VadFlags.VadType != VadRotatePhysical))
4689 {
4690 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4691 Status = STATUS_INVALID_PAGE_PROTECTION;
4692 goto FailPath;
4693 }
4694
4695 //
4696 // We should make sure that the section's permissions aren't being
4697 // messed with
4698 //
4699 if (FoundVad->u.VadFlags.NoChange)
4700 {
4701 //
4702 // Make sure it's okay to touch it
4703 // Note: The Windows 2003 kernel has a bug here, passing the
4704 // unaligned base address together with the aligned size,
4705 // potentially covering a region larger than the actual allocation.
4706 // Might be exposed through NtGdiCreateDIBSection w/ section handle
4707 // For now we keep this behavior.
4708 // TODO: analyze possible implications, create test case
4709 //
4710 Status = MiCheckSecuredVad(FoundVad,
4711 PBaseAddress,
4712 PRegionSize,
4713 ProtectionMask);
4714 if (!NT_SUCCESS(Status))
4715 {
4716 DPRINT1("Secured VAD being messed around with\n");
4717 goto FailPath;
4718 }
4719 }
4720
4721 //
4722 // ARM3 does not support file-backed sections, only shared memory
4723 //
4724 ASSERT(FoundVad->ControlArea->FilePointer == NULL);
4725
4726 //
4727 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4728 //
4729 if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) &&
4730 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD)))
4731 {
4732 DPRINT1("Invalid page protection for rotate VAD\n");
4733 Status = STATUS_INVALID_PAGE_PROTECTION;
4734 goto FailPath;
4735 }
4736
4737 //
4738 // Compute PTE addresses and the quota charge, then grab the commit lock
4739 //
4740 PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT);
4741 LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT);
4742 QuotaCharge = (ULONG)(LastPte - PointerPte + 1);
4743 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
4744
4745 //
4746 // Get the segment template PTE and start looping each page
4747 //
4748 TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate;
4749 ASSERT(TempPte.u.Long != 0);
4750 while (PointerPte <= LastPte)
4751 {
4752 //
4753 // For each non-already-committed page, write the invalid template PTE
4754 //
4755 if (PointerPte->u.Long == 0)
4756 {
4757 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4758 }
4759 else
4760 {
4761 QuotaFree++;
4762 }
4763 PointerPte++;
4764 }
4765
4766 //
4767 // Now do the commit accounting and release the lock
4768 //
4769 ASSERT(QuotaCharge >= QuotaFree);
4770 QuotaCharge -= QuotaFree;
4771 FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge;
4772 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
4773
4774 //
4775 // We are done with committing the section pages
4776 //
4777 Status = STATUS_SUCCESS;
4778 goto FailPath;
4779 }
4780
4781 //
4782 // This is a specific ReactOS check because we only use normal VADs
4783 //
4784 ASSERT(FoundVad->u.VadFlags.VadType == VadNone);
4785
4786 //
4787 // While this is an actual Windows check
4788 //
4789 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
4790
4791 //
4792 // Throw out attempts to use copy-on-write through this API path
4793 //
4794 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
4795 {
4796 DPRINT1("Write copy attempted when not allowed\n");
4797 Status = STATUS_INVALID_PAGE_PROTECTION;
4798 goto FailPath;
4799 }
4800
4801 //
4802 // Initialize a demand-zero PTE
4803 //
4804 TempPte.u.Long = 0;
4805 TempPte.u.Soft.Protection = ProtectionMask;
4806 NT_ASSERT(TempPte.u.Long != 0);
4807
4808 //
4809 // Get the PTE, PDE and the last PTE for this address range
4810 //
4811 PointerPde = MiAddressToPde(StartingAddress);
4812 PointerPte = MiAddressToPte(StartingAddress);
4813 LastPte = MiAddressToPte(EndingAddress);
4814
4815 //
4816 // Update the commit charge in the VAD as well as in the process, and check
4817 // if this commit charge was now higher than the last recorded peak, in which
4818 // case we also update the peak
4819 //
4820 FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte);
4821 Process->CommitCharge += (1 + LastPte - PointerPte);
4822 if (Process->CommitCharge > Process->CommitChargePeak)
4823 {
4824 Process->CommitChargePeak = Process->CommitCharge;
4825 }
4826
4827 //
4828 // Lock the working set while we play with user pages and page tables
4829 //
4830 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4831
4832 //
4833 // Make the current page table valid, and then loop each page within it
4834 //
4835 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4836 while (PointerPte <= LastPte)
4837 {
4838 //
4839 // Have we crossed into a new page table?
4840 //
4841 if (MiIsPteOnPdeBoundary(PointerPte))
4842 {
4843 //
4844 // Get the PDE and now make it valid too
4845 //
4846 PointerPde = MiAddressToPte(PointerPte);
4847 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4848 }
4849
4850 //
4851 // Is this a zero PTE as expected?
4852 //
4853 if (PointerPte->u.Long == 0)
4854 {
4855 //
4856 // First increment the count of pages in the page table for this
4857 // process
4858 //
4859 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
4860
4861 //
4862 // And now write the invalid demand-zero PTE as requested
4863 //
4864 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4865 }
4866 else if (PointerPte->u.Long == MmDecommittedPte.u.Long)
4867 {
4868 //
4869 // If the PTE was already decommitted, there is nothing else to do
4870 // but to write the new demand-zero PTE
4871 //
4872 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4873 }
4874 else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte)))
4875 {
4876 //
4877 // We don't handle these scenarios yet
4878 //
4879 if (PointerPte->u.Soft.Valid == 0)
4880 {
4881 ASSERT(PointerPte->u.Soft.Prototype == 0);
4882 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
4883 }
4884
4885 //
4886 // There's a change in protection, remember this for later, but do
4887 // not yet handle it.
4888 //
4889 ChangeProtection = TRUE;
4890 }
4891
4892 //
4893 // Move to the next PTE
4894 //
4895 PointerPte++;
4896 }
4897
4898 //
4899 // Release the working set lock, unlock the address space, and detach from
4900 // the target process if it was not the current process. Also dereference the
4901 // target process if this wasn't the case.
4902 //
4903 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
4904 Status = STATUS_SUCCESS;
4905 FailPath:
4906 MmUnlockAddressSpace(AddressSpace);
4907
4908 if (!NT_SUCCESS(Status))
4909 {
4910 if (Vad != NULL)
4911 {
4912 ExFreePoolWithTag(Vad, 'SdaV');
4913 }
4914 }
4915
4916 //
4917 // Check if we need to update the protection
4918 //
4919 if (ChangeProtection)
4920 {
4921 PVOID ProtectBaseAddress = (PVOID)StartingAddress;
4922 SIZE_T ProtectSize = PRegionSize;
4923 ULONG OldProtection;
4924
4925 //
4926 // Change the protection of the region
4927 //
4928 MiProtectVirtualMemory(Process,
4929 &ProtectBaseAddress,
4930 &ProtectSize,
4931 Protect,
4932 &OldProtection);
4933 }
4934
4935 FailPathNoLock:
4936 if (Attached) KeUnstackDetachProcess(&ApcState);
4937 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4938
4939 //
4940 // Only write back results on success
4941 //
4942 if (NT_SUCCESS(Status))
4943 {
4944 //
4945 // Use SEH to write back the base address and the region size. In the case
4946 // of an exception, we strangely do return back the exception code, even
4947 // though the memory *has* been allocated. This mimics Windows behavior and
4948 // there is not much we can do about it.
4949 //
4950 _SEH2_TRY
4951 {
4952 *URegionSize = PRegionSize;
4953 *UBaseAddress = (PVOID)StartingAddress;
4954 }
4955 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4956 {
4957 Status = _SEH2_GetExceptionCode();
4958 }
4959 _SEH2_END;
4960 }
4961
4962 return Status;
4963 }
4964
4965 /*
4966 * @implemented
4967 */
4968 NTSTATUS
4969 NTAPI
4970 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
4971 IN PVOID* UBaseAddress,
4972 IN PSIZE_T URegionSize,
4973 IN ULONG FreeType)
4974 {
4975 PMEMORY_AREA MemoryArea;
4976 SIZE_T PRegionSize;
4977 PVOID PBaseAddress;
4978 LONG_PTR CommitReduction = 0;
4979 ULONG_PTR StartingAddress, EndingAddress;
4980 PMMVAD Vad;
4981 NTSTATUS Status;
4982 PEPROCESS Process;
4983 PMMSUPPORT AddressSpace;
4984 PETHREAD CurrentThread = PsGetCurrentThread();
4985 PEPROCESS CurrentProcess = PsGetCurrentProcess();
4986 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
4987 KAPC_STATE ApcState;
4988 BOOLEAN Attached = FALSE;
4989 PAGED_CODE();
4990
4991 //
4992 // Only two flags are supported
4993 //
4994 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
4995 {
4996 DPRINT1("Invalid FreeType\n");
4997 return STATUS_INVALID_PARAMETER_4;
4998 }
4999
5000 //
5001 // Check if no flag was used, or if both flags were used
5002 //
5003 if (!((FreeType & (MEM_DECOMMIT | MEM_RELEASE))) ||
5004 ((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == (MEM_DECOMMIT | MEM_RELEASE)))
5005 {
5006 DPRINT1("Invalid FreeType combination\n");
5007 return STATUS_INVALID_PARAMETER_4;
5008 }
5009
5010 //
5011 // Enter SEH for probe and capture. On failure, return back to the caller
5012 // with an exception violation.
5013 //
5014 _SEH2_TRY
5015 {
5016 //
5017 // Check for user-mode parameters and make sure that they are writeable
5018 //
5019 if (PreviousMode != KernelMode)
5020 {
5021 ProbeForWritePointer(UBaseAddress);
5022 ProbeForWriteUlong(URegionSize);
5023 }
5024
5025 //
5026 // Capture the current values
5027 //
5028 PBaseAddress = *UBaseAddress;
5029 PRegionSize = *URegionSize;
5030 }
5031 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5032 {
5033 _SEH2_YIELD(return _SEH2_GetExceptionCode());
5034 }
5035 _SEH2_END;
5036
5037 //
5038 // Make sure the allocation isn't past the user area
5039 //
5040 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
5041 {
5042 DPRINT1("Virtual free base above User Space\n");
5043 return STATUS_INVALID_PARAMETER_2;
5044 }
5045
5046 //
5047 // Make sure the allocation wouldn't overflow past the user area
5048 //
5049 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
5050 {
5051 DPRINT1("Region size would overflow into kernel-memory\n");
5052 return STATUS_INVALID_PARAMETER_3;
5053 }
5054
5055 //
5056 // If this is for the current process, just use PsGetCurrentProcess
5057 //
5058 if (ProcessHandle == NtCurrentProcess())
5059 {
5060 Process = CurrentProcess;
5061 }
5062 else
5063 {
5064 //
5065 // Otherwise, reference the process with VM rights and attach to it if
5066 // this isn't the current process. We must attach because we'll be touching
5067 // PTEs and PDEs that belong to user-mode memory, and also touching the
5068 // Working Set which is stored in Hyperspace.
5069 //
5070 Status = ObReferenceObjectByHandle(ProcessHandle,
5071 PROCESS_VM_OPERATION,
5072 PsProcessType,
5073 PreviousMode,
5074 (PVOID*)&Process,
5075 NULL);
5076 if (!NT_SUCCESS(Status)) return Status;
5077 if (CurrentProcess != Process)
5078 {
5079 KeStackAttachProcess(&Process->Pcb, &ApcState);
5080 Attached = TRUE;
5081 }
5082 }
5083
5084 //
5085 // Lock the address space
5086 //
5087 AddressSpace = MmGetCurrentAddressSpace();
5088 MmLockAddressSpace(AddressSpace);
5089
5090 //
5091 // If the address space is being deleted, fail the de-allocation since it's
5092 // too late to do anything about it
5093 //
5094 if (Process->VmDeleted)
5095 {
5096 DPRINT1("Process is dead\n");
5097 Status = STATUS_PROCESS_IS_TERMINATING;
5098 goto FailPath;
5099 }
5100
5101 //
5102 // Compute start and end addresses, and locate the VAD
5103 //
5104 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
5105 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
5106 Vad = MiLocateAddress((PVOID)StartingAddress);
5107 if (!Vad)
5108 {
5109 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress);
5110 Status = STATUS_MEMORY_NOT_ALLOCATED;
5111 goto FailPath;
5112 }
5113
5114 //
5115 // If the range exceeds the VAD's ending VPN, fail this request
5116 //
5117 if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT))
5118 {
5119 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress);
5120 Status = STATUS_UNABLE_TO_FREE_VM;
5121 goto FailPath;
5122 }
5123
5124 //
5125 // Only private memory (except rotate VADs) can be freed through here */
5126 //
5127 if ((!(Vad->u.VadFlags.PrivateMemory) &&
5128 (Vad->u.VadFlags.VadType != VadRotatePhysical)) ||
5129 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory))
5130 {
5131 DPRINT1("Attempt to free section memory\n");
5132 Status = STATUS_UNABLE_TO_DELETE_SECTION;
5133 goto FailPath;
5134 }
5135
5136 //
5137 // ARM3 does not yet handle protected VM
5138 //
5139 ASSERT(Vad->u.VadFlags.NoChange == 0);
5140
5141 //
5142 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5143 // and that is is an ARM3 memory area, and not a section view, as we currently
5144 // don't support freeing those though this interface.
5145 //
5146 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5147 ASSERT(MemoryArea);
5148 ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
5149
5150 //
5151 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5152 //
5153 if (FreeType & MEM_RELEASE)
5154 {
5155 //
5156 // ARM3 only supports this VAD in this path
5157 //
5158 ASSERT(Vad->u.VadFlags.VadType == VadNone);
5159
5160 //
5161 // Is the caller trying to remove the whole VAD, or remove only a portion
5162 // of it? If no region size is specified, then the assumption is that the
5163 // whole VAD is to be destroyed
5164 //
5165 if (!PRegionSize)
5166 {
5167 //
5168 // The caller must specify the base address identically to the range
5169 // that is stored in the VAD.
5170 //
5171 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5172 {
5173 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress);
5174 Status = STATUS_FREE_VM_NOT_AT_BASE;
5175 goto FailPath;
5176 }
5177
5178 //
5179 // Now compute the actual start/end addresses based on the VAD
5180 //
5181 StartingAddress = Vad->StartingVpn << PAGE_SHIFT;
5182 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5183
5184 //
5185 // Finally lock the working set and remove the VAD from the VAD tree
5186 //
5187 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5188 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5189 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5190 }
5191 else
5192 {
5193 //
5194 // This means the caller wants to release a specific region within
5195 // the range. We have to find out which range this is -- the following
5196 // possibilities exist plus their union (CASE D):
5197 //
5198 // STARTING ADDRESS ENDING ADDRESS
5199 // [<========][========================================][=========>]
5200 // CASE A CASE B CASE C
5201 //
5202 //
5203 // First, check for case A or D
5204 //
5205 if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn)
5206 {
5207 //
5208 // Check for case D
5209 //
5210 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5211 {
5212 //
5213 // This is the easiest one to handle -- it is identical to
5214 // the code path above when the caller sets a zero region size
5215 // and the whole VAD is destroyed
5216 //
5217 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5218 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5219 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5220 }
5221 else
5222 {
5223 //
5224 // This case is pretty easy too -- we compute a bunch of
5225 // pages to decommit, and then push the VAD's starting address
5226 // a bit further down, then decrement the commit charge
5227 //
5228 // NOT YET IMPLEMENTED IN ARM3.
5229 //
5230 DPRINT1("Case A not handled\n");
5231 Status = STATUS_FREE_VM_NOT_AT_BASE;
5232 goto FailPath;
5233
5234 //
5235 // After analyzing the VAD, set it to NULL so that we don't
5236 // free it in the exit path
5237 //
5238 Vad = NULL;
5239 }
5240 }
5241 else
5242 {
5243 //
5244 // This is case B or case C. First check for case C
5245 //
5246 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5247 {
5248 PMEMORY_AREA MemoryArea;
5249
5250 //
5251 // This is pretty easy and similar to case A. We compute the
5252 // amount of pages to decommit, update the VAD's commit charge
5253 // and then change the ending address of the VAD to be a bit
5254 // smaller.
5255 //
5256 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5257 CommitReduction = MiCalculatePageCommitment(StartingAddress,
5258 EndingAddress,
5259 Vad,
5260 Process);
5261 Vad->u.VadFlags.CommitCharge -= CommitReduction;
5262 // For ReactOS: shrink the corresponding memory area
5263 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5264 ASSERT(Vad->StartingVpn << PAGE_SHIFT == (ULONG_PTR)MemoryArea->StartingAddress);
5265 ASSERT((Vad->EndingVpn + 1) << PAGE_SHIFT == (ULONG_PTR)MemoryArea->EndingAddress);
5266 Vad->EndingVpn = ((ULONG_PTR)StartingAddress - 1) >> PAGE_SHIFT;
5267 MemoryArea->EndingAddress = (PVOID)(((Vad->EndingVpn + 1) << PAGE_SHIFT) - 1);
5268 }
5269 else
5270 {
5271 //
5272 // This is case B and the hardest one. Because we are removing
5273 // a chunk of memory from the very middle of the VAD, we must
5274 // actually split the VAD into two new VADs and compute the
5275 // commit charges for each of them, and reinsert new charges.
5276 //
5277 // NOT YET IMPLEMENTED IN ARM3.
5278 //
5279 DPRINT1("Case B not handled\n");
5280 Status = STATUS_FREE_VM_NOT_AT_BASE;
5281 goto FailPath;
5282 }
5283
5284 //
5285 // After analyzing the VAD, set it to NULL so that we don't
5286 // free it in the exit path
5287 //
5288 Vad = NULL;
5289 }
5290 }
5291
5292 //
5293 // Now we have a range of pages to dereference, so call the right API
5294 // to do that and then release the working set, since we're done messing
5295 // around with process pages.
5296 //
5297 MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL);
5298 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5299 Status = STATUS_SUCCESS;
5300
5301 FinalPath:
5302 //
5303 // Update the process counters
5304 //
5305 PRegionSize = EndingAddress - StartingAddress + 1;
5306 Process->CommitCharge -= CommitReduction;
5307 if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize;
5308
5309 //
5310 // Unlock the address space and free the VAD in failure cases. Next,
5311 // detach from the target process so we can write the region size and the
5312 // base address to the correct source process, and dereference the target
5313 // process.
5314 //
5315 MmUnlockAddressSpace(AddressSpace);
5316 if (Vad) ExFreePool(Vad);
5317 if (Attached) KeUnstackDetachProcess(&ApcState);
5318 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5319
5320 //
5321 // Use SEH to safely return the region size and the base address of the
5322 // deallocation. If we get an access violation, don't return a failure code
5323 // as the deallocation *has* happened. The caller will just have to figure
5324 // out another way to find out where it is (such as VirtualQuery).
5325 //
5326 _SEH2_TRY
5327 {
5328 *URegionSize = PRegionSize;
5329 *UBaseAddress = (PVOID)StartingAddress;
5330 }
5331 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5332 {
5333 }
5334 _SEH2_END;
5335 return Status;
5336 }
5337
5338 //
5339 // This is the decommit path. You cannot decommit from the following VADs in
5340 // Windows, so fail the vall
5341 //
5342 if ((Vad->u.VadFlags.VadType == VadAwe) ||
5343 (Vad->u.VadFlags.VadType == VadLargePages) ||
5344 (Vad->u.VadFlags.VadType == VadRotatePhysical))
5345 {
5346 DPRINT1("Trying to decommit from invalid VAD\n");
5347 Status = STATUS_MEMORY_NOT_ALLOCATED;
5348 goto FailPath;
5349 }
5350
5351 //
5352 // If the caller did not specify a region size, first make sure that this
5353 // region is actually committed. If it is, then compute the ending address
5354 // based on the VAD.
5355 //
5356 if (!PRegionSize)
5357 {
5358 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5359 {
5360 DPRINT1("Decomitting non-committed memory\n");
5361 Status = STATUS_FREE_VM_NOT_AT_BASE;
5362 goto FailPath;
5363 }
5364 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5365 }
5366
5367 //
5368 // Decommit the PTEs for the range plus the actual backing pages for the
5369 // range, then reduce that amount from the commit charge in the VAD
5370 //
5371 CommitReduction = MiAddressToPte(EndingAddress) -
5372 MiAddressToPte(StartingAddress) +
5373 1 -
5374 MiDecommitPages((PVOID)StartingAddress,
5375 MiAddressToPte(EndingAddress),
5376 Process,
5377 Vad);
5378 ASSERT(CommitReduction >= 0);
5379 Vad->u.VadFlags.CommitCharge -= CommitReduction;
5380 ASSERT(Vad->u.VadFlags.CommitCharge >= 0);
5381
5382 //
5383 // We are done, go to the exit path without freeing the VAD as it remains
5384 // valid since we have not released the allocation.
5385 //
5386 Vad = NULL;
5387 Status = STATUS_SUCCESS;
5388 goto FinalPath;
5389
5390 //
5391 // In the failure path, we detach and derefernece the target process, and
5392 // return whatever failure code was sent.
5393 //
5394 FailPath:
5395 MmUnlockAddressSpace(AddressSpace);
5396 if (Attached) KeUnstackDetachProcess(&ApcState);
5397 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5398 return Status;
5399 }
5400
5401
5402 PHYSICAL_ADDRESS
5403 NTAPI
5404 MmGetPhysicalAddress(PVOID Address)
5405 {
5406 PHYSICAL_ADDRESS PhysicalAddress;
5407 MMPDE TempPde;
5408 MMPTE TempPte;
5409
5410 /* Check if the PXE/PPE/PDE is valid */
5411 if (
5412 #if (_MI_PAGING_LEVELS == 4)
5413 (MiAddressToPxe(Address)->u.Hard.Valid) &&
5414 #endif
5415 #if (_MI_PAGING_LEVELS >= 3)
5416 (MiAddressToPpe(Address)->u.Hard.Valid) &&
5417 #endif
5418 (MiAddressToPde(Address)->u.Hard.Valid))
5419 {
5420 /* Check for large pages */
5421 TempPde = *MiAddressToPde(Address);
5422 if (TempPde.u.Hard.LargePage)
5423 {
5424 /* Physical address is base page + large page offset */
5425 PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT;
5426 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1));
5427 return PhysicalAddress;
5428 }
5429
5430 /* Check if the PTE is valid */
5431 TempPte = *MiAddressToPte(Address);
5432 if (TempPte.u.Hard.Valid)
5433 {
5434 /* Physical address is base page + page offset */
5435 PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
5436 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
5437 return PhysicalAddress;
5438 }
5439 }
5440
5441 KeRosDumpStackFrames(NULL, 20);
5442 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
5443 PhysicalAddress.QuadPart = 0;
5444 return PhysicalAddress;
5445 }
5446
5447
5448 /* EOF */