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