[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_WRITE_INVALID_PTE(PointerPte, MmZeroPte);
354 }
355
356 /* Actual legitimate pages */
357 ActualPages++;
358 }
359 else
360 {
361 /*
362 * The only other ARM3 possibility is a demand zero page, which would
363 * mean freeing some of the paged pool pages that haven't even been
364 * touched yet, as part of a larger allocation.
365 *
366 * Right now, we shouldn't expect any page file information in the PTE
367 */
368 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
369
370 /* Destroy the PTE */
371 MI_WRITE_INVALID_PTE(PointerPte, MmZeroPte);
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_WRITE_INVALID_PTE(PointerPte, MmZeroPte);
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_WRITE_INVALID_PTE(PointerPte, MmZeroPte);
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_WRITE_INVALID_PTE(PointerPte, MmZeroPte);
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 ASSERT(NT_SUCCESS(Status));
1696 }
1697 else
1698 {
1699 /* Build the initial information block */
1700 Address = PAGE_ALIGN(BaseAddress);
1701 MemoryInfo.BaseAddress = Address;
1702 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
1703 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
1704 MemoryInfo.Type = MEM_PRIVATE;
1705
1706 /* Find the largest chunk of memory which has the same state and protection mask */
1707 MemoryInfo.State = MiQueryAddressState(Address,
1708 Vad,
1709 TargetProcess,
1710 &MemoryInfo.Protect,
1711 &NextAddress);
1712 Address = NextAddress;
1713 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
1714 {
1715 /* Keep going unless the state or protection mask changed */
1716 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
1717 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
1718 Address = NextAddress;
1719 }
1720
1721 /* Now that we know the last VA address, calculate the region size */
1722 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
1723 }
1724
1725 /* Unlock the address space of the process */
1726 MmUnlockAddressSpace(&TargetProcess->Vm);
1727
1728 /* Check if we were attached */
1729 if (ProcessHandle != NtCurrentProcess())
1730 {
1731 /* Detach and derefernece the process */
1732 KeUnstackDetachProcess(&ApcState);
1733 ObDereferenceObject(TargetProcess);
1734 }
1735
1736 /* Return the data, NtQueryInformation already probed it*/
1737 if (PreviousMode != KernelMode)
1738 {
1739 _SEH2_TRY
1740 {
1741 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1742 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1743 }
1744 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1745 {
1746 Status = _SEH2_GetExceptionCode();
1747 }
1748 _SEH2_END;
1749 }
1750 else
1751 {
1752 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
1753 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
1754 }
1755
1756 /* All went well */
1757 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
1758 "State: %lx Type: %lx Size: %lx\n",
1759 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
1760 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
1761 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
1762
1763 return Status;
1764 }
1765
1766 BOOLEAN
1767 NTAPI
1768 MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
1769 IN ULONG_PTR EndingAddress,
1770 IN PMMVAD Vad,
1771 IN PEPROCESS Process)
1772 {
1773 PMMPTE PointerPte, LastPte, PointerPde;
1774 BOOLEAN OnBoundary = TRUE;
1775 PAGED_CODE();
1776
1777 /* Get the PDE and PTE addresses */
1778 PointerPde = MiAddressToPde(StartingAddress);
1779 PointerPte = MiAddressToPte(StartingAddress);
1780 LastPte = MiAddressToPte(EndingAddress);
1781
1782 /* Loop all the PTEs */
1783 while (PointerPte <= LastPte)
1784 {
1785 /* Check if we've hit an new PDE boundary */
1786 if (OnBoundary)
1787 {
1788 /* Is this PDE demand zero? */
1789 PointerPde = MiAddressToPte(PointerPte);
1790 if (PointerPde->u.Long != 0)
1791 {
1792 /* It isn't -- is it valid? */
1793 if (PointerPde->u.Hard.Valid == 0)
1794 {
1795 /* Nope, fault it in */
1796 PointerPte = MiPteToAddress(PointerPde);
1797 MiMakeSystemAddressValid(PointerPte, Process);
1798 }
1799 }
1800 else
1801 {
1802 /* The PTE was already valid, so move to the next one */
1803 PointerPde++;
1804 PointerPte = MiPteToAddress(PointerPde);
1805
1806 /* Is the entire VAD committed? If not, fail */
1807 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1808
1809 /* Everything is committed so far past the range, return true */
1810 if (PointerPte > LastPte) return TRUE;
1811 }
1812 }
1813
1814 /* Is the PTE demand zero? */
1815 if (PointerPte->u.Long == 0)
1816 {
1817 /* Is the entire VAD committed? If not, fail */
1818 if (!Vad->u.VadFlags.MemCommit) return FALSE;
1819 }
1820 else
1821 {
1822 /* It isn't -- is it a decommited, invalid, or faulted PTE? */
1823 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
1824 (PointerPte->u.Hard.Valid == 0) &&
1825 ((PointerPte->u.Soft.Prototype == 0) ||
1826 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
1827 {
1828 /* Then part of the range is decommitted, so fail */
1829 return FALSE;
1830 }
1831 }
1832
1833 /* Move to the next PTE */
1834 PointerPte++;
1835 OnBoundary = MiIsPteOnPdeBoundary(PointerPte);
1836 }
1837
1838 /* All PTEs seem valid, and no VAD checks failed, the range is okay */
1839 return TRUE;
1840 }
1841
1842 NTSTATUS
1843 NTAPI
1844 MiRosProtectVirtualMemory(IN PEPROCESS Process,
1845 IN OUT PVOID *BaseAddress,
1846 IN OUT PSIZE_T NumberOfBytesToProtect,
1847 IN ULONG NewAccessProtection,
1848 OUT PULONG OldAccessProtection OPTIONAL)
1849 {
1850 PMEMORY_AREA MemoryArea;
1851 PMMSUPPORT AddressSpace;
1852 ULONG OldAccessProtection_;
1853 NTSTATUS Status;
1854
1855 *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
1856 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
1857
1858 AddressSpace = &Process->Vm;
1859 MmLockAddressSpace(AddressSpace);
1860 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
1861 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
1862 {
1863 MmUnlockAddressSpace(AddressSpace);
1864 return STATUS_UNSUCCESSFUL;
1865 }
1866
1867 if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
1868
1869 ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
1870 Status = MmProtectSectionView(AddressSpace,
1871 MemoryArea,
1872 *BaseAddress,
1873 *NumberOfBytesToProtect,
1874 NewAccessProtection,
1875 OldAccessProtection);
1876
1877 MmUnlockAddressSpace(AddressSpace);
1878
1879 return Status;
1880 }
1881
1882 NTSTATUS
1883 NTAPI
1884 MiProtectVirtualMemory(IN PEPROCESS Process,
1885 IN OUT PVOID *BaseAddress,
1886 IN OUT PSIZE_T NumberOfBytesToProtect,
1887 IN ULONG NewAccessProtection,
1888 OUT PULONG OldAccessProtection OPTIONAL)
1889 {
1890 PMEMORY_AREA MemoryArea;
1891 PMMVAD Vad;
1892 PMMSUPPORT AddressSpace;
1893 ULONG_PTR StartingAddress, EndingAddress;
1894 PMMPTE PointerPde, PointerPte, LastPte;
1895 MMPTE PteContents;
1896 PMMPFN Pfn1;
1897 ULONG ProtectionMask, OldProtect;
1898 BOOLEAN Committed;
1899 NTSTATUS Status = STATUS_SUCCESS;
1900 PETHREAD Thread = PsGetCurrentThread();
1901
1902 /* Calculate base address for the VAD */
1903 StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
1904 EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
1905
1906 /* Calculate the protection mask and make sure it's valid */
1907 ProtectionMask = MiMakeProtectionMask(NewAccessProtection);
1908 if (ProtectionMask == MM_INVALID_PROTECTION)
1909 {
1910 DPRINT1("Invalid protection mask\n");
1911 return STATUS_INVALID_PAGE_PROTECTION;
1912 }
1913
1914 /* Check for ROS specific memory area */
1915 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
1916 if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW))
1917 {
1918 /* Evil hack */
1919 return MiRosProtectVirtualMemory(Process,
1920 BaseAddress,
1921 NumberOfBytesToProtect,
1922 NewAccessProtection,
1923 OldAccessProtection);
1924 }
1925
1926 /* Lock the address space and make sure the process isn't already dead */
1927 AddressSpace = MmGetCurrentAddressSpace();
1928 MmLockAddressSpace(AddressSpace);
1929 if (Process->VmDeleted)
1930 {
1931 DPRINT1("Process is dying\n");
1932 Status = STATUS_PROCESS_IS_TERMINATING;
1933 goto FailPath;
1934 }
1935
1936 /* Get the VAD for this address range, and make sure it exists */
1937 Vad = (PMMVAD)MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
1938 EndingAddress >> PAGE_SHIFT,
1939 &Process->VadRoot);
1940 if (!Vad)
1941 {
1942 DPRINT("Could not find a VAD for this allocation\n");
1943 Status = STATUS_CONFLICTING_ADDRESSES;
1944 goto FailPath;
1945 }
1946
1947 /* Make sure the address is within this VAD's boundaries */
1948 if ((((ULONG_PTR)StartingAddress >> PAGE_SHIFT) < Vad->StartingVpn) ||
1949 (((ULONG_PTR)EndingAddress >> PAGE_SHIFT) > Vad->EndingVpn))
1950 {
1951 Status = STATUS_CONFLICTING_ADDRESSES;
1952 goto FailPath;
1953 }
1954
1955 /* These kinds of VADs are not supported atm */
1956 if ((Vad->u.VadFlags.VadType == VadAwe) ||
1957 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
1958 (Vad->u.VadFlags.VadType == VadLargePages))
1959 {
1960 DPRINT1("Illegal VAD for attempting to set protection\n");
1961 Status = STATUS_CONFLICTING_ADDRESSES;
1962 goto FailPath;
1963 }
1964
1965 /* Check for a VAD whose protection can't be changed */
1966 if (Vad->u.VadFlags.NoChange == 1)
1967 {
1968 DPRINT1("Trying to change protection of a NoChange VAD\n");
1969 Status = STATUS_INVALID_PAGE_PROTECTION;
1970 goto FailPath;
1971 }
1972
1973 /* Is this section, or private memory? */
1974 if (Vad->u.VadFlags.PrivateMemory == 0)
1975 {
1976 /* Not yet supported */
1977 if (Vad->u.VadFlags.VadType == VadLargePageSection)
1978 {
1979 DPRINT1("Illegal VAD for attempting to set protection\n");
1980 Status = STATUS_CONFLICTING_ADDRESSES;
1981 goto FailPath;
1982 }
1983
1984 /* Rotate VADs are not yet supported */
1985 if (Vad->u.VadFlags.VadType == VadRotatePhysical)
1986 {
1987 DPRINT1("Illegal VAD for attempting to set protection\n");
1988 Status = STATUS_CONFLICTING_ADDRESSES;
1989 goto FailPath;
1990 }
1991
1992 /* Not valid on section files */
1993 if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
1994 {
1995 /* Fail */
1996 DPRINT1("Invalid protection flags for section\n");
1997 Status = STATUS_INVALID_PARAMETER_4;
1998 goto FailPath;
1999 }
2000
2001 /* Check if data or page file mapping protection PTE is compatible */
2002 if (!Vad->ControlArea->u.Flags.Image)
2003 {
2004 /* Not yet */
2005 DPRINT1("Fixme: Not checking for valid protection\n");
2006 }
2007
2008 /* This is a section, and this is not yet supported */
2009 DPRINT1("Section protection not yet supported\n");
2010 OldProtect = 0;
2011 }
2012 else
2013 {
2014 /* Private memory, check protection flags */
2015 if ((NewAccessProtection & PAGE_WRITECOPY) ||
2016 (NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
2017 {
2018 DPRINT1("Invalid protection flags for private memory\n");
2019 Status = STATUS_INVALID_PARAMETER_4;
2020 goto FailPath;
2021 }
2022
2023 /* Lock the working set */
2024 MiLockProcessWorkingSetUnsafe(Process, Thread);
2025
2026 /* Check if all pages in this range are committed */
2027 Committed = MiIsEntireRangeCommitted(StartingAddress,
2028 EndingAddress,
2029 Vad,
2030 Process);
2031 if (!Committed)
2032 {
2033 /* Fail */
2034 DPRINT1("The entire range is not committed\n");
2035 Status = STATUS_NOT_COMMITTED;
2036 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2037 goto FailPath;
2038 }
2039
2040 /* Compute starting and ending PTE and PDE addresses */
2041 PointerPde = MiAddressToPde(StartingAddress);
2042 PointerPte = MiAddressToPte(StartingAddress);
2043 LastPte = MiAddressToPte(EndingAddress);
2044
2045 /* Make this PDE valid */
2046 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2047
2048 /* Save protection of the first page */
2049 if (PointerPte->u.Long != 0)
2050 {
2051 /* Capture the page protection and make the PDE valid */
2052 OldProtect = MiGetPageProtection(PointerPte);
2053 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2054 }
2055 else
2056 {
2057 /* Grab the old protection from the VAD itself */
2058 OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2059 }
2060
2061 /* Loop all the PTEs now */
2062 while (PointerPte <= LastPte)
2063 {
2064 /* Check if we've crossed a PDE boundary and make the new PDE valid too */
2065 if (MiIsPteOnPdeBoundary(PointerPte))
2066 {
2067 PointerPde = MiAddressToPte(PointerPte);
2068 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2069 }
2070
2071 /* Capture the PTE and check if it was empty */
2072 PteContents = *PointerPte;
2073 if (PteContents.u.Long == 0)
2074 {
2075 /* This used to be a zero PTE and it no longer is, so we must add a
2076 reference to the pagetable. */
2077 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2078 }
2079
2080 /* Check what kind of PTE we are dealing with */
2081 if (PteContents.u.Hard.Valid == 1)
2082 {
2083 /* Get the PFN entry */
2084 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2085
2086 /* We don't support this yet */
2087 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2088
2089 /* Check if the page should not be accessible at all */
2090 if ((NewAccessProtection & PAGE_NOACCESS) ||
2091 (NewAccessProtection & PAGE_GUARD))
2092 {
2093 /* The page should be in the WS and we should make it transition now */
2094 DPRINT1("Making valid page invalid is not yet supported!\n");
2095 Status = STATUS_NOT_IMPLEMENTED;
2096 /* Unlock the working set */
2097 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2098 goto FailPath;
2099 }
2100
2101 /* Write the protection mask and write it with a TLB flush */
2102 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2103 MiFlushTbAndCapture(Vad,
2104 PointerPte,
2105 ProtectionMask,
2106 Pfn1,
2107 TRUE);
2108 }
2109 else
2110 {
2111 /* We don't support these cases yet */
2112 ASSERT(PteContents.u.Soft.Prototype == 0);
2113 ASSERT(PteContents.u.Soft.Transition == 0);
2114
2115 /* The PTE is already demand-zero, just update the protection mask */
2116 PteContents.u.Soft.Protection = ProtectionMask;
2117 MI_WRITE_INVALID_PTE(PointerPte, PteContents);
2118 ASSERT(PointerPte->u.Long != 0);
2119 }
2120
2121 /* Move to the next PTE */
2122 PointerPte++;
2123 }
2124
2125 /* Unlock the working set */
2126 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2127 }
2128
2129 /* Unlock the address space */
2130 MmUnlockAddressSpace(AddressSpace);
2131
2132 /* Return parameters and success */
2133 *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
2134 *BaseAddress = (PVOID)StartingAddress;
2135 *OldAccessProtection = OldProtect;
2136 return STATUS_SUCCESS;
2137
2138 FailPath:
2139 /* Unlock the address space and return the failure code */
2140 MmUnlockAddressSpace(AddressSpace);
2141 return Status;
2142 }
2143
2144 VOID
2145 NTAPI
2146 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde,
2147 IN PEPROCESS TargetProcess,
2148 IN KIRQL OldIrql)
2149 {
2150 PMMPTE PointerPte, PointerPpe, PointerPxe;
2151
2152 //
2153 // Sanity checks. The latter is because we only use this function with the
2154 // PFN lock not held, so it may go away in the future.
2155 //
2156 ASSERT(KeAreAllApcsDisabled() == TRUE);
2157 ASSERT(OldIrql == MM_NOIRQL);
2158
2159 //
2160 // Also get the PPE and PXE. This is okay not to #ifdef because they will
2161 // return the same address as the PDE on 2-level page table systems.
2162 //
2163 // If everything is already valid, there is nothing to do.
2164 //
2165 PointerPpe = MiAddressToPte(PointerPde);
2166 PointerPxe = MiAddressToPde(PointerPde);
2167 if ((PointerPxe->u.Hard.Valid) &&
2168 (PointerPpe->u.Hard.Valid) &&
2169 (PointerPde->u.Hard.Valid))
2170 {
2171 return;
2172 }
2173
2174 //
2175 // At least something is invalid, so begin by getting the PTE for the PDE itself
2176 // and then lookup each additional level. We must do it in this precise order
2177 // because the pagfault.c code (as well as in Windows) depends that the next
2178 // level up (higher) must be valid when faulting a lower level
2179 //
2180 PointerPte = MiPteToAddress(PointerPde);
2181 do
2182 {
2183 //
2184 // Make sure APCs continued to be disabled
2185 //
2186 ASSERT(KeAreAllApcsDisabled() == TRUE);
2187
2188 //
2189 // First, make the PXE valid if needed
2190 //
2191 if (!PointerPxe->u.Hard.Valid)
2192 {
2193 MiMakeSystemAddressValid(PointerPpe, TargetProcess);
2194 ASSERT(PointerPxe->u.Hard.Valid == 1);
2195 }
2196
2197 //
2198 // Next, the PPE
2199 //
2200 if (!PointerPpe->u.Hard.Valid)
2201 {
2202 MiMakeSystemAddressValid(PointerPde, TargetProcess);
2203 ASSERT(PointerPpe->u.Hard.Valid == 1);
2204 }
2205
2206 //
2207 // And finally, make the PDE itself valid.
2208 //
2209 MiMakeSystemAddressValid(PointerPte, TargetProcess);
2210
2211 //
2212 // This should've worked the first time so the loop is really just for
2213 // show -- ASSERT that we're actually NOT going to be looping.
2214 //
2215 ASSERT(PointerPxe->u.Hard.Valid == 1);
2216 ASSERT(PointerPpe->u.Hard.Valid == 1);
2217 ASSERT(PointerPde->u.Hard.Valid == 1);
2218 } while (!(PointerPxe->u.Hard.Valid) ||
2219 !(PointerPpe->u.Hard.Valid) ||
2220 !(PointerPde->u.Hard.Valid));
2221 }
2222
2223 VOID
2224 NTAPI
2225 MiProcessValidPteList(IN PMMPTE *ValidPteList,
2226 IN ULONG Count)
2227 {
2228 KIRQL OldIrql;
2229 ULONG i;
2230 MMPTE TempPte;
2231 PFN_NUMBER PageFrameIndex;
2232 PMMPFN Pfn1, Pfn2;
2233
2234 //
2235 // Acquire the PFN lock and loop all the PTEs in the list
2236 //
2237 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2238 for (i = 0; i != Count; i++)
2239 {
2240 //
2241 // The PTE must currently be valid
2242 //
2243 TempPte = *ValidPteList[i];
2244 ASSERT(TempPte.u.Hard.Valid == 1);
2245
2246 //
2247 // Get the PFN entry for the page itself, and then for its page table
2248 //
2249 PageFrameIndex = PFN_FROM_PTE(&TempPte);
2250 Pfn1 = MiGetPfnEntry(PageFrameIndex);
2251 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
2252
2253 //
2254 // Decrement the share count on the page table, and then on the page
2255 // itself
2256 //
2257 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
2258 MI_SET_PFN_DELETED(Pfn1);
2259 MiDecrementShareCount(Pfn1, PageFrameIndex);
2260
2261 //
2262 // Make the page decommitted
2263 //
2264 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte);
2265 }
2266
2267 //
2268 // All the PTEs have been dereferenced and made invalid, flush the TLB now
2269 // and then release the PFN lock
2270 //
2271 KeFlushCurrentTb();
2272 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2273 }
2274
2275 ULONG
2276 NTAPI
2277 MiDecommitPages(IN PVOID StartingAddress,
2278 IN PMMPTE EndingPte,
2279 IN PEPROCESS Process,
2280 IN PMMVAD Vad)
2281 {
2282 PMMPTE PointerPde, PointerPte, CommitPte = NULL;
2283 ULONG CommitReduction = 0;
2284 PMMPTE ValidPteList[256];
2285 ULONG PteCount = 0;
2286 PMMPFN Pfn1;
2287 MMPTE PteContents;
2288 PETHREAD CurrentThread = PsGetCurrentThread();
2289
2290 //
2291 // Get the PTE and PTE for the address, and lock the working set
2292 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the
2293 // commited range ends so that we can do the right accounting.
2294 //
2295 PointerPde = MiAddressToPde(StartingAddress);
2296 PointerPte = MiAddressToPte(StartingAddress);
2297 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
2298 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
2299
2300 //
2301 // Make the PDE valid, and now loop through each page's worth of data
2302 //
2303 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2304 while (PointerPte <= EndingPte)
2305 {
2306 //
2307 // Check if we've crossed a PDE boundary
2308 //
2309 if (MiIsPteOnPdeBoundary(PointerPte))
2310 {
2311 //
2312 // Get the new PDE and flush the valid PTEs we had built up until
2313 // now. This helps reduce the amount of TLB flushing we have to do.
2314 // Note that Windows does a much better job using timestamps and
2315 // such, and does not flush the entire TLB all the time, but right
2316 // now we have bigger problems to worry about than TLB flushing.
2317 //
2318 PointerPde = MiAddressToPde(StartingAddress);
2319 if (PteCount)
2320 {
2321 MiProcessValidPteList(ValidPteList, PteCount);
2322 PteCount = 0;
2323 }
2324
2325 //
2326 // Make this PDE valid
2327 //
2328 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2329 }
2330
2331 //
2332 // Read this PTE. It might be active or still demand-zero.
2333 //
2334 PteContents = *PointerPte;
2335 if (PteContents.u.Long)
2336 {
2337 //
2338 // The PTE is active. It might be valid and in a working set, or
2339 // it might be a prototype PTE or paged out or even in transition.
2340 //
2341 if (PointerPte->u.Long == MmDecommittedPte.u.Long)
2342 {
2343 //
2344 // It's already decommited, so there's nothing for us to do here
2345 //
2346 CommitReduction++;
2347 }
2348 else
2349 {
2350 //
2351 // Remove it from the counters, and check if it was valid or not
2352 //
2353 //Process->NumberOfPrivatePages--;
2354 if (PteContents.u.Hard.Valid)
2355 {
2356 //
2357 // It's valid. At this point make sure that it is not a ROS
2358 // PFN. Also, we don't support ProtoPTEs in this code path.
2359 //
2360 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber);
2361 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
2362 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE);
2363
2364 //
2365 // Flush any pending PTEs that we had not yet flushed, if our
2366 // list has gotten too big, then add this PTE to the flush list.
2367 //
2368 if (PteCount == 256)
2369 {
2370 MiProcessValidPteList(ValidPteList, PteCount);
2371 PteCount = 0;
2372 }
2373 ValidPteList[PteCount++] = PointerPte;
2374 }
2375 else
2376 {
2377 //
2378 // We do not support any of these other scenarios at the moment
2379 //
2380 ASSERT(PteContents.u.Soft.Prototype == 0);
2381 ASSERT(PteContents.u.Soft.Transition == 0);
2382 ASSERT(PteContents.u.Soft.PageFileHigh == 0);
2383
2384 //
2385 // So the only other possibility is that it is still a demand
2386 // zero PTE, in which case we undo the accounting we did
2387 // earlier and simply make the page decommitted.
2388 //
2389 //Process->NumberOfPrivatePages++;
2390 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2391 }
2392 }
2393 }
2394 else
2395 {
2396 //
2397 // This used to be a zero PTE and it no longer is, so we must add a
2398 // reference to the pagetable.
2399 //
2400 MiIncrementPageTableReferences(StartingAddress);
2401
2402 //
2403 // Next, we account for decommitted PTEs and make the PTE as such
2404 //
2405 if (PointerPte > CommitPte) CommitReduction++;
2406 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte);
2407 }
2408
2409 //
2410 // Move to the next PTE and the next address
2411 //
2412 PointerPte++;
2413 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE);
2414 }
2415
2416 //
2417 // Flush any dangling PTEs from the loop in the last page table, and then
2418 // release the working set and return the commit reduction accounting.
2419 //
2420 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
2421 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
2422 return CommitReduction;
2423 }
2424
2425 /* PUBLIC FUNCTIONS ***********************************************************/
2426
2427 /*
2428 * @unimplemented
2429 */
2430 PVOID
2431 NTAPI
2432 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
2433 {
2434 UNIMPLEMENTED;
2435 return 0;
2436 }
2437
2438 /*
2439 * @unimplemented
2440 */
2441 PVOID
2442 NTAPI
2443 MmSecureVirtualMemory(IN PVOID Address,
2444 IN SIZE_T Length,
2445 IN ULONG Mode)
2446 {
2447 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2448 return Address;
2449 }
2450
2451 /*
2452 * @unimplemented
2453 */
2454 VOID
2455 NTAPI
2456 MmUnsecureVirtualMemory(IN PVOID SecureMem)
2457 {
2458 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
2459 }
2460
2461 /* SYSTEM CALLS ***************************************************************/
2462
2463 NTSTATUS
2464 NTAPI
2465 NtReadVirtualMemory(IN HANDLE ProcessHandle,
2466 IN PVOID BaseAddress,
2467 OUT PVOID Buffer,
2468 IN SIZE_T NumberOfBytesToRead,
2469 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
2470 {
2471 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2472 PEPROCESS Process;
2473 NTSTATUS Status = STATUS_SUCCESS;
2474 SIZE_T BytesRead = 0;
2475 PAGED_CODE();
2476
2477 //
2478 // Check if we came from user mode
2479 //
2480 if (PreviousMode != KernelMode)
2481 {
2482 //
2483 // Validate the read addresses
2484 //
2485 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
2486 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
2487 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
2488 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
2489 {
2490 //
2491 // Don't allow to write into kernel space
2492 //
2493 return STATUS_ACCESS_VIOLATION;
2494 }
2495
2496 //
2497 // Enter SEH for probe
2498 //
2499 _SEH2_TRY
2500 {
2501 //
2502 // Probe the output value
2503 //
2504 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
2505 }
2506 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2507 {
2508 //
2509 // Get exception code
2510 //
2511 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2512 }
2513 _SEH2_END;
2514 }
2515
2516 //
2517 // Don't do zero-byte transfers
2518 //
2519 if (NumberOfBytesToRead)
2520 {
2521 //
2522 // Reference the process
2523 //
2524 Status = ObReferenceObjectByHandle(ProcessHandle,
2525 PROCESS_VM_READ,
2526 PsProcessType,
2527 PreviousMode,
2528 (PVOID*)(&Process),
2529 NULL);
2530 if (NT_SUCCESS(Status))
2531 {
2532 //
2533 // Do the copy
2534 //
2535 Status = MmCopyVirtualMemory(Process,
2536 BaseAddress,
2537 PsGetCurrentProcess(),
2538 Buffer,
2539 NumberOfBytesToRead,
2540 PreviousMode,
2541 &BytesRead);
2542
2543 //
2544 // Dereference the process
2545 //
2546 ObDereferenceObject(Process);
2547 }
2548 }
2549
2550 //
2551 // Check if the caller sent this parameter
2552 //
2553 if (NumberOfBytesRead)
2554 {
2555 //
2556 // Enter SEH to guard write
2557 //
2558 _SEH2_TRY
2559 {
2560 //
2561 // Return the number of bytes read
2562 //
2563 *NumberOfBytesRead = BytesRead;
2564 }
2565 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2566 {
2567 }
2568 _SEH2_END;
2569 }
2570
2571 //
2572 // Return status
2573 //
2574 return Status;
2575 }
2576
2577 NTSTATUS
2578 NTAPI
2579 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
2580 IN PVOID BaseAddress,
2581 IN PVOID Buffer,
2582 IN SIZE_T NumberOfBytesToWrite,
2583 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
2584 {
2585 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2586 PEPROCESS Process;
2587 NTSTATUS Status = STATUS_SUCCESS;
2588 SIZE_T BytesWritten = 0;
2589 PAGED_CODE();
2590
2591 //
2592 // Check if we came from user mode
2593 //
2594 if (PreviousMode != KernelMode)
2595 {
2596 //
2597 // Validate the read addresses
2598 //
2599 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
2600 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
2601 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
2602 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
2603 {
2604 //
2605 // Don't allow to write into kernel space
2606 //
2607 return STATUS_ACCESS_VIOLATION;
2608 }
2609
2610 //
2611 // Enter SEH for probe
2612 //
2613 _SEH2_TRY
2614 {
2615 //
2616 // Probe the output value
2617 //
2618 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
2619 }
2620 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2621 {
2622 //
2623 // Get exception code
2624 //
2625 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2626 }
2627 _SEH2_END;
2628 }
2629
2630 //
2631 // Don't do zero-byte transfers
2632 //
2633 if (NumberOfBytesToWrite)
2634 {
2635 //
2636 // Reference the process
2637 //
2638 Status = ObReferenceObjectByHandle(ProcessHandle,
2639 PROCESS_VM_WRITE,
2640 PsProcessType,
2641 PreviousMode,
2642 (PVOID*)&Process,
2643 NULL);
2644 if (NT_SUCCESS(Status))
2645 {
2646 //
2647 // Do the copy
2648 //
2649 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
2650 Buffer,
2651 Process,
2652 BaseAddress,
2653 NumberOfBytesToWrite,
2654 PreviousMode,
2655 &BytesWritten);
2656
2657 //
2658 // Dereference the process
2659 //
2660 ObDereferenceObject(Process);
2661 }
2662 }
2663
2664 //
2665 // Check if the caller sent this parameter
2666 //
2667 if (NumberOfBytesWritten)
2668 {
2669 //
2670 // Enter SEH to guard write
2671 //
2672 _SEH2_TRY
2673 {
2674 //
2675 // Return the number of bytes written
2676 //
2677 *NumberOfBytesWritten = BytesWritten;
2678 }
2679 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2680 {
2681 }
2682 _SEH2_END;
2683 }
2684
2685 //
2686 // Return status
2687 //
2688 return Status;
2689 }
2690
2691 NTSTATUS
2692 NTAPI
2693 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
2694 IN OUT PVOID *UnsafeBaseAddress,
2695 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
2696 IN ULONG NewAccessProtection,
2697 OUT PULONG UnsafeOldAccessProtection)
2698 {
2699 PEPROCESS Process;
2700 ULONG OldAccessProtection;
2701 ULONG Protection;
2702 PEPROCESS CurrentProcess = PsGetCurrentProcess();
2703 PVOID BaseAddress = NULL;
2704 SIZE_T NumberOfBytesToProtect = 0;
2705 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2706 NTSTATUS Status;
2707 BOOLEAN Attached = FALSE;
2708 KAPC_STATE ApcState;
2709 PAGED_CODE();
2710
2711 //
2712 // Check for valid protection flags
2713 //
2714 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
2715 if (Protection != PAGE_NOACCESS &&
2716 Protection != PAGE_READONLY &&
2717 Protection != PAGE_READWRITE &&
2718 Protection != PAGE_WRITECOPY &&
2719 Protection != PAGE_EXECUTE &&
2720 Protection != PAGE_EXECUTE_READ &&
2721 Protection != PAGE_EXECUTE_READWRITE &&
2722 Protection != PAGE_EXECUTE_WRITECOPY)
2723 {
2724 //
2725 // Fail
2726 //
2727 return STATUS_INVALID_PAGE_PROTECTION;
2728 }
2729
2730 //
2731 // Check if we came from user mode
2732 //
2733 if (PreviousMode != KernelMode)
2734 {
2735 //
2736 // Enter SEH for probing
2737 //
2738 _SEH2_TRY
2739 {
2740 //
2741 // Validate all outputs
2742 //
2743 ProbeForWritePointer(UnsafeBaseAddress);
2744 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
2745 ProbeForWriteUlong(UnsafeOldAccessProtection);
2746
2747 //
2748 // Capture them
2749 //
2750 BaseAddress = *UnsafeBaseAddress;
2751 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2752 }
2753 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2754 {
2755 //
2756 // Get exception code
2757 //
2758 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2759 }
2760 _SEH2_END;
2761 }
2762 else
2763 {
2764 //
2765 // Capture directly
2766 //
2767 BaseAddress = *UnsafeBaseAddress;
2768 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
2769 }
2770
2771 //
2772 // Catch illegal base address
2773 //
2774 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2775
2776 //
2777 // Catch illegal region size
2778 //
2779 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
2780 {
2781 //
2782 // Fail
2783 //
2784 return STATUS_INVALID_PARAMETER_3;
2785 }
2786
2787 //
2788 // 0 is also illegal
2789 //
2790 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
2791
2792 //
2793 // Get a reference to the process
2794 //
2795 Status = ObReferenceObjectByHandle(ProcessHandle,
2796 PROCESS_VM_OPERATION,
2797 PsProcessType,
2798 PreviousMode,
2799 (PVOID*)(&Process),
2800 NULL);
2801 if (!NT_SUCCESS(Status)) return Status;
2802
2803 //
2804 // Check if we should attach
2805 //
2806 if (CurrentProcess != Process)
2807 {
2808 //
2809 // Do it
2810 //
2811 KeStackAttachProcess(&Process->Pcb, &ApcState);
2812 Attached = TRUE;
2813 }
2814
2815 //
2816 // Do the actual work
2817 //
2818 Status = MiProtectVirtualMemory(Process,
2819 &BaseAddress,
2820 &NumberOfBytesToProtect,
2821 NewAccessProtection,
2822 &OldAccessProtection);
2823
2824 //
2825 // Detach if needed
2826 //
2827 if (Attached) KeUnstackDetachProcess(&ApcState);
2828
2829 //
2830 // Release reference
2831 //
2832 ObDereferenceObject(Process);
2833
2834 //
2835 // Enter SEH to return data
2836 //
2837 _SEH2_TRY
2838 {
2839 //
2840 // Return data to user
2841 //
2842 *UnsafeOldAccessProtection = OldAccessProtection;
2843 *UnsafeBaseAddress = BaseAddress;
2844 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
2845 }
2846 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2847 {
2848 }
2849 _SEH2_END;
2850
2851 //
2852 // Return status
2853 //
2854 return Status;
2855 }
2856
2857 NTSTATUS
2858 NTAPI
2859 NtLockVirtualMemory(IN HANDLE ProcessHandle,
2860 IN OUT PVOID *BaseAddress,
2861 IN OUT PSIZE_T NumberOfBytesToLock,
2862 IN ULONG MapType)
2863 {
2864 PEPROCESS Process;
2865 PEPROCESS CurrentProcess = PsGetCurrentProcess();
2866 NTSTATUS Status;
2867 BOOLEAN Attached = FALSE;
2868 KAPC_STATE ApcState;
2869 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2870 PVOID CapturedBaseAddress;
2871 SIZE_T CapturedBytesToLock;
2872 PAGED_CODE();
2873
2874 //
2875 // Validate flags
2876 //
2877 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
2878 {
2879 //
2880 // Invalid set of flags
2881 //
2882 return STATUS_INVALID_PARAMETER;
2883 }
2884
2885 //
2886 // At least one flag must be specified
2887 //
2888 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
2889 {
2890 //
2891 // No flag given
2892 //
2893 return STATUS_INVALID_PARAMETER;
2894 }
2895
2896 //
2897 // Enter SEH for probing
2898 //
2899 _SEH2_TRY
2900 {
2901 //
2902 // Validate output data
2903 //
2904 ProbeForWritePointer(BaseAddress);
2905 ProbeForWriteSize_t(NumberOfBytesToLock);
2906
2907 //
2908 // Capture it
2909 //
2910 CapturedBaseAddress = *BaseAddress;
2911 CapturedBytesToLock = *NumberOfBytesToLock;
2912 }
2913 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2914 {
2915 //
2916 // Get exception code
2917 //
2918 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2919 }
2920 _SEH2_END;
2921
2922 //
2923 // Catch illegal base address
2924 //
2925 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
2926
2927 //
2928 // Catch illegal region size
2929 //
2930 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
2931 {
2932 //
2933 // Fail
2934 //
2935 return STATUS_INVALID_PARAMETER;
2936 }
2937
2938 //
2939 // 0 is also illegal
2940 //
2941 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
2942
2943 //
2944 // Get a reference to the process
2945 //
2946 Status = ObReferenceObjectByHandle(ProcessHandle,
2947 PROCESS_VM_OPERATION,
2948 PsProcessType,
2949 PreviousMode,
2950 (PVOID*)(&Process),
2951 NULL);
2952 if (!NT_SUCCESS(Status)) return Status;
2953
2954 //
2955 // Check if this is is system-mapped
2956 //
2957 if (MapType & MAP_SYSTEM)
2958 {
2959 //
2960 // Check for required privilege
2961 //
2962 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
2963 {
2964 //
2965 // Fail: Don't have it
2966 //
2967 ObDereferenceObject(Process);
2968 return STATUS_PRIVILEGE_NOT_HELD;
2969 }
2970 }
2971
2972 //
2973 // Check if we should attach
2974 //
2975 if (CurrentProcess != Process)
2976 {
2977 //
2978 // Do it
2979 //
2980 KeStackAttachProcess(&Process->Pcb, &ApcState);
2981 Attached = TRUE;
2982 }
2983
2984 //
2985 // Oops :(
2986 //
2987 UNIMPLEMENTED;
2988
2989 //
2990 // Detach if needed
2991 //
2992 if (Attached) KeUnstackDetachProcess(&ApcState);
2993
2994 //
2995 // Release reference
2996 //
2997 ObDereferenceObject(Process);
2998
2999 //
3000 // Enter SEH to return data
3001 //
3002 _SEH2_TRY
3003 {
3004 //
3005 // Return data to user
3006 //
3007 *BaseAddress = CapturedBaseAddress;
3008 *NumberOfBytesToLock = 0;
3009 }
3010 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3011 {
3012 //
3013 // Get exception code
3014 //
3015 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3016 }
3017 _SEH2_END;
3018
3019 //
3020 // Return status
3021 //
3022 return STATUS_SUCCESS;
3023 }
3024
3025 NTSTATUS
3026 NTAPI
3027 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
3028 IN OUT PVOID *BaseAddress,
3029 IN OUT PSIZE_T NumberOfBytesToUnlock,
3030 IN ULONG MapType)
3031 {
3032 PEPROCESS Process;
3033 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3034 NTSTATUS Status;
3035 BOOLEAN Attached = FALSE;
3036 KAPC_STATE ApcState;
3037 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3038 PVOID CapturedBaseAddress;
3039 SIZE_T CapturedBytesToUnlock;
3040 PAGED_CODE();
3041
3042 //
3043 // Validate flags
3044 //
3045 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
3046 {
3047 //
3048 // Invalid set of flags
3049 //
3050 return STATUS_INVALID_PARAMETER;
3051 }
3052
3053 //
3054 // At least one flag must be specified
3055 //
3056 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
3057 {
3058 //
3059 // No flag given
3060 //
3061 return STATUS_INVALID_PARAMETER;
3062 }
3063
3064 //
3065 // Enter SEH for probing
3066 //
3067 _SEH2_TRY
3068 {
3069 //
3070 // Validate output data
3071 //
3072 ProbeForWritePointer(BaseAddress);
3073 ProbeForWriteSize_t(NumberOfBytesToUnlock);
3074
3075 //
3076 // Capture it
3077 //
3078 CapturedBaseAddress = *BaseAddress;
3079 CapturedBytesToUnlock = *NumberOfBytesToUnlock;
3080 }
3081 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3082 {
3083 //
3084 // Get exception code
3085 //
3086 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3087 }
3088 _SEH2_END;
3089
3090 //
3091 // Catch illegal base address
3092 //
3093 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3094
3095 //
3096 // Catch illegal region size
3097 //
3098 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
3099 {
3100 //
3101 // Fail
3102 //
3103 return STATUS_INVALID_PARAMETER;
3104 }
3105
3106 //
3107 // 0 is also illegal
3108 //
3109 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
3110
3111 //
3112 // Get a reference to the process
3113 //
3114 Status = ObReferenceObjectByHandle(ProcessHandle,
3115 PROCESS_VM_OPERATION,
3116 PsProcessType,
3117 PreviousMode,
3118 (PVOID*)(&Process),
3119 NULL);
3120 if (!NT_SUCCESS(Status)) return Status;
3121
3122 //
3123 // Check if this is is system-mapped
3124 //
3125 if (MapType & MAP_SYSTEM)
3126 {
3127 //
3128 // Check for required privilege
3129 //
3130 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3131 {
3132 //
3133 // Fail: Don't have it
3134 //
3135 ObDereferenceObject(Process);
3136 return STATUS_PRIVILEGE_NOT_HELD;
3137 }
3138 }
3139
3140 //
3141 // Check if we should attach
3142 //
3143 if (CurrentProcess != Process)
3144 {
3145 //
3146 // Do it
3147 //
3148 KeStackAttachProcess(&Process->Pcb, &ApcState);
3149 Attached = TRUE;
3150 }
3151
3152 //
3153 // Oops :(
3154 //
3155 UNIMPLEMENTED;
3156
3157 //
3158 // Detach if needed
3159 //
3160 if (Attached) KeUnstackDetachProcess(&ApcState);
3161
3162 //
3163 // Release reference
3164 //
3165 ObDereferenceObject(Process);
3166
3167 //
3168 // Enter SEH to return data
3169 //
3170 _SEH2_TRY
3171 {
3172 //
3173 // Return data to user
3174 //
3175 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
3176 *NumberOfBytesToUnlock = 0;
3177 }
3178 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3179 {
3180 //
3181 // Get exception code
3182 //
3183 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3184 }
3185 _SEH2_END;
3186
3187 //
3188 // Return status
3189 //
3190 return STATUS_SUCCESS;
3191 }
3192
3193 NTSTATUS
3194 NTAPI
3195 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
3196 IN OUT PVOID *BaseAddress,
3197 IN OUT PSIZE_T NumberOfBytesToFlush,
3198 OUT PIO_STATUS_BLOCK IoStatusBlock)
3199 {
3200 PEPROCESS Process;
3201 NTSTATUS Status;
3202 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3203 PVOID CapturedBaseAddress;
3204 SIZE_T CapturedBytesToFlush;
3205 IO_STATUS_BLOCK LocalStatusBlock;
3206 PAGED_CODE();
3207
3208 //
3209 // Check if we came from user mode
3210 //
3211 if (PreviousMode != KernelMode)
3212 {
3213 //
3214 // Enter SEH for probing
3215 //
3216 _SEH2_TRY
3217 {
3218 //
3219 // Validate all outputs
3220 //
3221 ProbeForWritePointer(BaseAddress);
3222 ProbeForWriteSize_t(NumberOfBytesToFlush);
3223 ProbeForWriteIoStatusBlock(IoStatusBlock);
3224
3225 //
3226 // Capture them
3227 //
3228 CapturedBaseAddress = *BaseAddress;
3229 CapturedBytesToFlush = *NumberOfBytesToFlush;
3230 }
3231 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3232 {
3233 //
3234 // Get exception code
3235 //
3236 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3237 }
3238 _SEH2_END;
3239 }
3240 else
3241 {
3242 //
3243 // Capture directly
3244 //
3245 CapturedBaseAddress = *BaseAddress;
3246 CapturedBytesToFlush = *NumberOfBytesToFlush;
3247 }
3248
3249 //
3250 // Catch illegal base address
3251 //
3252 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3253
3254 //
3255 // Catch illegal region size
3256 //
3257 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
3258 {
3259 //
3260 // Fail
3261 //
3262 return STATUS_INVALID_PARAMETER;
3263 }
3264
3265 //
3266 // Get a reference to the process
3267 //
3268 Status = ObReferenceObjectByHandle(ProcessHandle,
3269 PROCESS_VM_OPERATION,
3270 PsProcessType,
3271 PreviousMode,
3272 (PVOID*)(&Process),
3273 NULL);
3274 if (!NT_SUCCESS(Status)) return Status;
3275
3276 //
3277 // Do it
3278 //
3279 Status = MmFlushVirtualMemory(Process,
3280 &CapturedBaseAddress,
3281 &CapturedBytesToFlush,
3282 &LocalStatusBlock);
3283
3284 //
3285 // Release reference
3286 //
3287 ObDereferenceObject(Process);
3288
3289 //
3290 // Enter SEH to return data
3291 //
3292 _SEH2_TRY
3293 {
3294 //
3295 // Return data to user
3296 //
3297 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
3298 *NumberOfBytesToFlush = 0;
3299 *IoStatusBlock = LocalStatusBlock;
3300 }
3301 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3302 {
3303 }
3304 _SEH2_END;
3305
3306 //
3307 // Return status
3308 //
3309 return Status;
3310 }
3311
3312 /*
3313 * @unimplemented
3314 */
3315 NTSTATUS
3316 NTAPI
3317 NtGetWriteWatch(IN HANDLE ProcessHandle,
3318 IN ULONG Flags,
3319 IN PVOID BaseAddress,
3320 IN SIZE_T RegionSize,
3321 IN PVOID *UserAddressArray,
3322 OUT PULONG_PTR EntriesInUserAddressArray,
3323 OUT PULONG Granularity)
3324 {
3325 PEPROCESS Process;
3326 NTSTATUS Status;
3327 PVOID EndAddress;
3328 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3329 ULONG_PTR CapturedEntryCount;
3330 PAGED_CODE();
3331
3332 //
3333 // Check if we came from user mode
3334 //
3335 if (PreviousMode != KernelMode)
3336 {
3337 //
3338 // Enter SEH for probing
3339 //
3340 _SEH2_TRY
3341 {
3342 //
3343 // Catch illegal base address
3344 //
3345 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2);
3346
3347 //
3348 // Catch illegal region size
3349 //
3350 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
3351 {
3352 //
3353 // Fail
3354 //
3355 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3);
3356 }
3357
3358 //
3359 // Validate all data
3360 //
3361 ProbeForWriteSize_t(EntriesInUserAddressArray);
3362 ProbeForWriteUlong(Granularity);
3363
3364 //
3365 // Capture them
3366 //
3367 CapturedEntryCount = *EntriesInUserAddressArray;
3368
3369 //
3370 // Must have a count
3371 //
3372 if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
3373
3374 //
3375 // Can't be larger than the maximum
3376 //
3377 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
3378 {
3379 //
3380 // Fail
3381 //
3382 _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
3383 }
3384
3385 //
3386 // Probe the actual array
3387 //
3388 ProbeForWrite(UserAddressArray,
3389 CapturedEntryCount * sizeof(PVOID),
3390 sizeof(PVOID));
3391 }
3392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3393 {
3394 //
3395 // Get exception code
3396 //
3397 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3398 }
3399 _SEH2_END;
3400 }
3401 else
3402 {
3403 //
3404 // Capture directly
3405 //
3406 CapturedEntryCount = *EntriesInUserAddressArray;
3407 ASSERT(CapturedEntryCount != 0);
3408 }
3409
3410 //
3411 // Check if this is a local request
3412 //
3413 if (ProcessHandle == NtCurrentProcess())
3414 {
3415 //
3416 // No need to reference the process
3417 //
3418 Process = PsGetCurrentProcess();
3419 }
3420 else
3421 {
3422 //
3423 // Reference the target
3424 //
3425 Status = ObReferenceObjectByHandle(ProcessHandle,
3426 PROCESS_VM_OPERATION,
3427 PsProcessType,
3428 PreviousMode,
3429 (PVOID *)&Process,
3430 NULL);
3431 if (!NT_SUCCESS(Status)) return Status;
3432 }
3433
3434 //
3435 // Compute the last address and validate it
3436 //
3437 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
3438 if (BaseAddress > EndAddress)
3439 {
3440 //
3441 // Fail
3442 //
3443 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3444 return STATUS_INVALID_PARAMETER_4;
3445 }
3446
3447 //
3448 // Oops :(
3449 //
3450 UNIMPLEMENTED;
3451
3452 //
3453 // Dereference if needed
3454 //
3455 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3456
3457 //
3458 // Enter SEH to return data
3459 //
3460 _SEH2_TRY
3461 {
3462 //
3463 // Return data to user
3464 //
3465 *EntriesInUserAddressArray = 0;
3466 *Granularity = PAGE_SIZE;
3467 }
3468 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3469 {
3470 //
3471 // Get exception code
3472 //
3473 Status = _SEH2_GetExceptionCode();
3474 }
3475 _SEH2_END;
3476
3477 //
3478 // Return success
3479 //
3480 return STATUS_SUCCESS;
3481 }
3482
3483 /*
3484 * @unimplemented
3485 */
3486 NTSTATUS
3487 NTAPI
3488 NtResetWriteWatch(IN HANDLE ProcessHandle,
3489 IN PVOID BaseAddress,
3490 IN SIZE_T RegionSize)
3491 {
3492 PVOID EndAddress;
3493 PEPROCESS Process;
3494 NTSTATUS Status;
3495 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3496 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
3497
3498 //
3499 // Catch illegal base address
3500 //
3501 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
3502
3503 //
3504 // Catch illegal region size
3505 //
3506 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
3507 {
3508 //
3509 // Fail
3510 //
3511 return STATUS_INVALID_PARAMETER_3;
3512 }
3513
3514 //
3515 // Check if this is a local request
3516 //
3517 if (ProcessHandle == NtCurrentProcess())
3518 {
3519 //
3520 // No need to reference the process
3521 //
3522 Process = PsGetCurrentProcess();
3523 }
3524 else
3525 {
3526 //
3527 // Reference the target
3528 //
3529 Status = ObReferenceObjectByHandle(ProcessHandle,
3530 PROCESS_VM_OPERATION,
3531 PsProcessType,
3532 PreviousMode,
3533 (PVOID *)&Process,
3534 NULL);
3535 if (!NT_SUCCESS(Status)) return Status;
3536 }
3537
3538 //
3539 // Compute the last address and validate it
3540 //
3541 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
3542 if (BaseAddress > EndAddress)
3543 {
3544 //
3545 // Fail
3546 //
3547 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3548 return STATUS_INVALID_PARAMETER_3;
3549 }
3550
3551 //
3552 // Oops :(
3553 //
3554 UNIMPLEMENTED;
3555
3556 //
3557 // Dereference if needed
3558 //
3559 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3560
3561 //
3562 // Return success
3563 //
3564 return STATUS_SUCCESS;
3565 }
3566
3567 NTSTATUS
3568 NTAPI
3569 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
3570 IN PVOID BaseAddress,
3571 IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
3572 OUT PVOID MemoryInformation,
3573 IN SIZE_T MemoryInformationLength,
3574 OUT PSIZE_T ReturnLength)
3575 {
3576 NTSTATUS Status = STATUS_SUCCESS;
3577 KPROCESSOR_MODE PreviousMode;
3578
3579 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
3580
3581 /* Bail out if the address is invalid */
3582 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
3583
3584 /* Probe return buffer */
3585 PreviousMode = ExGetPreviousMode();
3586 if (PreviousMode != KernelMode)
3587 {
3588 _SEH2_TRY
3589 {
3590 ProbeForWrite(MemoryInformation,
3591 MemoryInformationLength,
3592 sizeof(ULONG_PTR));
3593
3594 if (ReturnLength) ProbeForWriteSize_t(ReturnLength);
3595 }
3596 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3597 {
3598 Status = _SEH2_GetExceptionCode();
3599 }
3600 _SEH2_END;
3601
3602 if (!NT_SUCCESS(Status))
3603 {
3604 return Status;
3605 }
3606 }
3607
3608 switch(MemoryInformationClass)
3609 {
3610 case MemoryBasicInformation:
3611 /* Validate the size information of the class */
3612 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
3613 {
3614 /* The size is invalid */
3615 return STATUS_INFO_LENGTH_MISMATCH;
3616 }
3617 Status = MiQueryMemoryBasicInformation(ProcessHandle,
3618 BaseAddress,
3619 MemoryInformation,
3620 MemoryInformationLength,
3621 ReturnLength);
3622 break;
3623
3624 case MemorySectionName:
3625 /* Validate the size information of the class */
3626 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
3627 {
3628 /* The size is invalid */
3629 return STATUS_INFO_LENGTH_MISMATCH;
3630 }
3631 Status = MiQueryMemorySectionName(ProcessHandle,
3632 BaseAddress,
3633 MemoryInformation,
3634 MemoryInformationLength,
3635 ReturnLength);
3636 break;
3637 case MemoryWorkingSetList:
3638 case MemoryBasicVlmInformation:
3639 default:
3640 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
3641 break;
3642 }
3643
3644 return Status;
3645 }
3646
3647 /*
3648 * @implemented
3649 */
3650 NTSTATUS
3651 NTAPI
3652 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
3653 IN OUT PVOID* UBaseAddress,
3654 IN ULONG_PTR ZeroBits,
3655 IN OUT PSIZE_T URegionSize,
3656 IN ULONG AllocationType,
3657 IN ULONG Protect)
3658 {
3659 PEPROCESS Process;
3660 PMEMORY_AREA MemoryArea;
3661 PFN_NUMBER PageCount;
3662 PMMVAD Vad, FoundVad;
3663 NTSTATUS Status;
3664 PMMSUPPORT AddressSpace;
3665 PVOID PBaseAddress;
3666 ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
3667 PEPROCESS CurrentProcess = PsGetCurrentProcess();
3668 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
3669 PETHREAD CurrentThread = PsGetCurrentThread();
3670 KAPC_STATE ApcState;
3671 ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0;
3672 BOOLEAN Attached = FALSE, ChangeProtection = FALSE;
3673 MMPTE TempPte;
3674 PMMPTE PointerPte, PointerPde, LastPte;
3675 PAGED_CODE();
3676
3677 /* Check for valid Zero bits */
3678 if (ZeroBits > 21)
3679 {
3680 DPRINT1("Too many zero bits\n");
3681 return STATUS_INVALID_PARAMETER_3;
3682 }
3683
3684 /* Check for valid Allocation Types */
3685 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
3686 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
3687 {
3688 DPRINT1("Invalid Allocation Type\n");
3689 return STATUS_INVALID_PARAMETER_5;
3690 }
3691
3692 /* Check for at least one of these Allocation Types to be set */
3693 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
3694 {
3695 DPRINT1("No memory allocation base type\n");
3696 return STATUS_INVALID_PARAMETER_5;
3697 }
3698
3699 /* MEM_RESET is an exclusive flag, make sure that is valid too */
3700 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
3701 {
3702 DPRINT1("Invalid use of MEM_RESET\n");
3703 return STATUS_INVALID_PARAMETER_5;
3704 }
3705
3706 /* Check if large pages are being used */
3707 if (AllocationType & MEM_LARGE_PAGES)
3708 {
3709 /* Large page allocations MUST be committed */
3710 if (!(AllocationType & MEM_COMMIT))
3711 {
3712 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
3713 return STATUS_INVALID_PARAMETER_5;
3714 }
3715
3716 /* These flags are not allowed with large page allocations */
3717 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
3718 {
3719 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
3720 return STATUS_INVALID_PARAMETER_5;
3721 }
3722 }
3723
3724 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
3725 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
3726 {
3727 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3728 return STATUS_INVALID_PARAMETER_5;
3729 }
3730
3731 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
3732 if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
3733 {
3734 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
3735 return STATUS_INVALID_PARAMETER_5;
3736 }
3737
3738 /* Check for valid MEM_PHYSICAL usage */
3739 if (AllocationType & MEM_PHYSICAL)
3740 {
3741 /* Only these flags are allowed with MEM_PHYSIAL */
3742 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
3743 {
3744 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
3745 return STATUS_INVALID_PARAMETER_5;
3746 }
3747
3748 /* Then make sure PAGE_READWRITE is used */
3749 if (Protect != PAGE_READWRITE)
3750 {
3751 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
3752 return STATUS_INVALID_PARAMETER_6;
3753 }
3754 }
3755
3756 /* Calculate the protection mask and make sure it's valid */
3757 ProtectionMask = MiMakeProtectionMask(Protect);
3758 if (ProtectionMask == MM_INVALID_PROTECTION)
3759 {
3760 DPRINT1("Invalid protection mask\n");
3761 return STATUS_INVALID_PAGE_PROTECTION;
3762 }
3763
3764 /* Enter SEH */
3765 _SEH2_TRY
3766 {
3767 /* Check for user-mode parameters */
3768 if (PreviousMode != KernelMode)
3769 {
3770 /* Make sure they are writable */
3771 ProbeForWritePointer(UBaseAddress);
3772 ProbeForWriteUlong(URegionSize);
3773 }
3774
3775 /* Capture their values */
3776 PBaseAddress = *UBaseAddress;
3777 PRegionSize = *URegionSize;
3778 }
3779 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3780 {
3781 /* Return the exception code */
3782 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3783 }
3784 _SEH2_END;
3785
3786 /* Make sure the allocation isn't past the VAD area */
3787 if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
3788 {
3789 DPRINT1("Virtual allocation base above User Space\n");
3790 return STATUS_INVALID_PARAMETER_2;
3791 }
3792
3793 /* Make sure the allocation wouldn't overflow past the VAD area */
3794 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
3795 {
3796 DPRINT1("Region size would overflow into kernel-memory\n");
3797 return STATUS_INVALID_PARAMETER_4;
3798 }
3799
3800 /* Make sure there's a size specified */
3801 if (!PRegionSize)
3802 {
3803 DPRINT1("Region size is invalid (zero)\n");
3804 return STATUS_INVALID_PARAMETER_4;
3805 }
3806
3807 //
3808 // If this is for the current process, just use PsGetCurrentProcess
3809 //
3810 if (ProcessHandle == NtCurrentProcess())
3811 {
3812 Process = CurrentProcess;
3813 }
3814 else
3815 {
3816 //
3817 // Otherwise, reference the process with VM rights and attach to it if
3818 // this isn't the current process. We must attach because we'll be touching
3819 // PTEs and PDEs that belong to user-mode memory, and also touching the
3820 // Working Set which is stored in Hyperspace.
3821 //
3822 Status = ObReferenceObjectByHandle(ProcessHandle,
3823 PROCESS_VM_OPERATION,
3824 PsProcessType,
3825 PreviousMode,
3826 (PVOID*)&Process,
3827 NULL);
3828 if (!NT_SUCCESS(Status)) return Status;
3829 if (CurrentProcess != Process)
3830 {
3831 KeStackAttachProcess(&Process->Pcb, &ApcState);
3832 Attached = TRUE;
3833 }
3834 }
3835
3836 //
3837 // Check for large page allocations and make sure that the required privilege
3838 // is being held, before attempting to handle them.
3839 //
3840 if ((AllocationType & MEM_LARGE_PAGES) &&
3841 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)))
3842 {
3843 /* Fail without it */
3844 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3845 Status = STATUS_PRIVILEGE_NOT_HELD;
3846 goto FailPathNoLock;
3847 }
3848
3849 //
3850 // Fail on the things we don't yet support
3851 //
3852 if (ZeroBits != 0)
3853 {
3854 DPRINT1("Zero bits not supported\n");
3855 Status = STATUS_INVALID_PARAMETER;
3856 goto FailPathNoLock;
3857 }
3858 if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES)
3859 {
3860 DPRINT1("MEM_LARGE_PAGES not supported\n");
3861 Status = STATUS_INVALID_PARAMETER;
3862 goto FailPathNoLock;
3863 }
3864 if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL)
3865 {
3866 DPRINT1("MEM_PHYSICAL not supported\n");
3867 Status = STATUS_INVALID_PARAMETER;
3868 goto FailPathNoLock;
3869 }
3870 if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH)
3871 {
3872 DPRINT1("MEM_WRITE_WATCH not supported\n");
3873 Status = STATUS_INVALID_PARAMETER;
3874 goto FailPathNoLock;
3875 }
3876 if ((AllocationType & MEM_TOP_DOWN) == MEM_TOP_DOWN)
3877 {
3878 DPRINT1("MEM_TOP_DOWN not supported\n");
3879 AllocationType &= ~MEM_TOP_DOWN;
3880 }
3881
3882 if (Process->VmTopDown == 1)
3883 {
3884 DPRINT1("VmTopDown not supported\n");
3885 Status = STATUS_INVALID_PARAMETER;
3886 goto FailPathNoLock;
3887 }
3888
3889 //
3890 // Check if the caller is reserving memory, or committing memory and letting
3891 // us pick the base address
3892 //
3893 if (!(PBaseAddress) || (AllocationType & MEM_RESERVE))
3894 {
3895 //
3896 // Do not allow COPY_ON_WRITE through this API
3897 //
3898 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
3899 {
3900 DPRINT1("Copy on write not allowed through this path\n");
3901 Status = STATUS_INVALID_PAGE_PROTECTION;
3902 goto FailPathNoLock;
3903 }
3904
3905 //
3906 // Does the caller have an address in mind, or is this a blind commit?
3907 //
3908 if (!PBaseAddress)
3909 {
3910 //
3911 // This is a blind commit, all we need is the region size
3912 //
3913 PRegionSize = ROUND_TO_PAGES(PRegionSize);
3914 PageCount = BYTES_TO_PAGES(PRegionSize);
3915 EndingAddress = 0;
3916 StartingAddress = 0;
3917 }
3918 else
3919 {
3920 //
3921 // This is a reservation, so compute the starting address on the
3922 // expected 64KB granularity, and see where the ending address will
3923 // fall based on the aligned address and the passed in region size
3924 //
3925 StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
3926 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
3927 PageCount = BYTES_TO_PAGES(EndingAddress - StartingAddress);
3928 }
3929
3930 //
3931 // Allocate and initialize the VAD
3932 //
3933 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
3934 ASSERT(Vad != NULL);
3935 Vad->u.LongFlags = 0;
3936 if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
3937 Vad->u.VadFlags.Protection = ProtectionMask;
3938 Vad->u.VadFlags.PrivateMemory = 1;
3939 Vad->u.VadFlags.CommitCharge = AllocationType & MEM_COMMIT ? PageCount : 0;
3940
3941 //
3942 // Lock the address space and make sure the process isn't already dead
3943 //
3944 AddressSpace = MmGetCurrentAddressSpace();
3945 MmLockAddressSpace(AddressSpace);
3946 if (Process->VmDeleted)
3947 {
3948 Status = STATUS_PROCESS_IS_TERMINATING;
3949 goto FailPath;
3950 }
3951
3952 //
3953 // Did we have a base address? If no, find a valid address that is 64KB
3954 // aligned in the VAD tree. Otherwise, make sure that the address range
3955 // which was passed in isn't already conflicting with an existing address
3956 // range.
3957 //
3958 if (!PBaseAddress)
3959 {
3960 Status = MiFindEmptyAddressRangeInTree(PRegionSize,
3961 _64K,
3962 &Process->VadRoot,
3963 (PMMADDRESS_NODE*)&Process->VadFreeHint,
3964 &StartingAddress);
3965 if (!NT_SUCCESS(Status)) goto FailPath;
3966
3967 //
3968 // Now we know where the allocation ends. Make sure it doesn't end up
3969 // somewhere in kernel mode.
3970 //
3971 EndingAddress = ((ULONG_PTR)StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
3972 if ((PVOID)EndingAddress > MM_HIGHEST_VAD_ADDRESS)
3973 {
3974 Status = STATUS_NO_MEMORY;
3975 goto FailPath;
3976 }
3977 }
3978 else if (MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
3979 EndingAddress >> PAGE_SHIFT,
3980 &Process->VadRoot))
3981 {
3982 //
3983 // The address specified is in conflict!
3984 //
3985 Status = STATUS_CONFLICTING_ADDRESSES;
3986 goto FailPath;
3987 }
3988
3989 //
3990 // Write out the VAD fields for this allocation
3991 //
3992 Vad->StartingVpn = (ULONG_PTR)StartingAddress >> PAGE_SHIFT;
3993 Vad->EndingVpn = (ULONG_PTR)EndingAddress >> PAGE_SHIFT;
3994
3995 //
3996 // FIXME: Should setup VAD bitmap
3997 //
3998 Status = STATUS_SUCCESS;
3999
4000 //
4001 // Lock the working set and insert the VAD into the process VAD tree
4002 //
4003 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4004 Vad->ControlArea = NULL; // For Memory-Area hack
4005 MiInsertVad(Vad, Process);
4006 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
4007
4008 //
4009 // Update the virtual size of the process, and if this is now the highest
4010 // virtual size we have ever seen, update the peak virtual size to reflect
4011 // this.
4012 //
4013 Process->VirtualSize += PRegionSize;
4014 if (Process->VirtualSize > Process->PeakVirtualSize)
4015 {
4016 Process->PeakVirtualSize = Process->VirtualSize;
4017 }
4018
4019 //
4020 // Release address space and detach and dereference the target process if
4021 // it was different from the current process
4022 //
4023 MmUnlockAddressSpace(AddressSpace);
4024 if (Attached) KeUnstackDetachProcess(&ApcState);
4025 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4026
4027 //
4028 // Use SEH to write back the base address and the region size. In the case
4029 // of an exception, we do not return back the exception code, as the memory
4030 // *has* been allocated. The caller would now have to call VirtualQuery
4031 // or do some other similar trick to actually find out where its memory
4032 // allocation ended up
4033 //
4034 _SEH2_TRY
4035 {
4036 *URegionSize = PRegionSize;
4037 *UBaseAddress = (PVOID)StartingAddress;
4038 }
4039 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4040 {
4041 }
4042 _SEH2_END;
4043 return STATUS_SUCCESS;
4044 }
4045
4046 //
4047 // This is a MEM_COMMIT on top of an existing address which must have been
4048 // MEM_RESERVED already. Compute the start and ending base addresses based
4049 // on the user input, and then compute the actual region size once all the
4050 // alignments have been done.
4051 //
4052 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4053 EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
4054 PRegionSize = EndingAddress - StartingAddress + 1;
4055
4056 //
4057 // Lock the address space and make sure the process isn't already dead
4058 //
4059 AddressSpace = MmGetCurrentAddressSpace();
4060 MmLockAddressSpace(AddressSpace);
4061 if (Process->VmDeleted)
4062 {
4063 DPRINT1("Process is dying\n");
4064 Status = STATUS_PROCESS_IS_TERMINATING;
4065 goto FailPath;
4066 }
4067
4068 //
4069 // Get the VAD for this address range, and make sure it exists
4070 //
4071 FoundVad = (PMMVAD)MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
4072 EndingAddress >> PAGE_SHIFT,
4073 &Process->VadRoot);
4074 if (!FoundVad)
4075 {
4076 DPRINT1("Could not find a VAD for this allocation\n");
4077 Status = STATUS_CONFLICTING_ADDRESSES;
4078 goto FailPath;
4079 }
4080
4081 if ((AllocationType & MEM_RESET) == MEM_RESET)
4082 {
4083 /// @todo HACK: pretend success
4084 DPRINT("MEM_RESET not supported\n");
4085 Status = STATUS_SUCCESS;
4086 goto FailPath;
4087 }
4088
4089 //
4090 // These kinds of VADs are illegal for this Windows function when trying to
4091 // commit an existing range
4092 //
4093 if ((FoundVad->u.VadFlags.VadType == VadAwe) ||
4094 (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) ||
4095 (FoundVad->u.VadFlags.VadType == VadLargePages))
4096 {
4097 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n");
4098 Status = STATUS_CONFLICTING_ADDRESSES;
4099 goto FailPath;
4100 }
4101
4102 //
4103 // Make sure that this address range actually fits within the VAD for it
4104 //
4105 if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
4106 ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
4107 {
4108 DPRINT1("Address range does not fit into the VAD\n");
4109 Status = STATUS_CONFLICTING_ADDRESSES;
4110 goto FailPath;
4111 }
4112
4113 //
4114 // Make sure this is an ARM3 section
4115 //
4116 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress));
4117 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
4118 {
4119 DPRINT1("Illegal commit of non-ARM3 section!\n");
4120 Status = STATUS_ALREADY_COMMITTED;
4121 goto FailPath;
4122 }
4123
4124 // Is this a previously reserved section being committed? If so, enter the
4125 // special section path
4126 //
4127 if (FoundVad->u.VadFlags.PrivateMemory == FALSE)
4128 {
4129 //
4130 // You cannot commit large page sections through this API
4131 //
4132 if (FoundVad->u.VadFlags.VadType == VadLargePageSection)
4133 {
4134 DPRINT1("Large page sections cannot be VirtualAlloc'd\n");
4135 Status = STATUS_INVALID_PAGE_PROTECTION;
4136 goto FailPath;
4137 }
4138
4139 //
4140 // You can only use caching flags on a rotate VAD
4141 //
4142 if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) &&
4143 (FoundVad->u.VadFlags.VadType != VadRotatePhysical))
4144 {
4145 DPRINT1("Cannot use caching flags with anything but rotate VADs\n");
4146 Status = STATUS_INVALID_PAGE_PROTECTION;
4147 goto FailPath;
4148 }
4149
4150 //
4151 // We should make sure that the section's permissions aren't being
4152 // messed with
4153 //
4154 if (FoundVad->u.VadFlags.NoChange)
4155 {
4156 //
4157 // Make sure it's okay to touch it
4158 //
4159 Status = MiCheckSecuredVad(FoundVad,
4160 PBaseAddress,
4161 PRegionSize,
4162 ProtectionMask);
4163 if (!NT_SUCCESS(Status))
4164 {
4165 DPRINT1("Secured VAD being messed around with\n");
4166 goto FailPath;
4167 }
4168 }
4169
4170 //
4171 // ARM3 does not support file-backed sections, only shared memory
4172 //
4173 ASSERT(FoundVad->ControlArea->FilePointer == NULL);
4174
4175 //
4176 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write
4177 //
4178 if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) &&
4179 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD)))
4180 {
4181 DPRINT1("Invalid page protection for rotate VAD\n");
4182 Status = STATUS_INVALID_PAGE_PROTECTION;
4183 goto FailPath;
4184 }
4185
4186 //
4187 // Compute PTE addresses and the quota charge, then grab the commit lock
4188 //
4189 PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT);
4190 LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT);
4191 QuotaCharge = (ULONG)(LastPte - PointerPte + 1);
4192 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
4193
4194 //
4195 // Get the segment template PTE and start looping each page
4196 //
4197 TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate;
4198 ASSERT(TempPte.u.Long != 0);
4199 while (PointerPte <= LastPte)
4200 {
4201 //
4202 // For each non-already-committed page, write the invalid template PTE
4203 //
4204 if (PointerPte->u.Long == 0)
4205 {
4206 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4207 }
4208 else
4209 {
4210 QuotaFree++;
4211 }
4212 PointerPte++;
4213 }
4214
4215 //
4216 // Now do the commit accounting and release the lock
4217 //
4218 ASSERT(QuotaCharge >= QuotaFree);
4219 QuotaCharge -= QuotaFree;
4220 FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge;
4221 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
4222
4223 //
4224 // We are done with committing the section pages
4225 //
4226 Status = STATUS_SUCCESS;
4227 goto FailPath;
4228 }
4229
4230 //
4231 // This is a specific ReactOS check because we only use normal VADs
4232 //
4233 ASSERT(FoundVad->u.VadFlags.VadType == VadNone);
4234
4235 //
4236 // While this is an actual Windows check
4237 //
4238 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
4239
4240 //
4241 // Throw out attempts to use copy-on-write through this API path
4242 //
4243 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
4244 {
4245 DPRINT1("Write copy attempted when not allowed\n");
4246 Status = STATUS_INVALID_PAGE_PROTECTION;
4247 goto FailPath;
4248 }
4249
4250 //
4251 // Initialize a demand-zero PTE
4252 //
4253 TempPte.u.Long = 0;
4254 TempPte.u.Soft.Protection = ProtectionMask;
4255 NT_ASSERT(TempPte.u.Long != 0);
4256
4257 //
4258 // Get the PTE, PDE and the last PTE for this address range
4259 //
4260 PointerPde = MiAddressToPde(StartingAddress);
4261 PointerPte = MiAddressToPte(StartingAddress);
4262 LastPte = MiAddressToPte(EndingAddress);
4263
4264 //
4265 // Update the commit charge in the VAD as well as in the process, and check
4266 // if this commit charge was now higher than the last recorded peak, in which
4267 // case we also update the peak
4268 //
4269 FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte);
4270 Process->CommitCharge += (1 + LastPte - PointerPte);
4271 if (Process->CommitCharge > Process->CommitChargePeak)
4272 {
4273 Process->CommitChargePeak = Process->CommitCharge;
4274 }
4275
4276 //
4277 // Lock the working set while we play with user pages and page tables
4278 //
4279 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4280
4281 //
4282 // Make the current page table valid, and then loop each page within it
4283 //
4284 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4285 while (PointerPte <= LastPte)
4286 {
4287 //
4288 // Have we crossed into a new page table?
4289 //
4290 if (MiIsPteOnPdeBoundary(PointerPte))
4291 {
4292 //
4293 // Get the PDE and now make it valid too
4294 //
4295 PointerPde = MiAddressToPte(PointerPte);
4296 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
4297 }
4298
4299 //
4300 // Is this a zero PTE as expected?
4301 //
4302 if (PointerPte->u.Long == 0)
4303 {
4304 //
4305 // First increment the count of pages in the page table for this
4306 // process
4307 //
4308 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
4309
4310 //
4311 // And now write the invalid demand-zero PTE as requested
4312 //
4313 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4314 }
4315 else if (PointerPte->u.Long == MmDecommittedPte.u.Long)
4316 {
4317 //
4318 // If the PTE was already decommitted, there is nothing else to do
4319 // but to write the new demand-zero PTE
4320 //
4321 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
4322 }
4323 else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte)))
4324 {
4325 //
4326 // We don't handle these scenarios yet
4327 //
4328 if (PointerPte->u.Soft.Valid == 0)
4329 {
4330 ASSERT(PointerPte->u.Soft.Prototype == 0);
4331 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
4332 }
4333
4334 //
4335 // There's a change in protection, remember this for later, but do
4336 // not yet handle it.
4337 //
4338 ChangeProtection = TRUE;
4339 }
4340
4341 //
4342 // Move to the next PTE
4343 //
4344 PointerPte++;
4345 }
4346
4347 //
4348 // Release the working set lock, unlock the address space, and detach from
4349 // the target process if it was not the current process. Also dereference the
4350 // target process if this wasn't the case.
4351 //
4352 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
4353 Status = STATUS_SUCCESS;
4354 FailPath:
4355 MmUnlockAddressSpace(AddressSpace);
4356
4357 //
4358 // Check if we need to update the protection
4359 //
4360 if (ChangeProtection)
4361 {
4362 PVOID ProtectBaseAddress = (PVOID)StartingAddress;
4363 SIZE_T ProtectSize = PRegionSize;
4364 ULONG OldProtection;
4365
4366 //
4367 // Change the protection of the region
4368 //
4369 MiProtectVirtualMemory(Process,
4370 &ProtectBaseAddress,
4371 &ProtectSize,
4372 Protect,
4373 &OldProtection);
4374 }
4375
4376 FailPathNoLock:
4377 if (Attached) KeUnstackDetachProcess(&ApcState);
4378 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4379
4380 //
4381 // Use SEH to write back the base address and the region size. In the case
4382 // of an exception, we strangely do return back the exception code, even
4383 // though the memory *has* been allocated. This mimics Windows behavior and
4384 // there is not much we can do about it.
4385 //
4386 _SEH2_TRY
4387 {
4388 *URegionSize = PRegionSize;
4389 *UBaseAddress = (PVOID)StartingAddress;
4390 }
4391 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4392 {
4393 Status = _SEH2_GetExceptionCode();
4394 }
4395 _SEH2_END;
4396 return Status;
4397 }
4398
4399 /*
4400 * @implemented
4401 */
4402 NTSTATUS
4403 NTAPI
4404 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
4405 IN PVOID* UBaseAddress,
4406 IN PSIZE_T URegionSize,
4407 IN ULONG FreeType)
4408 {
4409 PMEMORY_AREA MemoryArea;
4410 SIZE_T PRegionSize;
4411 PVOID PBaseAddress;
4412 ULONG_PTR CommitReduction = 0;
4413 ULONG_PTR StartingAddress, EndingAddress;
4414 PMMVAD Vad;
4415 NTSTATUS Status;
4416 PEPROCESS Process;
4417 PMMSUPPORT AddressSpace;
4418 PETHREAD CurrentThread = PsGetCurrentThread();
4419 PEPROCESS CurrentProcess = PsGetCurrentProcess();
4420 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
4421 KAPC_STATE ApcState;
4422 BOOLEAN Attached = FALSE;
4423 PAGED_CODE();
4424
4425 //
4426 // Only two flags are supported
4427 //
4428 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
4429 {
4430 DPRINT1("Invalid FreeType\n");
4431 return STATUS_INVALID_PARAMETER_4;
4432 }
4433
4434 //
4435 // Check if no flag was used, or if both flags were used
4436 //
4437 if (!((FreeType & (MEM_DECOMMIT | MEM_RELEASE))) ||
4438 ((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == (MEM_DECOMMIT | MEM_RELEASE)))
4439 {
4440 DPRINT1("Invalid FreeType combination\n");
4441 return STATUS_INVALID_PARAMETER_4;
4442 }
4443
4444 //
4445 // Enter SEH for probe and capture. On failure, return back to the caller
4446 // with an exception violation.
4447 //
4448 _SEH2_TRY
4449 {
4450 //
4451 // Check for user-mode parameters and make sure that they are writeable
4452 //
4453 if (PreviousMode != KernelMode)
4454 {
4455 ProbeForWritePointer(UBaseAddress);
4456 ProbeForWriteUlong(URegionSize);
4457 }
4458
4459 //
4460 // Capture the current values
4461 //
4462 PBaseAddress = *UBaseAddress;
4463 PRegionSize = *URegionSize;
4464 }
4465 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4466 {
4467 _SEH2_YIELD(return _SEH2_GetExceptionCode());
4468 }
4469 _SEH2_END;
4470
4471 //
4472 // Make sure the allocation isn't past the user area
4473 //
4474 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
4475 {
4476 DPRINT1("Virtual free base above User Space\n");
4477 return STATUS_INVALID_PARAMETER_2;
4478 }
4479
4480 //
4481 // Make sure the allocation wouldn't overflow past the user area
4482 //
4483 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
4484 {
4485 DPRINT1("Region size would overflow into kernel-memory\n");
4486 return STATUS_INVALID_PARAMETER_3;
4487 }
4488
4489 //
4490 // If this is for the current process, just use PsGetCurrentProcess
4491 //
4492 if (ProcessHandle == NtCurrentProcess())
4493 {
4494 Process = CurrentProcess;
4495 }
4496 else
4497 {
4498 //
4499 // Otherwise, reference the process with VM rights and attach to it if
4500 // this isn't the current process. We must attach because we'll be touching
4501 // PTEs and PDEs that belong to user-mode memory, and also touching the
4502 // Working Set which is stored in Hyperspace.
4503 //
4504 Status = ObReferenceObjectByHandle(ProcessHandle,
4505 PROCESS_VM_OPERATION,
4506 PsProcessType,
4507 PreviousMode,
4508 (PVOID*)&Process,
4509 NULL);
4510 if (!NT_SUCCESS(Status)) return Status;
4511 if (CurrentProcess != Process)
4512 {
4513 KeStackAttachProcess(&Process->Pcb, &ApcState);
4514 Attached = TRUE;
4515 }
4516 }
4517
4518 //
4519 // Lock the address space
4520 //
4521 AddressSpace = MmGetCurrentAddressSpace();
4522 MmLockAddressSpace(AddressSpace);
4523
4524 //
4525 // If the address space is being deleted, fail the de-allocation since it's
4526 // too late to do anything about it
4527 //
4528 if (Process->VmDeleted)
4529 {
4530 DPRINT1("Process is dead\n");
4531 Status = STATUS_PROCESS_IS_TERMINATING;
4532 goto FailPath;
4533 }
4534
4535 //
4536 // Compute start and end addresses, and locate the VAD
4537 //
4538 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
4539 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
4540 Vad = MiLocateAddress((PVOID)StartingAddress);
4541 if (!Vad)
4542 {
4543 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress);
4544 Status = STATUS_MEMORY_NOT_ALLOCATED;
4545 goto FailPath;
4546 }
4547
4548 //
4549 // If the range exceeds the VAD's ending VPN, fail this request
4550 //
4551 if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT))
4552 {
4553 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress);
4554 Status = STATUS_UNABLE_TO_FREE_VM;
4555 goto FailPath;
4556 }
4557
4558 //
4559 // Only private memory (except rotate VADs) can be freed through here */
4560 //
4561 if ((!(Vad->u.VadFlags.PrivateMemory) &&
4562 (Vad->u.VadFlags.VadType != VadRotatePhysical)) ||
4563 (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory))
4564 {
4565 DPRINT1("Attempt to free section memory\n");
4566 Status = STATUS_UNABLE_TO_DELETE_SECTION;
4567 goto FailPath;
4568 }
4569
4570 //
4571 // ARM3 does not yet handle protected VM
4572 //
4573 ASSERT(Vad->u.VadFlags.NoChange == 0);
4574
4575 //
4576 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
4577 // and that is is an ARM3 memory area, and not a section view, as we currently
4578 // don't support freeing those though this interface.
4579 //
4580 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
4581 ASSERT(MemoryArea);
4582 ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
4583
4584 //
4585 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT
4586 //
4587 if (FreeType & MEM_RELEASE)
4588 {
4589 //
4590 // ARM3 only supports this VAD in this path
4591 //
4592 ASSERT(Vad->u.VadFlags.VadType == VadNone);
4593
4594 //
4595 // Is the caller trying to remove the whole VAD, or remove only a portion
4596 // of it? If no region size is specified, then the assumption is that the
4597 // whole VAD is to be destroyed
4598 //
4599 if (!PRegionSize)
4600 {
4601 //
4602 // The caller must specify the base address identically to the range
4603 // that is stored in the VAD.
4604 //
4605 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
4606 {
4607 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress);
4608 Status = STATUS_FREE_VM_NOT_AT_BASE;
4609 goto FailPath;
4610 }
4611
4612 //
4613 // Now compute the actual start/end addresses based on the VAD
4614 //
4615 StartingAddress = Vad->StartingVpn << PAGE_SHIFT;
4616 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
4617
4618 //
4619 // Finally lock the working set and remove the VAD from the VAD tree
4620 //
4621 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4622 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
4623 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
4624 }
4625 else
4626 {
4627 //
4628 // This means the caller wants to release a specific region within
4629 // the range. We have to find out which range this is -- the following
4630 // possibilities exist plus their union (CASE D):
4631 //
4632 // STARTING ADDRESS ENDING ADDRESS
4633 // [<========][========================================][=========>]
4634 // CASE A CASE B CASE C
4635 //
4636 //
4637 // First, check for case A or D
4638 //
4639 if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn)
4640 {
4641 //
4642 // Check for case D
4643 //
4644 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
4645 {
4646 //
4647 // This is the easiest one to handle -- it is identical to
4648 // the code path above when the caller sets a zero region size
4649 // and the whole VAD is destroyed
4650 //
4651 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4652 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
4653 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
4654 }
4655 else
4656 {
4657 //
4658 // This case is pretty easy too -- we compute a bunch of
4659 // pages to decommit, and then push the VAD's starting address
4660 // a bit further down, then decrement the commit charge
4661 //
4662 // NOT YET IMPLEMENTED IN ARM3.
4663 //
4664 DPRINT1("Case A not handled\n");
4665 Status = STATUS_FREE_VM_NOT_AT_BASE;
4666 goto FailPath;
4667
4668 //
4669 // After analyzing the VAD, set it to NULL so that we don't
4670 // free it in the exit path
4671 //
4672 Vad = NULL;
4673 }
4674 }
4675 else
4676 {
4677 //
4678 // This is case B or case C. First check for case C
4679 //
4680 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn)
4681 {
4682 PMEMORY_AREA MemoryArea;
4683
4684 //
4685 // This is pretty easy and similar to case A. We compute the
4686 // amount of pages to decommit, update the VAD's commit charge
4687 // and then change the ending address of the VAD to be a bit
4688 // smaller.
4689 //
4690 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
4691 CommitReduction = MiCalculatePageCommitment(StartingAddress,
4692 EndingAddress,
4693 Vad,
4694 Process);
4695 Vad->u.VadFlags.CommitCharge -= CommitReduction;
4696 // For ReactOS: shrink the corresponding memory area
4697 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress);
4698 ASSERT(Vad->StartingVpn << PAGE_SHIFT == (ULONG_PTR)MemoryArea->StartingAddress);
4699 ASSERT((Vad->EndingVpn + 1) << PAGE_SHIFT == (ULONG_PTR)MemoryArea->EndingAddress);
4700 Vad->EndingVpn = ((ULONG_PTR)StartingAddress - 1) >> PAGE_SHIFT;
4701 MemoryArea->EndingAddress = (PVOID)(((Vad->EndingVpn + 1) << PAGE_SHIFT) - 1);
4702 }
4703 else
4704 {
4705 //
4706 // This is case B and the hardest one. Because we are removing
4707 // a chunk of memory from the very middle of the VAD, we must
4708 // actually split the VAD into two new VADs and compute the
4709 // commit charges for each of them, and reinsert new charges.
4710 //
4711 // NOT YET IMPLEMENTED IN ARM3.
4712 //
4713 DPRINT1("Case B not handled\n");
4714 Status = STATUS_FREE_VM_NOT_AT_BASE;
4715 goto FailPath;
4716 }
4717
4718 //
4719 // After analyzing the VAD, set it to NULL so that we don't
4720 // free it in the exit path
4721 //
4722 Vad = NULL;
4723 }
4724 }
4725
4726 //
4727 // Now we have a range of pages to dereference, so call the right API
4728 // to do that and then release the working set, since we're done messing
4729 // around with process pages.
4730 //
4731 MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL);
4732 MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
4733 Status = STATUS_SUCCESS;
4734
4735 FinalPath:
4736 //
4737 // Update the process counters
4738 //
4739 PRegionSize = EndingAddress - StartingAddress + 1;
4740 Process->CommitCharge -= CommitReduction;
4741 if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize;
4742
4743 //
4744 // Unlock the address space and free the VAD in failure cases. Next,
4745 // detach from the target process so we can write the region size and the
4746 // base address to the correct source process, and dereference the target
4747 // process.
4748 //
4749 MmUnlockAddressSpace(AddressSpace);
4750 if (Vad) ExFreePool(Vad);
4751 if (Attached) KeUnstackDetachProcess(&ApcState);
4752 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4753
4754 //
4755 // Use SEH to safely return the region size and the base address of the
4756 // deallocation. If we get an access violation, don't return a failure code
4757 // as the deallocation *has* happened. The caller will just have to figure
4758 // out another way to find out where it is (such as VirtualQuery).
4759 //
4760 _SEH2_TRY
4761 {
4762 *URegionSize = PRegionSize;
4763 *UBaseAddress = (PVOID)StartingAddress;
4764 }
4765 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
4766 {
4767 }
4768 _SEH2_END;
4769 return Status;
4770 }
4771
4772 //
4773 // This is the decommit path. You cannot decommit from the following VADs in
4774 // Windows, so fail the vall
4775 //
4776 if ((Vad->u.VadFlags.VadType == VadAwe) ||
4777 (Vad->u.VadFlags.VadType == VadLargePages) ||
4778 (Vad->u.VadFlags.VadType == VadRotatePhysical))
4779 {
4780 DPRINT1("Trying to decommit from invalid VAD\n");
4781 Status = STATUS_MEMORY_NOT_ALLOCATED;
4782 goto FailPath;
4783 }
4784
4785 //
4786 // If the caller did not specify a region size, first make sure that this
4787 // region is actually committed. If it is, then compute the ending address
4788 // based on the VAD.
4789 //
4790 if (!PRegionSize)
4791 {
4792 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn)
4793 {
4794 DPRINT1("Decomitting non-committed memory\n");
4795 Status = STATUS_FREE_VM_NOT_AT_BASE;
4796 goto FailPath;
4797 }
4798 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
4799 }
4800
4801 //
4802 // Decommit the PTEs for the range plus the actual backing pages for the
4803 // range, then reduce that amount from the commit charge in the VAD
4804 //
4805 CommitReduction = MiAddressToPte(EndingAddress) -
4806 MiAddressToPte(StartingAddress) +
4807 1 -
4808 MiDecommitPages((PVOID)StartingAddress,
4809 MiAddressToPte(EndingAddress),
4810 Process,
4811 Vad);
4812 ASSERT(CommitReduction >= 0);
4813 Vad->u.VadFlags.CommitCharge -= CommitReduction;
4814 ASSERT(Vad->u.VadFlags.CommitCharge >= 0);
4815
4816 //
4817 // We are done, go to the exit path without freeing the VAD as it remains
4818 // valid since we have not released the allocation.
4819 //
4820 Vad = NULL;
4821 Status = STATUS_SUCCESS;
4822 goto FinalPath;
4823
4824 //
4825 // In the failure path, we detach and derefernece the target process, and
4826 // return whatever failure code was sent.
4827 //
4828 FailPath:
4829 MmUnlockAddressSpace(AddressSpace);
4830 if (Attached) KeUnstackDetachProcess(&ApcState);
4831 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
4832 return Status;
4833 }
4834
4835
4836 PHYSICAL_ADDRESS
4837 NTAPI
4838 MmGetPhysicalAddress(PVOID Address)
4839 {
4840 PHYSICAL_ADDRESS PhysicalAddress;
4841 MMPDE TempPde;
4842 MMPTE TempPte;
4843
4844 /* Check if the PXE/PPE/PDE is valid */
4845 if (
4846 #if (_MI_PAGING_LEVELS == 4)
4847 (MiAddressToPxe(Address)->u.Hard.Valid) &&
4848 #endif
4849 #if (_MI_PAGING_LEVELS >= 3)
4850 (MiAddressToPpe(Address)->u.Hard.Valid) &&
4851 #endif
4852 (MiAddressToPde(Address)->u.Hard.Valid))
4853 {
4854 /* Check for large pages */
4855 TempPde = *MiAddressToPde(Address);
4856 if (TempPde.u.Hard.LargePage)
4857 {
4858 /* Physical address is base page + large page offset */
4859 PhysicalAddress.QuadPart = TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT;
4860 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1));
4861 return PhysicalAddress;
4862 }
4863
4864 /* Check if the PTE is valid */
4865 TempPte = *MiAddressToPte(Address);
4866 if (TempPte.u.Hard.Valid)
4867 {
4868 /* Physical address is base page + page offset */
4869 PhysicalAddress.QuadPart = TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
4870 PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
4871 return PhysicalAddress;
4872 }
4873 }
4874
4875 DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
4876 PhysicalAddress.QuadPart = 0;
4877 return PhysicalAddress;
4878 }
4879
4880
4881 /* EOF */