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