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