[NTOSKRNL]: This is kind of embarssing, but after doing a local grep for some of...
[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 LockChange = FALSE;
38
39 /* Must be a non-pool page table, since those are double-mapped already */
40 ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
41 ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
42 (PageTableVirtualAddress > MmPagedPoolEnd));
43
44 /* Working set lock or PFN lock should be held */
45 ASSERT(KeAreAllApcsDisabled() == TRUE);
46
47 /* Check if the page table is valid */
48 while (!MmIsAddressValid(PageTableVirtualAddress))
49 {
50 /* Fault it in */
51 Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
52 if (!NT_SUCCESS(Status))
53 {
54 /* This should not fail */
55 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
56 1,
57 Status,
58 (ULONG_PTR)CurrentProcess,
59 (ULONG_PTR)PageTableVirtualAddress);
60 }
61
62 /* This flag will be useful later when we do better locking */
63 LockChange = TRUE;
64 }
65
66 /* Let caller know what the lock state is */
67 return LockChange;
68 }
69
70 ULONG
71 NTAPI
72 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
73 IN KIRQL OldIrql)
74 {
75 NTSTATUS Status;
76 BOOLEAN LockChange = FALSE;
77
78 /* Must be e kernel address */
79 ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS);
80
81 /* Check if the page is valid */
82 while (!MmIsAddressValid(VirtualAddress))
83 {
84 /* Release the PFN database */
85 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
86
87 /* Fault it in */
88 Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL);
89 if (!NT_SUCCESS(Status))
90 {
91 /* This should not fail */
92 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
93 3,
94 Status,
95 0,
96 (ULONG_PTR)VirtualAddress);
97 }
98
99 /* This flag will be useful later when we do better locking */
100 LockChange = TRUE;
101
102 /* Lock the PFN database */
103 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
104 }
105
106 /* Let caller know what the lock state is */
107 return LockChange;
108 }
109
110 PFN_COUNT
111 NTAPI
112 MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
113 IN PFN_NUMBER PageCount,
114 IN ULONG Flags,
115 OUT PPFN_NUMBER ValidPages)
116 {
117 PFN_COUNT ActualPages = 0;
118 PETHREAD CurrentThread = PsGetCurrentThread();
119 PMMPFN Pfn1;
120 //PMMPFN Pfn2;
121 PFN_NUMBER PageFrameIndex, PageTableIndex;
122 KIRQL OldIrql;
123 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
124
125 /* Lock the system working set */
126 MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
127
128 /* Loop all pages */
129 while (PageCount)
130 {
131 /* Make sure there's some data about the page */
132 if (PointerPte->u.Long)
133 {
134 /* As always, only handle current ARM3 scenarios */
135 ASSERT(PointerPte->u.Soft.Prototype == 0);
136 ASSERT(PointerPte->u.Soft.Transition == 0);
137
138 /* Normally this is one possibility -- freeing a valid page */
139 if (PointerPte->u.Hard.Valid)
140 {
141 /* Get the page PFN */
142 PageFrameIndex = PFN_FROM_PTE(PointerPte);
143 Pfn1 = MiGetPfnEntry(PageFrameIndex);
144
145 /* Should not have any working set data yet */
146 ASSERT(Pfn1->u1.WsIndex == 0);
147
148 /* Actual valid, legitimate, pages */
149 if (ValidPages) (*ValidPages)++;
150
151 /* Get the page table entry */
152 PageTableIndex = Pfn1->u4.PteFrame;
153 //Pfn2 = MiGetPfnEntry(PageTableIndex);
154
155 /* Lock the PFN database */
156 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
157
158 /* Delete it the page */
159 MI_SET_PFN_DELETED(Pfn1);
160 MiDecrementShareCount(Pfn1, PageFrameIndex);
161
162 /* Decrement the page table too */
163 DPRINT("FIXME: ARM3 should decrement the pool PDE refcount for: %p\n", PageTableIndex);
164 #if 0 // ARM3: Dont't trust this yet
165 MiDecrementShareCount(Pfn2, PageTableIndex);
166 #endif
167
168 /* Release the PFN database */
169 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
170
171 /* Destroy the PTE */
172 PointerPte->u.Long = 0;
173 }
174
175 /* Actual legitimate pages */
176 ActualPages++;
177 }
178 else
179 {
180 /*
181 * The only other ARM3 possibility is a demand zero page, which would
182 * mean freeing some of the paged pool pages that haven't even been
183 * touched yet, as part of a larger allocation.
184 *
185 * Right now, we shouldn't expect any page file information in the PTE
186 */
187 ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
188
189 /* Destroy the PTE */
190 PointerPte->u.Long = 0;
191 }
192
193 /* Keep going */
194 PointerPte++;
195 PageCount--;
196 }
197
198 /* Release the working set */
199 MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
200
201 /* Flush the entire TLB */
202 KeFlushEntireTb(TRUE, TRUE);
203
204 /* Done */
205 return ActualPages;
206 }
207
208 VOID
209 NTAPI
210 MiDeletePte(IN PMMPTE PointerPte,
211 IN PVOID VirtualAddress,
212 IN PEPROCESS CurrentProcess,
213 IN PMMPTE PrototypePte)
214 {
215 PMMPFN Pfn1;
216 MMPTE TempPte;
217 PFN_NUMBER PageFrameIndex;
218 PMMPDE PointerPde;
219
220 /* PFN lock must be held */
221 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
222
223 /* Capture the PTE */
224 TempPte = *PointerPte;
225
226 /* We only support valid PTEs for now */
227 ASSERT(TempPte.u.Hard.Valid == 1);
228 if (TempPte.u.Hard.Valid == 0)
229 {
230 /* Invalid PTEs not supported yet */
231 ASSERT(TempPte.u.Soft.Prototype == 0);
232 ASSERT(TempPte.u.Soft.Transition == 0);
233 }
234
235 /* Get the PFN entry */
236 PageFrameIndex = PFN_FROM_PTE(&TempPte);
237 Pfn1 = MiGetPfnEntry(PageFrameIndex);
238
239 /* Check if this is a valid, prototype PTE */
240 if (Pfn1->u3.e1.PrototypePte == 1)
241 {
242 /* Get the PDE and make sure it's faulted in */
243 PointerPde = MiPteToPde(PointerPte);
244 if (PointerPde->u.Hard.Valid == 0)
245 {
246 #if (_MI_PAGING_LEVELS == 2)
247 /* Could be paged pool access from a new process -- synchronize the page directories */
248 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
249 {
250 #endif
251 /* The PDE must be valid at this point */
252 KeBugCheckEx(MEMORY_MANAGEMENT,
253 0x61940,
254 (ULONG_PTR)PointerPte,
255 PointerPte->u.Long,
256 (ULONG_PTR)VirtualAddress);
257 }
258 #if (_MI_PAGING_LEVELS == 2)
259 }
260 #endif
261 /* Drop the reference on the page table. */
262 MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde)), PFN_FROM_PTE(PointerPde));
263
264 /* Drop the share count */
265 MiDecrementShareCount(Pfn1, PageFrameIndex);
266
267 /* Either a fork, or this is the shared user data page */
268 if (PointerPte <= MiHighestUserPte)
269 {
270 /* If it's not the shared user page, then crash, since there's no fork() yet */
271 if ((PAGE_ALIGN(VirtualAddress) != (PVOID)USER_SHARED_DATA) ||
272 (MmHighestUserAddress <= (PVOID)USER_SHARED_DATA))
273 {
274 /* Must be some sort of memory corruption */
275 KeBugCheckEx(MEMORY_MANAGEMENT,
276 0x400,
277 (ULONG_PTR)PointerPte,
278 (ULONG_PTR)PrototypePte,
279 (ULONG_PTR)Pfn1->PteAddress);
280 }
281 }
282 }
283 else
284 {
285 /* Make sure the saved PTE address is valid */
286 if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
287 {
288 /* The PFN entry is illegal, or invalid */
289 KeBugCheckEx(MEMORY_MANAGEMENT,
290 0x401,
291 (ULONG_PTR)PointerPte,
292 PointerPte->u.Long,
293 (ULONG_PTR)Pfn1->PteAddress);
294 }
295
296 /* There should only be 1 shared reference count */
297 ASSERT(Pfn1->u2.ShareCount == 1);
298
299 /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
300 //DPRINT1("Dropping a ref...\n");
301 MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
302
303 /* Mark the PFN for deletion and dereference what should be the last ref */
304 MI_SET_PFN_DELETED(Pfn1);
305 MiDecrementShareCount(Pfn1, PageFrameIndex);
306
307 /* We should eventually do this */
308 //CurrentProcess->NumberOfPrivatePages--;
309 }
310
311 /* Destroy the PTE and flush the TLB */
312 PointerPte->u.Long = 0;
313 KeFlushCurrentTb();
314 }
315
316 VOID
317 NTAPI
318 MiDeleteVirtualAddresses(IN ULONG_PTR Va,
319 IN ULONG_PTR EndingAddress,
320 IN PMMVAD Vad)
321 {
322 PMMPTE PointerPte, PrototypePte, LastPrototypePte;
323 PMMPDE PointerPde;
324 MMPTE TempPte;
325 PEPROCESS CurrentProcess;
326 KIRQL OldIrql;
327 BOOLEAN AddressGap = FALSE;
328 PSUBSECTION Subsection;
329 PUSHORT UsedPageTableEntries;
330
331 /* Get out if this is a fake VAD, RosMm will free the marea pages */
332 if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
333
334 /* Grab the process and PTE/PDE for the address being deleted */
335 CurrentProcess = PsGetCurrentProcess();
336 PointerPde = MiAddressToPde(Va);
337 PointerPte = MiAddressToPte(Va);
338
339 /* Check if this is a section VAD or a VM VAD */
340 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
341 {
342 /* Don't worry about prototypes */
343 PrototypePte = LastPrototypePte = NULL;
344 }
345 else
346 {
347 /* Get the prototype PTE */
348 PrototypePte = Vad->FirstPrototypePte;
349 LastPrototypePte = Vad->FirstPrototypePte + 1;
350 }
351
352 /* In all cases, we don't support fork() yet */
353 ASSERT(CurrentProcess->CloneRoot == NULL);
354
355 /* Loop the PTE for each VA */
356 while (TRUE)
357 {
358 /* First keep going until we find a valid PDE */
359 while (!PointerPde->u.Long)
360 {
361 /* There are gaps in the address space */
362 AddressGap = TRUE;
363
364 /* Still no valid PDE, try the next 4MB (or whatever) */
365 PointerPde++;
366
367 /* Update the PTE on this new boundary */
368 PointerPte = MiPteToAddress(PointerPde);
369
370 /* Check if all the PDEs are invalid, so there's nothing to free */
371 Va = (ULONG_PTR)MiPteToAddress(PointerPte);
372 if (Va > EndingAddress) return;
373 }
374
375 /* Now check if the PDE is mapped in */
376 if (!PointerPde->u.Hard.Valid)
377 {
378 /* It isn't, so map it in */
379 PointerPte = MiPteToAddress(PointerPde);
380 MiMakeSystemAddressValid(PointerPte, CurrentProcess);
381 }
382
383 /* Now we should have a valid PDE, mapped in, and still have some VA */
384 ASSERT(PointerPde->u.Hard.Valid == 1);
385 ASSERT(Va <= EndingAddress);
386 UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Va)];
387
388 /* Check if this is a section VAD with gaps in it */
389 if ((AddressGap) && (LastPrototypePte))
390 {
391 /* We need to skip to the next correct prototype PTE */
392 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
393
394 /* And we need the subsection to skip to the next last prototype PTE */
395 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
396 if (Subsection)
397 {
398 /* Found it! */
399 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
400 }
401 else
402 {
403 /* No more subsections, we are done with prototype PTEs */
404 PrototypePte = NULL;
405 }
406 }
407
408 /* Lock the PFN Database while we delete the PTEs */
409 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
410 do
411 {
412 /* Capture the PDE and make sure it exists */
413 TempPte = *PointerPte;
414 if (TempPte.u.Long)
415 {
416 DPRINT("Decrement used PTEs by address: %lx\n", Va);
417 (*UsedPageTableEntries)--;
418 ASSERT((*UsedPageTableEntries) < PTE_COUNT);
419 DPRINT("Refs: %lx\n", (*UsedPageTableEntries));
420
421 /* Check if the PTE is actually mapped in */
422 if (TempPte.u.Long & 0xFFFFFC01)
423 {
424 /* Are we dealing with section VAD? */
425 if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
426 {
427 /* We need to skip to the next correct prototype PTE */
428 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
429
430 /* And we need the subsection to skip to the next last prototype PTE */
431 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
432 if (Subsection)
433 {
434 /* Found it! */
435 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
436 }
437 else
438 {
439 /* No more subsections, we are done with prototype PTEs */
440 PrototypePte = NULL;
441 }
442 }
443
444 /* Check for prototype PTE */
445 if ((TempPte.u.Hard.Valid == 0) &&
446 (TempPte.u.Soft.Prototype == 1))
447 {
448 /* Just nuke it */
449 PointerPte->u.Long = 0;
450 }
451 else
452 {
453 /* Delete the PTE proper */
454 MiDeletePte(PointerPte,
455 (PVOID)Va,
456 CurrentProcess,
457 PrototypePte);
458 }
459 }
460 else
461 {
462 /* The PTE was never mapped, just nuke it here */
463 PointerPte->u.Long = 0;
464 }
465 }
466
467 /* Update the address and PTE for it */
468 Va += PAGE_SIZE;
469 PointerPte++;
470 PrototypePte++;
471
472 /* Making sure the PDE is still valid */
473 ASSERT(PointerPde->u.Hard.Valid == 1);
474 }
475 while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
476
477 /* The PDE should still be valid at this point */
478 ASSERT(PointerPde->u.Hard.Valid == 1);
479
480 DPRINT("Should check if handles for: %p are zero (PDE: %lx)\n", Va, PointerPde->u.Hard.PageFrameNumber);
481 if (!(*UsedPageTableEntries))
482 {
483 DPRINT("They are!\n");
484 if (PointerPde->u.Long != 0)
485 {
486 DPRINT("PDE active: %lx in %16s\n", PointerPde->u.Hard.PageFrameNumber, CurrentProcess->ImageFileName);
487
488 /* Delete the PTE proper */
489 MiDeletePte(PointerPde,
490 MiPteToAddress(PointerPde),
491 CurrentProcess,
492 NULL);
493 }
494 }
495
496 /* Release the lock and get out if we're done */
497 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
498 if (Va > EndingAddress) return;
499
500 /* Otherwise, we exited because we hit a new PDE boundary, so start over */
501 PointerPde = MiAddressToPde(Va);
502 AddressGap = FALSE;
503 }
504 }
505
506 LONG
507 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
508 OUT PBOOLEAN HaveBadAddress,
509 OUT PULONG_PTR BadAddress)
510 {
511 PEXCEPTION_RECORD ExceptionRecord;
512 PAGED_CODE();
513
514 //
515 // Assume default
516 //
517 *HaveBadAddress = FALSE;
518
519 //
520 // Get the exception record
521 //
522 ExceptionRecord = ExceptionInfo->ExceptionRecord;
523
524 //
525 // Look at the exception code
526 //
527 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) ||
528 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) ||
529 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR))
530 {
531 //
532 // We can tell the address if we have more than one parameter
533 //
534 if (ExceptionRecord->NumberParameters > 1)
535 {
536 //
537 // Return the address
538 //
539 *HaveBadAddress = TRUE;
540 *BadAddress = ExceptionRecord->ExceptionInformation[1];
541 }
542 }
543
544 //
545 // Continue executing the next handler
546 //
547 return EXCEPTION_EXECUTE_HANDLER;
548 }
549
550 NTSTATUS
551 NTAPI
552 MiDoMappedCopy(IN PEPROCESS SourceProcess,
553 IN PVOID SourceAddress,
554 IN PEPROCESS TargetProcess,
555 OUT PVOID TargetAddress,
556 IN SIZE_T BufferSize,
557 IN KPROCESSOR_MODE PreviousMode,
558 OUT PSIZE_T ReturnSize)
559 {
560 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1];
561 PMDL Mdl = (PMDL)MdlBuffer;
562 SIZE_T TotalSize, CurrentSize, RemainingSize;
563 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving;
564 volatile BOOLEAN PagesLocked;
565 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
566 volatile PVOID MdlAddress;
567 KAPC_STATE ApcState;
568 BOOLEAN HaveBadAddress;
569 ULONG_PTR BadAddress;
570 NTSTATUS Status = STATUS_SUCCESS;
571 PAGED_CODE();
572
573 //
574 // Calculate the maximum amount of data to move
575 //
576 TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE;
577 if (BufferSize <= TotalSize) TotalSize = BufferSize;
578 CurrentSize = TotalSize;
579 RemainingSize = BufferSize;
580
581 //
582 // Loop as long as there is still data
583 //
584 while (RemainingSize > 0)
585 {
586 //
587 // Check if this transfer will finish everything off
588 //
589 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
590
591 //
592 // Attach to the source address space
593 //
594 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
595
596 //
597 // Reset state for this pass
598 //
599 MdlAddress = NULL;
600 PagesLocked = FALSE;
601 FailedInMoving = FALSE;
602 ASSERT(FailedInProbe == FALSE);
603
604 //
605 // Protect user-mode copy
606 //
607 _SEH2_TRY
608 {
609 //
610 // If this is our first time, probe the buffer
611 //
612 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
613 {
614 //
615 // Catch a failure here
616 //
617 FailedInProbe = TRUE;
618
619 //
620 // Do the probe
621 //
622 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
623
624 //
625 // Passed
626 //
627 FailedInProbe = FALSE;
628 }
629
630 //
631 // Initialize and probe and lock the MDL
632 //
633 MmInitializeMdl(Mdl, CurrentAddress, CurrentSize);
634 MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
635 PagesLocked = TRUE;
636
637 //
638 // Now map the pages
639 //
640 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl,
641 KernelMode,
642 MmCached,
643 NULL,
644 FALSE,
645 HighPagePriority);
646 if (!MdlAddress)
647 {
648 //
649 // Use our SEH handler to pick this up
650 //
651 FailedInMapping = TRUE;
652 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
653 }
654
655 //
656 // Now let go of the source and grab to the target process
657 //
658 KeUnstackDetachProcess(&ApcState);
659 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
660
661 //
662 // Check if this is our first time through
663 //
664 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
665 {
666 //
667 // Catch a failure here
668 //
669 FailedInProbe = TRUE;
670
671 //
672 // Do the probe
673 //
674 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
675
676 //
677 // Passed
678 //
679 FailedInProbe = FALSE;
680 }
681
682 //
683 // Now do the actual move
684 //
685 FailedInMoving = TRUE;
686 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize);
687 }
688 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
689 &HaveBadAddress,
690 &BadAddress))
691 {
692 //
693 // Detach from whoever we may be attached to
694 //
695 KeUnstackDetachProcess(&ApcState);
696
697 //
698 // Check if we had mapped the pages
699 //
700 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl);
701
702 //
703 // Check if we had locked the pages
704 //
705 if (PagesLocked) MmUnlockPages(Mdl);
706
707 //
708 // Check if we hit working set quota
709 //
710 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA)
711 {
712 //
713 // Return the error
714 //
715 return STATUS_WORKING_SET_QUOTA;
716 }
717
718 //
719 // Check if we failed during the probe or mapping
720 //
721 if ((FailedInProbe) || (FailedInMapping))
722 {
723 //
724 // Exit
725 //
726 Status = _SEH2_GetExceptionCode();
727 _SEH2_YIELD(return Status);
728 }
729
730 //
731 // Otherwise, we failed probably during the move
732 //
733 *ReturnSize = BufferSize - RemainingSize;
734 if (FailedInMoving)
735 {
736 //
737 // Check if we know exactly where we stopped copying
738 //
739 if (HaveBadAddress)
740 {
741 //
742 // Return the exact number of bytes copied
743 //
744 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
745 }
746 }
747
748 //
749 // Return partial copy
750 //
751 Status = STATUS_PARTIAL_COPY;
752 }
753 _SEH2_END;
754
755 //
756 // Check for SEH status
757 //
758 if (Status != STATUS_SUCCESS) return Status;
759
760 //
761 // Detach from target
762 //
763 KeUnstackDetachProcess(&ApcState);
764
765 //
766 // Unmap and unlock
767 //
768 MmUnmapLockedPages(MdlAddress, Mdl);
769 MmUnlockPages(Mdl);
770
771 //
772 // Update location and size
773 //
774 RemainingSize -= CurrentSize;
775 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
776 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize);
777 }
778
779 //
780 // All bytes read
781 //
782 *ReturnSize = BufferSize;
783 return STATUS_SUCCESS;
784 }
785
786 NTSTATUS
787 NTAPI
788 MiDoPoolCopy(IN PEPROCESS SourceProcess,
789 IN PVOID SourceAddress,
790 IN PEPROCESS TargetProcess,
791 OUT PVOID TargetAddress,
792 IN SIZE_T BufferSize,
793 IN KPROCESSOR_MODE PreviousMode,
794 OUT PSIZE_T ReturnSize)
795 {
796 UCHAR StackBuffer[MI_POOL_COPY_BYTES];
797 SIZE_T TotalSize, CurrentSize, RemainingSize;
798 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE;
799 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress;
800 PVOID PoolAddress;
801 KAPC_STATE ApcState;
802 BOOLEAN HaveBadAddress;
803 ULONG_PTR BadAddress;
804 NTSTATUS Status = STATUS_SUCCESS;
805 PAGED_CODE();
806
807 //
808 // Calculate the maximum amount of data to move
809 //
810 TotalSize = MI_MAX_TRANSFER_SIZE;
811 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize;
812 CurrentSize = TotalSize;
813 RemainingSize = BufferSize;
814
815 //
816 // Check if we can use the stack
817 //
818 if (BufferSize <= MI_POOL_COPY_BYTES)
819 {
820 //
821 // Use it
822 //
823 PoolAddress = (PVOID)StackBuffer;
824 }
825 else
826 {
827 //
828 // Allocate pool
829 //
830 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw');
831 if (!PoolAddress) ASSERT(FALSE);
832 HavePoolAddress = TRUE;
833 }
834
835 //
836 // Loop as long as there is still data
837 //
838 while (RemainingSize > 0)
839 {
840 //
841 // Check if this transfer will finish everything off
842 //
843 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize;
844
845 //
846 // Attach to the source address space
847 //
848 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState);
849
850 //
851 // Reset state for this pass
852 //
853 FailedInMoving = FALSE;
854 ASSERT(FailedInProbe == FALSE);
855
856 //
857 // Protect user-mode copy
858 //
859 _SEH2_TRY
860 {
861 //
862 // If this is our first time, probe the buffer
863 //
864 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
865 {
866 //
867 // Catch a failure here
868 //
869 FailedInProbe = TRUE;
870
871 //
872 // Do the probe
873 //
874 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR));
875
876 //
877 // Passed
878 //
879 FailedInProbe = FALSE;
880 }
881
882 //
883 // Do the copy
884 //
885 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize);
886
887 //
888 // Now let go of the source and grab to the target process
889 //
890 KeUnstackDetachProcess(&ApcState);
891 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
892
893 //
894 // Check if this is our first time through
895 //
896 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode))
897 {
898 //
899 // Catch a failure here
900 //
901 FailedInProbe = TRUE;
902
903 //
904 // Do the probe
905 //
906 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR));
907
908 //
909 // Passed
910 //
911 FailedInProbe = FALSE;
912 }
913
914 //
915 // Now do the actual move
916 //
917 FailedInMoving = TRUE;
918 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize);
919 }
920 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(),
921 &HaveBadAddress,
922 &BadAddress))
923 {
924 //
925 // Detach from whoever we may be attached to
926 //
927 KeUnstackDetachProcess(&ApcState);
928
929 //
930 // Check if we had allocated pool
931 //
932 if (HavePoolAddress) ExFreePool(PoolAddress);
933
934 //
935 // Check if we failed during the probe
936 //
937 if (FailedInProbe)
938 {
939 //
940 // Exit
941 //
942 Status = _SEH2_GetExceptionCode();
943 _SEH2_YIELD(return Status);
944 }
945
946 //
947 // Otherwise, we failed, probably during the move
948 //
949 *ReturnSize = BufferSize - RemainingSize;
950 if (FailedInMoving)
951 {
952 //
953 // Check if we know exactly where we stopped copying
954 //
955 if (HaveBadAddress)
956 {
957 //
958 // Return the exact number of bytes copied
959 //
960 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress;
961 }
962 }
963
964 //
965 // Return partial copy
966 //
967 Status = STATUS_PARTIAL_COPY;
968 }
969 _SEH2_END;
970
971 //
972 // Check for SEH status
973 //
974 if (Status != STATUS_SUCCESS) return Status;
975
976 //
977 // Detach from target
978 //
979 KeUnstackDetachProcess(&ApcState);
980
981 //
982 // Update location and size
983 //
984 RemainingSize -= CurrentSize;
985 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize);
986 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress +
987 CurrentSize);
988 }
989
990 //
991 // Check if we had allocated pool
992 //
993 if (HavePoolAddress) ExFreePool(PoolAddress);
994
995 //
996 // All bytes read
997 //
998 *ReturnSize = BufferSize;
999 return STATUS_SUCCESS;
1000 }
1001
1002 NTSTATUS
1003 NTAPI
1004 MmCopyVirtualMemory(IN PEPROCESS SourceProcess,
1005 IN PVOID SourceAddress,
1006 IN PEPROCESS TargetProcess,
1007 OUT PVOID TargetAddress,
1008 IN SIZE_T BufferSize,
1009 IN KPROCESSOR_MODE PreviousMode,
1010 OUT PSIZE_T ReturnSize)
1011 {
1012 NTSTATUS Status;
1013 PEPROCESS Process = SourceProcess;
1014
1015 //
1016 // Don't accept zero-sized buffers
1017 //
1018 if (!BufferSize) return STATUS_SUCCESS;
1019
1020 //
1021 // If we are copying from ourselves, lock the target instead
1022 //
1023 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess;
1024
1025 //
1026 // Acquire rundown protection
1027 //
1028 if (!ExAcquireRundownProtection(&Process->RundownProtect))
1029 {
1030 //
1031 // Fail
1032 //
1033 return STATUS_PROCESS_IS_TERMINATING;
1034 }
1035
1036 //
1037 // See if we should use the pool copy
1038 //
1039 if (BufferSize > MI_POOL_COPY_BYTES)
1040 {
1041 //
1042 // Use MDL-copy
1043 //
1044 Status = MiDoMappedCopy(SourceProcess,
1045 SourceAddress,
1046 TargetProcess,
1047 TargetAddress,
1048 BufferSize,
1049 PreviousMode,
1050 ReturnSize);
1051 }
1052 else
1053 {
1054 //
1055 // Do pool copy
1056 //
1057 Status = MiDoPoolCopy(SourceProcess,
1058 SourceAddress,
1059 TargetProcess,
1060 TargetAddress,
1061 BufferSize,
1062 PreviousMode,
1063 ReturnSize);
1064 }
1065
1066 //
1067 // Release the lock
1068 //
1069 ExReleaseRundownProtection(&Process->RundownProtect);
1070 return Status;
1071 }
1072
1073 NTSTATUS
1074 NTAPI
1075 MmFlushVirtualMemory(IN PEPROCESS Process,
1076 IN OUT PVOID *BaseAddress,
1077 IN OUT PSIZE_T RegionSize,
1078 OUT PIO_STATUS_BLOCK IoStatusBlock)
1079 {
1080 PAGED_CODE();
1081 UNIMPLEMENTED;
1082
1083 //
1084 // Fake success
1085 //
1086 return STATUS_SUCCESS;
1087 }
1088
1089 ULONG
1090 NTAPI
1091 MiGetPageProtection(IN PMMPTE PointerPte)
1092 {
1093 MMPTE TempPte;
1094 PMMPFN Pfn;
1095 PAGED_CODE();
1096
1097 /* Copy this PTE's contents */
1098 TempPte = *PointerPte;
1099
1100 /* Assure it's not totally zero */
1101 ASSERT(TempPte.u.Long);
1102
1103 /* Check for a special prototype format */
1104 if (TempPte.u.Soft.Valid == 0 &&
1105 TempPte.u.Soft.Prototype == 1)
1106 {
1107 /* Unsupported now */
1108 UNIMPLEMENTED;
1109 ASSERT(FALSE);
1110 }
1111
1112 /* In the easy case of transition or demand zero PTE just return its protection */
1113 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection];
1114
1115 /* If we get here, the PTE is valid, so look up the page in PFN database */
1116 Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber);
1117
1118 if (!Pfn->u3.e1.PrototypePte)
1119 {
1120 /* Return protection of the original pte */
1121 return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
1122 }
1123
1124 /* This is hardware PTE */
1125 UNIMPLEMENTED;
1126 ASSERT(FALSE);
1127
1128 return PAGE_NOACCESS;
1129 }
1130
1131 ULONG
1132 NTAPI
1133 MiQueryAddressState(IN PVOID Va,
1134 IN PMMVAD Vad,
1135 IN PEPROCESS TargetProcess,
1136 OUT PULONG ReturnedProtect,
1137 OUT PVOID *NextVa)
1138 {
1139
1140 PMMPTE PointerPte;
1141 PMMPDE PointerPde;
1142 MMPTE TempPte;
1143 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
1144 ULONG State = MEM_RESERVE, Protect = 0, LockChange;
1145 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
1146 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
1147
1148 /* Only normal VADs supported */
1149 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1150
1151 /* Get the PDE and PTE for the address */
1152 PointerPde = MiAddressToPde(Va);
1153 PointerPte = MiAddressToPte(Va);
1154
1155 /* Return the next range */
1156 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
1157
1158 /* Loop to make sure the PDE is valid */
1159 do
1160 {
1161 /* Try again */
1162 LockChange = 0;
1163
1164 /* Is the PDE empty? */
1165 if (!PointerPde->u.Long)
1166 {
1167 /* No address in this range used yet, move to the next PDE range */
1168 *NextVa = MiPdeToAddress(PointerPde + 1);
1169 break;
1170 }
1171
1172 /* The PDE is not empty, but is it faulted in? */
1173 if (!PointerPde->u.Hard.Valid)
1174 {
1175 /* It isn't, go ahead and do the fault */
1176 LockChange = MiMakeSystemAddressValid(MiPdeToPte(PointerPde),
1177 TargetProcess);
1178 }
1179
1180 /* Check if the PDE was faulted in, making the PTE readable */
1181 if (!LockChange) ValidPte = TRUE;
1182 } while (LockChange);
1183
1184 /* Is it safe to try reading the PTE? */
1185 if (ValidPte)
1186 {
1187 /* FIXME: watch out for large pages */
1188
1189 /* Capture the PTE */
1190 TempPte = *PointerPte;
1191 if (TempPte.u.Long)
1192 {
1193 /* The PTE is valid, so it's not zeroed out */
1194 DemandZeroPte = FALSE;
1195
1196 /* Check if it's valid or has a valid protection mask */
1197 ASSERT(TempPte.u.Soft.Prototype == 0);
1198 if ((TempPte.u.Soft.Protection != MM_DECOMMIT) ||
1199 (TempPte.u.Hard.Valid == 1))
1200 {
1201 /* This means it's committed */
1202 State = MEM_COMMIT;
1203
1204 /* Get protection state of this page */
1205 Protect = MiGetPageProtection(PointerPte);
1206 }
1207 else
1208 {
1209 /* Otherwise our defaults should hold */
1210 ASSERT(Protect == 0);
1211 ASSERT(State == MEM_RESERVE);
1212 }
1213 }
1214 }
1215
1216 /* Check if this was a demand-zero PTE, since we need to find the state */
1217 if (DemandZeroPte)
1218 {
1219 /* Check if the VAD is for committed memory */
1220 if (Vad->u.VadFlags.MemCommit)
1221 {
1222 /* This is committed memory */
1223 State = MEM_COMMIT;
1224
1225 /* Convert the protection */
1226 Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
1227 }
1228 }
1229
1230 /* Return the protection code */
1231 *ReturnedProtect = Protect;
1232 return State;
1233 }
1234
1235 /* PUBLIC FUNCTIONS ***********************************************************/
1236
1237 /*
1238 * @unimplemented
1239 */
1240 PVOID
1241 NTAPI
1242 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress)
1243 {
1244 UNIMPLEMENTED;
1245 return 0;
1246 }
1247
1248 /*
1249 * @unimplemented
1250 */
1251 PVOID
1252 NTAPI
1253 MmSecureVirtualMemory(IN PVOID Address,
1254 IN SIZE_T Length,
1255 IN ULONG Mode)
1256 {
1257 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
1258 return Address;
1259 }
1260
1261 /*
1262 * @unimplemented
1263 */
1264 VOID
1265 NTAPI
1266 MmUnsecureVirtualMemory(IN PVOID SecureMem)
1267 {
1268 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED;
1269 }
1270
1271 /* SYSTEM CALLS ***************************************************************/
1272
1273 NTSTATUS
1274 NTAPI
1275 NtReadVirtualMemory(IN HANDLE ProcessHandle,
1276 IN PVOID BaseAddress,
1277 OUT PVOID Buffer,
1278 IN SIZE_T NumberOfBytesToRead,
1279 OUT PSIZE_T NumberOfBytesRead OPTIONAL)
1280 {
1281 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1282 PEPROCESS Process;
1283 NTSTATUS Status = STATUS_SUCCESS;
1284 SIZE_T BytesRead = 0;
1285 PAGED_CODE();
1286
1287 //
1288 // Check if we came from user mode
1289 //
1290 if (PreviousMode != KernelMode)
1291 {
1292 //
1293 // Validate the read addresses
1294 //
1295 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) ||
1296 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) ||
1297 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) ||
1298 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress))
1299 {
1300 //
1301 // Don't allow to write into kernel space
1302 //
1303 return STATUS_ACCESS_VIOLATION;
1304 }
1305
1306 //
1307 // Enter SEH for probe
1308 //
1309 _SEH2_TRY
1310 {
1311 //
1312 // Probe the output value
1313 //
1314 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead);
1315 }
1316 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1317 {
1318 //
1319 // Get exception code
1320 //
1321 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1322 }
1323 _SEH2_END;
1324 }
1325
1326 //
1327 // Don't do zero-byte transfers
1328 //
1329 if (NumberOfBytesToRead)
1330 {
1331 //
1332 // Reference the process
1333 //
1334 Status = ObReferenceObjectByHandle(ProcessHandle,
1335 PROCESS_VM_READ,
1336 PsProcessType,
1337 PreviousMode,
1338 (PVOID*)(&Process),
1339 NULL);
1340 if (NT_SUCCESS(Status))
1341 {
1342 //
1343 // Do the copy
1344 //
1345 Status = MmCopyVirtualMemory(Process,
1346 BaseAddress,
1347 PsGetCurrentProcess(),
1348 Buffer,
1349 NumberOfBytesToRead,
1350 PreviousMode,
1351 &BytesRead);
1352
1353 //
1354 // Dereference the process
1355 //
1356 ObDereferenceObject(Process);
1357 }
1358 }
1359
1360 //
1361 // Check if the caller sent this parameter
1362 //
1363 if (NumberOfBytesRead)
1364 {
1365 //
1366 // Enter SEH to guard write
1367 //
1368 _SEH2_TRY
1369 {
1370 //
1371 // Return the number of bytes read
1372 //
1373 *NumberOfBytesRead = BytesRead;
1374 }
1375 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1376 {
1377 }
1378 _SEH2_END;
1379 }
1380
1381 //
1382 // Return status
1383 //
1384 return Status;
1385 }
1386
1387 NTSTATUS
1388 NTAPI
1389 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
1390 IN PVOID BaseAddress,
1391 IN PVOID Buffer,
1392 IN SIZE_T NumberOfBytesToWrite,
1393 OUT PSIZE_T NumberOfBytesWritten OPTIONAL)
1394 {
1395 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1396 PEPROCESS Process;
1397 NTSTATUS Status = STATUS_SUCCESS;
1398 SIZE_T BytesWritten = 0;
1399 PAGED_CODE();
1400
1401 //
1402 // Check if we came from user mode
1403 //
1404 if (PreviousMode != KernelMode)
1405 {
1406 //
1407 // Validate the read addresses
1408 //
1409 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) ||
1410 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) ||
1411 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) ||
1412 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress))
1413 {
1414 //
1415 // Don't allow to write into kernel space
1416 //
1417 return STATUS_ACCESS_VIOLATION;
1418 }
1419
1420 //
1421 // Enter SEH for probe
1422 //
1423 _SEH2_TRY
1424 {
1425 //
1426 // Probe the output value
1427 //
1428 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten);
1429 }
1430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1431 {
1432 //
1433 // Get exception code
1434 //
1435 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1436 }
1437 _SEH2_END;
1438 }
1439
1440 //
1441 // Don't do zero-byte transfers
1442 //
1443 if (NumberOfBytesToWrite)
1444 {
1445 //
1446 // Reference the process
1447 //
1448 Status = ObReferenceObjectByHandle(ProcessHandle,
1449 PROCESS_VM_WRITE,
1450 PsProcessType,
1451 PreviousMode,
1452 (PVOID*)&Process,
1453 NULL);
1454 if (NT_SUCCESS(Status))
1455 {
1456 //
1457 // Do the copy
1458 //
1459 Status = MmCopyVirtualMemory(PsGetCurrentProcess(),
1460 Buffer,
1461 Process,
1462 BaseAddress,
1463 NumberOfBytesToWrite,
1464 PreviousMode,
1465 &BytesWritten);
1466
1467 //
1468 // Dereference the process
1469 //
1470 ObDereferenceObject(Process);
1471 }
1472 }
1473
1474 //
1475 // Check if the caller sent this parameter
1476 //
1477 if (NumberOfBytesWritten)
1478 {
1479 //
1480 // Enter SEH to guard write
1481 //
1482 _SEH2_TRY
1483 {
1484 //
1485 // Return the number of bytes written
1486 //
1487 *NumberOfBytesWritten = BytesWritten;
1488 }
1489 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1490 {
1491 }
1492 _SEH2_END;
1493 }
1494
1495 //
1496 // Return status
1497 //
1498 return Status;
1499 }
1500
1501 NTSTATUS
1502 NTAPI
1503 NtProtectVirtualMemory(IN HANDLE ProcessHandle,
1504 IN OUT PVOID *UnsafeBaseAddress,
1505 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect,
1506 IN ULONG NewAccessProtection,
1507 OUT PULONG UnsafeOldAccessProtection)
1508 {
1509 PEPROCESS Process;
1510 ULONG OldAccessProtection;
1511 ULONG Protection;
1512 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1513 PVOID BaseAddress = NULL;
1514 SIZE_T NumberOfBytesToProtect = 0;
1515 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1516 NTSTATUS Status;
1517 BOOLEAN Attached = FALSE;
1518 KAPC_STATE ApcState;
1519 PAGED_CODE();
1520
1521 //
1522 // Check for valid protection flags
1523 //
1524 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE);
1525 if (Protection != PAGE_NOACCESS &&
1526 Protection != PAGE_READONLY &&
1527 Protection != PAGE_READWRITE &&
1528 Protection != PAGE_WRITECOPY &&
1529 Protection != PAGE_EXECUTE &&
1530 Protection != PAGE_EXECUTE_READ &&
1531 Protection != PAGE_EXECUTE_READWRITE &&
1532 Protection != PAGE_EXECUTE_WRITECOPY)
1533 {
1534 //
1535 // Fail
1536 //
1537 return STATUS_INVALID_PAGE_PROTECTION;
1538 }
1539
1540 //
1541 // Check if we came from user mode
1542 //
1543 if (PreviousMode != KernelMode)
1544 {
1545 //
1546 // Enter SEH for probing
1547 //
1548 _SEH2_TRY
1549 {
1550 //
1551 // Validate all outputs
1552 //
1553 ProbeForWritePointer(UnsafeBaseAddress);
1554 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect);
1555 ProbeForWriteUlong(UnsafeOldAccessProtection);
1556
1557 //
1558 // Capture them
1559 //
1560 BaseAddress = *UnsafeBaseAddress;
1561 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1562 }
1563 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1564 {
1565 //
1566 // Get exception code
1567 //
1568 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1569 }
1570 _SEH2_END;
1571 }
1572 else
1573 {
1574 //
1575 // Capture directly
1576 //
1577 BaseAddress = *UnsafeBaseAddress;
1578 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect;
1579 }
1580
1581 //
1582 // Catch illegal base address
1583 //
1584 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
1585
1586 //
1587 // Catch illegal region size
1588 //
1589 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect)
1590 {
1591 //
1592 // Fail
1593 //
1594 return STATUS_INVALID_PARAMETER_3;
1595 }
1596
1597 //
1598 // 0 is also illegal
1599 //
1600 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3;
1601
1602 //
1603 // Get a reference to the process
1604 //
1605 Status = ObReferenceObjectByHandle(ProcessHandle,
1606 PROCESS_VM_OPERATION,
1607 PsProcessType,
1608 PreviousMode,
1609 (PVOID*)(&Process),
1610 NULL);
1611 if (!NT_SUCCESS(Status)) return Status;
1612
1613 //
1614 // Check if we should attach
1615 //
1616 if (CurrentProcess != Process)
1617 {
1618 //
1619 // Do it
1620 //
1621 KeStackAttachProcess(&Process->Pcb, &ApcState);
1622 Attached = TRUE;
1623 }
1624
1625 //
1626 // Do the actual work
1627 //
1628 Status = MiProtectVirtualMemory(Process,
1629 &BaseAddress,
1630 &NumberOfBytesToProtect,
1631 NewAccessProtection,
1632 &OldAccessProtection);
1633
1634 //
1635 // Detach if needed
1636 //
1637 if (Attached) KeUnstackDetachProcess(&ApcState);
1638
1639 //
1640 // Release reference
1641 //
1642 ObDereferenceObject(Process);
1643
1644 //
1645 // Enter SEH to return data
1646 //
1647 _SEH2_TRY
1648 {
1649 //
1650 // Return data to user
1651 //
1652 *UnsafeOldAccessProtection = OldAccessProtection;
1653 *UnsafeBaseAddress = BaseAddress;
1654 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect;
1655 }
1656 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1657 {
1658 }
1659 _SEH2_END;
1660
1661 //
1662 // Return status
1663 //
1664 return Status;
1665 }
1666
1667 NTSTATUS
1668 NTAPI
1669 NtLockVirtualMemory(IN HANDLE ProcessHandle,
1670 IN OUT PVOID *BaseAddress,
1671 IN OUT PSIZE_T NumberOfBytesToLock,
1672 IN ULONG MapType)
1673 {
1674 PEPROCESS Process;
1675 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1676 NTSTATUS Status;
1677 BOOLEAN Attached = FALSE;
1678 KAPC_STATE ApcState;
1679 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1680 PVOID CapturedBaseAddress;
1681 SIZE_T CapturedBytesToLock;
1682 PAGED_CODE();
1683
1684 //
1685 // Validate flags
1686 //
1687 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
1688 {
1689 //
1690 // Invalid set of flags
1691 //
1692 return STATUS_INVALID_PARAMETER;
1693 }
1694
1695 //
1696 // At least one flag must be specified
1697 //
1698 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
1699 {
1700 //
1701 // No flag given
1702 //
1703 return STATUS_INVALID_PARAMETER;
1704 }
1705
1706 //
1707 // Enter SEH for probing
1708 //
1709 _SEH2_TRY
1710 {
1711 //
1712 // Validate output data
1713 //
1714 ProbeForWritePointer(BaseAddress);
1715 ProbeForWriteSize_t(NumberOfBytesToLock);
1716
1717 //
1718 // Capture it
1719 //
1720 CapturedBaseAddress = *BaseAddress;
1721 CapturedBytesToLock = *NumberOfBytesToLock;
1722 }
1723 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1724 {
1725 //
1726 // Get exception code
1727 //
1728 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1729 }
1730 _SEH2_END;
1731
1732 //
1733 // Catch illegal base address
1734 //
1735 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
1736
1737 //
1738 // Catch illegal region size
1739 //
1740 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock)
1741 {
1742 //
1743 // Fail
1744 //
1745 return STATUS_INVALID_PARAMETER;
1746 }
1747
1748 //
1749 // 0 is also illegal
1750 //
1751 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER;
1752
1753 //
1754 // Get a reference to the process
1755 //
1756 Status = ObReferenceObjectByHandle(ProcessHandle,
1757 PROCESS_VM_OPERATION,
1758 PsProcessType,
1759 PreviousMode,
1760 (PVOID*)(&Process),
1761 NULL);
1762 if (!NT_SUCCESS(Status)) return Status;
1763
1764 //
1765 // Check if this is is system-mapped
1766 //
1767 if (MapType & MAP_SYSTEM)
1768 {
1769 //
1770 // Check for required privilege
1771 //
1772 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
1773 {
1774 //
1775 // Fail: Don't have it
1776 //
1777 ObDereferenceObject(Process);
1778 return STATUS_PRIVILEGE_NOT_HELD;
1779 }
1780 }
1781
1782 //
1783 // Check if we should attach
1784 //
1785 if (CurrentProcess != Process)
1786 {
1787 //
1788 // Do it
1789 //
1790 KeStackAttachProcess(&Process->Pcb, &ApcState);
1791 Attached = TRUE;
1792 }
1793
1794 //
1795 // Oops :(
1796 //
1797 UNIMPLEMENTED;
1798
1799 //
1800 // Detach if needed
1801 //
1802 if (Attached) KeUnstackDetachProcess(&ApcState);
1803
1804 //
1805 // Release reference
1806 //
1807 ObDereferenceObject(Process);
1808
1809 //
1810 // Enter SEH to return data
1811 //
1812 _SEH2_TRY
1813 {
1814 //
1815 // Return data to user
1816 //
1817 *BaseAddress = CapturedBaseAddress;
1818 *NumberOfBytesToLock = 0;
1819 }
1820 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1821 {
1822 //
1823 // Get exception code
1824 //
1825 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1826 }
1827 _SEH2_END;
1828
1829 //
1830 // Return status
1831 //
1832 return STATUS_SUCCESS;
1833 }
1834
1835 NTSTATUS
1836 NTAPI
1837 NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
1838 IN OUT PVOID *BaseAddress,
1839 IN OUT PSIZE_T NumberOfBytesToUnlock,
1840 IN ULONG MapType)
1841 {
1842 PEPROCESS Process;
1843 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1844 NTSTATUS Status;
1845 BOOLEAN Attached = FALSE;
1846 KAPC_STATE ApcState;
1847 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1848 PVOID CapturedBaseAddress;
1849 SIZE_T CapturedBytesToUnlock;
1850 PAGED_CODE();
1851
1852 //
1853 // Validate flags
1854 //
1855 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
1856 {
1857 //
1858 // Invalid set of flags
1859 //
1860 return STATUS_INVALID_PARAMETER;
1861 }
1862
1863 //
1864 // At least one flag must be specified
1865 //
1866 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
1867 {
1868 //
1869 // No flag given
1870 //
1871 return STATUS_INVALID_PARAMETER;
1872 }
1873
1874 //
1875 // Enter SEH for probing
1876 //
1877 _SEH2_TRY
1878 {
1879 //
1880 // Validate output data
1881 //
1882 ProbeForWritePointer(BaseAddress);
1883 ProbeForWriteSize_t(NumberOfBytesToUnlock);
1884
1885 //
1886 // Capture it
1887 //
1888 CapturedBaseAddress = *BaseAddress;
1889 CapturedBytesToUnlock = *NumberOfBytesToUnlock;
1890 }
1891 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1892 {
1893 //
1894 // Get exception code
1895 //
1896 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1897 }
1898 _SEH2_END;
1899
1900 //
1901 // Catch illegal base address
1902 //
1903 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
1904
1905 //
1906 // Catch illegal region size
1907 //
1908 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock)
1909 {
1910 //
1911 // Fail
1912 //
1913 return STATUS_INVALID_PARAMETER;
1914 }
1915
1916 //
1917 // 0 is also illegal
1918 //
1919 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER;
1920
1921 //
1922 // Get a reference to the process
1923 //
1924 Status = ObReferenceObjectByHandle(ProcessHandle,
1925 PROCESS_VM_OPERATION,
1926 PsProcessType,
1927 PreviousMode,
1928 (PVOID*)(&Process),
1929 NULL);
1930 if (!NT_SUCCESS(Status)) return Status;
1931
1932 //
1933 // Check if this is is system-mapped
1934 //
1935 if (MapType & MAP_SYSTEM)
1936 {
1937 //
1938 // Check for required privilege
1939 //
1940 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
1941 {
1942 //
1943 // Fail: Don't have it
1944 //
1945 ObDereferenceObject(Process);
1946 return STATUS_PRIVILEGE_NOT_HELD;
1947 }
1948 }
1949
1950 //
1951 // Check if we should attach
1952 //
1953 if (CurrentProcess != Process)
1954 {
1955 //
1956 // Do it
1957 //
1958 KeStackAttachProcess(&Process->Pcb, &ApcState);
1959 Attached = TRUE;
1960 }
1961
1962 //
1963 // Oops :(
1964 //
1965 UNIMPLEMENTED;
1966
1967 //
1968 // Detach if needed
1969 //
1970 if (Attached) KeUnstackDetachProcess(&ApcState);
1971
1972 //
1973 // Release reference
1974 //
1975 ObDereferenceObject(Process);
1976
1977 //
1978 // Enter SEH to return data
1979 //
1980 _SEH2_TRY
1981 {
1982 //
1983 // Return data to user
1984 //
1985 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
1986 *NumberOfBytesToUnlock = 0;
1987 }
1988 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1989 {
1990 //
1991 // Get exception code
1992 //
1993 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1994 }
1995 _SEH2_END;
1996
1997 //
1998 // Return status
1999 //
2000 return STATUS_SUCCESS;
2001 }
2002
2003 NTSTATUS
2004 NTAPI
2005 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
2006 IN OUT PVOID *BaseAddress,
2007 IN OUT PSIZE_T NumberOfBytesToFlush,
2008 OUT PIO_STATUS_BLOCK IoStatusBlock)
2009 {
2010 PEPROCESS Process;
2011 NTSTATUS Status;
2012 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2013 PVOID CapturedBaseAddress;
2014 SIZE_T CapturedBytesToFlush;
2015 IO_STATUS_BLOCK LocalStatusBlock;
2016 PAGED_CODE();
2017
2018 //
2019 // Check if we came from user mode
2020 //
2021 if (PreviousMode != KernelMode)
2022 {
2023 //
2024 // Enter SEH for probing
2025 //
2026 _SEH2_TRY
2027 {
2028 //
2029 // Validate all outputs
2030 //
2031 ProbeForWritePointer(BaseAddress);
2032 ProbeForWriteSize_t(NumberOfBytesToFlush);
2033 ProbeForWriteIoStatusBlock(IoStatusBlock);
2034
2035 //
2036 // Capture them
2037 //
2038 CapturedBaseAddress = *BaseAddress;
2039 CapturedBytesToFlush = *NumberOfBytesToFlush;
2040 }
2041 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2042 {
2043 //
2044 // Get exception code
2045 //
2046 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2047 }
2048 _SEH2_END;
2049 }
2050 else
2051 {
2052 //
2053 // Capture directly
2054 //
2055 CapturedBaseAddress = *BaseAddress;
2056 CapturedBytesToFlush = *NumberOfBytesToFlush;
2057 }
2058
2059 //
2060 // Catch illegal base address
2061 //
2062 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
2063
2064 //
2065 // Catch illegal region size
2066 //
2067 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush)
2068 {
2069 //
2070 // Fail
2071 //
2072 return STATUS_INVALID_PARAMETER;
2073 }
2074
2075 //
2076 // Get a reference to the process
2077 //
2078 Status = ObReferenceObjectByHandle(ProcessHandle,
2079 PROCESS_VM_OPERATION,
2080 PsProcessType,
2081 PreviousMode,
2082 (PVOID*)(&Process),
2083 NULL);
2084 if (!NT_SUCCESS(Status)) return Status;
2085
2086 //
2087 // Do it
2088 //
2089 Status = MmFlushVirtualMemory(Process,
2090 &CapturedBaseAddress,
2091 &CapturedBytesToFlush,
2092 &LocalStatusBlock);
2093
2094 //
2095 // Release reference
2096 //
2097 ObDereferenceObject(Process);
2098
2099 //
2100 // Enter SEH to return data
2101 //
2102 _SEH2_TRY
2103 {
2104 //
2105 // Return data to user
2106 //
2107 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
2108 *NumberOfBytesToFlush = 0;
2109 *IoStatusBlock = LocalStatusBlock;
2110 }
2111 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2112 {
2113 }
2114 _SEH2_END;
2115
2116 //
2117 // Return status
2118 //
2119 return Status;
2120 }
2121
2122 /*
2123 * @unimplemented
2124 */
2125 NTSTATUS
2126 NTAPI
2127 NtGetWriteWatch(IN HANDLE ProcessHandle,
2128 IN ULONG Flags,
2129 IN PVOID BaseAddress,
2130 IN SIZE_T RegionSize,
2131 IN PVOID *UserAddressArray,
2132 OUT PULONG_PTR EntriesInUserAddressArray,
2133 OUT PULONG Granularity)
2134 {
2135 PEPROCESS Process;
2136 NTSTATUS Status;
2137 PVOID EndAddress;
2138 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2139 ULONG_PTR CapturedEntryCount;
2140 PAGED_CODE();
2141
2142 //
2143 // Check if we came from user mode
2144 //
2145 if (PreviousMode != KernelMode)
2146 {
2147 //
2148 // Enter SEH for probing
2149 //
2150 _SEH2_TRY
2151 {
2152 //
2153 // Catch illegal base address
2154 //
2155 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2156
2157 //
2158 // Catch illegal region size
2159 //
2160 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
2161 {
2162 //
2163 // Fail
2164 //
2165 return STATUS_INVALID_PARAMETER_3;
2166 }
2167
2168 //
2169 // Validate all data
2170 //
2171 ProbeForWriteSize_t(EntriesInUserAddressArray);
2172 ProbeForWriteUlong(Granularity);
2173
2174 //
2175 // Capture them
2176 //
2177 CapturedEntryCount = *EntriesInUserAddressArray;
2178
2179 //
2180 // Must have a count
2181 //
2182 if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5;
2183
2184 //
2185 // Can't be larger than the maximum
2186 //
2187 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR)))
2188 {
2189 //
2190 // Fail
2191 //
2192 return STATUS_INVALID_PARAMETER_5;
2193 }
2194
2195 //
2196 // Probe the actual array
2197 //
2198 ProbeForWrite(UserAddressArray,
2199 CapturedEntryCount * sizeof(PVOID),
2200 sizeof(PVOID));
2201 }
2202 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2203 {
2204 //
2205 // Get exception code
2206 //
2207 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2208 }
2209 _SEH2_END;
2210 }
2211 else
2212 {
2213 //
2214 // Capture directly
2215 //
2216 CapturedEntryCount = *EntriesInUserAddressArray;
2217 ASSERT(CapturedEntryCount != 0);
2218 }
2219
2220 //
2221 // Check if this is a local request
2222 //
2223 if (ProcessHandle == NtCurrentProcess())
2224 {
2225 //
2226 // No need to reference the process
2227 //
2228 Process = PsGetCurrentProcess();
2229 }
2230 else
2231 {
2232 //
2233 // Reference the target
2234 //
2235 Status = ObReferenceObjectByHandle(ProcessHandle,
2236 PROCESS_VM_OPERATION,
2237 PsProcessType,
2238 PreviousMode,
2239 (PVOID *)&Process,
2240 NULL);
2241 if (!NT_SUCCESS(Status)) return Status;
2242 }
2243
2244 //
2245 // Compute the last address and validate it
2246 //
2247 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
2248 if (BaseAddress > EndAddress)
2249 {
2250 //
2251 // Fail
2252 //
2253 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2254 return STATUS_INVALID_PARAMETER_4;
2255 }
2256
2257 //
2258 // Oops :(
2259 //
2260 UNIMPLEMENTED;
2261
2262 //
2263 // Dereference if needed
2264 //
2265 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2266
2267 //
2268 // Enter SEH to return data
2269 //
2270 _SEH2_TRY
2271 {
2272 //
2273 // Return data to user
2274 //
2275 *EntriesInUserAddressArray = 0;
2276 *Granularity = PAGE_SIZE;
2277 }
2278 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2279 {
2280 //
2281 // Get exception code
2282 //
2283 Status = _SEH2_GetExceptionCode();
2284 }
2285 _SEH2_END;
2286
2287 //
2288 // Return success
2289 //
2290 return STATUS_SUCCESS;
2291 }
2292
2293 /*
2294 * @unimplemented
2295 */
2296 NTSTATUS
2297 NTAPI
2298 NtResetWriteWatch(IN HANDLE ProcessHandle,
2299 IN PVOID BaseAddress,
2300 IN SIZE_T RegionSize)
2301 {
2302 PVOID EndAddress;
2303 PEPROCESS Process;
2304 NTSTATUS Status;
2305 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2306 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
2307
2308 //
2309 // Catch illegal base address
2310 //
2311 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
2312
2313 //
2314 // Catch illegal region size
2315 //
2316 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize)
2317 {
2318 //
2319 // Fail
2320 //
2321 return STATUS_INVALID_PARAMETER_3;
2322 }
2323
2324 //
2325 // Check if this is a local request
2326 //
2327 if (ProcessHandle == NtCurrentProcess())
2328 {
2329 //
2330 // No need to reference the process
2331 //
2332 Process = PsGetCurrentProcess();
2333 }
2334 else
2335 {
2336 //
2337 // Reference the target
2338 //
2339 Status = ObReferenceObjectByHandle(ProcessHandle,
2340 PROCESS_VM_OPERATION,
2341 PsProcessType,
2342 PreviousMode,
2343 (PVOID *)&Process,
2344 NULL);
2345 if (!NT_SUCCESS(Status)) return Status;
2346 }
2347
2348 //
2349 // Compute the last address and validate it
2350 //
2351 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1);
2352 if (BaseAddress > EndAddress)
2353 {
2354 //
2355 // Fail
2356 //
2357 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2358 return STATUS_INVALID_PARAMETER_3;
2359 }
2360
2361 //
2362 // Oops :(
2363 //
2364 UNIMPLEMENTED;
2365
2366 //
2367 // Dereference if needed
2368 //
2369 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
2370
2371 //
2372 // Return success
2373 //
2374 return STATUS_SUCCESS;
2375 }
2376
2377 NTSTATUS
2378 NTAPI
2379 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle,
2380 IN PVOID BaseAddress,
2381 OUT PVOID MemoryInformation,
2382 IN SIZE_T MemoryInformationLength,
2383 OUT PSIZE_T ReturnLength)
2384 {
2385 PEPROCESS TargetProcess;
2386 NTSTATUS Status = STATUS_SUCCESS;
2387 PMMVAD Vad = NULL;
2388 PVOID Address, NextAddress;
2389 BOOLEAN Found = FALSE;
2390 ULONG NewProtect, NewState;
2391 ULONG_PTR BaseVpn;
2392 MEMORY_BASIC_INFORMATION MemoryInfo;
2393 KAPC_STATE ApcState;
2394 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2395 PMEMORY_AREA MemoryArea;
2396 SIZE_T ResultLength;
2397
2398 /* Check for illegal addresses in user-space, or the shared memory area */
2399 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
2400 (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA))
2401 {
2402 Address = PAGE_ALIGN(BaseAddress);
2403
2404 /* Make up an info structure describing this range */
2405 MemoryInfo.BaseAddress = Address;
2406 MemoryInfo.AllocationProtect = PAGE_READONLY;
2407 MemoryInfo.Type = MEM_PRIVATE;
2408
2409 /* Special case for shared data */
2410 if (Address == (PVOID)MM_SHARED_USER_DATA_VA)
2411 {
2412 MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA;
2413 MemoryInfo.State = MEM_COMMIT;
2414 MemoryInfo.Protect = PAGE_READONLY;
2415 MemoryInfo.RegionSize = PAGE_SIZE;
2416 }
2417 else
2418 {
2419 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1;
2420 MemoryInfo.State = MEM_RESERVE;
2421 MemoryInfo.Protect = PAGE_NOACCESS;
2422 MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address;
2423 }
2424
2425 /* Return the data, NtQueryInformation already probed it*/
2426 if (PreviousMode != KernelMode)
2427 {
2428 _SEH2_TRY
2429 {
2430 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2431 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2432 }
2433 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2434 {
2435 Status = _SEH2_GetExceptionCode();
2436 }
2437 _SEH2_END;
2438 }
2439 else
2440 {
2441 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2442 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2443 }
2444
2445 return Status;
2446 }
2447
2448 /* Check if this is for a local or remote process */
2449 if (ProcessHandle == NtCurrentProcess())
2450 {
2451 TargetProcess = PsGetCurrentProcess();
2452 }
2453 else
2454 {
2455 /* Reference the target process */
2456 Status = ObReferenceObjectByHandle(ProcessHandle,
2457 PROCESS_QUERY_INFORMATION,
2458 PsProcessType,
2459 ExGetPreviousMode(),
2460 (PVOID*)&TargetProcess,
2461 NULL);
2462 if (!NT_SUCCESS(Status)) return Status;
2463
2464 /* Attach to it now */
2465 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
2466 }
2467
2468 /* Loop the VADs */
2469 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
2470 if (TargetProcess->VadRoot.NumberGenericTableElements)
2471 {
2472 /* Scan on the right */
2473 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
2474 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
2475 while (Vad)
2476 {
2477 /* Check if this VAD covers the allocation range */
2478 if ((BaseVpn >= Vad->StartingVpn) &&
2479 (BaseVpn <= Vad->EndingVpn))
2480 {
2481 /* We're done */
2482 Found = TRUE;
2483 break;
2484 }
2485
2486 /* Check if this VAD is too high */
2487 if (BaseVpn < Vad->StartingVpn)
2488 {
2489 /* Stop if there is no left child */
2490 if (!Vad->LeftChild) break;
2491
2492 /* Search on the left next */
2493 Vad = Vad->LeftChild;
2494 }
2495 else
2496 {
2497 /* Then this VAD is too low, keep searching on the right */
2498 ASSERT(BaseVpn > Vad->EndingVpn);
2499
2500 /* Stop if there is no right child */
2501 if (!Vad->RightChild) break;
2502
2503 /* Search on the right next */
2504 Vad = Vad->RightChild;
2505 }
2506 }
2507 }
2508
2509 /* Was a VAD found? */
2510 if (!Found)
2511 {
2512 Address = PAGE_ALIGN(BaseAddress);
2513
2514 /* Calculate region size */
2515 if (Vad)
2516 {
2517 if (Vad->StartingVpn >= BaseVpn)
2518 {
2519 /* Region size is the free space till the start of that VAD */
2520 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
2521 }
2522 else
2523 {
2524 /* Get the next VAD */
2525 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad);
2526 if (Vad)
2527 {
2528 /* Region size is the free space till the start of that VAD */
2529 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address;
2530 }
2531 else
2532 {
2533 /* Maximum possible region size with that base address */
2534 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
2535 }
2536 }
2537 }
2538 else
2539 {
2540 /* Maximum possible region size with that base address */
2541 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
2542 }
2543
2544 /* Check if we were attached */
2545 if (ProcessHandle != NtCurrentProcess())
2546 {
2547 /* Detach and derefernece the process */
2548 KeUnstackDetachProcess(&ApcState);
2549 ObDereferenceObject(TargetProcess);
2550 }
2551
2552 /* Build the rest of the initial information block */
2553 MemoryInfo.BaseAddress = Address;
2554 MemoryInfo.AllocationBase = NULL;
2555 MemoryInfo.AllocationProtect = 0;
2556 MemoryInfo.State = MEM_FREE;
2557 MemoryInfo.Protect = PAGE_NOACCESS;
2558 MemoryInfo.Type = 0;
2559
2560 /* Return the data, NtQueryInformation already probed it*/
2561 if (PreviousMode != KernelMode)
2562 {
2563 _SEH2_TRY
2564 {
2565 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2566 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2567 }
2568 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2569 {
2570 Status = _SEH2_GetExceptionCode();
2571 }
2572 _SEH2_END;
2573 }
2574 else
2575 {
2576 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2577 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2578 }
2579
2580 return Status;
2581 }
2582
2583 /* This must be a VM VAD */
2584 ASSERT(Vad->u.VadFlags.PrivateMemory);
2585
2586 /* Lock the address space of the process */
2587 MmLockAddressSpace(&TargetProcess->Vm);
2588
2589 /* Find the memory area the specified address belongs to */
2590 MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
2591 ASSERT(MemoryArea != NULL);
2592
2593 /* Determine information dependent on the memory area type */
2594 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
2595 {
2596 Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
2597 ASSERT(NT_SUCCESS(Status));
2598 }
2599 else
2600 {
2601 /* Build the initial information block */
2602 Address = PAGE_ALIGN(BaseAddress);
2603 MemoryInfo.BaseAddress = Address;
2604 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
2605 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
2606 MemoryInfo.Type = MEM_PRIVATE;
2607
2608 /* Find the largest chunk of memory which has the same state and protection mask */
2609 MemoryInfo.State = MiQueryAddressState(Address,
2610 Vad,
2611 TargetProcess,
2612 &MemoryInfo.Protect,
2613 &NextAddress);
2614 Address = NextAddress;
2615 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
2616 {
2617 /* Keep going unless the state or protection mask changed */
2618 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
2619 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
2620 Address = NextAddress;
2621 }
2622
2623 /* Now that we know the last VA address, calculate the region size */
2624 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
2625 }
2626
2627 /* Unlock the address space of the process */
2628 MmUnlockAddressSpace(&TargetProcess->Vm);
2629
2630 /* Check if we were attached */
2631 if (ProcessHandle != NtCurrentProcess())
2632 {
2633 /* Detach and derefernece the process */
2634 KeUnstackDetachProcess(&ApcState);
2635 ObDereferenceObject(TargetProcess);
2636 }
2637
2638 /* Return the data, NtQueryInformation already probed it*/
2639 if (PreviousMode != KernelMode)
2640 {
2641 _SEH2_TRY
2642 {
2643 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2644 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2645 }
2646 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2647 {
2648 Status = _SEH2_GetExceptionCode();
2649 }
2650 _SEH2_END;
2651 }
2652 else
2653 {
2654 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
2655 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
2656 }
2657
2658 /* All went well */
2659 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx "
2660 "State: %lx Type: %lx Size: %lx\n",
2661 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
2662 MemoryInfo.AllocationProtect, MemoryInfo.Protect,
2663 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
2664
2665 return Status;
2666 }
2667
2668 NTSTATUS
2669 NTAPI
2670 MiQueryMemorySectionName(IN HANDLE ProcessHandle,
2671 IN PVOID BaseAddress,
2672 OUT PVOID MemoryInformation,
2673 IN SIZE_T MemoryInformationLength,
2674 OUT PSIZE_T ReturnLength)
2675 {
2676 PEPROCESS Process;
2677 NTSTATUS Status;
2678 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
2679 UNICODE_STRING ModuleFileName;
2680 PMEMORY_SECTION_NAME SectionName = NULL;
2681 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2682
2683 Status = ObReferenceObjectByHandle(ProcessHandle,
2684 PROCESS_QUERY_INFORMATION,
2685 NULL,
2686 PreviousMode,
2687 (PVOID*)(&Process),
2688 NULL);
2689
2690 if (!NT_SUCCESS(Status))
2691 {
2692 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status);
2693 return Status;
2694 }
2695
2696 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
2697 Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName);
2698
2699 if (NT_SUCCESS(Status))
2700 {
2701 SectionName = MemoryInformation;
2702 if (PreviousMode != KernelMode)
2703 {
2704 _SEH2_TRY
2705 {
2706 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
2707 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
2708 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
2709
2710 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
2711
2712 }
2713 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2714 {
2715 Status = _SEH2_GetExceptionCode();
2716 }
2717 _SEH2_END;
2718 }
2719 else
2720 {
2721 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
2722 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
2723 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
2724
2725 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
2726
2727 }
2728 }
2729 ObDereferenceObject(Process);
2730 return Status;
2731 }
2732
2733 NTSTATUS
2734 NTAPI
2735 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
2736 IN PVOID BaseAddress,
2737 IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
2738 OUT PVOID MemoryInformation,
2739 IN SIZE_T MemoryInformationLength,
2740 OUT PSIZE_T ReturnLength)
2741 {
2742 NTSTATUS Status = STATUS_SUCCESS;
2743 KPROCESSOR_MODE PreviousMode;
2744
2745 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
2746
2747 /* Bail out if the address is invalid */
2748 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
2749
2750 /* Probe return buffer */
2751 PreviousMode = ExGetPreviousMode();
2752 if (PreviousMode != KernelMode)
2753 {
2754 _SEH2_TRY
2755 {
2756 ProbeForWrite(MemoryInformation,
2757 MemoryInformationLength,
2758 sizeof(ULONG_PTR));
2759
2760 if (ReturnLength) ProbeForWriteSize_t(ReturnLength);
2761 }
2762 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2763 {
2764 Status = _SEH2_GetExceptionCode();
2765 }
2766 _SEH2_END;
2767
2768 if (!NT_SUCCESS(Status))
2769 {
2770 return Status;
2771 }
2772 }
2773
2774 switch(MemoryInformationClass)
2775 {
2776 case MemoryBasicInformation:
2777 /* Validate the size information of the class */
2778 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
2779 {
2780 /* The size is invalid */
2781 return STATUS_INFO_LENGTH_MISMATCH;
2782 }
2783 Status = MiQueryMemoryBasicInformation(ProcessHandle,
2784 BaseAddress,
2785 MemoryInformation,
2786 MemoryInformationLength,
2787 ReturnLength);
2788 break;
2789
2790 case MemorySectionName:
2791 /* Validate the size information of the class */
2792 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME))
2793 {
2794 /* The size is invalid */
2795 return STATUS_INFO_LENGTH_MISMATCH;
2796 }
2797 Status = MiQueryMemorySectionName(ProcessHandle,
2798 BaseAddress,
2799 MemoryInformation,
2800 MemoryInformationLength,
2801 ReturnLength);
2802 break;
2803 case MemoryWorkingSetList:
2804 case MemoryBasicVlmInformation:
2805 default:
2806 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass);
2807 break;
2808 }
2809
2810 return Status;
2811 }
2812
2813 #ifdef __USE_ARM3__
2814 /*
2815 * @implemented
2816 */
2817 NTSTATUS
2818 NTAPI
2819 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
2820 IN OUT PVOID* UBaseAddress,
2821 IN ULONG_PTR ZeroBits,
2822 IN OUT PSIZE_T URegionSize,
2823 IN ULONG AllocationType,
2824 IN ULONG Protect)
2825 {
2826 PEPROCESS Process;
2827 ULONG Type;
2828 NTSTATUS Status = STATUS_SUCCESS;
2829 PVOID BaseAddress;
2830 ULONG RegionSize;
2831 PMMVAD Vad;
2832 PMMADDRESS_NODE ParentNode;
2833 ULONG_PTR StartVpn, EndVpn;
2834 PHYSICAL_ADDRESS BoundaryAddressMultiple;
2835 PEPROCESS CurrentProcess = PsGetCurrentProcess();
2836 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
2837 KAPC_STATE ApcState;
2838 ULONG ProtectionMask;
2839 BOOLEAN Attached = FALSE;
2840 BoundaryAddressMultiple.QuadPart = 0;
2841 TABLE_SEARCH_RESULT Result;
2842
2843 PAGED_CODE();
2844
2845 /* Check for valid Zero bits */
2846 if (ZeroBits > 21)
2847 {
2848 DPRINT1("Too many zero bits\n");
2849 return STATUS_INVALID_PARAMETER_3;
2850 }
2851
2852 /* Check for valid Allocation Types */
2853 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
2854 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
2855 {
2856 DPRINT1("Invalid Allocation Type\n");
2857 return STATUS_INVALID_PARAMETER_5;
2858 }
2859
2860 /* Check for at least one of these Allocation Types to be set */
2861 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
2862 {
2863 DPRINT1("No memory allocation base type\n");
2864 return STATUS_INVALID_PARAMETER_5;
2865 }
2866
2867 /* MEM_RESET is an exclusive flag, make sure that is valid too */
2868 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
2869 {
2870 DPRINT1("Invalid use of MEM_RESET\n");
2871 return STATUS_INVALID_PARAMETER_5;
2872 }
2873
2874 /* Check if large pages are being used */
2875 if (AllocationType & MEM_LARGE_PAGES)
2876 {
2877 /* Large page allocations MUST be committed */
2878 if (!(AllocationType & MEM_COMMIT))
2879 {
2880 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
2881 return STATUS_INVALID_PARAMETER_5;
2882 }
2883
2884 /* These flags are not allowed with large page allocations */
2885 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
2886 {
2887 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
2888 return STATUS_INVALID_PARAMETER_5;
2889 }
2890 }
2891
2892 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
2893 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
2894 {
2895 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
2896 return STATUS_INVALID_PARAMETER_5;
2897 }
2898
2899 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
2900 if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
2901 {
2902 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
2903 return STATUS_INVALID_PARAMETER_5;
2904 }
2905
2906 /* Check for valid MEM_PHYSICAL usage */
2907 if (AllocationType & MEM_PHYSICAL)
2908 {
2909 /* Only these flags are allowed with MEM_PHYSIAL */
2910 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
2911 {
2912 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
2913 return STATUS_INVALID_PARAMETER_5;
2914 }
2915
2916 /* Then make sure PAGE_READWRITE is used */
2917 if (Protect != PAGE_READWRITE)
2918 {
2919 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
2920 return STATUS_INVALID_PARAMETER_6;
2921 }
2922 }
2923
2924 /* Calculate the protection mask and make sure it's valid */
2925 ProtectionMask = MiMakeProtectionMask(Protect);
2926 if (ProtectionMask == MM_INVALID_PROTECTION)
2927 {
2928 DPRINT1("Invalid protection mask\n");
2929 return STATUS_INVALID_PAGE_PROTECTION;
2930 }
2931
2932 /* Enter SEH */
2933 _SEH2_TRY
2934 {
2935 /* Check for user-mode parameters */
2936 if (PreviousMode != KernelMode)
2937 {
2938 /* Make sure they are writable */
2939 ProbeForWritePointer(UBaseAddress);
2940 ProbeForWriteUlong(URegionSize);
2941 }
2942
2943 /* Capture their values */
2944 BaseAddress = *UBaseAddress;
2945 RegionSize = *URegionSize;
2946 }
2947 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2948 {
2949 /* Return the exception code */
2950 _SEH2_YIELD(return _SEH2_GetExceptionCode());
2951 }
2952 _SEH2_END;
2953
2954 /* Make sure there's a size specified */
2955 if (!RegionSize)
2956 {
2957 DPRINT1("Region size is invalid (zero)\n");
2958 return STATUS_INVALID_PARAMETER_4;
2959 }
2960
2961 RegionSize = PAGE_ROUND_UP((ULONG_PTR)BaseAddress + RegionSize) -
2962 PAGE_ROUND_DOWN(BaseAddress);
2963 BaseAddress = (PVOID)PAGE_ROUND_DOWN(BaseAddress);
2964 StartVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
2965 EndVpn = ((ULONG_PTR)BaseAddress + RegionSize - 1) >> PAGE_SHIFT;
2966
2967 /* Make sure the allocation isn't past the VAD area */
2968 if (BaseAddress >= MM_HIGHEST_VAD_ADDRESS)
2969 {
2970 DPRINT1("Virtual allocation base above User Space\n");
2971 return STATUS_INVALID_PARAMETER_2;
2972 }
2973
2974 /* Make sure the allocation wouldn't overflow past the VAD area */
2975 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)BaseAddress) < RegionSize)
2976 {
2977 DPRINT1("Region size would overflow into kernel-memory\n");
2978 return STATUS_INVALID_PARAMETER_4;
2979 }
2980
2981 /* Check if this is for the current process */
2982 if (ProcessHandle == NtCurrentProcess())
2983 {
2984 /* We already have the current process, no need to go through Ob */
2985 Process = CurrentProcess;
2986 }
2987 else
2988 {
2989 /* Reference the handle for correct permissions */
2990 Status = ObReferenceObjectByHandle(ProcessHandle,
2991 PROCESS_VM_OPERATION,
2992 PsProcessType,
2993 PreviousMode,
2994 (PVOID*)&Process,
2995 NULL);
2996 if (!NT_SUCCESS(Status)) return Status;
2997
2998 /* Check if not running in the current process */
2999 if (CurrentProcess != Process)
3000 {
3001 /* Attach to it */
3002 KeStackAttachProcess(&Process->Pcb, &ApcState);
3003 Attached = TRUE;
3004 }
3005 }
3006
3007 /* Check for large page allocations */
3008 if (AllocationType & MEM_LARGE_PAGES)
3009 {
3010 /* The lock memory privilege is required */
3011 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
3012 {
3013 /* Fail without it */
3014 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
3015 if (Attached) KeUnstackDetachProcess(&ApcState);
3016 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3017 return STATUS_PRIVILEGE_NOT_HELD;
3018 }
3019 }
3020
3021
3022 /*
3023 * Copy on Write is reserved for system use. This case is a certain failure
3024 * but there may be other cases...needs more testing
3025 */
3026 if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
3027 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
3028 {
3029 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
3030 if (Attached) KeUnstackDetachProcess(&ApcState);
3031 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3032 return STATUS_INVALID_PAGE_PROTECTION;
3033 }
3034
3035 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
3036 DPRINT("Type %x\n", Type);
3037
3038 /* Lock the process address space */
3039 KeAcquireGuardedMutex(&Process->AddressCreationLock);
3040
3041 if(BaseAddress != 0)
3042 {
3043 /*
3044 * An address was provided. Let's see if we've already
3045 * something there
3046 */
3047 if(MiCheckForConflictingNode(StartVpn, EndVpn, &Process->VadRoot) != NULL)
3048 {
3049 /* Can't reserve twice the same range */
3050 if(AllocationType & MEM_RESERVE)
3051 {
3052 Status = STATUS_CONFLICTING_ADDRESSES;
3053 DPRINT1("Trying to reserve twice the same range.\n");
3054 goto cleanup;
3055 }
3056 /* Great there's already something there. What shall we do ? */
3057 if(AllocationType == MEM_RESET)
3058 {
3059 UNIMPLEMENTED;
3060 /* Reset the dirty bits for each PTEs */
3061 goto cleanup;
3062 }
3063 else
3064 {
3065 ASSERT(AllocationType & MEM_COMMIT);
3066 UNIMPLEMENTED;
3067 /* Mark the VAD as committed */
3068 goto cleanup;
3069 }
3070 }
3071
3072 /* There's nothing */
3073 if(!(AllocationType & MEM_RESERVE))
3074 {
3075 Status = STATUS_ACCESS_DENIED;
3076 goto cleanup;
3077 }
3078
3079 /* Now we can reserve our chunk of memory */
3080 goto buildVad;
3081 }
3082
3083 /* No base address was given. */
3084 if(!(AllocationType & MEM_RESERVE))
3085 {
3086 DPRINT1("Providing NULL base address witout MEM_RESERVE.\n");
3087 ASSERT(FALSE);
3088 Status = STATUS_INVALID_PARAMETER_5;
3089 goto cleanup;
3090 }
3091
3092 /* Find an empty range in Address Space */
3093 if(AllocationType & MEM_TOP_DOWN)
3094 {
3095 /* Top down allocation */
3096 Result = MiFindEmptyAddressRangeDownTree(RegionSize,
3097 (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS,
3098 (ZeroBits > PAGE_SHIFT) ? 1 << ZeroBits : PAGE_SIZE,
3099 &Process->VadRoot,
3100 (PULONG_PTR)&BaseAddress,
3101 &ParentNode);
3102
3103 if(Result == TableFoundNode)
3104 {
3105 /* This means failure */
3106 Status = STATUS_NO_MEMORY;
3107 goto cleanup;
3108 }
3109 }
3110 else
3111 {
3112 /* Good old bottom up allocation */
3113 Status = MiFindEmptyAddressRangeInTree(RegionSize,
3114 (ZeroBits > PAGE_SHIFT) ? 1 << ZeroBits : PAGE_SIZE,
3115 &Process->VadRoot,
3116 &ParentNode,
3117 (PULONG_PTR)&BaseAddress);
3118 if(!NT_SUCCESS(Status))
3119 {
3120 /* Failed... */
3121 goto cleanup;
3122 }
3123 }
3124 StartVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
3125 EndVpn = ((ULONG_PTR)BaseAddress + RegionSize - 1) >> PAGE_SHIFT;
3126
3127 /* Build the Vad */
3128 buildVad:
3129 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD), TAG_MVAD);
3130 if(!Vad)
3131 {
3132 Status = STATUS_INSUFFICIENT_RESOURCES;
3133 goto cleanup;
3134 }
3135 RtlZeroMemory(Vad, sizeof(MMVAD));
3136
3137 /* Set min/max */
3138 Vad->StartingVpn = StartVpn;
3139 Vad->EndingVpn = EndVpn;
3140 /* Set protection */
3141 Vad->u.VadFlags.Protection = ProtectionMask;
3142 /* Should it be already marked as committed ? */
3143 if(AllocationType & MEM_COMMIT)
3144 Vad->u.VadFlags.MemCommit = 1;
3145 if(AllocationType & MEM_PHYSICAL)
3146 {
3147 UNIMPLEMENTED;
3148 Vad->u.VadFlags.VadType = VadAwe;
3149 }
3150 /* Add it */
3151 MiLockProcessWorkingSet(Process, PsGetCurrentThread());
3152 MiInsertVad(Vad, Process);
3153 MiUnlockProcessWorkingSet(Process, PsGetCurrentThread());
3154
3155 /* we're done */
3156 cleanup:
3157 KeReleaseGuardedMutex(&Process->AddressCreationLock);
3158 if (Attached) KeUnstackDetachProcess(&ApcState);
3159 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
3160
3161 *UBaseAddress = BaseAddress;
3162 *URegionSize = RegionSize;
3163 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
3164
3165 return Status;
3166 }
3167 #endif
3168 /* EOF */