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