[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / virtual.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/virtual.c
5 * PURPOSE: ARM Memory Manager Virtual Memory Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10 /* So long, and Thanks for All the Fish */
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
18
19 #define MI_MAPPED_COPY_PAGES 14
20 #define MI_POOL_COPY_BYTES 512
21 #define MI_MAX_TRANSFER_SIZE 64 * 1024
22
23 NTSTATUS NTAPI
24 MiProtectVirtualMemory(IN PEPROCESS Process,
25 IN OUT PVOID *BaseAddress,
26 IN OUT PSIZE_T NumberOfBytesToProtect,
27 IN ULONG NewAccessProtection,
28 OUT PULONG OldAccessProtection OPTIONAL);
29
30 VOID
31 NTAPI
32 MiFlushTbAndCapture(IN PMMVAD FoundVad,
33 IN PMMPTE PointerPte,
34 IN ULONG ProtectionMask,
35 IN PMMPFN Pfn1,
36 IN BOOLEAN CaptureDirtyBit);
37
38
39 /* PRIVATE FUNCTIONS **********************************************************/
40
41 ULONG
42 NTAPI
43 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress,
44 IN ULONG_PTR EndingAddress,
45 IN PMMVAD Vad,
46 IN PEPROCESS Process)
47 {
48 PMMPTE PointerPte, LastPte, PointerPde;
49 ULONG CommittedPages;
50
51 /* Compute starting and ending PTE and PDE addresses */
52 PointerPde = MiAddressToPde(StartingAddress);
53 PointerPte = MiAddressToPte(StartingAddress);
54 LastPte = MiAddressToPte(EndingAddress);
55
56 /* Handle commited pages first */
57 if (Vad->u.VadFlags.MemCommit == 1)
58 {
59 /* This is a committed VAD, so Assume the whole range is committed */
60 CommittedPages = BYTES_TO_PAGES(EndingAddress - StartingAddress);
61
62 /* Is the PDE demand-zero? */
63 PointerPde = MiAddressToPte(PointerPte);
64 if (PointerPde->u.Long != 0)
65 {
66 /* It is not. Is it valid? */
67 if (PointerPde->u.Hard.Valid == 0)
68 {
69 /* Fault it in */
70 PointerPte = MiPteToAddress(PointerPde);
71 MiMakeSystemAddressValid(PointerPte, Process);
72 }
73 }
74 else
75 {
76 /* It is, skip it and move to the next PDE, unless we're done */
77 PointerPde++;
78 PointerPte = MiPteToAddress(PointerPde);
79 if (PointerPte > LastPte) return CommittedPages;
80 }
81
82 /* Now loop all the PTEs in the range */
83 while (PointerPte <= LastPte)
84 {
85 /* Have we crossed a PDE boundary? */
86 if (MiIsPteOnPdeBoundary(PointerPte))
87 {
88 /* Is this PDE demand zero? */
89 PointerPde = MiAddressToPte(PointerPte);
90 if (PointerPde->u.Long != 0)
91 {
92 /* It isn't -- is it valid? */
93 if (PointerPde->u.Hard.Valid == 0)
94 {
95 /* Nope, fault it in */
96 PointerPte = MiPteToAddress(PointerPde);
97 MiMakeSystemAddressValid(PointerPte, Process);
98 }
99 }
100 else
101 {
102 /* It is, skip it and move to the next PDE */
103 PointerPde++;
104 PointerPte = MiPteToAddress(PointerPde);
105 continue;
106 }
107 }
108
109 /* Is this PTE demand zero? */
110 if (PointerPte->u.Long != 0)
111 {
112 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
113 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
114 (PointerPte->u.Hard.Valid == 0) &&
115 ((PointerPte->u.Soft.Prototype == 0) ||
116 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
117 {
118 /* It is, so remove it from the count of commited pages */
119 CommittedPages--;
120 }
121 }
122
123 /* Move to the next PTE */
124 PointerPte++;
125 }
126
127 /* Return how many committed pages there still are */
128 return CommittedPages;
129 }
130
131 /* This is a non-commited VAD, so assume none of it is committed */
132 CommittedPages = 0;
133
134 /* Is the PDE demand-zero? */
135 PointerPde = MiAddressToPte(PointerPte);
136 if (PointerPde->u.Long != 0)
137 {
138 /* It isn't -- is it invalid? */
139 if (PointerPde->u.Hard.Valid == 0)
140 {
141 /* It is, so page it in */
142 PointerPte = MiPteToAddress(PointerPde);
143 MiMakeSystemAddressValid(PointerPte, Process);
144 }
145 }
146 else
147 {
148 /* It is, so skip it and move to the next PDE */
149 PointerPde++;
150 PointerPte = MiPteToAddress(PointerPde);
151 if (PointerPte > LastPte) return CommittedPages;
152 }
153
154 /* Loop all the PTEs in this PDE */
155 while (PointerPte <= LastPte)
156 {
157 /* Have we crossed a PDE boundary? */
158 if (MiIsPteOnPdeBoundary(PointerPte))
159 {
160 /* Is this new PDE demand-zero? */
161 PointerPde = MiAddressToPte(PointerPte);
162 if (PointerPde->u.Long != 0)
163 {
164 /* It isn't. Is it valid? */
165 if (PointerPde->u.Hard.Valid == 0)
166 {
167 /* It isn't, so make it valid */
168 PointerPte = MiPteToAddress(PointerPde);
169 MiMakeSystemAddressValid(PointerPte, Process);
170 }
171 }
172 else
173 {
174 /* It is, so skip it and move to the next one */
175 PointerPde++;
176 PointerPte = MiPteToAddress(PointerPde);
177 continue;
178 }
179 }
180
181 /* Is this PTE demand-zero? */
182 if (PointerPte->u.Long != 0)
183 {
184 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */
185 if ((PointerPte->u.Soft.Protection != MM_DECOMMIT) ||
186 (PointerPte->u.Hard.Valid == 1) ||
187 ((PointerPte->u.Soft.Prototype == 1) &&
188 (PointerPte->u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)))
189 {
190 /* It is! So we'll treat this as a committed page */
191 CommittedPages++;
192 }
193 }
194
195 /* Move to the next PTE */
196 PointerPte++;
197 }
198
199 /* Return how many committed pages we found in this VAD */
200 return CommittedPages;
201 }
202
203 ULONG
204 NTAPI
205 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
206 IN PEPROCESS CurrentProcess)
207 {
208 NTSTATUS Status;
209 BOOLEAN WsShared = FALSE, WsSafe = FALSE, LockChange = FALSE;
210 PETHREAD CurrentThread = PsGetCurrentThread();
211
212 /* Must be a non-pool page table, since those are double-mapped already */
213 ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
214 ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
215 (PageTableVirtualAddress > MmPagedPoolEnd));
216
217 /* Working set lock or PFN lock should be held */
218 ASSERT(KeAreAllApcsDisabled() == TRUE);
219
220 /* Check if the page table is valid */
221 while (!MmIsAddressValid(PageTableVirtualAddress))
222 {
223 /* Release the working set lock */
224 MiUnlockProcessWorkingSetForFault(CurrentProcess,
225 CurrentThread,
226 &WsSafe,
227 &WsShared);
228
229 /* Fault it in */
230 Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
231 if (!NT_SUCCESS(Status))
232 {
233 /* This should not fail */
234 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
235 1,
236 Status,
237 (ULONG_PTR)CurrentProcess,
238 (ULONG_PTR)PageTableVirtualAddress);
239 }
240
241 /* Lock the working set again */
242 MiLockProcessWorkingSetForFault(CurrentProcess,
243 CurrentThread,
244 WsSafe,
245 WsShared);
246
247 /* This flag will be useful later when we do better locking */
248 LockChange = TRUE;
249 }
250
251 /* Let caller know what the lock state is */
252 return LockChange;
253 }
254
255 ULONG
256 NTAPI
257 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
258 IN KIRQL OldIrql)
259 {
260 NTSTATUS Status;
261 BOOLEAN LockChange = FALSE;
262
263 /* Must be e kernel address */
264 ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS);
265
266 /* Check if the page is valid */
267 while (!MmIsAddressValid(VirtualAddress))
268 {
269 /* Release the PFN database */
270 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
271
272 /* Fault it in */
273 Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL);
274 if (!NT_SUCCESS(Status))
275 {
276 /* This should not fail */
277 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
278 3,
279 Status,
280 0,
281 (ULONG_PTR)VirtualAddress);
282 }
283
284 /* This flag will be useful later when we do better locking */
285 LockChange = TRUE;
286
287 /* Lock the PFN database */
288 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
289 }
290
291 /* Let caller know what the lock state is */
292 return LockChange;
293 }
294
295 PFN_COUNT
296 NTAPI
297 MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
298 IN PFN_NUMBER PageCount,
299 IN ULONG Flags,
300 OUT PPFN_NUMBER ValidPages)
301 {
302 PFN_COUNT ActualPages = 0;
303 PETHREAD CurrentThread = PsGetCurrentThread();
304 PMMPFN Pfn1, Pfn2;
305 PFN_NUMBER PageFrameIndex, PageTableIndex;
306 KIRQL OldIrql;
307 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
308
309 /* Lock the system working set */
310 MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
311
312 /* Loop all pages */
313 while (PageCount)
314 {
315 /* Make sure there's some data about the page */
316 if (PointerPte->u.Long)
317 {
318 /* As always, only handle current ARM3 scenarios */
319 ASSERT(PointerPte->u.Soft.Prototype == 0);
320 ASSERT(PointerPte->u.Soft.Transition == 0);
321
322 /* Normally this is one possibility -- freeing a valid page */
323 if (PointerPte->u.Hard.Valid)
324 {
325 /* Get the page PFN */
326 PageFrameIndex = PFN_FROM_PTE(PointerPte);
327 Pfn1 = MiGetPfnEntry(PageFrameIndex);
328
329 /* Should not have any working set data yet */
330 ASSERT(Pfn1->u1.WsIndex == 0);
331
332 /* Actual valid, legitimate, pages */
333 if (ValidPages) (*ValidPages)++;
334
335 /* Get the page table entry */
336 PageTableIndex = Pfn1->u4.PteFrame;
337 Pfn2 = MiGetPfnEntry(PageTableIndex);
338
339 /* Lock the PFN database */
340 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
341
342 /* Delete it the page */
343 MI_SET_PFN_DELETED(Pfn1);
344 MiDecrementShareCount(Pfn1, PageFrameIndex);
345
346 /* Decrement the page table too */
347 MiDecrementShareCount(Pfn2, PageTableIndex);
348
349 /* Release the PFN database */
350 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
351
352 /* Destroy the PTE */
353 MI_ERASE_PTE(PointerPte);
354 }
355 else
356 {
357 /*
358 * The only other ARM3 possibility is a demand zero page, which would
359 * mean freeing some of the paged pool pages that haven't even been
360 * touched yet, as part of a larger allocation.
361 *
362 * Right now, we shouldn't expect any page file information in the PTE
363 */
364 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
365
366 /* Destroy the PTE */
367 MI_ERASE_PTE(PointerPte);
368 }
369
370 /* Actual legitimate pages */
371 ActualPages++;
372 }
373
374 /* Keep going */
375 PointerPte++;
376 PageCount--;
377 }
378
379 /* Release the working set */
380 MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
381
382 /* Flush the entire TLB */
383 KeFlushEntireTb(TRUE, TRUE);
384
385 /* Done */
386 return ActualPages;
387 }
388
389 VOID
390 NTAPI
391 MiDeletePte(IN PMMPTE PointerPte,
392 IN PVOID VirtualAddress,
393 IN PEPROCESS CurrentProcess,
394 IN PMMPTE PrototypePte)
395 {
396 PMMPFN Pfn1;
397 MMPTE TempPte;
398 PFN_NUMBER PageFrameIndex;
399 PMMPDE PointerPde;
400
401 /* PFN lock must be held */
402 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
403
404 /* Capture the PTE */
405 TempPte = *PointerPte;
406
407 /* We only support valid PTEs for now */
408 ASSERT(TempPte.u.Hard.Valid == 1);
409 if (TempPte.u.Hard.Valid == 0)
410 {
411 /* Invalid PTEs not supported yet */
412 ASSERT(TempPte.u.Soft.Prototype == 0);
413 ASSERT(TempPte.u.Soft.Transition == 0);
414 }
415
416 /* Get the PFN entry */
417 PageFrameIndex = PFN_FROM_PTE(&TempPte);
418 Pfn1 = MiGetPfnEntry(PageFrameIndex);
419
420 /* Check if this is a valid, prototype PTE */
421 if (Pfn1->u3.e1.PrototypePte == 1)
422 {
423 /* Get the PDE and make sure it's faulted in */
424 PointerPde = MiPteToPde(PointerPte);
425 if (PointerPde->u.Hard.Valid == 0)
426 {
427 #if (_MI_PAGING_LEVELS == 2)
428 /* Could be paged pool access from a new process -- synchronize the page directories */
429 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
430 {
431 #endif
432 /* The PDE must be valid at this point */
433 KeBugCheckEx(MEMORY_MANAGEMENT,
434 0x61940,
435 (ULONG_PTR)PointerPte,
436 PointerPte->u.Long,
437 (ULONG_PTR)VirtualAddress);
438 }
439 #if (_MI_PAGING_LEVELS == 2)
440 }
441 #endif
442 /* Drop the share count */
443 MiDecrementShareCount(Pfn1, PageFrameIndex);
444
445 /* Either a fork, or this is the shared user data page */
446 if ((PointerPte <= MiHighestUserPte) && (PrototypePte != Pfn1->PteAddress))
447 {
448 /* If it's not the shared user page, then crash, since there's no fork() yet */
449 if ((PAGE_ALIGN(VirtualAddress) != (PVOID)USER_SHARED_DATA) ||
450 (MmHighestUserAddress <= (PVOID)USER_SHARED_DATA))
451 {
452 /* Must be some sort of memory corruption */
453 KeBugCheckEx(MEMORY_MANAGEMENT,
454 0x400,
455 (ULONG_PTR)PointerPte,
456 (ULONG_PTR)PrototypePte,
457 (ULONG_PTR)Pfn1->PteAddress);
458 }
459 }
460 }
461 else
462 {
463 /* Make sure the saved PTE address is valid */
464 if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
465 {
466 /* The PFN entry is illegal, or invalid */
467 KeBugCheckEx(MEMORY_MANAGEMENT,
468 0x401,
469 (ULONG_PTR)PointerPte,
470 PointerPte->u.Long,
471 (ULONG_PTR)Pfn1->PteAddress);
472 }
473
474 /* There should only be 1 shared reference count */
475 ASSERT(Pfn1->u2.ShareCount == 1);
476
477 /* Drop the reference on the page table. */
478 MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
479
480 /* Mark the PFN for deletion and dereference what should be the last ref */
481 MI_SET_PFN_DELETED(Pfn1);
482 MiDecrementShareCount(Pfn1, PageFrameIndex);
483
484 /* We should eventually do this */
485 //CurrentProcess->NumberOfPrivatePages--;
486 }
487
488 /* Destroy the PTE and flush the TLB */
489 MI_ERASE_PTE(PointerPte);
490 KeFlushCurrentTb();
491 }
492
493 VOID
494 NTAPI
495 MiDeleteVirtualAddresses(IN ULONG_PTR Va,
496 IN ULONG_PTR EndingAddress,
497 IN PMMVAD Vad)
498 {
499 PMMPTE PointerPte, PrototypePte, LastPrototypePte;
500 PMMPDE PointerPde;
501 MMPTE TempPte;
502 PEPROCESS CurrentProcess;
503 KIRQL OldIrql;
504 BOOLEAN AddressGap = FALSE;
505 PSUBSECTION Subsection;
506
507 /* Get out if this is a fake VAD, RosMm will free the marea pages */
508 if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
509
510 /* Grab the process and PTE/PDE for the address being deleted */
511 CurrentProcess = PsGetCurrentProcess();
512 PointerPde = MiAddressToPde(Va);
513 PointerPte = MiAddressToPte(Va);
514
515 /* Check if this is a section VAD or a VM VAD */
516 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
517 {
518 /* Don't worry about prototypes */
519 PrototypePte = LastPrototypePte = NULL;
520 }
521 else
522 {
523 /* Get the prototype PTE */
524 PrototypePte = Vad->FirstPrototypePte;
525 LastPrototypePte = Vad->FirstPrototypePte + 1;
526 }
527
528 /* In all cases, we don't support fork() yet */
529 ASSERT(CurrentProcess->CloneRoot == NULL);
530
531 /* Loop the PTE for each VA */
532 while (TRUE)
533 {
534 /* First keep going until we find a valid PDE */
535 while (!PointerPde->u.Long)
536 {
537 /* There are gaps in the address space */
538 AddressGap = TRUE;
539
540 /* Still no valid PDE, try the next 4MB (or whatever) */
541 PointerPde++;
542
543 /* Update the PTE on this new boundary */
544 PointerPte = MiPteToAddress(PointerPde);
545
546 /* Check if all the PDEs are invalid, so there's nothing to free */
547 Va = (ULONG_PTR)MiPteToAddress(PointerPte);
548 if (Va > EndingAddress) return;
549 }
550
551 /* Now check if the PDE is mapped in */
552 if (!PointerPde->u.Hard.Valid)
553 {
554 /* It isn't, so map it in */
555 PointerPte = MiPteToAddress(PointerPde);
556 MiMakeSystemAddressValid(PointerPte, CurrentProcess);
557 }
558
559 /* Now we should have a valid PDE, mapped in, and still have some VA */
560 ASSERT(PointerPde->u.Hard.Valid == 1);
561 ASSERT(Va <= EndingAddress);
562
563 /* Check if this is a section VAD with gaps in it */
564 if ((AddressGap) && (LastPrototypePte))
565 {
566 /* We need to skip to the next correct prototype PTE */
567 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
568
569 /* And we need the subsection to skip to the next last prototype PTE */
570 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
571 if (Subsection)
572 {
573 /* Found it! */
574 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
575 }
576 else
577 {
578 /* No more subsections, we are done with prototype PTEs */
579 PrototypePte = NULL;
580 }
581 }
582
583 /* Lock the PFN Database while we delete the PTEs */
584 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
585 do
586 {
587 /* Capture the PDE and make sure it exists */
588 TempPte = *PointerPte;
589 if (TempPte.u.Long)
590 {
591 MiDecrementPageTableReferences((PVOID)Va);
592
593 /* Check if the PTE is actually mapped in */
594 if (MI_IS_MAPPED_PTE(&TempPte))
595 {
596 /* Are we dealing with section VAD? */
597 if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
598 {
599 /* We need to skip to the next correct prototype PTE */
600 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
601
602 /* And we need the subsection to skip to the next last prototype PTE */
603 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
604 if (Subsection)
605 {
606 /* Found it! */
607 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
608 }
609 else
610 {
611 /* No more subsections, we are done with prototype PTEs */
612 PrototypePte = NULL;
613 }
614 }
615
616 /* Check for prototype PTE */
617 if ((TempPte.u.Hard.Valid == 0) &&
618 (TempPte.u.Soft.Prototype == 1))
619 {
620 /* Just nuke it */
621 MI_ERASE_PTE(PointerPte);
622 }
623 else
624 {
625 /* Delete the PTE proper */
626 MiDeletePte(PointerPte,
627 (PVOID)Va,
628 CurrentProcess,
629 PrototypePte);
630 }
631 }
632 else
633 {
634 /* The PTE was never mapped, just nuke it here */
635 MI_ERASE_PTE(PointerPte);
636 }
637 }
638
639 /* Update the address and PTE for it */
640 Va += PAGE_SIZE;
641 PointerPte++;
642 PrototypePte++;
643
644 /* Making sure the PDE is still valid */
645 ASSERT(PointerPde->u.Hard.Valid == 1);
646 }
647 while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
648
649 /* The PDE should still be valid at this point */
650 ASSERT(PointerPde->u.Hard.Valid == 1);
651
652 /* Check remaining PTE count (go back 1 page due to above loop) */
653 if (MiQueryPageTableReferences((PVOID)(Va - PAGE_SIZE)) == 0)
654 {
655 if (PointerPde->u.Long != 0)
656 {
657 /* Delete the PTE proper */
658 MiDeletePte(PointerPde,
659 MiPteToAddress(PointerPde),
660 CurrentProcess,
661 NULL);
662 }
663 }
664
665 /* Release the lock and get out if we're done */
666 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
667 if (Va > EndingAddress) return;
668
669 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
670 PointerPde = MiAddressToPde(Va);
671 AddressGap = FALSE;
672 }
673 }
674
675 LONG
676 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
677 OUT PBOOLEAN HaveBadAddress,
678 OUT PULONG_PTR BadAddress)
679 {
680 PEXCEPTION_RECORD ExceptionRecord;
681 PAGED_CODE();
682
683 //
684 // Assume default
685 //
686 *HaveBadAddress = FALSE;
687
688 //
689 // Get the exception record
690 //
691 ExceptionRecord = ExceptionInfo->ExceptionRecord;
692
693 //
694 // Look at the exception code
695 //
696 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
697 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
698 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
699 {
700 //
701 // We can tell the address if we have more than one parameter
702 //
703 if (ExceptionRecord->NumberParameters > 1)
704 {
705 //
706 // Return the address
707 //
708 *HaveBadAddress = TRUE;
709 *BadAddress = ExceptionRecord->ExceptionInformation[1];
710 }
711 }
712
713 //
714 // Continue executing the next handler
715 //
716 return EXCEPTION_EXECUTE_HANDLER;
717 }
718
719 NTSTATUS
720 NTAPI
721 MiDoMappedCopy(IN PEPROCESS SourceProcess,
722 IN PVOID SourceAddress,
723 IN PEPROCESS TargetProcess,
724 OUT PVOID TargetAddress,
725 IN SIZE_T BufferSize,
726 IN KPROCESSOR_MODE PreviousMode,
727 OUT PSIZE_T ReturnSize)
728 {
729 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
730 PMDL Mdl = (PMDL)MdlBuffer;
731 SIZE_T TotalSize, CurrentSize, RemainingSize;
732 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
733 volatile BOOLEAN PagesLocked;
734 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
735 volatile PVOID MdlAddress;
736 KAPC_STATE ApcState;
737 BOOLEAN HaveBadAddress;
738 ULONG_PTR BadAddress;
739 NTSTATUS Status = STATUS_SUCCESS;
740 PAGED_CODE();
741
742 //
743 // Calculate the maximum amount of data to move
744 //
745 TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
746 if (BufferSize <= TotalSize) TotalSize = BufferSize;
747 CurrentSize = TotalSize;
748 RemainingSize = BufferSize;
749
750 //
751 // Loop as long as there is still data
752 //
753 while (RemainingSize > 0)
754 {
755 //
756 // Check if this transfer will finish everything off
757 //
758 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
759
760 //
761 // Attach to the source address space
762 //
763 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
764
765 //
766 // Reset state for this pass
767 //
768 MdlAddress = NULL;
769 PagesLocked = FALSE;
770 FailedInMoving = FALSE;
771 ASSERT(FailedInProbe == FALSE);
772
773 //
774 // Protect user-mode copy
775 //
776 _SEH2_TRY
777 {
778 //
779 // If this is our first time, probe the buffer
780 //
781 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
782 {
783 //
784 // Catch a failure here
785 //
786 FailedInProbe = TRUE;
787
788 //
789 // Do the probe
790 //
791 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
792
793 //
794 // Passed
795 //
796 FailedInProbe = FALSE;
797 }
798
799 //
800 // Initialize and probe and lock the MDL
801 //
802 MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
803 MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
804 PagesLocked = TRUE;
805
806 //
807 // Now map the pages
808 //
809 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
810 KernelMode,
811 MmCached,
812 NULL,
813 FALSE,
814 HighPagePriority);
815 if (!MdlAddress)
816 {
817 //
818 // Use our SEH handler to pick this up
819 //
820 FailedInMapping = TRUE;
821 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
822 }
823
824 //
825 // Now let go of the source and grab to the target process
826 //
827 KeUnstackDetachProcess(&ApcState);
828 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
829
830 //
831 // Check if this is our first time through
832 //
833 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
834 {
835 //
836 // Catch a failure here
837 //
838 FailedInProbe = TRUE;
839
840 //
841 // Do the probe
842 //
843 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
844
845 //
846 // Passed
847 //
848 FailedInProbe = FALSE;
849 }
850
851 //
852 // Now do the actual move
853 //
854 FailedInMoving = TRUE;
855 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
856 }
857 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
858 &HaveBadAddress,
859 &BadAddress))
860 {
861 //
862 // Detach from whoever we may be attached to
863 //
864 KeUnstackDetachProcess(&ApcState);
865
866 //
867 // Check if we had mapped the pages
868 //
869 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
870
871 //
872 // Check if we had locked the pages
873 //
874 if (PagesLocked) MmUnlockPages(Mdl);
875
876 //
877 // Check if we hit working set quota
878 //
879 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA)
880 {
881 //
882 // Return the error
883 //
884 _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA);
885 }
886
887 //
888 // Check if we failed during the probe or mapping
889 //
890 if ((FailedInProbe) || (FailedInMapping))
891 {
892 //
893 // Exit
894 //
895 Status = _SEH2_GetExceptionCode();
896 _SEH2_YIELD(return Status);
897 }
898
899 //
900 // Otherwise, we failed probably during the move
901 //
902 *ReturnSize = BufferSize - RemainingSize;
903 if (FailedInMoving)
904 {
905 //
906 // Check if we know exactly where we stopped copying
907 //
908 if (HaveBadAddress)
909 {
910 //
911 // Return the exact number of bytes copied
912 //
913 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
914 }
915 }
916
917 //
918 // Return partial copy
919 //
920 Status = STATUS_PARTIAL_COPY;
921 }
922 _SEH2_END;
923
924 //
925 // Check for SEH status
926 //
927 if (Status != STATUS_SUCCESS) return Status;
928
929 //
930 // Detach from target
931 //
932 KeUnstackDetachProcess(&ApcState);
933
934 //
935 // Unmap and unlock
936 //
937 MmUnmapLockedPages(MdlAddress, Mdl);
938 MmUnlockPages(Mdl);
939
940 //
941 // Update location and size
942 //
943 RemainingSize -= CurrentSize;
944 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
945 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
946 }
947
948 //
949 // All bytes read
950 //
951 *ReturnSize = BufferSize;
952 return STATUS_SUCCESS;
953 }
954
955 NTSTATUS
956 NTAPI
957 MiDoPoolCopy(IN PEPROCESS SourceProcess,
958 IN PVOID SourceAddress,
959 IN PEPROCESS TargetProcess,
960 OUT PVOID TargetAddress,
961 IN SIZE_T BufferSize,
962 IN KPROCESSOR_MODE PreviousMode,
963 OUT PSIZE_T ReturnSize)
964 {
965 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
966 SIZE_T TotalSize, CurrentSize, RemainingSize;
967 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
968 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
969 PVOID PoolAddress;
970 KAPC_STATE ApcState;
971 BOOLEAN HaveBadAddress;
972 ULONG_PTR BadAddress;
973 NTSTATUS Status = STATUS_SUCCESS;
974 PAGED_CODE();
975
976 //
977 // Calculate the maximum amount of data to move
978 //
979 TotalSize = MI_MAX_TRANSFER_SIZE;
980 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
981 CurrentSize = TotalSize;
982 RemainingSize = BufferSize;
983
984 //
985 // Check if we can use the stack
986 //
987 if (BufferSize <= MI_POOL_COPY_BYTES)
988 {
989 //
990 // Use it
991 //
992 PoolAddress = (PVOID)StackBuffer;
993 }
994 else
995 {
996 //
997 // Allocate pool
998 //
999 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
1000 if (!PoolAddress) ASSERT(FALSE);
1001 HavePoolAddress = TRUE;
1002 }
1003
1004 //
1005 // Loop as long as there is still data
1006 //
1007 while (RemainingSize > 0)
1008 {
1009 //
1010 // Check if this transfer will finish everything off
1011 //
1012 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
1013
1014 //
1015 // Attach to the source address space
1016 //
1017 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
1018
1019 //
1020 // Reset state for this pass
1021 //
1022 FailedInMoving = FALSE;
1023 ASSERT(FailedInProbe == FALSE);
1024
1025 //
1026 // Protect user-mode copy
1027 //
1028 _SEH2_TRY
1029 {
1030 //
1031 // If this is our first time, probe the buffer
1032 //
1033 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
1034 {
1035 //
1036 // Catch a failure here
1037 //
1038 FailedInProbe = TRUE;
1039
1040 //
1041 // Do the probe
1042 //
1043 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
1044
1045 //
1046 // Passed
1047 //
1048 FailedInProbe = FALSE;
1049 }
1050
1051 //
1052 // Do the copy
1053 //
1054 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
1055
1056 //
1057 // Now let go of the source and grab to the target process
1058 //
1059 KeUnstackDetachProcess(&ApcState);
1060 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1061
1062 //
1063 // Check if this is our first time through
1064 //
1065 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
1066 {
1067 //
1068 // Catch a failure here
1069 //
1070 FailedInProbe = TRUE;
1071
1072 //
1073 // Do the probe
1074 //
1075 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
1076
1077 //
1078 // Passed
1079 //
1080 FailedInProbe = FALSE;
1081 }
1082
1083 //
1084 // Now do the actual move
1085 //
1086 FailedInMoving = TRUE;
1087 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
1088 }
1089 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
1090 &HaveBadAddress,
1091 &BadAddress))
1092 {
1093 //
1094 // Detach from whoever we may be attached to
1095 //
1096 KeUnstackDetachProcess(&ApcState);
1097
1098 //
1099 // Check if we had allocated pool
1100 //
1101 if (HavePoolAddress) ExFreePoolWithTag(PoolAddress, 'VmRw');
1102
1103 //
1104 // Check if we failed during the probe
1105 //
1106 if (FailedInProbe)
1107 {
1108 //
1109 // Exit
1110 //
1111 Status = _SEH2_GetExceptionCode();
1112 _SEH2_YIELD(return Status);
1113 }
1114
1115 //
1116 // Otherwise, we failed, probably during the move
1117 //
1118 *ReturnSize = BufferSize - RemainingSize;
1119 if (FailedInMoving)
1120 {
1121 //
1122 // Check if we know exactly where we stopped copying
1123 //
1124 if (HaveBadAddress)
1125 {
1126 //
1127 // Return the exact number of bytes copied
1128 //
1129 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
1130 }
1131 }
1132
1133 //
1134 // Return partial copy
1135 //
1136 Status = STATUS_PARTIAL_COPY;
1137 }
1138 _SEH2_END;
1139
1140 //
1141 // Check for SEH status
1142 //
1143 if (Status != STATUS_SUCCESS) return Status;
1144
1145 //
1146 // Detach from target
1147 //
1148 KeUnstackDetachProcess(&ApcState);
1149
1150 //
1151 // Update location and size
1152 //
1153 RemainingSize -= CurrentSize;
1154 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
1155 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
1156 CurrentSize);
1157 }
1158
1159 //
1160 // Check if we had allocated pool
1161 //
1162 if (HavePoolAddress) ExFreePoolWithTag(PoolAddress, 'VmRw');
1163
1164 //
1165 // All bytes read
1166 //
1167 *ReturnSize = BufferSize;
1168 return STATUS_SUCCESS;
1169 }
1170
1171 NTSTATUS
1172 NTAPI
1173 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
1174 IN PVOID SourceAddress,
1175 IN PEPROCESS TargetProcess,
1176 OUT PVOID TargetAddress,
1177 IN SIZE_T BufferSize,
1178 IN KPROCESSOR_MODE PreviousMode,
1179 OUT PSIZE_T ReturnSize)
1180 {
1181 NTSTATUS Status;
1182 PEPROCESS Process = SourceProcess;
1183
1184 //
1185 // Don't accept zero-sized buffers
1186 //
1187 if (!BufferSize) return STATUS_SUCCESS;
1188
1189 //
1190 // If we are copying from ourselves, lock the target instead
1191 //
1192 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
1193
1194 //
1195 // Acquire rundown protection
1196 //
1197 if (!ExAcquireRundownProtection(&Process->RundownProtect))
1198 {
1199 //
1200 // Fail
1201 //
1202 return STATUS_PROCESS_IS_TERMINATING;
1203 }
1204
1205 //
1206 // See if we should use the pool copy
1207 //
1208 if (BufferSize > MI_POOL_COPY_BYTES)
1209 {
1210 //
1211 // Use MDL-copy
1212 //
1213 Status = MiDoMappedCopy(SourceProcess,
1214 SourceAddress,
1215 TargetProcess,
1216 TargetAddress,
1217 BufferSize,
1218 PreviousMode,
1219 ReturnSize);
1220 }
1221 else
1222 {
1223 //
1224 // Do pool copy
1225 //
1226 Status = MiDoPoolCopy(SourceProcess,
1227 SourceAddress,
1228 TargetProcess,
1229 TargetAddress,
1230 BufferSize,
1231 PreviousMode,
1232 ReturnSize);
1233 }
1234
1235 //
1236 // Release the lock
1237 //
1238 ExReleaseRundownProtection(&Process->RundownProtect);
1239 return Status;
1240 }
1241
1242 NTSTATUS
1243 NTAPI
1244 MmFlushVirtualMemory(IN PEPROCESS Process,
1245 IN OUT PVOID *BaseAddress,
1246 IN OUT PSIZE_T RegionSize,
1247 OUT PIO_STATUS_BLOCK IoStatusBlock)
1248 {
1249 PAGED_CODE();
1250 UNIMPLEMENTED;
1251
1252 //
1253 // Fake success
1254 //
1255 return STATUS_SUCCESS;
1256 }
1257
1258 ULONG
1259 NTAPI
1260 MiGetPageProtection(IN PMMPTE PointerPte)
1261 {
1262 MMPTE TempPte;
1263 PMMPFN Pfn;
1264 PAGED_CODE();
1265
1266 /* Copy this PTE's contents */
1267 TempPte = *PointerPte;
1268
1269 /* Assure it's not totally zero */
1270 ASSERT(TempPte.u.Long);
1271
1272 /* Check for a special prototype format */
1273 if (TempPte.u.Soft.Valid == 0 &&
1274 TempPte.u.Soft.Prototype == 1)
1275 {
1276 /* Unsupported now */
1277 UNIMPLEMENTED;
1278 ASSERT(FALSE);
1279 }
1280
1281 /* In the easy case of transition or demand zero PTE just return its protection */
1282 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1283
1284 /* If we get here, the PTE is valid, so look up the page in PFN database */
1285 Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1286 if (!Pfn->u3.e1.PrototypePte)
1287 {
1288 /* Return protection of the original pte */
1289 ASSERT(Pfn->u4.AweAllocation == 0);
1290 return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1291 }
1292
1293 /* This is software PTE */
1294 DPRINT1("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
1295 DPRINT1("VA: %p\n", MiPteToAddress(&TempPte));
1296 DPRINT1("Mask: %lx\n", TempPte.u.Soft.Protection);
1297 DPRINT1("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
1298 return MmProtectToValue[TempPte.u.Soft.Protection];
1299 }
1300
1301 ULONG
1302 NTAPI
1303 MiQueryAddressState(IN PVOID Va,
1304 IN PMMVAD Vad,
1305 IN PEPROCESS TargetProcess,
1306 OUT PULONG ReturnedProtect,
1307 OUT PVOID *NextVa)
1308 {
1309
1310 PMMPTE PointerPte, ProtoPte;
1311 PMMPDE PointerPde;
1312 MMPTE TempPte, TempProtoPte;
1313 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1314 ULONG State = MEM_RESERVE, Protect = 0;
1315 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1316 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1317
1318 /* Only normal VADs supported */
1319 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1320
1321 /* Get the PDE and PTE for the address */
1322 PointerPde = MiAddressToPde(Va);
1323 PointerPte = MiAddressToPte(Va);
1324
1325 /* Return the next range */
1326 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1327
1328 /* Is the PDE demand-zero? */
1329 if (PointerPde->u.Long != 0)
1330 {
1331 /* It is not. Is it valid? */
1332 if (PointerPde->u.Hard.Valid == 0)
1333 {
1334 /* Is isn't, fault it in */
1335 PointerPte = MiPteToAddress(PointerPde);
1336 MiMakeSystemAddressValid(PointerPte, TargetProcess);
1337 ValidPte = TRUE;
1338 }
1339 }
1340 else
1341 {
1342 /* It is, skip it and move to the next PDE */
1343 *NextVa = MiPdeToAddress(PointerPde + 1);
1344 }
1345
1346 /* Is it safe to try reading the PTE? */
1347 if (ValidPte)
1348 {
1349 /* FIXME: watch out for large pages */
1350 ASSERT(PointerPde->u.Hard.LargePage == FALSE);
1351
1352 /* Capture the PTE */
1353 TempPte = *PointerPte;
1354 if (TempPte.u.Long != 0)
1355 {
1356 /* The PTE is valid, so it's not zeroed out */
1357 DemandZeroPte = FALSE;
1358
1359 /* Is it a decommited, invalid, or faulted PTE? */
1360 if ((TempPte.u.Soft.Protection == MM_DECOMMIT) &&
1361 (TempPte.u.Hard.Valid == 0) &&
1362 ((TempPte.u.Soft.Prototype == 0) ||
1363 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1364 {
1365 /* Otherwise our defaults should hold */
1366 ASSERT(Protect == 0);
1367 ASSERT(State == MEM_RESERVE);
1368 }
1369 else
1370 {
1371 /* This means it's committed */
1372 State = MEM_COMMIT;
1373
1374 /* We don't support these */
1375 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1376 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
1377 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1378
1379 /* Get protection state of this page */
1380 Protect = MiGetPageProtection(PointerPte);
1381
1382 /* Check if this is an image-backed VAD */
1383 if ((TempPte.u.Soft.Valid == 0) &&
1384 (TempPte.u.Soft.Prototype == 1) &&
1385 (Vad->u.VadFlags.PrivateMemory == 0) &&
1386 (Vad->ControlArea))
1387 {
1388 DPRINT1("Not supported\n");
1389 ASSERT(FALSE);
1390 }
1391 }
1392 }
1393 }
1394
1395 /* Check if this was a demand-zero PTE, since we need to find the state */
1396 if (DemandZeroPte)
1397 {
1398 /* Not yet handled */
1399 ASSERT(Vad->u.VadFlags.VadType != VadDevicePhysicalMemory);
1400 ASSERT(Vad->u.VadFlags.VadType != VadAwe);
1401
1402 /* Check if this is private commited memory, or an section-backed VAD */
1403 if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea))
1404 {
1405 /* Tell caller about the next range */
1406 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1407
1408 /* Get the prototype PTE for this VAD */
1409 ProtoPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad,
1410 (ULONG_PTR)Va >> PAGE_SHIFT);
1411 if (ProtoPte)
1412 {
1413 /* We should unlock the working set, but it's not being held! */
1414
1415 /* Is the prototype PTE actually valid (committed)? */
1416 TempProtoPte = *ProtoPte;
1417 if (TempProtoPte.u.Long)
1418 {
1419 /* Unless this is a memory-mapped file, handle it like private VAD */
1420 State = MEM_COMMIT;
1421 ASSERT(Vad->u.VadFlags.VadType != VadImageMap);
1422 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1423 }
1424
1425 /* We should re-lock the working set */
1426 }
1427 }
1428 else if (Vad->u.VadFlags.MemCommit)
1429 {
1430 /* This is committed memory */
1431 State = MEM_COMMIT;
1432
1433 /* Convert the protection */
1434 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1435 }
1436 }
1437
1438 /* Return the protection code */
1439 *ReturnedProtect = Protect;
1440 return State;
1441 }
1442
1443 NTSTATUS
1444 NTAPI
1445 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
1446 IN PVOID BaseAddress,
1447 OUT PVOID MemoryInformation,
1448 IN SIZE_T MemoryInformationLength,
1449 OUT PSIZE_T ReturnLength)
1450 {
1451 PEPROCESS TargetProcess;
1452 NTSTATUS Status = STATUS_SUCCESS;
1453 PMMVAD Vad = NULL;
1454 PVOID Address, NextAddress;
1455 BOOLEAN Found = FALSE;
1456 ULONG NewProtect, NewState;
1457 ULONG_PTR BaseVpn;
1458 MEMORY_BASIC_INFORMATION MemoryInfo;
1459 KAPC_STATE ApcState;
1460 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1461 PMEMORY_AREA MemoryArea;
1462 SIZE_T ResultLength;
1463
1464 /* Check for illegal addresses in user-space, or the shared memory area */
1465 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
1466 (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA))
1467 {
1468 Address = PAGE_ALIGN(BaseAddress);
1469
1470 /* Make up an info structure describing this range */
1471 MemoryInfo.BaseAddress = Address;
1472 MemoryInfo.AllocationProtect = PAGE_READONLY;
1473 MemoryInfo.Type = MEM_PRIVATE;
1474
1475 /* Special case for shared data */
1476 if (Address == (PVOID)MM_SHARED_USER_DATA_VA)
1477 {
1478 MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA;
1479 MemoryInfo.State = MEM_COMMIT;
1480 MemoryInfo.Protect = PAGE_READONLY;
1481 MemoryInfo.RegionSize = PAGE_SIZE;
1482 }
1483 else
1484 {
1485 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
1486 MemoryInfo.State = MEM_RESERVE;
1487 MemoryInfo.Protect = PAGE_NOACCESS;
1488 MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address;
1489 }
1490
1491 /* Return the data, NtQueryInformation already probed it*/
1492 if (PreviousMode != KernelMode)
1493 {
1494 _SEH2_TRY
1495 {
1496 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1497 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1498 }
1499 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1500 {
1501 Status = _SEH2_GetExceptionCode();
1502 }
1503 _SEH2_END;
1504 }
1505 else
1506 {
1507 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1508 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1509 }
1510
1511 return Status;
1512 }
1513
1514 /* Check if this is for a local or remote process */
1515 if (ProcessHandle == NtCurrentProcess())
1516 {
1517 TargetProcess = PsGetCurrentProcess();
1518 }
1519 else
1520 {
1521 /* Reference the target process */
1522 Status = ObReferenceObjectByHandle(ProcessHandle,
1523 PROCESS_QUERY_INFORMATION,
1524 PsProcessType,
1525 ExGetPreviousMode(),
1526 (PVOID*)&TargetProcess,
1527 NULL);
1528 if (!NT_SUCCESS(Status)) return Status;
1529
1530 /* Attach to it now */
1531 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
1532 }
1533
1534 /* Lock the address space and make sure the process isn't already dead */
1535 MmLockAddressSpace(&TargetProcess->Vm);
1536 if (TargetProcess->VmDeleted)
1537 {
1538 /* Unlock the address space of the process */
1539 MmUnlockAddressSpace(&TargetProcess->Vm);
1540
1541 /* Check if we were attached */
1542 if (ProcessHandle != NtCurrentProcess())
1543 {
1544 /* Detach and dereference the process */
1545 KeUnstackDetachProcess(&ApcState);
1546 ObDereferenceObject(TargetProcess);
1547 }
1548
1549 /* Bail out */
1550 DPRINT1("Process is dying\n");
1551 return STATUS_PROCESS_IS_TERMINATING;
1552 }
1553
1554 /* Loop the VADs */
1555 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
1556 if (TargetProcess->VadRoot.NumberGenericTableElements)
1557 {
1558 /* Scan on the right */
1559 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
1560 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
1561 while (Vad)
1562 {
1563 /* Check if this VAD covers the allocation range */
1564 if ((BaseVpn >= Vad->StartingVpn) &&
1565 (BaseVpn <= Vad->EndingVpn))
1566 {
1567 /* We're done */
1568 Found = TRUE;
1569 break;
1570 }
1571
1572 /* Check if this VAD is too high */
1573 if (BaseVpn < Vad->StartingVpn)
1574 {
1575 /* Stop if there is no left child */
1576 if (!Vad->LeftChild) break;
1577
1578 /* Search on the left next */
1579 Vad = Vad->LeftChild;
1580 }
1581 else
1582 {
1583 /* Then this VAD is too low, keep searching on the right */
1584 ASSERT(BaseVpn > Vad->EndingVpn);
1585
1586 /* Stop if there is no right child */
1587 if (!Vad->RightChild) break;
1588
1589 /* Search on the right next */
1590 Vad = Vad->RightChild;
1591 }
1592 }
1593 }
1594
1595 /* Was a VAD found? */
1596 if (!Found)
1597 {
1598 Address = PAGE_ALIGN(BaseAddress);
1599
1600 /* Calculate region size */
1601 if (Vad)
1602 {
1603 if (Vad->StartingVpn >= BaseVpn)
1604 {
1605 /* Region size is the free space till the start of that VAD */
1606 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1607 }
1608 else
1609 {
1610 /* Get the next VAD */
1611 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
1612 if (Vad)
1613 {
1614 /* Region size is the free space till the start of that VAD */
1615 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
1616 }
1617 else
1618 {
1619 /* Maximum possible region size with that base address */
1620 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1621 }
1622 }
1623 }
1624 else
1625 {
1626 /* Maximum possible region size with that base address */
1627 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
1628 }
1629
1630 /* Unlock the address space of the process */
1631 MmUnlockAddressSpace(&TargetProcess->Vm);
1632
1633 /* Check if we were attached */
1634 if (ProcessHandle != NtCurrentProcess())
1635 {
1636 /* Detach and derefernece the process */
1637 KeUnstackDetachProcess(&ApcState);
1638 ObDereferenceObject(TargetProcess);
1639 }
1640
1641 /* Build the rest of the initial information block */
1642 MemoryInfo.BaseAddress = Address;
1643 MemoryInfo.AllocationBase = NULL;
1644 MemoryInfo.AllocationProtect = 0;
1645 MemoryInfo.State = MEM_FREE;
1646 MemoryInfo.Protect = PAGE_NOACCESS;
1647 MemoryInfo.Type = 0;
1648
1649 /* Return the data, NtQueryInformation already probed it*/
1650 if (PreviousMode != KernelMode)
1651 {
1652 _SEH2_TRY
1653 {
1654 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1655 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1656 }
1657 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1658 {
1659 Status = _SEH2_GetExceptionCode();
1660 }
1661 _SEH2_END;
1662 }
1663 else
1664 {
1665 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1666 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1667 }
1668
1669 return Status;
1670 }
1671
1672 /* Set the correct memory type based on what kind of VAD this is */
1673 if ((Vad->u.VadFlags.PrivateMemory) ||
1674 (Vad->u.VadFlags.VadType == VadRotatePhysical))
1675 {
1676 MemoryInfo.Type = MEM_PRIVATE;
1677 }
1678 else if (Vad->u.VadFlags.VadType == VadImageMap)
1679 {
1680 MemoryInfo.Type = MEM_IMAGE;
1681 }
1682 else
1683 {
1684 MemoryInfo.Type = MEM_MAPPED;
1685 }
1686
1687 /* Find the memory area the specified address belongs to */
1688 MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
1689 ASSERT(MemoryArea != NULL);
1690
1691 /* Determine information dependent on the memory area type */
1692 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1693 {
1694 Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
1695 if (!NT_SUCCESS(Status))
1696 {
1697 DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
1698 MemoryArea, MemoryArea->StartingAddress, MemoryArea->EndingAddress, BaseAddress);
1699 NT_ASSERT(NT_SUCCESS(Status));
1700 }
1701 }
1702 else
1703 {
1704 /* Build the initial information block */
1705 Address = PAGE_ALIGN(BaseAddress);
1706 MemoryInfo.BaseAddress = Address;
1707 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1708 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
1709 MemoryInfo.Type = MEM_PRIVATE;
1710
1711 /* Find the largest chunk of memory which has the same state and protection mask */
1712 MemoryInfo.State = MiQueryAddressState(Address,
1713 Vad,
1714 TargetProcess,
1715 &MemoryInfo.Protect,
1716 &NextAddress);
1717 Address = NextAddress;
1718 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1719 {
1720 /* Keep going unless the state or protection mask changed */
1721 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1722 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1723 Address = NextAddress;
1724 }
1725
1726 /* Now that we know the last VA address, calculate the region size */
1727 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1728 }
1729
1730 /* Unlock the address space of the process */
1731 MmUnlockAddressSpace(&TargetProcess->Vm);
1732
1733 /* Check if we were attached */
1734 if (ProcessHandle != NtCurrentProcess())
1735 {
1736 /* Detach and derefernece the process */
1737 KeUnstackDetachProcess(&ApcState);
1738 ObDereferenceObject(TargetProcess);
1739 }
1740
1741 /* Return the data, NtQueryInformation already probed it */
1742 if (PreviousMode != KernelMode)
1743 {
1744 _SEH2_TRY
1745 {
1746 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1747 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1748 }
1749 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1750 {
1751 Status = _SEH2_GetExceptionCode();
1752 }
1753 _SEH2_END;
1754 }
1755 else
1756 {
1757 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1758 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1759 }
1760
1761 /* All went well */
1762 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1763 "State: %lx Type: %lx Size: %lx\n",
1764 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
1765 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
1766 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
1767
1768 return Status;
1769 }
1770
1771 BOOLEAN
1772 NTAPI
1773 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
1774 IN ULONG_PTR EndingAddress,
1775 IN PMMVAD Vad,
1776 IN PEPROCESS Process)
1777 {
1778 PMMPTE PointerPte, LastPte, PointerPde;
1779 BOOLEAN OnBoundary = TRUE;
1780 PAGED_CODE();
1781
1782 /* Get the PDE and PTE addresses */
1783 PointerPde = MiAddressToPde(StartingAddress);
1784 PointerPte = MiAddressToPte(StartingAddress);
1785 LastPte = MiAddressToPte(EndingAddress);
1786
1787 /* Loop all the PTEs */
1788 while (PointerPte <= LastPte)
1789 {
1790 /* Check if we've hit an new PDE boundary */
1791 if (OnBoundary)
1792 {
1793 /* Is this PDE demand zero? */
1794 PointerPde = MiAddressToPte(PointerPte);
1795 if (PointerPde->u.Long != 0)
1796 {
1797 /* It isn't -- is it valid? */
1798 if (PointerPde->u.Hard.Valid == 0)
1799 {
1800 /* Nope, fault it in */
1801 PointerPte = MiPteToAddress(PointerPde);
1802 MiMakeSystemAddressValid(PointerPte, Process);
1803 }
1804 }
1805 else
1806 {
1807 /* The PTE was already valid, so move to the next one */
1808 PointerPde++;
1809 PointerPte = MiPteToAddress(PointerPde);
1810
1811 /* Is the entire VAD committed? If not, fail */
1812 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1813
1814 /* Everything is committed so far past the range, return true */
1815 if (PointerPte > LastPte) return TRUE;
1816 }
1817 }
1818
1819 /* Is the PTE demand zero? */
1820 if (PointerPte->u.Long == 0)
1821 {
1822 /* Is the entire VAD committed? If not, fail */
1823 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1824 }
1825 else
1826 {
1827 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1828 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
1829 (PointerPte->u.Hard.Valid == 0) &&
1830 ((PointerPte->u.Soft.Prototype == 0) ||
1831 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1832 {
1833 /* Then part of the range is decommitted, so fail */
1834 return FALSE;
1835 }
1836 }
1837
1838 /* Move to the next PTE */
1839 PointerPte++;
1840 OnBoundary = MiIsPteOnPdeBoundary(PointerPte);
1841 }
1842
1843 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1844 return TRUE;
1845 }
1846
1847 NTSTATUS
1848 NTAPI
1849 MiRosProtectVirtualMemory(IN PEPROCESS Process,
1850 IN OUT PVOID *BaseAddress,
1851 IN OUT PSIZE_T NumberOfBytesToProtect,
1852 IN ULONG NewAccessProtection,
1853 OUT PULONG OldAccessProtection OPTIONAL)
1854 {
1855 PMEMORY_AREA MemoryArea;
1856 PMMSUPPORT AddressSpace;
1857 ULONG OldAccessProtection_;
1858 NTSTATUS Status;
1859
1860 *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
1861 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
1862
1863 AddressSpace = &Process->Vm;
1864 MmLockAddressSpace(AddressSpace);
1865 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
1866 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
1867 {
1868 MmUnlockAddressSpace(AddressSpace);
1869 return STATUS_UNSUCCESSFUL;
1870 }
1871
1872 if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
1873
1874 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
1875 Status = MmProtectSectionView(AddressSpace,
1876 MemoryArea,
1877 *BaseAddress,
1878 *NumberOfBytesToProtect,
1879 NewAccessProtection,
1880 OldAccessProtection);
1881
1882 MmUnlockAddressSpace(AddressSpace);
1883
1884 return Status;
1885 }
1886
1887 NTSTATUS
1888 NTAPI
1889 MiProtectVirtualMemory(IN PEPROCESS Process,
1890 IN OUT PVOID *BaseAddress,
1891 IN OUT PSIZE_T NumberOfBytesToProtect,
1892 IN ULONG NewAccessProtection,
1893 OUT PULONG OldAccessProtection OPTIONAL)
1894 {
1895 PMEMORY_AREA MemoryArea;
1896 PMMVAD Vad;
1897 PMMSUPPORT AddressSpace;
1898 ULONG_PTR StartingAddress, EndingAddress;
1899 PMMPTE PointerPde, PointerPte, LastPte;
1900 MMPTE PteContents;
1901 PMMPFN Pfn1;
1902 ULONG ProtectionMask, OldProtect;
1903 BOOLEAN Committed;
1904 NTSTATUS Status = STATUS_SUCCESS;
1905 PETHREAD Thread = PsGetCurrentThread();
1906 TABLE_SEARCH_RESULT Result;
1907
1908 /* Calculate base address for the VAD */
1909 StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
1910 EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
1911
1912 /* Calculate the protection mask and make sure it's valid */
1913 ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
1914 if (ProtectionMask == MM_INVALID_PROTECTION)
1915 {
1916 DPRINT1("Invalid protection mask\n");
1917 return STATUS_INVALID_PAGE_PROTECTION;
1918 }
1919
1920 /* Check for ROS specific memory area */
1921 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
1922 if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW))
1923 {
1924 /* Evil hack */
1925 return MiRosProtectVirtualMemory(Process,
1926 BaseAddress,
1927 NumberOfBytesToProtect,
1928 NewAccessProtection,
1929 OldAccessProtection);
1930 }
1931
1932 /* Lock the address space and make sure the process isn't already dead */
1933 AddressSpace = MmGetCurrentAddressSpace();
1934 MmLockAddressSpace(AddressSpace);
1935 if (Process->VmDeleted)
1936 {
1937 DPRINT1("Process is dying\n");
1938 Status = STATUS_PROCESS_IS_TERMINATING;
1939 goto FailPath;
1940 }
1941
1942 /* Get the VAD for this address range, and make sure it exists */
1943 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
1944 EndingAddress >> PAGE_SHIFT,
1945 &Process->VadRoot,
1946 (PMMADDRESS_NODE*)&Vad);
1947 if (Result != TableFoundNode)
1948 {
1949 DPRINT("Could not find a VAD for this allocation\n");
1950 Status = STATUS_CONFLICTING_ADDRESSES;
1951 goto FailPath;
1952 }
1953
1954 /* Make sure the address is within this VAD's boundaries */
1955 if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
1956 (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
1957 {
1958 Status = STATUS_CONFLICTING_ADDRESSES;
1959 goto FailPath;
1960 }
1961
1962 /* These kinds of VADs are not supported atm */
1963 if ((Vad->u.VadFlags.VadType == VadAwe) ||
1964 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
1965 (Vad->u.VadFlags.VadType == VadLargePages))
1966 {
1967 DPRINT1("Illegal VAD for attempting to set protection\n");
1968 Status = STATUS_CONFLICTING_ADDRESSES;
1969 goto FailPath;
1970 }
1971
1972 /* Check for a VAD whose protection can't be changed */
1973 if (Vad->u.VadFlags.NoChange == 1)
1974 {
1975 DPRINT1("Trying to change protection of a NoChange VAD\n");
1976 Status = STATUS_INVALID_PAGE_PROTECTION;
1977 goto FailPath;
1978 }
1979
1980 /* Is this section, or private memory? */
1981 if (Vad->u.VadFlags.PrivateMemory == 0)
1982 {
1983 /* Not yet supported */
1984 if (Vad->u.VadFlags.VadType == VadLargePageSection)
1985 {
1986 DPRINT1("Illegal VAD for attempting to set protection\n");
1987 Status = STATUS_CONFLICTING_ADDRESSES;
1988 goto FailPath;
1989 }
1990
1991 /* Rotate VADs are not yet supported */
1992 if (Vad->u.VadFlags.VadType == VadRotatePhysical)
1993 {
1994 DPRINT1("Illegal VAD for attempting to set protection\n");
1995 Status = STATUS_CONFLICTING_ADDRESSES;
1996 goto FailPath;
1997 }
1998
1999 /* Not valid on section files */
2000 if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
2001 {
2002 /* Fail */
2003 DPRINT1("Invalid protection flags for section\n");
2004 Status = STATUS_INVALID_PARAMETER_4;
2005 goto FailPath;
2006 }
2007
2008 /* Check if data or page file mapping protection PTE is compatible */
2009 if (!Vad->ControlArea->u.Flags.Image)
2010 {
2011 /* Not yet */
2012 DPRINT1("Fixme: Not checking for valid protection\n");
2013 }
2014
2015 /* This is a section, and this is not yet supported */
2016 DPRINT1("Section protection not yet supported\n");
2017 OldProtect = 0;
2018 }
2019 else
2020 {
2021 /* Private memory, check protection flags */
2022 if ((NewAccessProtection & PAGE_WRITECOPY) ||
2023 (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2024 {
2025 DPRINT1("Invalid protection flags for private memory\n");
2026 Status = STATUS_INVALID_PARAMETER_4;
2027 goto FailPath;
2028 }
2029
2030 /* Lock the working set */
2031 MiLockProcessWorkingSetUnsafe(Process, Thread);
2032
2033 /* Check if all pages in this range are committed */
2034 Committed = MiIsEntireRangeCommitted(StartingAddress,
2035 EndingAddress,
2036 Vad,
2037 Process);
2038 if (!Committed)
2039 {
2040 /* Fail */
2041 DPRINT1("The entire range is not committed\n");
2042 Status = STATUS_NOT_COMMITTED;
2043 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2044 goto FailPath;
2045 }
2046
2047 /* Compute starting and ending PTE and PDE addresses */
2048 PointerPde = MiAddressToPde(StartingAddress);
2049 PointerPte = MiAddressToPte(StartingAddress);
2050 LastPte = MiAddressToPte(EndingAddress);
2051
2052 /* Make this PDE valid */
2053 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2054
2055 /* Save protection of the first page */
2056 if (PointerPte->u.Long != 0)
2057 {
2058 /* Capture the page protection and make the PDE valid */
2059 OldProtect = MiGetPageProtection(PointerPte);
2060 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2061 }
2062 else
2063 {
2064 /* Grab the old protection from the VAD itself */
2065 OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2066 }
2067
2068 /* Loop all the PTEs now */
2069 while (PointerPte <= LastPte)
2070 {
2071 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2072 if (MiIsPteOnPdeBoundary(PointerPte))
2073 {
2074 PointerPde = MiAddressToPte(PointerPte);
2075 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2076 }
2077
2078 /* Capture the PTE and check if it was empty */
2079 PteContents = *PointerPte;
2080 if (PteContents.u.Long == 0)
2081 {
2082 /* This used to be a zero PTE and it no longer is, so we must add a
2083 reference to the pagetable. */
2084 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2085 }
2086
2087 /* Check what kind of PTE we are dealing with */
2088 if (PteContents.u.Hard.Valid == 1)
2089 {
2090 /* Get the PFN entry */
2091 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2092
2093 /* We don't support this yet */
2094 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2095
2096 /* Check if the page should not be accessible at all */
2097 if ((NewAccessProtection & PAGE_NOACCESS) ||
2098 (NewAccessProtection & PAGE_GUARD))
2099 {
2100 /* The page should be in the WS and we should make it transition now */
2101 DPRINT1("Making valid page invalid is not yet supported!\n");
2102 Status = STATUS_NOT_IMPLEMENTED;
2103 /* Unlock the working set */
2104 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2105 goto FailPath;
2106 }
2107
2108 /* Write the protection mask and write it with a TLB flush */
2109 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2110 MiFlushTbAndCapture(Vad,
2111 PointerPte,
2112 ProtectionMask,
2113 Pfn1,
2114 TRUE);
2115 }
2116 else
2117 {
2118 /* We don't support these cases yet */
2119 ASSERT(PteContents.u.Soft.Prototype == 0);
2120 ASSERT(PteContents.u.Soft.Transition == 0);
2121
2122 /* The PTE is already demand-zero, just update the protection mask */
2123 PteContents.u.Soft.Protection = ProtectionMask;
2124 MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2125 ASSERT(PointerPte->u.Long != 0);
2126 }
2127
2128 /* Move to the next PTE */
2129 PointerPte++;
2130 }
2131
2132 /* Unlock the working set */
2133 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2134 }
2135
2136 /* Unlock the address space */
2137 MmUnlockAddressSpace(AddressSpace);
2138
2139 /* Return parameters and success */
2140 *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2141 *BaseAddress = (PVOID)StartingAddress;
2142 *OldAccessProtection = OldProtect;
2143 return STATUS_SUCCESS;
2144
2145 FailPath:
2146 /* Unlock the address space and return the failure code */
2147 MmUnlockAddressSpace(AddressSpace);
2148 return Status;
2149 }
2150
2151 VOID
2152 NTAPI
2153 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde,
2154 IN PEPROCESS TargetProcess,
2155 IN KIRQL OldIrql)
2156 {
2157 PMMPTE PointerPte, PointerPpe, PointerPxe;
2158
2159 //
2160 // Sanity checks. The latter is because we only use this function with the
2161 // PFN lock not held, so it may go away in the future.
2162 //
2163 ASSERT(KeAreAllApcsDisabled() == TRUE);
2164 ASSERT(OldIrql == MM_NOIRQL);
2165
2166 //
2167 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2168 // return the same address as the PDE on 2-level page table systems.
2169 //
2170 // If everything is already valid, there is nothing to do.
2171 //
2172 PointerPpe = MiAddressToPte(PointerPde);
2173 PointerPxe = MiAddressToPde(PointerPde);
2174 if ((PointerPxe->u.Hard.Valid) &&
2175 (PointerPpe->u.Hard.Valid) &&
2176 (PointerPde->u.Hard.Valid))
2177 {
2178 return;
2179 }
2180
2181 //
2182 // At least something is invalid, so begin by getting the PTE for the PDE itself
2183 // and then lookup each additional level. We must do it in this precise order
2184 // because the pagfault.c code (as well as in Windows) depends that the next
2185 // level up (higher) must be valid when faulting a lower level
2186 //
2187 PointerPte = MiPteToAddress(PointerPde);
2188 do
2189 {
2190 //
2191 // Make sure APCs continued to be disabled
2192 //
2193 ASSERT(KeAreAllApcsDisabled() == TRUE);
2194
2195 //
2196 // First, make the PXE valid if needed
2197 //
2198 if (!PointerPxe->u.Hard.Valid)
2199 {
2200 MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2201 ASSERT(PointerPxe->u.Hard.Valid == 1);
2202 }
2203
2204 //
2205 // Next, the PPE
2206 //
2207 if (!PointerPpe->u.Hard.Valid)
2208 {
2209 MiMakeSystemAddressValid(PointerPde, TargetProcess);
2210 ASSERT(PointerPpe->u.Hard.Valid == 1);
2211 }
2212
2213 //
2214 // And finally, make the PDE itself valid.
2215 //
2216 MiMakeSystemAddressValid(PointerPte, TargetProcess);
2217
2218 //
2219 // This should've worked the first time so the loop is really just for
2220 // show -- ASSERT that we're actually NOT going to be looping.
2221 //
2222 ASSERT(PointerPxe->u.Hard.Valid == 1);
2223 ASSERT(PointerPpe->u.Hard.Valid == 1);
2224 ASSERT(PointerPde->u.Hard.Valid == 1);
2225 } while (!(PointerPxe->u.Hard.Valid) ||
2226 !(PointerPpe->u.Hard.Valid) ||
2227 !(PointerPde->u.Hard.Valid));
2228 }
2229
2230 VOID
2231 NTAPI
2232 MiProcessValidPteList(IN PMMPTE *ValidPteList,
2233 IN ULONG Count)
2234 {
2235 KIRQL OldIrql;
2236 ULONG i;
2237 MMPTE TempPte;
2238 PFN_NUMBER PageFrameIndex;
2239 PMMPFN Pfn1, Pfn2;
2240
2241 //
2242 // Acquire the PFN lock and loop all the PTEs in the list
2243 //
2244 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2245 for (i = 0; i != Count; i++)
2246 {
2247 //
2248 // The PTE must currently be valid
2249 //
2250 TempPte = *ValidPteList[i];
2251 ASSERT(TempPte.u.Hard.Valid == 1);
2252
2253 //
2254 // Get the PFN entry for the page itself, and then for its page table
2255 //
2256 PageFrameIndex = PFN_FROM_PTE(&TempPte);
2257 Pfn1 = MiGetPfnEntry(PageFrameIndex);
2258 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2259
2260 //
2261 // Decrement the share count on the page table, and then on the page
2262 // itself
2263 //
2264 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2265 MI_SET_PFN_DELETED(Pfn1);
2266 MiDecrementShareCount(Pfn1, PageFrameIndex);
2267
2268 //
2269 // Make the page decommitted
2270 //
2271 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2272 }
2273
2274 //
2275 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2276 // and then release the PFN lock
2277 //
2278 KeFlushCurrentTb();
2279 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2280 }
2281
2282 ULONG
2283 NTAPI
2284 MiDecommitPages(IN PVOID StartingAddress,
2285 IN PMMPTE EndingPte,
2286 IN PEPROCESS Process,
2287 IN PMMVAD Vad)
2288 {
2289 PMMPTE PointerPde, PointerPte, CommitPte = NULL;
2290 ULONG CommitReduction = 0;
2291 PMMPTE ValidPteList[256];
2292 ULONG PteCount = 0;
2293 PMMPFN Pfn1;
2294 MMPTE PteContents;
2295 PETHREAD CurrentThread = PsGetCurrentThread();
2296
2297 //
2298 // Get the PTE and PTE for the address, and lock the working set
2299 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2300 // commited range ends so that we can do the right accounting.
2301 //
2302 PointerPde = MiAddressToPde(StartingAddress);
2303 PointerPte = MiAddressToPte(StartingAddress);
2304 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2305 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2306
2307 //
2308 // Make the PDE valid, and now loop through each page's worth of data
2309 //
2310 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2311 while (PointerPte <= EndingPte)
2312 {
2313 //
2314 // Check if we've crossed a PDE boundary
2315 //
2316 if (MiIsPteOnPdeBoundary(PointerPte))
2317 {
2318 //
2319 // Get the new PDE and flush the valid PTEs we had built up until
2320 // now. This helps reduce the amount of TLB flushing we have to do.
2321 // Note that Windows does a much better job using timestamps and
2322 // such, and does not flush the entire TLB all the time, but right
2323 // now we have bigger problems to worry about than TLB flushing.
2324 //
2325 PointerPde = MiAddressToPde(StartingAddress);
2326 if (PteCount)
2327 {
2328 MiProcessValidPteList(ValidPteList, PteCount);
2329 PteCount = 0;
2330 }
2331
2332 //
2333 // Make this PDE valid
2334 //
2335 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2336 }
2337
2338 //
2339 // Read this PTE. It might be active or still demand-zero.
2340 //
2341 PteContents = *PointerPte;
2342 if (PteContents.u.Long)
2343 {
2344 //
2345 // The PTE is active. It might be valid and in a working set, or
2346 // it might be a prototype PTE or paged out or even in transition.
2347 //
2348 if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2349 {
2350 //
2351 // It's already decommited, so there's nothing for us to do here
2352 //
2353 CommitReduction++;
2354 }
2355 else
2356 {
2357 //
2358 // Remove it from the counters, and check if it was valid or not
2359 //
2360 //Process->NumberOfPrivatePages--;
2361 if (PteContents.u.Hard.Valid)
2362 {
2363 //
2364 // It's valid. At this point make sure that it is not a ROS
2365 // PFN. Also, we don't support ProtoPTEs in this code path.
2366 //
2367 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2368 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2369 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2370
2371 //
2372 // Flush any pending PTEs that we had not yet flushed, if our
2373 // list has gotten too big, then add this PTE to the flush list.
2374 //
2375 if (PteCount == 256)
2376 {
2377 MiProcessValidPteList(ValidPteList, PteCount);
2378 PteCount = 0;
2379 }
2380 ValidPteList[PteCount++] = PointerPte;
2381 }
2382 else
2383 {
2384 //
2385 // We do not support any of these other scenarios at the moment
2386 //
2387 ASSERT(PteContents.u.Soft.Prototype == 0);
2388 ASSERT(PteContents.u.Soft.Transition == 0);
2389 ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2390
2391 //
2392 // So the only other possibility is that it is still a demand
2393 // zero PTE, in which case we undo the accounting we did
2394 // earlier and simply make the page decommitted.
2395 //
2396 //Process->NumberOfPrivatePages++;
2397 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2398 }
2399 }
2400 }
2401 else
2402 {
2403 //
2404 // This used to be a zero PTE and it no longer is, so we must add a
2405 // reference to the pagetable.
2406 //
2407 MiIncrementPageTableReferences(StartingAddress);
2408
2409 //
2410 // Next, we account for decommitted PTEs and make the PTE as such
2411 //
2412 if (PointerPte > CommitPte) CommitReduction++;
2413 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2414 }
2415
2416 //
2417 // Move to the next PTE and the next address
2418 //
2419 PointerPte++;
2420 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2421 }
2422
2423 //
2424 // Flush any dangling PTEs from the loop in the last page table, and then
2425 // release the working set and return the commit reduction accounting.
2426 //
2427 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2428 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
2429 return CommitReduction;
2430 }
2431
2432 /* PUBLIC FUNCTIONS ***********************************************************/
2433
2434 /*
2435 * @unimplemented
2436 */
2437 PVOID
2438 NTAPI
2439 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
2440 {
2441 UNIMPLEMENTED;
2442 return 0;
2443 }
2444
2445 /*
2446 * @unimplemented
2447 */
2448 PVOID
2449 NTAPI
2450 MmSecureVirtualMemory(IN PVOID Address,
2451 IN SIZE_T Length,
2452 IN ULONG Mode)
2453 {
2454 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2455 return Address;
2456 }
2457
2458 /*
2459 * @unimplemented
2460 */
2461 VOID
2462 NTAPI
2463 MmUnsecureVirtualMemory(IN PVOID SecureMem)
2464 {
2465 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2466 }
2467
2468 /* SYSTEM CALLS ***************************************************************/
2469
2470 NTSTATUS
2471 NTAPI
2472 NtReadVirtualMemory(IN HANDLE ProcessHandle,
2473 IN PVOID BaseAddress,
2474 OUT PVOID Buffer,
2475 IN SIZE_T NumberOfBytesToRead,
2476 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2477 {
2478 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2479 PEPROCESS Process;
2480 NTSTATUS Status = STATUS_SUCCESS;
2481 SIZE_T BytesRead = 0;
2482 PAGED_CODE();
2483
2484 //
2485 // Check if we came from user mode
2486 //
2487 if (PreviousMode != KernelMode)
2488 {
2489 //
2490 // Validate the read addresses
2491 //
2492 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2493 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2494 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2495 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2496 {
2497 //
2498 // Don't allow to write into kernel space
2499 //
2500 return STATUS_ACCESS_VIOLATION;
2501 }
2502
2503 //
2504 // Enter SEH for probe
2505 //
2506 _SEH2_TRY
2507 {
2508 //
2509 // Probe the output value
2510 //
2511 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2512 }
2513 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2514 {
2515 //
2516 // Get exception code
2517 //
2518 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2519 }
2520 _SEH2_END;
2521 }
2522
2523 //
2524 // Don't do zero-byte transfers
2525 //
2526 if (NumberOfBytesToRead)
2527 {
2528 //
2529 // Reference the process
2530 //
2531 Status = ObReferenceObjectByHandle(ProcessHandle,
2532 PROCESS_VM_READ,
2533 PsProcessType,
2534 PreviousMode,
2535 (PVOID*)(&Process),
2536 NULL);
2537 if (NT_SUCCESS(Status))
2538 {
2539 //
2540 // Do the copy
2541 //
2542 Status = MmCopyVirtualMemory(Process,
2543 BaseAddress,
2544 PsGetCurrentProcess(),
2545 Buffer,
2546 NumberOfBytesToRead,
2547 PreviousMode,
2548 &BytesRead);
2549
2550 //
2551 // Dereference the process
2552 //
2553 ObDereferenceObject(Process);
2554 }
2555 }
2556
2557 //
2558 // Check if the caller sent this parameter
2559 //
2560 if (NumberOfBytesRead)
2561 {
2562 //
2563 // Enter SEH to guard write
2564 //
2565 _SEH2_TRY
2566 {
2567 //
2568 // Return the number of bytes read
2569 //
2570 *NumberOfBytesRead = BytesRead;
2571 }
2572 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2573 {
2574 }
2575 _SEH2_END;
2576 }
2577
2578 //
2579 // Return status
2580 //
2581 return Status;
2582 }
2583
2584 NTSTATUS
2585 NTAPI
2586 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
2587 IN PVOID BaseAddress,
2588 IN PVOID Buffer,
2589 IN SIZE_T NumberOfBytesToWrite,
2590 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2591 {
2592 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2593 PEPROCESS Process;
2594 NTSTATUS Status = STATUS_SUCCESS;
2595 SIZE_T BytesWritten = 0;
2596 PAGED_CODE();
2597
2598 //
2599 // Check if we came from user mode
2600 //
2601 if (PreviousMode != KernelMode)
2602 {
2603 //
2604 // Validate the read addresses
2605 //
2606 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2607 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2608 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2609 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2610 {
2611 //
2612 // Don't allow to write into kernel space
2613 //
2614 return STATUS_ACCESS_VIOLATION;
2615 }
2616
2617 //
2618 // Enter SEH for probe
2619 //
2620 _SEH2_TRY
2621 {
2622 //
2623 // Probe the output value
2624 //
2625 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2626 }
2627 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2628 {
2629 //
2630 // Get exception code
2631 //
2632 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2633 }
2634 _SEH2_END;
2635 }
2636
2637 //
2638 // Don't do zero-byte transfers
2639 //
2640 if (NumberOfBytesToWrite)
2641 {
2642 //
2643 // Reference the process
2644 //
2645 Status = ObReferenceObjectByHandle(ProcessHandle,
2646 PROCESS_VM_WRITE,
2647 PsProcessType,
2648 PreviousMode,
2649 (PVOID*)&Process,
2650 NULL);
2651 if (NT_SUCCESS(Status))
2652 {
2653 //
2654 // Do the copy
2655 //
2656 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
2657 Buffer,
2658 Process,
2659 BaseAddress,
2660 NumberOfBytesToWrite,
2661 PreviousMode,
2662 &BytesWritten);
2663
2664 //
2665 // Dereference the process
2666 //
2667 ObDereferenceObject(Process);
2668 }
2669 }
2670
2671 //
2672 // Check if the caller sent this parameter
2673 //
2674 if (NumberOfBytesWritten)
2675 {
2676 //
2677 // Enter SEH to guard write
2678 //
2679 _SEH2_TRY
2680 {
2681 //
2682 // Return the number of bytes written
2683 //
2684 *NumberOfBytesWritten = BytesWritten;
2685 }
2686 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2687 {
2688 }
2689 _SEH2_END;
2690 }
2691
2692 //
2693 // Return status
2694 //
2695 return Status;
2696 }
2697
2698 NTSTATUS
2699 NTAPI
2700 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
2701 IN OUT PVOID *UnsafeBaseAddress,
2702 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
2703 IN ULONG NewAccessProtection,
2704 OUT PULONG UnsafeOldAccessProtection)
2705 {
2706 PEPROCESS Process;
2707 ULONG OldAccessProtection;
2708 ULONG Protection;
2709 PEPROCESS CurrentProcess = PsGetCurrentProcess();
2710 PVOID BaseAddress = NULL;
2711 SIZE_T NumberOfBytesToProtect = 0;
2712 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2713 NTSTATUS Status;
2714 BOOLEAN Attached = FALSE;
2715 KAPC_STATE ApcState;
2716 PAGED_CODE();
2717
2718 //
2719 // Check for valid protection flags
2720 //
2721 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
2722 if (Protection != PAGE_NOACCESS &&
2723 Protection != PAGE_READONLY &&
2724 Protection != PAGE_READWRITE &&
2725 Protection != PAGE_WRITECOPY &&
2726 Protection != PAGE_EXECUTE &&
2727 Protection != PAGE_EXECUTE_READ &&
2728 Protection != PAGE_EXECUTE_READWRITE &&
2729 Protection != PAGE_EXECUTE_WRITECOPY)
2730 {
2731 //
2732 // Fail
2733 //
2734 return STATUS_INVALID_PAGE_PROTECTION;
2735 }
2736
2737 //
2738 // Check if we came from user mode
2739 //
2740 if (PreviousMode != KernelMode)
2741 {
2742 //
2743 // Enter SEH for probing
2744 //
2745 _SEH2_TRY
2746 {
2747 //
2748 // Validate all outputs
2749 //
2750 ProbeForWritePointer(UnsafeBaseAddress);
2751 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
2752 ProbeForWriteUlong(UnsafeOldAccessProtection);
2753
2754 //
2755 // Capture them
2756 //
2757 BaseAddress = *UnsafeBaseAddress;
2758 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2759 }
2760 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2761 {
2762 //
2763 // Get exception code
2764 //
2765 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2766 }
2767 _SEH2_END;
2768 }
2769 else
2770 {
2771 //
2772 // Capture directly
2773 //
2774 BaseAddress = *UnsafeBaseAddress;
2775 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2776 }
2777
2778 //
2779 // Catch illegal base address
2780 //
2781 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2782
2783 //
2784 // Catch illegal region size
2785 //
2786 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
2787 {
2788 //
2789 // Fail
2790 //
2791 return STATUS_INVALID_PARAMETER_3;
2792 }
2793
2794 //
2795 // 0 is also illegal
2796 //
2797 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
2798
2799 //
2800 // Get a reference to the process
2801 //
2802 Status = ObReferenceObjectByHandle(ProcessHandle,
2803 PROCESS_VM_OPERATION,
2804 PsProcessType,
2805 PreviousMode,
2806 (PVOID*)(&Process),
2807 NULL);
2808 if (!NT_SUCCESS(Status)) return Status;
2809
2810 //
2811 // Check if we should attach
2812 //
2813 if (CurrentProcess != Process)
2814 {
2815 //
2816 // Do it
2817 //
2818 KeStackAttachProcess(&Process->Pcb, &ApcState);
2819 Attached = TRUE;
2820 }
2821
2822 //
2823 // Do the actual work
2824 //
2825 Status = MiProtectVirtualMemory(Process,
2826 &BaseAddress,
2827 &NumberOfBytesToProtect,
2828 NewAccessProtection,
2829 &OldAccessProtection);
2830
2831 //
2832 // Detach if needed
2833 //
2834 if (Attached) KeUnstackDetachProcess(&ApcState);
2835
2836 //
2837 // Release reference
2838 //
2839 ObDereferenceObject(Process);
2840
2841 //
2842 // Enter SEH to return data
2843 //
2844 _SEH2_TRY
2845 {
2846 //
2847 // Return data to user
2848 //
2849 *UnsafeOldAccessProtection = OldAccessProtection;
2850 *UnsafeBaseAddress = BaseAddress;
2851 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
2852 }
2853 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2854 {
2855 }
2856 _SEH2_END;
2857
2858 //
2859 // Return status
2860 //
2861 return Status;
2862 }
2863
2864 FORCEINLINE
2865 BOOLEAN
2866 MI_IS_LOCKED_VA(
2867 PMMPFN Pfn1,
2868 ULONG LockType)
2869 {
2870 // HACK until we have proper WSLIST support
2871 PMMWSLE Wsle = &Pfn1->Wsle;
2872
2873 if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
2874 return TRUE;
2875 if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
2876 return TRUE;
2877
2878 return FALSE;
2879 }
2880
2881 FORCEINLINE
2882 VOID
2883 MI_LOCK_VA(
2884 PMMPFN Pfn1,
2885 ULONG LockType)
2886 {
2887 // HACK until we have proper WSLIST support
2888 PMMWSLE Wsle = &Pfn1->Wsle;
2889
2890 if (!Wsle->u1.e1.LockedInWs &&
2891 !Wsle->u1.e1.LockedInMemory)
2892 {
2893 MiReferenceProbedPageAndBumpLockCount(Pfn1);
2894 }
2895
2896 if (LockType & MAP_PROCESS)
2897 Wsle->u1.e1.LockedInWs = 1;
2898 if (LockType & MAP_SYSTEM)
2899 Wsle->u1.e1.LockedInMemory = 1;
2900 }
2901
2902 FORCEINLINE
2903 VOID
2904 MI_UNLOCK_VA(
2905 PMMPFN Pfn1,
2906 ULONG LockType)
2907 {
2908 // HACK until we have proper WSLIST support
2909 PMMWSLE Wsle = &Pfn1->Wsle;
2910
2911 if (LockType & MAP_PROCESS)
2912 Wsle->u1.e1.LockedInWs = 0;
2913 if (LockType & MAP_SYSTEM)
2914 Wsle->u1.e1.LockedInMemory = 0;
2915
2916 if (!Wsle->u1.e1.LockedInWs &&
2917 !Wsle->u1.e1.LockedInMemory)
2918 {
2919 MiDereferencePfnAndDropLockCount(Pfn1);
2920 }
2921 }
2922
2923 static
2924 NTSTATUS
2925 MiCheckVadsForLockOperation(
2926 _Inout_ PVOID *BaseAddress,
2927 _Inout_ PSIZE_T RegionSize,
2928 _Inout_ PVOID *EndAddress)
2929
2930 {
2931 PMMVAD Vad;
2932 PVOID CurrentVa;
2933
2934 /* Get the base address and align the start address */
2935 *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
2936 *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
2937 *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE);
2938
2939 /* First loop and check all VADs */
2940 CurrentVa = *BaseAddress;
2941 while (CurrentVa < *EndAddress)
2942 {
2943 /* Get VAD */
2944 Vad = MiLocateAddress(CurrentVa);
2945 if (Vad == NULL)
2946 {
2947 /// FIXME: this might be a memory area for a section view...
2948 return STATUS_ACCESS_VIOLATION;
2949 }
2950
2951 /* Check VAD type */
2952 if ((Vad->u.VadFlags.VadType != VadNone) &&
2953 (Vad->u.VadFlags.VadType != VadImageMap) &&
2954 (Vad->u.VadFlags.VadType != VadWriteWatch))
2955 {
2956 *EndAddress = CurrentVa;
2957 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
2958 return STATUS_INCOMPATIBLE_FILE_MAP;
2959 }
2960
2961 CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
2962 }
2963
2964 *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
2965 return STATUS_SUCCESS;
2966 }
2967
2968 static
2969 NTSTATUS
2970 MiLockVirtualMemory(
2971 IN OUT PVOID *BaseAddress,
2972 IN OUT PSIZE_T RegionSize,
2973 IN ULONG MapType)
2974 {
2975 PEPROCESS CurrentProcess;
2976 PMMSUPPORT AddressSpace;
2977 PVOID CurrentVa, EndAddress;
2978 PMMPTE PointerPte, LastPte;
2979 PMMPDE PointerPde;
2980 #if (_MI_PAGING_LEVELS >= 3)
2981 PMMPDE PointerPpe;
2982 #endif
2983 #if (_MI_PAGING_LEVELS == 4)
2984 PMMPDE PointerPxe;
2985 #endif
2986 PMMPFN Pfn1;
2987 NTSTATUS Status, TempStatus;
2988
2989 /* Lock the address space */
2990 AddressSpace = MmGetCurrentAddressSpace();
2991 MmLockAddressSpace(AddressSpace);
2992
2993 /* Make sure we still have an address space */
2994 CurrentProcess = PsGetCurrentProcess();
2995 if (CurrentProcess->VmDeleted)
2996 {
2997 Status = STATUS_PROCESS_IS_TERMINATING;
2998 goto Cleanup;
2999 }
3000
3001 /* Check the VADs in the requested range */
3002 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3003 if (!NT_SUCCESS(Status))
3004 {
3005 goto Cleanup;
3006 }
3007
3008 /* Enter SEH for probing */
3009 _SEH2_TRY
3010 {
3011 /* Loop all pages and probe them */
3012 CurrentVa = *BaseAddress;
3013 while (CurrentVa < EndAddress)
3014 {
3015 (void)(*(volatile CHAR*)CurrentVa);
3016 CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
3017 }
3018 }
3019 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3020 {
3021 Status = _SEH2_GetExceptionCode();
3022 goto Cleanup;
3023 }
3024 _SEH2_END;
3025
3026 /* All pages were accessible, since we hold the address space lock, nothing
3027 can be de-committed. Assume success for now. */
3028 Status = STATUS_SUCCESS;
3029
3030 /* Get the PTE and PDE */
3031 PointerPte = MiAddressToPte(*BaseAddress);
3032 PointerPde = MiAddressToPde(*BaseAddress);
3033 #if (_MI_PAGING_LEVELS >= 3)
3034 PointerPpe = MiAddressToPpe(*BaseAddress);
3035 #endif
3036 #if (_MI_PAGING_LEVELS == 4)
3037 PointerPxe = MiAddressToPxe(*BaseAddress);
3038 #endif
3039
3040 /* Get the last PTE */
3041 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3042
3043 /* Lock the process working set */
3044 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3045
3046 /* Loop the pages */
3047 do
3048 {
3049 /* Check for a page that is not accessible */
3050 while (
3051 #if (_MI_PAGING_LEVELS == 4)
3052 (PointerPxe->u.Hard.Valid == 0) ||
3053 #endif
3054 #if (_MI_PAGING_LEVELS >= 3)
3055 (PointerPpe->u.Hard.Valid == 0) ||
3056 #endif
3057 (PointerPde->u.Hard.Valid == 0) ||
3058 (PointerPte->u.Hard.Valid == 0))
3059 {
3060 /* Release process working set */
3061 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3062
3063 /* Access the page */
3064 CurrentVa = MiPteToAddress(PointerPte);
3065
3066 //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
3067 TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)0xBADBADA3);
3068 if (!NT_SUCCESS(TempStatus))
3069 {
3070 // This should only happen, when remote backing storage is not accessible
3071 ASSERT(FALSE);
3072 Status = TempStatus;
3073 goto Cleanup;
3074 }
3075
3076 /* Lock the process working set */
3077 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3078 }
3079
3080 /* Get the PFN */
3081 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3082 ASSERT(Pfn1 != NULL);
3083
3084 /* Check the previous lock status */
3085 if (MI_IS_LOCKED_VA(Pfn1, MapType))
3086 {
3087 Status = STATUS_WAS_LOCKED;
3088 }
3089
3090 /* Lock it */
3091 MI_LOCK_VA(Pfn1, MapType);
3092
3093 /* Go to the next PTE */
3094 PointerPte++;
3095
3096 /* Check if we're on a PDE boundary */
3097 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3098 #if (_MI_PAGING_LEVELS >= 3)
3099 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3100 #endif
3101 #if (_MI_PAGING_LEVELS == 4)
3102 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3103 #endif
3104 } while (PointerPte <= LastPte);
3105
3106 /* Release process working set */
3107 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3108
3109 Cleanup:
3110 /* Unlock address space */
3111 MmUnlockAddressSpace(AddressSpace);
3112
3113 return Status;
3114 }
3115
3116 NTSTATUS
3117 NTAPI
3118 NtLockVirtualMemory(IN HANDLE ProcessHandle,
3119 IN OUT PVOID *BaseAddress,
3120 IN OUT PSIZE_T NumberOfBytesToLock,
3121 IN ULONG MapType)
3122 {
3123 PEPROCESS Process;
3124 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3125 NTSTATUS Status;
3126 BOOLEAN Attached = FALSE;
3127 KAPC_STATE ApcState;
3128 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3129 PVOID CapturedBaseAddress;
3130 SIZE_T CapturedBytesToLock;
3131 PAGED_CODE();
3132
3133 //
3134 // Validate flags
3135 //
3136 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3137 {
3138 //
3139 // Invalid set of flags
3140 //
3141 return STATUS_INVALID_PARAMETER;
3142 }
3143
3144 //
3145 // At least one flag must be specified
3146 //
3147 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3148 {
3149 //
3150 // No flag given
3151 //
3152 return STATUS_INVALID_PARAMETER;
3153 }
3154
3155 //
3156 // Enter SEH for probing
3157 //
3158 _SEH2_TRY
3159 {
3160 //
3161 // Validate output data
3162 //
3163 ProbeForWritePointer(BaseAddress);
3164 ProbeForWriteSize_t(NumberOfBytesToLock);
3165
3166 //
3167 // Capture it
3168 //
3169 CapturedBaseAddress = *BaseAddress;
3170 CapturedBytesToLock = *NumberOfBytesToLock;
3171 }
3172 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3173 {
3174 //
3175 // Get exception code
3176 //
3177 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3178 }
3179 _SEH2_END;
3180
3181 //
3182 // Catch illegal base address
3183 //
3184 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3185
3186 //
3187 // Catch illegal region size
3188 //
3189 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
3190 {
3191 //
3192 // Fail
3193 //
3194 return STATUS_INVALID_PARAMETER;
3195 }
3196
3197 //
3198 // 0 is also illegal
3199 //
3200 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
3201
3202 //
3203 // Get a reference to the process
3204 //
3205 Status = ObReferenceObjectByHandle(ProcessHandle,
3206 PROCESS_VM_OPERATION,
3207 PsProcessType,
3208 PreviousMode,
3209 (PVOID*)(&Process),
3210 NULL);
3211 if (!NT_SUCCESS(Status)) return Status;
3212
3213 //
3214 // Check if this is is system-mapped
3215 //
3216 if (MapType & MAP_SYSTEM)
3217 {
3218 //
3219 // Check for required privilege
3220 //
3221 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3222 {
3223 //
3224 // Fail: Don't have it
3225 //
3226 ObDereferenceObject(Process);
3227 return STATUS_PRIVILEGE_NOT_HELD;
3228 }
3229 }
3230
3231 //
3232 // Check if we should attach
3233 //
3234 if (CurrentProcess != Process)
3235 {
3236 //
3237 // Do it
3238 //
3239 KeStackAttachProcess(&Process->Pcb, &ApcState);
3240 Attached = TRUE;
3241 }
3242
3243 //
3244 // Call the internal function
3245 //
3246 Status = MiLockVirtualMemory(&CapturedBaseAddress,
3247 &CapturedBytesToLock,
3248 MapType);
3249
3250 //
3251 // Detach if needed
3252 //
3253 if (Attached) KeUnstackDetachProcess(&ApcState);
3254
3255 //
3256 // Release reference
3257 //
3258 ObDereferenceObject(Process);
3259
3260 //
3261 // Enter SEH to return data
3262 //
3263 _SEH2_TRY
3264 {
3265 //
3266 // Return data to user
3267 //
3268 *BaseAddress = CapturedBaseAddress;
3269 *NumberOfBytesToLock = CapturedBytesToLock;
3270 }
3271 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3272 {
3273 //
3274 // Get exception code
3275 //
3276 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3277 }
3278 _SEH2_END;
3279
3280 //
3281 // Return status
3282 //
3283 return Status;
3284 }
3285
3286
3287 static
3288 NTSTATUS
3289 MiUnlockVirtualMemory(
3290 IN OUT PVOID *BaseAddress,
3291 IN OUT PSIZE_T RegionSize,
3292 IN ULONG MapType)
3293 {
3294 PEPROCESS CurrentProcess;
3295 PMMSUPPORT AddressSpace;
3296 PVOID EndAddress;
3297 PMMPTE PointerPte, LastPte;
3298 PMMPDE PointerPde;
3299 #if (_MI_PAGING_LEVELS >= 3)
3300 PMMPDE PointerPpe;
3301 #endif
3302 #if (_MI_PAGING_LEVELS == 4)
3303 PMMPDE PointerPxe;
3304 #endif
3305 PMMPFN Pfn1;
3306 NTSTATUS Status;
3307
3308 /* Lock the address space */
3309 AddressSpace = MmGetCurrentAddressSpace();
3310 MmLockAddressSpace(AddressSpace);
3311
3312 /* Make sure we still have an address space */
3313 CurrentProcess = PsGetCurrentProcess();
3314 if (CurrentProcess->VmDeleted)
3315 {
3316 Status = STATUS_PROCESS_IS_TERMINATING;
3317 goto Cleanup;
3318 }
3319
3320 /* Check the VADs in the requested range */
3321 Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
3322
3323 /* Note: only bail out, if we hit an area without a VAD. If we hit an
3324 incompatible VAD we continue, like Windows does */
3325 if (Status == STATUS_ACCESS_VIOLATION)
3326 {
3327 Status = STATUS_NOT_LOCKED;
3328 goto Cleanup;
3329 }
3330
3331 /* Get the PTE and PDE */
3332 PointerPte = MiAddressToPte(*BaseAddress);
3333 PointerPde = MiAddressToPde(*BaseAddress);
3334 #if (_MI_PAGING_LEVELS >= 3)
3335 PointerPpe = MiAddressToPpe(*BaseAddress);
3336 #endif
3337 #if (_MI_PAGING_LEVELS == 4)
3338 PointerPxe = MiAddressToPxe(*BaseAddress);
3339 #endif
3340
3341 /* Get the last PTE */
3342 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
3343
3344 /* Lock the process working set */
3345 MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3346
3347 /* Loop the pages */
3348 do
3349 {
3350 /* Check for a page that is not present */
3351 if (
3352 #if (_MI_PAGING_LEVELS == 4)
3353 (PointerPxe->u.Hard.Valid == 0) ||
3354 #endif
3355 #if (_MI_PAGING_LEVELS >= 3)
3356 (PointerPpe->u.Hard.Valid == 0) ||
3357 #endif
3358 (PointerPde->u.Hard.Valid == 0) ||
3359 (PointerPte->u.Hard.Valid == 0))
3360 {
3361 /* Remember it, but keep going */
3362 Status = STATUS_NOT_LOCKED;
3363 }
3364 else
3365 {
3366 /* Get the PFN */
3367 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3368 ASSERT(Pfn1 != NULL);
3369
3370 /* Check if all of the requested locks are present */
3371 if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
3372 ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
3373 {
3374 /* Remember it, but keep going */
3375 Status = STATUS_NOT_LOCKED;
3376
3377 /* Check if no lock is present */
3378 if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM))
3379 {
3380 DPRINT1("FIXME: Should remove the page from WS\n");
3381 }
3382 }
3383 }
3384
3385 /* Go to the next PTE */
3386 PointerPte++;
3387
3388 /* Check if we're on a PDE boundary */
3389 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3390 #if (_MI_PAGING_LEVELS >= 3)
3391 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3392 #endif
3393 #if (_MI_PAGING_LEVELS == 4)
3394 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3395 #endif
3396 } while (PointerPte <= LastPte);
3397
3398 /* Check if we hit a page that was not locked */
3399 if (Status == STATUS_NOT_LOCKED)
3400 {
3401 goto CleanupWithWsLock;
3402 }
3403
3404 /* All pages in the region were locked, so unlock them all */
3405
3406 /* Get the PTE and PDE */
3407 PointerPte = MiAddressToPte(*BaseAddress);
3408 PointerPde = MiAddressToPde(*BaseAddress);
3409 #if (_MI_PAGING_LEVELS >= 3)
3410 PointerPpe = MiAddressToPpe(*BaseAddress);
3411 #endif
3412 #if (_MI_PAGING_LEVELS == 4)
3413 PointerPxe = MiAddressToPxe(*BaseAddress);
3414 #endif
3415
3416 /* Loop the pages */
3417 do
3418 {
3419 /* Unlock it */
3420 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
3421 MI_UNLOCK_VA(Pfn1, MapType);
3422
3423 /* Go to the next PTE */
3424 PointerPte++;
3425
3426 /* Check if we're on a PDE boundary */
3427 if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
3428 #if (_MI_PAGING_LEVELS >= 3)
3429 if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
3430 #endif
3431 #if (_MI_PAGING_LEVELS == 4)
3432 if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
3433 #endif
3434 } while (PointerPte <= LastPte);
3435
3436 /* Everything is done */
3437 Status = STATUS_SUCCESS;
3438
3439 CleanupWithWsLock:
3440
3441 /* Release process working set */
3442 MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
3443
3444 Cleanup:
3445 /* Unlock address space */
3446 MmUnlockAddressSpace(AddressSpace);
3447
3448 return Status;
3449 }
3450
3451
3452 NTSTATUS
3453 NTAPI
3454 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
3455 IN OUT PVOID *BaseAddress,
3456 IN OUT PSIZE_T NumberOfBytesToUnlock,
3457 IN ULONG MapType)
3458 {
3459 PEPROCESS Process;
3460 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3461 NTSTATUS Status;
3462 BOOLEAN Attached = FALSE;
3463 KAPC_STATE ApcState;
3464 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3465 PVOID CapturedBaseAddress;
3466 SIZE_T CapturedBytesToUnlock;
3467 PAGED_CODE();
3468
3469 //
3470 // Validate flags
3471 //
3472 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3473 {
3474 //
3475 // Invalid set of flags
3476 //
3477 return STATUS_INVALID_PARAMETER;
3478 }
3479
3480 //
3481 // At least one flag must be specified
3482 //
3483 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3484 {
3485 //
3486 // No flag given
3487 //
3488 return STATUS_INVALID_PARAMETER;
3489 }
3490
3491 //
3492 // Enter SEH for probing
3493 //
3494 _SEH2_TRY
3495 {
3496 //
3497 // Validate output data
3498 //
3499 ProbeForWritePointer(BaseAddress);
3500 ProbeForWriteSize_t(NumberOfBytesToUnlock);
3501
3502 //
3503 // Capture it
3504 //
3505 CapturedBaseAddress = *BaseAddress;
3506 CapturedBytesToUnlock = *NumberOfBytesToUnlock;
3507 }
3508 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3509 {
3510 //
3511 // Get exception code
3512 //
3513 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3514 }
3515 _SEH2_END;
3516
3517 //
3518 // Catch illegal base address
3519 //
3520 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3521
3522 //
3523 // Catch illegal region size
3524 //
3525 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
3526 {
3527 //
3528 // Fail
3529 //
3530 return STATUS_INVALID_PARAMETER;
3531 }
3532
3533 //
3534 // 0 is also illegal
3535 //
3536 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
3537
3538 //
3539 // Get a reference to the process
3540 //
3541 Status = ObReferenceObjectByHandle(ProcessHandle,
3542 PROCESS_VM_OPERATION,
3543 PsProcessType,
3544 PreviousMode,
3545 (PVOID*)(&Process),
3546 NULL);
3547 if (!NT_SUCCESS(Status)) return Status;
3548
3549 //
3550 // Check if this is is system-mapped
3551 //
3552 if (MapType & MAP_SYSTEM)
3553 {
3554 //
3555 // Check for required privilege
3556 //
3557 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3558 {
3559 //
3560 // Fail: Don't have it
3561 //
3562 ObDereferenceObject(Process);
3563 return STATUS_PRIVILEGE_NOT_HELD;
3564 }
3565 }
3566
3567 //
3568 // Check if we should attach
3569 //
3570 if (CurrentProcess != Process)
3571 {
3572 //
3573 // Do it
3574 //
3575 KeStackAttachProcess(&Process->Pcb, &ApcState);
3576 Attached = TRUE;
3577 }
3578
3579 //
3580 // Call the internal function
3581 //
3582 Status = MiUnlockVirtualMemory(&CapturedBaseAddress,
3583 &CapturedBytesToUnlock,
3584 MapType);
3585
3586 //
3587 // Detach if needed
3588 //
3589 if (Attached) KeUnstackDetachProcess(&ApcState);
3590
3591 //
3592 // Release reference
3593 //
3594 ObDereferenceObject(Process);
3595
3596 //
3597 // Enter SEH to return data
3598 //
3599 _SEH2_TRY
3600 {
3601 //
3602 // Return data to user
3603 //
3604 *BaseAddress = CapturedBaseAddress;
3605 *NumberOfBytesToUnlock = CapturedBytesToUnlock;
3606 }
3607 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3608 {
3609 //
3610 // Get exception code
3611 //
3612 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3613 }
3614 _SEH2_END;
3615
3616 //
3617 // Return status
3618 //
3619 return STATUS_SUCCESS;
3620 }
3621
3622 NTSTATUS
3623 NTAPI
3624 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
3625 IN OUT PVOID *BaseAddress,
3626 IN OUT PSIZE_T NumberOfBytesToFlush,
3627 OUT PIO_STATUS_BLOCK IoStatusBlock)
3628 {
3629 PEPROCESS Process;
3630 NTSTATUS Status;
3631 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3632 PVOID CapturedBaseAddress;
3633 SIZE_T CapturedBytesToFlush;
3634 IO_STATUS_BLOCK LocalStatusBlock;
3635 PAGED_CODE();
3636
3637 //
3638 // Check if we came from user mode
3639 //
3640 if (PreviousMode != KernelMode)
3641 {
3642 //
3643 // Enter SEH for probing
3644 //
3645 _SEH2_TRY
3646 {
3647 //
3648 // Validate all outputs
3649 //
3650 ProbeForWritePointer(BaseAddress);
3651 ProbeForWriteSize_t(NumberOfBytesToFlush);
3652 ProbeForWriteIoStatusBlock(IoStatusBlock);
3653
3654 //
3655 // Capture them
3656 //
3657 CapturedBaseAddress = *BaseAddress;
3658 CapturedBytesToFlush = *NumberOfBytesToFlush;
3659 }
3660 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3661 {
3662 //
3663 // Get exception code
3664 //
3665 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3666 }
3667 _SEH2_END;
3668 }
3669 else
3670 {
3671 //
3672 // Capture directly
3673 //
3674 CapturedBaseAddress = *BaseAddress;
3675 CapturedBytesToFlush = *NumberOfBytesToFlush;
3676 }
3677
3678 //
3679 // Catch illegal base address
3680 //
3681 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3682
3683 //
3684 // Catch illegal region size
3685 //
3686 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
3687 {
3688 //
3689 // Fail
3690 //
3691 return STATUS_INVALID_PARAMETER;
3692 }
3693
3694 //
3695 // Get a reference to the process
3696 //
3697 Status = ObReferenceObjectByHandle(ProcessHandle,
3698 PROCESS_VM_OPERATION,
3699 PsProcessType,
3700 PreviousMode,
3701 (PVOID*)(&Process),
3702 NULL);
3703 if (!NT_SUCCESS(Status)) return Status;
3704
3705 //
3706 // Do it
3707 //
3708 Status = MmFlushVirtualMemory(Process,
3709 &CapturedBaseAddress,
3710 &CapturedBytesToFlush,
3711 &LocalStatusBlock);
3712
3713 //
3714 // Release reference
3715 //
3716 ObDereferenceObject(Process);
3717
3718 //
3719 // Enter SEH to return data
3720 //
3721 _SEH2_TRY
3722 {
3723 //
3724 // Return data to user
3725 //
3726 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
3727 *NumberOfBytesToFlush = 0;
3728 *IoStatusBlock = LocalStatusBlock;
3729 }
3730 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3731 {
3732 }
3733 _SEH2_END;
3734
3735 //
3736 // Return status
3737 //
3738 return Status;
3739 }
3740
3741 /*
3742 * @unimplemented
3743 */
3744 NTSTATUS
3745 NTAPI
3746 NtGetWriteWatch(IN HANDLE ProcessHandle,
3747 IN ULONG Flags,
3748 IN PVOID BaseAddress,
3749 IN SIZE_T RegionSize,
3750 IN PVOID *UserAddressArray,
3751 OUT PULONG_PTR EntriesInUserAddressArray,
3752 OUT PULONG Granularity)
3753 {
3754 PEPROCESS Process;
3755 NTSTATUS Status;
3756 PVOID EndAddress;
3757 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3758 ULONG_PTR CapturedEntryCount;
3759 PAGED_CODE();
3760
3761 //
3762 // Check if we came from user mode
3763 //
3764 if (PreviousMode != KernelMode)
3765 {
3766 //
3767 // Enter SEH for probing
3768 //
3769 _SEH2_TRY
3770 {
3771 //
3772 // Catch illegal base address
3773 //
3774 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2);
3775
3776 //
3777 // Catch illegal region size
3778 //
3779 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
3780 {
3781 //
3782 // Fail
3783 //
3784 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3);
3785 }
3786
3787 //
3788 // Validate all data
3789 //
3790 ProbeForWriteSize_t(EntriesInUserAddressArray);
3791 ProbeForWriteUlong(Granularity);
3792
3793 //
3794 // Capture them
3795 //
3796 CapturedEntryCount = *EntriesInUserAddressArray;
3797
3798 //
3799 // Must have a count
3800 //
3801 if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
3802
3803 //
3804 // Can't be larger than the maximum
3805 //
3806 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
3807 {
3808 //
3809 // Fail
3810 //
3811 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
3812 }
3813
3814 //
3815 // Probe the actual array
3816 //
3817 ProbeForWrite(UserAddressArray,
3818 CapturedEntryCount * sizeof(PVOID),
3819 sizeof(PVOID));
3820 }
3821 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3822 {
3823 //
3824 // Get exception code
3825 //
3826 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3827 }
3828 _SEH2_END;
3829 }
3830 else
3831 {
3832 //
3833 // Capture directly
3834 //
3835 CapturedEntryCount = *EntriesInUserAddressArray;
3836 ASSERT(CapturedEntryCount != 0);
3837 }
3838
3839 //
3840 // Check if this is a local request
3841 //
3842 if (ProcessHandle == NtCurrentProcess())
3843 {
3844 //
3845 // No need to reference the process
3846 //
3847 Process = PsGetCurrentProcess();
3848 }
3849 else
3850 {
3851 //
3852 // Reference the target
3853 //
3854 Status = ObReferenceObjectByHandle(ProcessHandle,
3855 PROCESS_VM_OPERATION,
3856 PsProcessType,
3857 PreviousMode,
3858 (PVOID *)&Process,
3859 NULL);
3860 if (!NT_SUCCESS(Status)) return Status;
3861 }
3862
3863 //
3864 // Compute the last address and validate it
3865 //
3866 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
3867 if (BaseAddress > EndAddress)
3868 {
3869 //
3870 // Fail
3871 //
3872 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3873 return STATUS_INVALID_PARAMETER_4;
3874 }
3875
3876 //
3877 // Oops :(
3878 //
3879 UNIMPLEMENTED;
3880
3881 //
3882 // Dereference if needed
3883 //
3884 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3885
3886 //
3887 // Enter SEH to return data
3888 //
3889 _SEH2_TRY
3890 {
3891 //
3892 // Return data to user
3893 //
3894 *EntriesInUserAddressArray = 0;
3895 *Granularity = PAGE_SIZE;
3896 }
3897 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3898 {
3899 //
3900 // Get exception code
3901 //
3902 Status = _SEH2_GetExceptionCode();
3903 }
3904 _SEH2_END;
3905
3906 //
3907 // Return success
3908 //
3909 return STATUS_SUCCESS;
3910 }
3911
3912 /*
3913 * @unimplemented
3914 */
3915 NTSTATUS
3916 NTAPI
3917 NtResetWriteWatch(IN HANDLE ProcessHandle,
3918 IN PVOID BaseAddress,
3919 IN SIZE_T RegionSize)
3920 {
3921 PVOID EndAddress;
3922 PEPROCESS Process;
3923 NTSTATUS Status;
3924 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3925 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
3926
3927 //
3928 // Catch illegal base address
3929 //
3930 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
3931
3932 //
3933 // Catch illegal region size
3934 //
3935 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
3936 {
3937 //
3938 // Fail
3939 //
3940 return STATUS_INVALID_PARAMETER_3;
3941 }
3942
3943 //
3944 // Check if this is a local request
3945 //
3946 if (ProcessHandle == NtCurrentProcess())
3947 {
3948 //
3949 // No need to reference the process
3950 //
3951 Process = PsGetCurrentProcess();
3952 }
3953 else
3954 {
3955 //
3956 // Reference the target
3957 //
3958 Status = ObReferenceObjectByHandle(ProcessHandle,
3959 PROCESS_VM_OPERATION,
3960 PsProcessType,
3961 PreviousMode,
3962 (PVOID *)&Process,
3963 NULL);
3964 if (!NT_SUCCESS(Status)) return Status;
3965 }
3966
3967 //
3968 // Compute the last address and validate it
3969 //
3970 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
3971 if (BaseAddress > EndAddress)
3972 {
3973 //
3974 // Fail
3975 //
3976 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3977 return STATUS_INVALID_PARAMETER_3;
3978 }
3979
3980 //
3981 // Oops :(
3982 //
3983 UNIMPLEMENTED;
3984
3985 //
3986 // Dereference if needed
3987 //
3988 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3989
3990 //
3991 // Return success
3992 //
3993 return STATUS_SUCCESS;
3994 }
3995
3996 NTSTATUS
3997 NTAPI
3998 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
3999 IN PVOID BaseAddress,
4000 IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
4001 OUT PVOID MemoryInformation,
4002 IN SIZE_T MemoryInformationLength,
4003 OUT PSIZE_T ReturnLength)
4004 {
4005 NTSTATUS Status = STATUS_SUCCESS;
4006 KPROCESSOR_MODE PreviousMode;
4007
4008 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
4009
4010 /* Bail out if the address is invalid */
4011 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
4012
4013 /* Probe return buffer */
4014 PreviousMode = ExGetPreviousMode();
4015 if (PreviousMode != KernelMode)
4016 {
4017 _SEH2_TRY
4018 {
4019 ProbeForWrite(MemoryInformation,
4020 MemoryInformationLength,
4021 sizeof(ULONG_PTR));
4022
4023 if (ReturnLength) ProbeForWriteSize_t(ReturnLength);
4024 }
4025 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4026 {
4027 Status = _SEH2_GetExceptionCode();
4028 }
4029 _SEH2_END;
4030
4031 if (!NT_SUCCESS(Status))
4032 {
4033 return Status;
4034 }
4035 }
4036
4037 switch(MemoryInformationClass)
4038 {
4039 case MemoryBasicInformation:
4040 /* Validate the size information of the class */
4041 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
4042 {
4043 /* The size is invalid */
4044 return STATUS_INFO_LENGTH_MISMATCH;
4045 }
4046 Status = MiQueryMemoryBasicInformation(ProcessHandle,
4047 BaseAddress,
4048 MemoryInformation,
4049 MemoryInformationLength,
4050 ReturnLength);
4051 break;
4052
4053 case MemorySectionName:
4054 /* Validate the size information of the class */
4055 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
4056 {
4057 /* The size is invalid */
4058 return STATUS_INFO_LENGTH_MISMATCH;
4059 }
4060 Status = MiQueryMemorySectionName(ProcessHandle,
4061 BaseAddress,
4062 MemoryInformation,
4063 MemoryInformationLength,
4064 ReturnLength);
4065 break;
4066 case MemoryWorkingSetList:
4067 case MemoryBasicVlmInformation:
4068 default:
4069 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
4070 break;
4071 }
4072
4073 return Status;
4074 }
4075
4076 /*
4077 * @implemented
4078 */
4079 NTSTATUS
4080 NTAPI
4081 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
4082 IN OUT PVOID* UBaseAddress,
4083 IN ULONG_PTR ZeroBits,
4084 IN OUT PSIZE_T URegionSize,
4085 IN ULONG AllocationType,
4086 IN ULONG Protect)
4087 {
4088 PEPROCESS Process;
4089 PMEMORY_AREA MemoryArea;
4090 PFN_NUMBER PageCount;
4091 PMMVAD Vad, FoundVad;
4092 NTSTATUS Status;
4093 PMMSUPPORT AddressSpace;
4094 PVOID PBaseAddress;
4095 ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
4096 PEPROCESS CurrentProcess = PsGetCurrentProcess();
4097 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
4098 PETHREAD CurrentThread = PsGetCurrentThread();
4099 KAPC_STATE ApcState;
4100 ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0;
4101 BOOLEAN Attached = FALSE, ChangeProtection = FALSE;
4102 MMPTE TempPte;
4103 PMMPTE PointerPte, PointerPde, LastPte;
4104 TABLE_SEARCH_RESULT Result;
4105 PMMADDRESS_NODE Parent;
4106 PAGED_CODE();
4107
4108 /* Check for valid Zero bits */
4109 if (ZeroBits > 21)
4110 {
4111 DPRINT1("Too many zero bits\n");
4112 return STATUS_INVALID_PARAMETER_3;
4113 }
4114
4115 /* Check for valid Allocation Types */
4116 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
4117 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
4118 {
4119 DPRINT1("Invalid Allocation Type\n");
4120 return STATUS_INVALID_PARAMETER_5;
4121 }
4122
4123 /* Check for at least one of these Allocation Types to be set */
4124 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
4125 {
4126 DPRINT1("No memory allocation base type\n");
4127 return STATUS_INVALID_PARAMETER_5;
4128 }
4129
4130 /* MEM_RESET is an exclusive flag, make sure that is valid too */
4131 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
4132 {
4133 DPRINT1("Invalid use of MEM_RESET\n");
4134 return STATUS_INVALID_PARAMETER_5;
4135 }
4136
4137 /* Check if large pages are being used */
4138 if (AllocationType & MEM_LARGE_PAGES)
4139 {
4140 /* Large page allocations MUST be committed */
4141 if (!(AllocationType & MEM_COMMIT))
4142 {
4143 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
4144 return STATUS_INVALID_PARAMETER_5;
4145 }
4146
4147 /* These flags are not allowed with large page allocations */
4148 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
4149 {
4150 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
4151 return STATUS_INVALID_PARAMETER_5;
4152 }
4153 }
4154
4155 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
4156 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
4157 {
4158 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4159 return STATUS_INVALID_PARAMETER_5;
4160 }
4161
4162 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
4163 if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
4164 {
4165 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
4166 return STATUS_INVALID_PARAMETER_5;
4167 }
4168
4169 /* Check for valid MEM_PHYSICAL usage */
4170 if (AllocationType & MEM_PHYSICAL)
4171 {
4172 /* Only these flags are allowed with MEM_PHYSIAL */
4173 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
4174 {
4175 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
4176 return STATUS_INVALID_PARAMETER_5;
4177 }
4178
4179 /* Then make sure PAGE_READWRITE is used */
4180 if (Protect != PAGE_READWRITE)
4181 {
4182 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
4183 return STATUS_INVALID_PARAMETER_6;
4184 }
4185 }
4186
4187 /* Calculate the protection mask and make sure it's valid */
4188 ProtectionMask = MiMakeProtectionMask(Protect);
4189 if (ProtectionMask == MM_INVALID_PROTECTION)
4190 {
4191 DPRINT1("Invalid protection mask\n");
4192 return STATUS_INVALID_PAGE_PROTECTION;
4193 }
4194
4195 /* Enter SEH */
4196 _SEH2_TRY
4197 {
4198 /* Check for user-mode parameters */
4199 if (PreviousMode != KernelMode)
4200 {
4201 /* Make sure they are writable */
4202 ProbeForWritePointer(UBaseAddress);
4203 ProbeForWriteUlong(URegionSize);
4204 }
4205
4206 /* Capture their values */
4207 PBaseAddress = *UBaseAddress;
4208 PRegionSize = *URegionSize;
4209 }
4210 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4211 {
4212 /* Return the exception code */
4213 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4214 }
4215 _SEH2_END;
4216
4217 /* Make sure the allocation isn't past the VAD area */
4218 if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
4219 {
4220 DPRINT1("Virtual allocation base above User Space\n");
4221 return STATUS_INVALID_PARAMETER_2;
4222 }
4223
4224 /* Make sure the allocation wouldn't overflow past the VAD area */
4225 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
4226 {
4227 DPRINT1("Region size would overflow into kernel-memory\n");
4228 return STATUS_INVALID_PARAMETER_4;
4229 }
4230
4231 /* Make sure there's a size specified */
4232 if (!PRegionSize)
4233 {
4234 DPRINT1("Region size is invalid (zero)\n");
4235 return STATUS_INVALID_PARAMETER_4;
4236 }
4237
4238 //
4239 // If this is for the current process, just use PsGetCurrentProcess
4240 //
4241 if (ProcessHandle == NtCurrentProcess())
4242 {
4243 Process = CurrentProcess;
4244 }
4245 else
4246 {
4247 //
4248 // Otherwise, reference the process with VM rights and attach to it if
4249 // this isn't the current process. We must attach because we'll be touching
4250 // PTEs and PDEs that belong to user-mode memory, and also touching the
4251 // Working Set which is stored in Hyperspace.
4252 //
4253 Status = ObReferenceObjectByHandle(ProcessHandle,
4254 PROCESS_VM_OPERATION,
4255 PsProcessType,
4256 PreviousMode,
4257 (PVOID*)&Process,
4258 NULL);
4259 if (!NT_SUCCESS(Status)) return Status;
4260 if (CurrentProcess != Process)
4261 {
4262 KeStackAttachProcess(&Process->Pcb, &ApcState);
4263 Attached = TRUE;
4264 }
4265 }
4266
4267 //
4268 // Check for large page allocations and make sure that the required privilege
4269 // is being held, before attempting to handle them.
4270 //
4271 if ((AllocationType & MEM_LARGE_PAGES) &&
4272 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)))
4273 {
4274 /* Fail without it */
4275 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
4276 Status = STATUS_PRIVILEGE_NOT_HELD;
4277 goto FailPathNoLock;
4278 }
4279
4280 //
4281 // Fail on the things we don't yet support
4282 //
4283 if (ZeroBits != 0)
4284 {
4285 DPRINT1("Zero bits not supported\n");
4286 Status = STATUS_INVALID_PARAMETER;
4287 goto FailPathNoLock;
4288 }
4289 if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES)
4290 {
4291 DPRINT1("MEM_LARGE_PAGES not supported\n");
4292 Status = STATUS_INVALID_PARAMETER;
4293 goto FailPathNoLock;
4294 }
4295 if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL)
4296 {
4297 DPRINT1("MEM_PHYSICAL not supported\n");
4298 Status = STATUS_INVALID_PARAMETER;
4299 goto FailPathNoLock;
4300 }
4301 if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH)
4302 {
4303 DPRINT1("MEM_WRITE_WATCH not supported\n");
4304 Status = STATUS_INVALID_PARAMETER;
4305 goto FailPathNoLock;
4306 }
4307 if ((AllocationType & MEM_TOP_DOWN) == MEM_TOP_DOWN)
4308 {
4309 DPRINT1("MEM_TOP_DOWN not supported\n");
4310 AllocationType &= ~MEM_TOP_DOWN;
4311 }
4312
4313 if (Process->VmTopDown == 1)
4314 {
4315 DPRINT1("VmTopDown not supported\n");
4316 Status = STATUS_INVALID_PARAMETER;
4317 goto FailPathNoLock;
4318 }
4319
4320 //
4321 // Check if the caller is reserving memory, or committing memory and letting
4322 // us pick the base address
4323 //
4324 if (!(PBaseAddress) || (AllocationType & MEM_RESERVE))
4325 {
4326 //
4327 // Do not allow COPY_ON_WRITE through this API
4328 //
4329 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
4330 {
4331 DPRINT1("Copy on write not allowed through this path\n");
4332 Status = STATUS_INVALID_PAGE_PROTECTION;
4333 goto FailPathNoLock;
4334 }
4335
4336 //
4337 // Does the caller have an address in mind, or is this a blind commit?
4338 //
4339 if (!PBaseAddress)
4340 {
4341 //
4342 // This is a blind commit, all we need is the region size
4343 //
4344 PRegionSize = ROUND_TO_PAGES(PRegionSize);
4345 PageCount = BYTES_TO_PAGES(PRegionSize);
4346 EndingAddress = 0;
4347 StartingAddress = 0;
4348 }
4349 else
4350 {
4351 //
4352 // This is a reservation, so compute the starting address on the
4353 // expected 64KB granularity, and see where the ending address will
4354 // fall based on the aligned address and the passed in region size
4355 //
4356 StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
4357 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4358 PageCount = BYTES_TO_PAGES(EndingAddress - StartingAddress);
4359 }
4360
4361 //
4362 // Allocate and initialize the VAD
4363 //
4364 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
4365 ASSERT(Vad != NULL);
4366 Vad->u.LongFlags = 0;
4367 if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
4368 Vad->u.VadFlags.Protection = ProtectionMask;
4369 Vad->u.VadFlags.PrivateMemory = 1;
4370 Vad->u.VadFlags.CommitCharge = AllocationType & MEM_COMMIT ? PageCount : 0;
4371
4372 //
4373 // Lock the address space and make sure the process isn't already dead
4374 //
4375 AddressSpace = MmGetCurrentAddressSpace();
4376 MmLockAddressSpace(AddressSpace);
4377 if (Process->VmDeleted)
4378 {
4379 Status = STATUS_PROCESS_IS_TERMINATING;
4380 goto FailPath;
4381 }
4382
4383 //
4384 // Did we have a base address? If no, find a valid address that is 64KB
4385 // aligned in the VAD tree. Otherwise, make sure that the address range
4386 // which was passed in isn't already conflicting with an existing address
4387 // range.
4388 //
4389 if (!PBaseAddress)
4390 {
4391 /* Which way should we search? */
4392 if (AllocationType & MEM_TOP_DOWN)
4393 {
4394 /* Find an address top-down */
4395 Result = MiFindEmptyAddressRangeDownTree(PRegionSize,
4396 (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS,
4397 _64K,
4398 &Process->VadRoot,
4399 &StartingAddress,
4400 &Parent);
4401 }
4402 else
4403 {
4404 /* Find an address bottom-up */
4405 Result = MiFindEmptyAddressRangeInTree(PRegionSize,
4406 _64K,
4407 &Process->VadRoot,
4408 &Parent,
4409 &StartingAddress);
4410 }
4411
4412 if (Result == TableFoundNode)
4413 {
4414 Status = STATUS_NO_MEMORY;
4415 goto FailPath;
4416 }
4417
4418 //
4419 // Now we know where the allocation ends. Make sure it doesn't end up
4420 // somewhere in kernel mode.
4421 //
4422 NT_ASSERT(StartingAddress != 0);
4423 EndingAddress = (StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4424 if ((PVOID)EndingAddress > MM_HIGHEST_VAD_ADDRESS)
4425 {
4426 Status = STATUS_NO_MEMORY;
4427 goto FailPath;
4428 }
4429 }
4430 else
4431 {
4432 /* Make sure it doesn't conflict with an existing allocation */
4433 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4434 EndingAddress >> PAGE_SHIFT,
4435 &Process->VadRoot,
4436 &Parent);
4437 if (Result == TableFoundNode)
4438 {
4439 //
4440 // The address specified is in conflict!
4441 //
4442 Status = STATUS_CONFLICTING_ADDRESSES;
4443 goto FailPath;
4444 }
4445 }
4446
4447 //
4448 // Write out the VAD fields for this allocation
4449 //
4450 Vad->StartingVpn = (ULONG_PTR)StartingAddress >> PAGE_SHIFT;
4451 Vad->EndingVpn = (ULONG_PTR)EndingAddress >> PAGE_SHIFT;
4452
4453 //
4454 // FIXME: Should setup VAD bitmap
4455 //
4456 Status = STATUS_SUCCESS;
4457
4458 //
4459 // Lock the working set and insert the VAD into the process VAD tree
4460 //
4461 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4462 Vad->ControlArea = NULL; // For Memory-Area hack
4463 Process->VadRoot.NodeHint = Vad;
4464 MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
4465 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
4466
4467 //
4468 // Update the virtual size of the process, and if this is now the highest
4469 // virtual size we have ever seen, update the peak virtual size to reflect
4470 // this.
4471 //
4472 Process->VirtualSize += PRegionSize;
4473 if (Process->VirtualSize > Process->PeakVirtualSize)
4474 {
4475 Process->PeakVirtualSize = Process->VirtualSize;
4476 }
4477
4478 //
4479 // Release address space and detach and dereference the target process if
4480 // it was different from the current process
4481 //
4482 MmUnlockAddressSpace(AddressSpace);
4483 if (Attached) KeUnstackDetachProcess(&ApcState);
4484 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4485
4486 //
4487 // Use SEH to write back the base address and the region size. In the case
4488 // of an exception, we do not return back the exception code, as the memory
4489 // *has* been allocated. The caller would now have to call VirtualQuery
4490 // or do some other similar trick to actually find out where its memory
4491 // allocation ended up
4492 //
4493 _SEH2_TRY
4494 {
4495 *URegionSize = PRegionSize;
4496 *UBaseAddress = (PVOID)StartingAddress;
4497 }
4498 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4499 {
4500 }
4501 _SEH2_END;
4502 return STATUS_SUCCESS;
4503 }
4504
4505 //
4506 // This is a MEM_COMMIT on top of an existing address which must have been
4507 // MEM_RESERVED already. Compute the start and ending base addresses based
4508 // on the user input, and then compute the actual region size once all the
4509 // alignments have been done.
4510 //
4511 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4512 EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
4513 PRegionSize = EndingAddress - StartingAddress + 1;
4514
4515 //
4516 // Lock the address space and make sure the process isn't already dead
4517 //
4518 AddressSpace = MmGetCurrentAddressSpace();
4519 MmLockAddressSpace(AddressSpace);
4520 if (Process->VmDeleted)
4521 {
4522 DPRINT1("Process is dying\n");
4523 Status = STATUS_PROCESS_IS_TERMINATING;
4524 goto FailPath;
4525 }
4526
4527 //
4528 // Get the VAD for this address range, and make sure it exists
4529 //
4530 Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4531 EndingAddress >> PAGE_SHIFT,
4532 &Process->VadRoot,
4533 (PMMADDRESS_NODE*)&FoundVad);
4534 if (Result != TableFoundNode)
4535 {
4536 DPRINT1("Could not find a VAD for this allocation\n");
4537 Status = STATUS_CONFLICTING_ADDRESSES;
4538 goto FailPath;
4539 }
4540
4541 if ((AllocationType & MEM_RESET) == MEM_RESET)
4542 {
4543 /// @todo HACK: pretend success
4544 DPRINT("MEM_RESET not supported\n");
4545 Status = STATUS_SUCCESS;
4546 goto FailPath;
4547 }
4548
4549 //
4550 // These kinds of VADs are illegal for this Windows function when trying to
4551 // commit an existing range
4552 //
4553 if ((FoundVad->u.VadFlags.VadType == VadAwe) ||
4554 (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
4555 (FoundVad->u.VadFlags.VadType == VadLargePages))
4556 {
4557 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4558 Status = STATUS_CONFLICTING_ADDRESSES;
4559 goto FailPath;
4560 }
4561
4562 //
4563 // Make sure that this address range actually fits within the VAD for it
4564 //
4565 if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
4566 ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
4567 {
4568 DPRINT1("Address range does not fit into the VAD\n");
4569 Status = STATUS_CONFLICTING_ADDRESSES;
4570 goto FailPath;
4571 }
4572
4573 //
4574 // Make sure this is an ARM3 section
4575 //
4576 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress));
4577 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
4578 {
4579 DPRINT1("Illegal commit of non-ARM3 section!\n");
4580 Status = STATUS_ALREADY_COMMITTED;
4581 goto FailPath;
4582 }
4583
4584 // Is this a previously reserved section being committed? If so, enter the
4585 // special section path
4586 //
4587 if (FoundVad->u.VadFlags.PrivateMemory == FALSE)
4588 {
4589 //
4590 // You cannot commit large page sections through this API
4591 //
4592 if (FoundVad->u.VadFlags.VadType == VadLargePageSection)
4593 {
4594 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4595 Status = STATUS_INVALID_PAGE_PROTECTION;
4596 goto FailPath;
4597 }
4598
4599 //
4600 // You can only use caching flags on a rotate VAD
4601 //
4602 if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) &&
4603 (FoundVad->u.VadFlags.VadType != VadRotatePhysical))
4604 {
4605 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4606 Status = STATUS_INVALID_PAGE_PROTECTION;
4607 goto FailPath;
4608 }
4609
4610 //
4611 // We should make sure that the section's permissions aren't being
4612 // messed with
4613 //
4614 if (FoundVad->u.VadFlags.NoChange)
4615 {
4616 //
4617 // Make sure it's okay to touch it
4618 //
4619 Status = MiCheckSecuredVad(FoundVad,
4620 PBaseAddress,
4621 PRegionSize,
4622 ProtectionMask);
4623 if (!NT_SUCCESS(Status))
4624 {
4625 DPRINT1("Secured VAD being messed around with\n");
4626 goto FailPath;
4627 }
4628 }
4629
4630 //
4631 // ARM3 does not support file-backed sections, only shared memory
4632 //
4633 ASSERT(FoundVad->ControlArea->FilePointer == NULL);
4634
4635 //
4636 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4637 //
4638 if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) &&
4639 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD)))
4640 {
4641 DPRINT1("Invalid page protection for rotate VAD\n");
4642 Status = STATUS_INVALID_PAGE_PROTECTION;
4643 goto FailPath;
4644 }
4645
4646 //
4647 // Compute PTE addresses and the quota charge, then grab the commit lock
4648 //
4649 PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT);
4650 LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT);
4651 QuotaCharge = (ULONG)(LastPte - PointerPte + 1);
4652 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
4653
4654 //
4655 // Get the segment template PTE and start looping each page
4656 //
4657 TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate;
4658 ASSERT(TempPte.u.Long != 0);
4659 while (PointerPte <= LastPte)
4660 {
4661 //
4662 // For each non-already-committed page, write the invalid template PTE
4663 //
4664 if (PointerPte->u.Long == 0)
4665 {
4666 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4667 }
4668 else
4669 {
4670 QuotaFree++;
4671 }
4672 PointerPte++;
4673 }
4674
4675 //
4676 // Now do the commit accounting and release the lock
4677 //
4678 ASSERT(QuotaCharge >= QuotaFree);
4679 QuotaCharge -= QuotaFree;
4680 FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge;
4681 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
4682
4683 //
4684 // We are done with committing the section pages
4685 //
4686 Status = STATUS_SUCCESS;
4687 goto FailPath;
4688 }
4689
4690 //
4691 // This is a specific ReactOS check because we only use normal VADs
4692 //
4693 ASSERT(FoundVad->u.VadFlags.VadType == VadNone);
4694
4695 //
4696 // While this is an actual Windows check
4697 //
4698 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
4699
4700 //
4701 // Throw out attempts to use copy-on-write through this API path
4702 //
4703 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
4704 {
4705 DPRINT1("Write copy attempted when not allowed\n");
4706 Status = STATUS_INVALID_PAGE_PROTECTION;
4707 goto FailPath;
4708 }
4709
4710 //
4711 // Initialize a demand-zero PTE
4712 //
4713 TempPte.u.Long = 0;
4714 TempPte.u.Soft.Protection = ProtectionMask;
4715 NT_ASSERT(TempPte.u.Long != 0);
4716
4717 //
4718 // Get the PTE, PDE and the last PTE for this address range
4719 //
4720 PointerPde = MiAddressToPde(StartingAddress);
4721 PointerPte = MiAddressToPte(StartingAddress);
4722 LastPte = MiAddressToPte(EndingAddress);
4723
4724 //
4725 // Update the commit charge in the VAD as well as in the process, and check
4726 // if this commit charge was now higher than the last recorded peak, in which
4727 // case we also update the peak
4728 //
4729 FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte);
4730 Process->CommitCharge += (1 + LastPte - PointerPte);
4731 if (Process->CommitCharge > Process->CommitChargePeak)
4732 {
4733 Process->CommitChargePeak = Process->CommitCharge;
4734 }
4735
4736 //
4737 // Lock the working set while we play with user pages and page tables
4738 //
4739 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4740
4741 //
4742 // Make the current page table valid, and then loop each page within it
4743 //
4744 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4745 while (PointerPte <= LastPte)
4746 {
4747 //
4748 // Have we crossed into a new page table?
4749 //
4750 if (MiIsPteOnPdeBoundary(PointerPte))
4751 {
4752 //
4753 // Get the PDE and now make it valid too
4754 //
4755 PointerPde = MiAddressToPte(PointerPte);
4756 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4757 }
4758
4759 //
4760 // Is this a zero PTE as expected?
4761 //
4762 if (PointerPte->u.Long == 0)
4763 {
4764 //
4765 // First increment the count of pages in the page table for this
4766 // process
4767 //
4768 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
4769
4770 //
4771 // And now write the invalid demand-zero PTE as requested
4772 //
4773 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4774 }
4775 else if (PointerPte->u.Long == MmDecommittedPte.u.Long)
4776 {
4777 //
4778 // If the PTE was already decommitted, there is nothing else to do
4779 // but to write the new demand-zero PTE
4780 //
4781 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4782 }
4783 else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte)))
4784 {
4785 //
4786 // We don't handle these scenarios yet
4787 //
4788 if (PointerPte->u.Soft.Valid == 0)
4789 {
4790 ASSERT(PointerPte->u.Soft.Prototype == 0);
4791 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
4792 }
4793
4794 //
4795 // There's a change in protection, remember this for later, but do
4796 // not yet handle it.
4797 //
4798 ChangeProtection = TRUE;
4799 }
4800
4801 //
4802 // Move to the next PTE
4803 //
4804 PointerPte++;
4805 }
4806
4807 //
4808 // Release the working set lock, unlock the address space, and detach from
4809 // the target process if it was not the current process. Also dereference the
4810 // target process if this wasn't the case.
4811 //
4812 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
4813 Status = STATUS_SUCCESS;
4814 FailPath:
4815 MmUnlockAddressSpace(AddressSpace);
4816
4817 //
4818 // Check if we need to update the protection
4819 //
4820 if (ChangeProtection)
4821 {
4822 PVOID ProtectBaseAddress = (PVOID)StartingAddress;
4823 SIZE_T ProtectSize = PRegionSize;
4824 ULONG OldProtection;
4825
4826 //
4827 // Change the protection of the region
4828 //
4829 MiProtectVirtualMemory(Process,
4830 &ProtectBaseAddress,
4831 &ProtectSize,
4832 Protect,
4833 &OldProtection);
4834 }
4835
4836 FailPathNoLock:
4837 if (Attached) KeUnstackDetachProcess(&ApcState);
4838 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4839
4840 //
4841 // Use SEH to write back the base address and the region size. In the case
4842 // of an exception, we strangely do return back the exception code, even
4843 // though the memory *has* been allocated. This mimics Windows behavior and
4844 // there is not much we can do about it.
4845 //
4846 _SEH2_TRY
4847 {
4848 *URegionSize = PRegionSize;
4849 *UBaseAddress = (PVOID)StartingAddress;
4850 }
4851 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4852 {
4853 Status = _SEH2_GetExceptionCode();
4854 }
4855 _SEH2_END;
4856 return Status;
4857 }
4858
4859 /*
4860 * @implemented
4861 */
4862 NTSTATUS
4863 NTAPI
4864 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
4865 IN PVOID* UBaseAddress,
4866 IN PSIZE_T URegionSize,
4867 IN ULONG FreeType)
4868 {
4869 PMEMORY_AREA MemoryArea;
4870 SIZE_T PRegionSize;
4871 PVOID PBaseAddress;
4872 LONG_PTR CommitReduction = 0;
4873 ULONG_PTR StartingAddress, EndingAddress;
4874 PMMVAD Vad;
4875 NTSTATUS Status;
4876 PEPROCESS Process;
4877 PMMSUPPORT AddressSpace;
4878 PETHREAD CurrentThread = PsGetCurrentThread();
4879 PEPROCESS CurrentProcess = PsGetCurrentProcess();
4880 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
4881 KAPC_STATE ApcState;
4882 BOOLEAN Attached = FALSE;
4883 PAGED_CODE();
4884
4885 //
4886 // Only two flags are supported
4887 //
4888 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
4889 {
4890 DPRINT1("Invalid FreeType\n");
4891 return STATUS_INVALID_PARAMETER_4;
4892 }
4893
4894 //
4895 // Check if no flag was used, or if both flags were used
4896 //
4897 if (!((FreeType & (MEM_DECOMMIT | MEM_RELEASE))) ||
4898 ((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == (MEM_DECOMMIT | MEM_RELEASE)))
4899 {
4900 DPRINT1("Invalid FreeType combination\n");
4901 return STATUS_INVALID_PARAMETER_4;
4902 }
4903
4904 //
4905 // Enter SEH for probe and capture. On failure, return back to the caller
4906 // with an exception violation.
4907 //
4908 _SEH2_TRY
4909 {
4910 //
4911 // Check for user-mode parameters and make sure that they are writeable
4912 //
4913 if (PreviousMode != KernelMode)
4914 {
4915 ProbeForWritePointer(UBaseAddress);
4916 ProbeForWriteUlong(URegionSize);
4917 }
4918
4919 //
4920 // Capture the current values
4921 //
4922 PBaseAddress = *UBaseAddress;
4923 PRegionSize = *URegionSize;
4924 }
4925 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4926 {
4927 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4928 }
4929 _SEH2_END;
4930
4931 //
4932 // Make sure the allocation isn't past the user area
4933 //
4934 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
4935 {
4936 DPRINT1("Virtual free base above User Space\n");
4937 return STATUS_INVALID_PARAMETER_2;
4938 }
4939
4940 //
4941 // Make sure the allocation wouldn't overflow past the user area
4942 //
4943 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
4944 {
4945 DPRINT1("Region size would overflow into kernel-memory\n");
4946 return STATUS_INVALID_PARAMETER_3;
4947 }
4948
4949 //
4950 // If this is for the current process, just use PsGetCurrentProcess
4951 //
4952 if (ProcessHandle == NtCurrentProcess())
4953 {
4954 Process = CurrentProcess;
4955 }
4956 else
4957 {
4958 //
4959 // Otherwise, reference the process with VM rights and attach to it if
4960 // this isn't the current process. We must attach because we'll be touching
4961 // PTEs and PDEs that belong to user-mode memory, and also touching the
4962 // Working Set which is stored in Hyperspace.
4963 //
4964 Status = ObReferenceObjectByHandle(ProcessHandle,
4965 PROCESS_VM_OPERATION,
4966 PsProcessType,
4967 PreviousMode,
4968 (PVOID*)&Process,
4969 NULL);
4970 if (!NT_SUCCESS(Status)) return Status;
4971 if (CurrentProcess != Process)
4972 {
4973 KeStackAttachProcess(&Process->Pcb, &ApcState);
4974 Attached = TRUE;
4975 }
4976 }
4977
4978 //
4979 // Lock the address space
4980 //
4981 AddressSpace = MmGetCurrentAddressSpace();
4982 MmLockAddressSpace(AddressSpace);
4983
4984 //
4985 // If the address space is being deleted, fail the de-allocation since it's
4986 // too late to do anything about it
4987 //
4988 if (Process->VmDeleted)
4989 {
4990 DPRINT1("Process is dead\n");
4991 Status = STATUS_PROCESS_IS_TERMINATING;
4992 goto FailPath;
4993 }
4994
4995 //
4996 // Compute start and end addresses, and locate the VAD
4997 //
4998 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4999 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
5000 Vad = MiLocateAddress((PVOID)StartingAddress);
5001 if (!Vad)
5002 {
5003 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress);
5004 Status = STATUS_MEMORY_NOT_ALLOCATED;
5005 goto FailPath;
5006 }
5007
5008 //
5009 // If the range exceeds the VAD's ending VPN, fail this request
5010 //
5011 if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT))
5012 {
5013 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress);
5014 Status = STATUS_UNABLE_TO_FREE_VM;
5015 goto FailPath;
5016 }
5017
5018 //
5019 // Only private memory (except rotate VADs) can be freed through here */
5020 //
5021 if ((!(Vad->u.VadFlags.PrivateMemory) &&
5022 (Vad->u.VadFlags.VadType != VadRotatePhysical)) ||
5023 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory))
5024 {
5025 DPRINT1("Attempt to free section memory\n");
5026 Status = STATUS_UNABLE_TO_DELETE_SECTION;
5027 goto FailPath;
5028 }
5029
5030 //
5031 // ARM3 does not yet handle protected VM
5032 //
5033 ASSERT(Vad->u.VadFlags.NoChange == 0);
5034
5035 //
5036 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
5037 // and that is is an ARM3 memory area, and not a section view, as we currently
5038 // don't support freeing those though this interface.
5039 //
5040 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5041 ASSERT(MemoryArea);
5042 ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
5043
5044 //
5045 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
5046 //
5047 if (FreeType & MEM_RELEASE)
5048 {
5049 //
5050 // ARM3 only supports this VAD in this path
5051 //
5052 ASSERT(Vad->u.VadFlags.VadType == VadNone);
5053
5054 //
5055 // Is the caller trying to remove the whole VAD, or remove only a portion
5056 // of it? If no region size is specified, then the assumption is that the
5057 // whole VAD is to be destroyed
5058 //
5059 if (!PRegionSize)
5060 {
5061 //
5062 // The caller must specify the base address identically to the range
5063 // that is stored in the VAD.
5064 //
5065 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5066 {
5067 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress);
5068 Status = STATUS_FREE_VM_NOT_AT_BASE;
5069 goto FailPath;
5070 }
5071
5072 //
5073 // Now compute the actual start/end addresses based on the VAD
5074 //
5075 StartingAddress = Vad->StartingVpn << PAGE_SHIFT;
5076 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5077
5078 //
5079 // Finally lock the working set and remove the VAD from the VAD tree
5080 //
5081 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5082 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5083 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5084 }
5085 else
5086 {
5087 //
5088 // This means the caller wants to release a specific region within
5089 // the range. We have to find out which range this is -- the following
5090 // possibilities exist plus their union (CASE D):
5091 //
5092 // STARTING ADDRESS ENDING ADDRESS
5093 // [<========][========================================][=========>]
5094 // CASE A CASE B CASE C
5095 //
5096 //
5097 // First, check for case A or D
5098 //
5099 if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn)
5100 {
5101 //
5102 // Check for case D
5103 //
5104 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5105 {
5106 //
5107 // This is the easiest one to handle -- it is identical to
5108 // the code path above when the caller sets a zero region size
5109 // and the whole VAD is destroyed
5110 //
5111 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5112 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
5113 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
5114 }
5115 else
5116 {
5117 //
5118 // This case is pretty easy too -- we compute a bunch of
5119 // pages to decommit, and then push the VAD's starting address
5120 // a bit further down, then decrement the commit charge
5121 //
5122 // NOT YET IMPLEMENTED IN ARM3.
5123 //
5124 DPRINT1("Case A not handled\n");
5125 Status = STATUS_FREE_VM_NOT_AT_BASE;
5126 goto FailPath;
5127
5128 //
5129 // After analyzing the VAD, set it to NULL so that we don't
5130 // free it in the exit path
5131 //
5132 Vad = NULL;
5133 }
5134 }
5135 else
5136 {
5137 //
5138 // This is case B or case C. First check for case C
5139 //
5140 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
5141 {
5142 PMEMORY_AREA MemoryArea;
5143
5144 //
5145 // This is pretty easy and similar to case A. We compute the
5146 // amount of pages to decommit, update the VAD's commit charge
5147 // and then change the ending address of the VAD to be a bit
5148 // smaller.
5149 //
5150 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
5151 CommitReduction = MiCalculatePageCommitment(StartingAddress,
5152 EndingAddress,
5153 Vad,
5154 Process);
5155 Vad->u.VadFlags.CommitCharge -= CommitReduction;
5156 // For ReactOS: shrink the corresponding memory area
5157 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
5158 ASSERT(Vad->StartingVpn << PAGE_SHIFT == (ULONG_PTR)MemoryArea->StartingAddress);
5159 ASSERT((Vad->EndingVpn + 1) << PAGE_SHIFT == (ULONG_PTR)MemoryArea->EndingAddress);
5160 Vad->EndingVpn = ((ULONG_PTR)StartingAddress - 1) >> PAGE_SHIFT;
5161 MemoryArea->EndingAddress = (PVOID)(((Vad->EndingVpn + 1) << PAGE_SHIFT) - 1);
5162 }
5163 else
5164 {
5165 //
5166 // This is case B and the hardest one. Because we are removing
5167 // a chunk of memory from the very middle of the VAD, we must
5168 // actually split the VAD into two new VADs and compute the
5169 // commit charges for each of them, and reinsert new charges.
5170 //
5171 // NOT YET IMPLEMENTED IN ARM3.
5172 //
5173 DPRINT1("Case B not handled\n");
5174 Status = STATUS_FREE_VM_NOT_AT_BASE;
5175 goto FailPath;
5176 }
5177
5178 //
5179 // After analyzing the VAD, set it to NULL so that we don't
5180 // free it in the exit path
5181 //
5182 Vad = NULL;
5183 }
5184 }
5185
5186 //
5187 // Now we have a range of pages to dereference, so call the right API
5188 // to do that and then release the working set, since we're done messing
5189 // around with process pages.
5190 //
5191 MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL);
5192 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
5193 Status = STATUS_SUCCESS;
5194
5195 FinalPath:
5196 //
5197 // Update the process counters
5198 //
5199 PRegionSize = EndingAddress - StartingAddress + 1;
5200 Process->CommitCharge -= CommitReduction;
5201 if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize;
5202
5203 //
5204 // Unlock the address space and free the VAD in failure cases. Next,
5205 // detach from the target process so we can write the region size and the
5206 // base address to the correct source process, and dereference the target
5207 // process.
5208 //
5209 MmUnlockAddressSpace(AddressSpace);
5210 if (Vad) ExFreePool(Vad);
5211 if (Attached) KeUnstackDetachProcess(&ApcState);
5212 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5213
5214 //
5215 // Use SEH to safely return the region size and the base address of the
5216 // deallocation. If we get an access violation, don't return a failure code
5217 // as the deallocation *has* happened. The caller will just have to figure
5218 // out another way to find out where it is (such as VirtualQuery).
5219 //
5220 _SEH2_TRY
5221 {
5222 *URegionSize = PRegionSize;
5223 *UBaseAddress = (PVOID)StartingAddress;
5224 }
5225 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
5226 {
5227 }
5228 _SEH2_END;
5229 return Status;
5230 }
5231
5232 //
5233 // This is the decommit path. You cannot decommit from the following VADs in
5234 // Windows, so fail the vall
5235 //
5236 if ((Vad->u.VadFlags.VadType == VadAwe) ||
5237 (Vad->u.VadFlags.VadType == VadLargePages) ||
5238 (Vad->u.VadFlags.VadType == VadRotatePhysical))
5239 {
5240 DPRINT1("Trying to decommit from invalid VAD\n");
5241 Status = STATUS_MEMORY_NOT_ALLOCATED;
5242 goto FailPath;
5243 }
5244
5245 //
5246 // If the caller did not specify a region size, first make sure that this
5247 // region is actually committed. If it is, then compute the ending address
5248 // based on the VAD.
5249 //
5250 if (!PRegionSize)
5251 {
5252 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
5253 {
5254 DPRINT1("Decomitting non-committed memory\n");
5255 Status = STATUS_FREE_VM_NOT_AT_BASE;
5256 goto FailPath;
5257 }
5258 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
5259 }
5260
5261 //
5262 // Decommit the PTEs for the range plus the actual backing pages for the
5263 // range, then reduce that amount from the commit charge in the VAD
5264 //
5265 CommitReduction = MiAddressToPte(EndingAddress) -
5266 MiAddressToPte(StartingAddress) +
5267 1 -
5268 MiDecommitPages((PVOID)StartingAddress,
5269 MiAddressToPte(EndingAddress),
5270 Process,
5271 Vad);
5272 ASSERT(CommitReduction >= 0);
5273 Vad->u.VadFlags.CommitCharge -= CommitReduction;
5274 ASSERT(Vad->u.VadFlags.CommitCharge >= 0);
5275
5276 //
5277 // We are done, go to the exit path without freeing the VAD as it remains
5278 // valid since we have not released the allocation.
5279 //
5280 Vad = NULL;
5281 Status = STATUS_SUCCESS;
5282 goto FinalPath;
5283
5284 //
5285 // In the failure path, we detach and derefernece the target process, and
5286 // return whatever failure code was sent.
5287 //
5288 FailPath:
5289 MmUnlockAddressSpace(AddressSpace);
5290 if (Attached) KeUnstackDetachProcess(&ApcState);
5291 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
5292 return Status;
5293 }
5294
5295
5296 PHYSICAL_ADDRESS
5297 NTAPI
5298 MmGetPhysicalAddress(PVOID Address)
5299 {
5300 PHYSICAL_ADDRESS PhysicalAddress;
5301 MMPDE TempPde;
5302 MMPTE TempPte;
5303
5304 /* Check if the PXE/PPE/PDE is valid */
5305 if (
5306 #if (_MI_PAGING_LEVELS == 4)
5307 (MiAddressToPxe(Address)->u.Hard.Valid) &&
5308 #endif
5309 #if (_MI_PAGING_LEVELS >= 3)
5310 (MiAddressToPpe(Address)->u.Hard.Valid) &&
5311 #endif
5312 (MiAddressToPde(Address)->u.Hard.Valid))
5313 {
5314 /* Check for large pages */
5315 TempPde = *MiAddressToPde(Address);
5316 if (TempPde.u.Hard.LargePage)
5317 {
5318 /* Physical address is base page + large page offset */
5319 PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT;
5320 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1));
5321 return PhysicalAddress;
5322 }
5323
5324 /* Check if the PTE is valid */
5325 TempPte = *MiAddressToPte(Address);
5326 if (TempPte.u.Hard.Valid)
5327 {
5328 /* Physical address is base page + page offset */
5329 PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
5330 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
5331 return PhysicalAddress;
5332 }
5333 }
5334
5335 KeRosDumpStackFrames(NULL, 20);
5336 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
5337 PhysicalAddress.QuadPart = 0;
5338 return PhysicalAddress;
5339 }
5340
5341
5342 /* EOF */