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