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