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