8c7be81e5139d3603bd47c402f788146bc1a8c31
[reactos.git] / reactos / ntoskrnl / mm / anonmem.c
1 /*
2 * Copyright (C) 2002-2005 ReactOS Team (and the authors from the programmers section)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/anonmem.c
21 * PURPOSE: Implementing anonymous memory.
22 *
23 * PROGRAMMERS: David Welch
24 * Casper Hornstrup
25 * KJK::Hyperion
26 * Ge van Geldorp
27 * Eric Kohl
28 * Royce Mitchell III
29 * Aleksey Bragin
30 * Jason Filby
31 * Art Yerkes
32 * Gunnar Andre' Dalsnes
33 * Filip Navara
34 * Thomas Weidenmueller
35 * Alex Ionescu
36 * Trevor McCort
37 * Steven Edwards
38 */
39
40 /* INCLUDE *****************************************************************/
41
42 #include <ntoskrnl.h>
43 #define NDEBUG
44 #include <debug.h>
45
46 #define MODULE_INVOLVED_IN_ARM3
47 #include "ARM3/miarm.h"
48
49 /* FUNCTIONS *****************************************************************/
50
51 NTSTATUS
52 NTAPI
53 MmPageOutVirtualMemory(PMMSUPPORT AddressSpace,
54 PMEMORY_AREA MemoryArea,
55 PVOID Address,
56 PMM_PAGEOP PageOp)
57 {
58 PFN_NUMBER Page;
59 BOOLEAN WasDirty;
60 SWAPENTRY SwapEntry;
61 NTSTATUS Status;
62 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
63
64 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
65 Address, Process->UniqueProcessId);
66
67 /*
68 * Check for paging out from a deleted virtual memory area.
69 */
70 if (MemoryArea->DeleteInProgress)
71 {
72 PageOp->Status = STATUS_UNSUCCESSFUL;
73 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
74 MmReleasePageOp(PageOp);
75 return(STATUS_UNSUCCESSFUL);
76 }
77
78 /*
79 * Check the reference count to ensure this page can be paged out
80 */
81 Page = MmGetPfnForProcess(Process, Address);
82 if (MmGetReferenceCountPage(Page) != 1)
83 {
84 DPRINT1("Cannot page out locked virtual memory page: 0x%p (RefCount: %d)\n",
85 Page, MmGetReferenceCountPage(Page));
86 PageOp->Status = STATUS_UNSUCCESSFUL;
87 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
88 MmReleasePageOp(PageOp);
89 return(STATUS_UNSUCCESSFUL);
90 }
91
92 /*
93 * Disable the virtual mapping.
94 */
95 MmDisableVirtualMapping(Process, Address,
96 &WasDirty, &Page);
97
98 if (Page == 0)
99 {
100 KeBugCheck(MEMORY_MANAGEMENT);
101 }
102
103 /*
104 * Paging out non-dirty data is easy.
105 */
106 if (!WasDirty)
107 {
108 MmLockAddressSpace(AddressSpace);
109 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
110 MmDeleteAllRmaps(Page, NULL, NULL);
111 if ((SwapEntry = MmGetSavedSwapEntryPage(Page)) != 0)
112 {
113 MmCreatePageFileMapping(Process, Address, SwapEntry);
114 MmSetSavedSwapEntryPage(Page, 0);
115 }
116 MmUnlockAddressSpace(AddressSpace);
117 MmReleasePageMemoryConsumer(MC_USER, Page);
118 PageOp->Status = STATUS_SUCCESS;
119 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
120 MmReleasePageOp(PageOp);
121 return(STATUS_SUCCESS);
122 }
123
124 /*
125 * If necessary, allocate an entry in the paging file for this page
126 */
127 SwapEntry = MmGetSavedSwapEntryPage(Page);
128 if (SwapEntry == 0)
129 {
130 SwapEntry = MmAllocSwapPage();
131 if (SwapEntry == 0)
132 {
133 MmShowOutOfSpaceMessagePagingFile();
134 MmEnableVirtualMapping(Process, Address);
135 PageOp->Status = STATUS_UNSUCCESSFUL;
136 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
137 MmReleasePageOp(PageOp);
138 return(STATUS_PAGEFILE_QUOTA);
139 }
140 }
141
142 /*
143 * Write the page to the pagefile
144 */
145 Status = MmWriteToSwapPage(SwapEntry, Page);
146 if (!NT_SUCCESS(Status))
147 {
148 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
149 Status);
150 MmEnableVirtualMapping(Process, Address);
151 PageOp->Status = STATUS_UNSUCCESSFUL;
152 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
153 MmReleasePageOp(PageOp);
154 return(STATUS_UNSUCCESSFUL);
155 }
156
157 /*
158 * Otherwise we have succeeded, free the page
159 */
160 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page << PAGE_SHIFT);
161 MmLockAddressSpace(AddressSpace);
162 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
163 MmCreatePageFileMapping(Process, Address, SwapEntry);
164 MmUnlockAddressSpace(AddressSpace);
165 MmDeleteAllRmaps(Page, NULL, NULL);
166 MmSetSavedSwapEntryPage(Page, 0);
167 MmReleasePageMemoryConsumer(MC_USER, Page);
168 PageOp->Status = STATUS_SUCCESS;
169 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
170 MmReleasePageOp(PageOp);
171 return(STATUS_SUCCESS);
172 }
173
174 NTSTATUS
175 NTAPI
176 MmNotPresentFaultVirtualMemory(PMMSUPPORT AddressSpace,
177 MEMORY_AREA* MemoryArea,
178 PVOID Address)
179 /*
180 * FUNCTION: Move data into memory to satisfy a page not present fault
181 * ARGUMENTS:
182 * AddressSpace = Address space within which the fault occurred
183 * MemoryArea = The memory area within which the fault occurred
184 * Address = The absolute address of fault
185 * RETURNS: Status
186 * NOTES: This function is called with the address space lock held.
187 */
188 {
189 PFN_NUMBER Page;
190 NTSTATUS Status;
191 PMM_REGION Region;
192 PMM_PAGEOP PageOp;
193 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
194
195 /*
196 * There is a window between taking the page fault and locking the
197 * address space when another thread could load the page so we check
198 * that.
199 */
200 if (MmIsPagePresent(Process, Address))
201 {
202 return(STATUS_SUCCESS);
203 }
204
205 /*
206 * Check for the virtual memory area being deleted.
207 */
208 if (MemoryArea->DeleteInProgress)
209 {
210 return(STATUS_UNSUCCESSFUL);
211 }
212
213 /*
214 * Get the segment corresponding to the virtual address
215 */
216 Region = MmFindRegion(MemoryArea->StartingAddress,
217 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
218 Address, NULL);
219
220 if (Region->Type == MEM_RESERVE || Region->Protect == PAGE_NOACCESS)
221 {
222 return(STATUS_ACCESS_VIOLATION);
223 }
224
225 /*
226 * FIXME
227 */
228 if (Region->Protect & PAGE_GUARD)
229 {
230 return(STATUS_GUARD_PAGE_VIOLATION);
231 }
232
233 /*
234 * Get or create a page operation
235 */
236 PageOp = MmGetPageOp(MemoryArea, Process->UniqueProcessId,
237 (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
238 MM_PAGEOP_PAGEIN, FALSE);
239 if (PageOp == NULL)
240 {
241 DPRINT1("MmGetPageOp failed");
242 KeBugCheck(MEMORY_MANAGEMENT);
243 }
244
245 /*
246 * Check if someone else is already handling this fault, if so wait
247 * for them
248 */
249 if (PageOp->Thread != PsGetCurrentThread())
250 {
251 MmUnlockAddressSpace(AddressSpace);
252 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
253 0,
254 KernelMode,
255 FALSE,
256 NULL);
257 /*
258 * Check for various strange conditions
259 */
260 if (Status != STATUS_SUCCESS)
261 {
262 DPRINT1("Failed to wait for page op\n");
263 KeBugCheck(MEMORY_MANAGEMENT);
264 }
265 if (PageOp->Status == STATUS_PENDING)
266 {
267 DPRINT1("Woke for page op before completion\n");
268 KeBugCheck(MEMORY_MANAGEMENT);
269 }
270 /*
271 * If this wasn't a pagein then we need to restart the handling
272 */
273 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
274 {
275 MmLockAddressSpace(AddressSpace);
276 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
277 MmReleasePageOp(PageOp);
278 return(STATUS_MM_RESTART_OPERATION);
279 }
280 /*
281 * If the thread handling this fault has failed then we don't retry
282 */
283 if (!NT_SUCCESS(PageOp->Status))
284 {
285 MmLockAddressSpace(AddressSpace);
286 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
287 Status = PageOp->Status;
288 MmReleasePageOp(PageOp);
289 return(Status);
290 }
291 MmLockAddressSpace(AddressSpace);
292 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
293 MmReleasePageOp(PageOp);
294 return(STATUS_SUCCESS);
295 }
296
297 /*
298 * Try to allocate a page
299 */
300 MI_SET_USAGE(MI_USAGE_VAD);
301 MI_SET_PROCESS2(Process->ImageFileName);
302 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
303 if (Status == STATUS_NO_MEMORY)
304 {
305 MmUnlockAddressSpace(AddressSpace);
306 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
307 MmLockAddressSpace(AddressSpace);
308 }
309 if (!NT_SUCCESS(Status))
310 {
311 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
312 KeBugCheck(MEMORY_MANAGEMENT);
313 }
314
315 /*
316 * Handle swapped out pages.
317 */
318 if (MmIsPageSwapEntry(Process, Address))
319 {
320 SWAPENTRY SwapEntry;
321
322 MmDeletePageFileMapping(Process, Address, &SwapEntry);
323 Status = MmReadFromSwapPage(SwapEntry, Page);
324 if (!NT_SUCCESS(Status))
325 {
326 KeBugCheck(MEMORY_MANAGEMENT);
327 }
328 MmSetSavedSwapEntryPage(Page, SwapEntry);
329 }
330
331 /*
332 * Set the page. If we fail because we are out of memory then
333 * try again
334 */
335 Status = MmCreateVirtualMapping(Process,
336 (PVOID)PAGE_ROUND_DOWN(Address),
337 Region->Protect,
338 &Page,
339 1);
340 while (Status == STATUS_NO_MEMORY)
341 {
342 MmUnlockAddressSpace(AddressSpace);
343 Status = MmCreateVirtualMapping(Process,
344 (PVOID)PAGE_ROUND_DOWN(Address),
345 Region->Protect,
346 &Page,
347 1);
348 MmLockAddressSpace(AddressSpace);
349 }
350 if (!NT_SUCCESS(Status))
351 {
352 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
353 KeBugCheck(MEMORY_MANAGEMENT);
354 return(Status);
355 }
356
357 /*
358 * Add the page to the process's working set
359 */
360 MmInsertRmap(Page, Process, (PVOID)PAGE_ROUND_DOWN(Address));
361
362 /*
363 * Finish the operation
364 */
365 PageOp->Status = STATUS_SUCCESS;
366 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
367 MmReleasePageOp(PageOp);
368 return(STATUS_SUCCESS);
369 }
370
371 static VOID
372 MmModifyAttributes(PMMSUPPORT AddressSpace,
373 PVOID BaseAddress,
374 SIZE_T RegionSize,
375 ULONG OldType,
376 ULONG OldProtect,
377 ULONG NewType,
378 ULONG NewProtect)
379 /*
380 * FUNCTION: Modify the attributes of a memory region
381 */
382 {
383 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
384
385 /*
386 * If we are switching a previously committed region to reserved then
387 * free any allocated pages within the region
388 */
389 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
390 {
391 ULONG i;
392
393 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
394 {
395 PFN_NUMBER Page;
396
397 if (MmIsPageSwapEntry(Process,
398 (char*)BaseAddress + (i * PAGE_SIZE)))
399 {
400 SWAPENTRY SwapEntry;
401
402 MmDeletePageFileMapping(Process,
403 (char*)BaseAddress + (i * PAGE_SIZE),
404 &SwapEntry);
405 MmFreeSwapPage(SwapEntry);
406 }
407 else
408 {
409 MmDeleteVirtualMapping(Process,
410 (char*)BaseAddress + (i*PAGE_SIZE),
411 FALSE, NULL, &Page);
412 if (Page != 0)
413 {
414 SWAPENTRY SavedSwapEntry;
415 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
416 if (SavedSwapEntry != 0)
417 {
418 MmFreeSwapPage(SavedSwapEntry);
419 MmSetSavedSwapEntryPage(Page, 0);
420 }
421 MmDeleteRmap(Page, Process,
422 (char*)BaseAddress + (i * PAGE_SIZE));
423 MmReleasePageMemoryConsumer(MC_USER, Page);
424 }
425 }
426 }
427 }
428
429 /*
430 * If we are changing the protection attributes of a committed region then
431 * alter the attributes for any allocated pages within the region
432 */
433 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
434 OldProtect != NewProtect)
435 {
436 ULONG i;
437 PMM_PAGEOP PageOp;
438 PMEMORY_AREA MArea;
439 char* addr = (char*)BaseAddress;
440
441 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
442 {
443 MArea = MmLocateMemoryAreaByAddress(AddressSpace, addr);
444 do
445 {
446 PageOp = MmGetPageOp(MArea, Process->UniqueProcessId, addr,
447 NULL, 0, MM_PAGEOP_CHANGEPROTECT, TRUE);
448 } while(PageOp == NULL);
449
450 /* Should we enable/disable virtual mapping? */
451 if((NewProtect & PAGE_NOACCESS) &&
452 !(OldProtect & PAGE_NOACCESS) &&
453 (MmIsPagePresent(Process, addr)))
454 {
455 /* Set other flags if any */
456 if(NewProtect != PAGE_NOACCESS)
457 MmSetPageProtect(Process, addr, NewProtect & ~PAGE_NOACCESS);
458 MmDisableVirtualMapping(Process, addr, NULL, NULL);
459 }
460 else if((OldProtect & PAGE_NOACCESS) && !(NewProtect & PAGE_NOACCESS))
461 {
462 MmEnableVirtualMapping(Process, addr);
463 }
464
465 /* Set new protection flags */
466 if(MmIsPagePresent(Process, addr))
467 {
468 MmSetPageProtect(Process, addr, NewProtect);
469 }
470 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
471 MmReleasePageOp(PageOp);
472 addr += PAGE_SIZE;
473 }
474 }
475 }
476
477 NTSTATUS NTAPI
478 MiProtectVirtualMemory(IN PEPROCESS Process,
479 IN OUT PVOID *BaseAddress,
480 IN OUT PSIZE_T NumberOfBytesToProtect,
481 IN ULONG NewAccessProtection,
482 OUT PULONG OldAccessProtection OPTIONAL)
483 {
484 PMEMORY_AREA MemoryArea;
485 PMMSUPPORT AddressSpace;
486 ULONG OldAccessProtection_;
487 NTSTATUS Status;
488
489 *NumberOfBytesToProtect =
490 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
491 PAGE_ROUND_DOWN(*BaseAddress);
492 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
493
494 AddressSpace = &Process->Vm;
495
496 MmLockAddressSpace(AddressSpace);
497 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
498 if (MemoryArea == NULL)
499 {
500 MmUnlockAddressSpace(AddressSpace);
501 return STATUS_UNSUCCESSFUL;
502 }
503
504 if (OldAccessProtection == NULL)
505 OldAccessProtection = &OldAccessProtection_;
506
507 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
508 {
509 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
510 *NumberOfBytesToProtect, NewAccessProtection,
511 OldAccessProtection);
512 }
513 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
514 {
515 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
516 *NumberOfBytesToProtect,
517 NewAccessProtection,
518 OldAccessProtection);
519 }
520 else
521 {
522 /* FIXME: Should we return failure or success in this case? */
523 Status = STATUS_CONFLICTING_ADDRESSES;
524 }
525
526 MmUnlockAddressSpace(AddressSpace);
527
528 return Status;
529 }
530
531 /*
532 * @implemented
533 */
534 NTSTATUS
535 NTAPI
536 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
537 IN OUT PVOID* UBaseAddress,
538 IN ULONG_PTR ZeroBits,
539 IN OUT PSIZE_T URegionSize,
540 IN ULONG AllocationType,
541 IN ULONG Protect)
542 {
543 PEPROCESS Process;
544 MEMORY_AREA* MemoryArea;
545 ULONG_PTR MemoryAreaLength;
546 ULONG Type;
547 NTSTATUS Status;
548 PMMSUPPORT AddressSpace;
549 PVOID BaseAddress;
550 ULONG RegionSize;
551 PVOID PBaseAddress;
552 ULONG_PTR PRegionSize;
553 PHYSICAL_ADDRESS BoundaryAddressMultiple;
554 PEPROCESS CurrentProcess = PsGetCurrentProcess();
555 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
556 KAPC_STATE ApcState;
557 ULONG ProtectionMask;
558 BOOLEAN Attached = FALSE;
559 BoundaryAddressMultiple.QuadPart = 0;
560 PAGED_CODE();
561
562 /* Check for valid Zero bits */
563 if (ZeroBits > 21)
564 {
565 DPRINT1("Too many zero bits\n");
566 return STATUS_INVALID_PARAMETER_3;
567 }
568
569 /* Check for valid Allocation Types */
570 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
571 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
572 {
573 DPRINT1("Invalid Allocation Type\n");
574 return STATUS_INVALID_PARAMETER_5;
575 }
576
577 /* Check for at least one of these Allocation Types to be set */
578 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
579 {
580 DPRINT1("No memory allocation base type\n");
581 return STATUS_INVALID_PARAMETER_5;
582 }
583
584 /* MEM_RESET is an exclusive flag, make sure that is valid too */
585 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
586 {
587 DPRINT1("Invalid use of MEM_RESET\n");
588 return STATUS_INVALID_PARAMETER_5;
589 }
590
591 /* Check if large pages are being used */
592 if (AllocationType & MEM_LARGE_PAGES)
593 {
594 /* Large page allocations MUST be committed */
595 if (!(AllocationType & MEM_COMMIT))
596 {
597 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
598 return STATUS_INVALID_PARAMETER_5;
599 }
600
601 /* These flags are not allowed with large page allocations */
602 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
603 {
604 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
605 return STATUS_INVALID_PARAMETER_5;
606 }
607 }
608
609 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
610 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
611 {
612 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
613 return STATUS_INVALID_PARAMETER_5;
614 }
615
616 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
617 if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
618 {
619 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
620 return STATUS_INVALID_PARAMETER_5;
621 }
622
623 /* Check for valid MEM_PHYSICAL usage */
624 if (AllocationType & MEM_PHYSICAL)
625 {
626 /* Only these flags are allowed with MEM_PHYSIAL */
627 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
628 {
629 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
630 return STATUS_INVALID_PARAMETER_5;
631 }
632
633 /* Then make sure PAGE_READWRITE is used */
634 if (Protect != PAGE_READWRITE)
635 {
636 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
637 return STATUS_INVALID_PARAMETER_6;
638 }
639 }
640
641 /* Calculate the protection mask and make sure it's valid */
642 ProtectionMask = MiMakeProtectionMask(Protect);
643 if (ProtectionMask == MM_INVALID_PROTECTION)
644 {
645 DPRINT1("Invalid protection mask\n");
646 return STATUS_INVALID_PAGE_PROTECTION;
647 }
648
649 /* Enter SEH */
650 _SEH2_TRY
651 {
652 /* Check for user-mode parameters */
653 if (PreviousMode != KernelMode)
654 {
655 /* Make sure they are writable */
656 ProbeForWritePointer(UBaseAddress);
657 ProbeForWriteUlong(URegionSize);
658 }
659
660 /* Capture their values */
661 PBaseAddress = *UBaseAddress;
662 PRegionSize = *URegionSize;
663 }
664 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
665 {
666 /* Return the exception code */
667 _SEH2_YIELD(return _SEH2_GetExceptionCode());
668 }
669 _SEH2_END;
670
671 /* Make sure the allocation isn't past the VAD area */
672 if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
673 {
674 DPRINT1("Virtual allocation base above User Space\n");
675 return STATUS_INVALID_PARAMETER_2;
676 }
677
678 /* Make sure the allocation wouldn't overflow past the VAD area */
679 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
680 {
681 DPRINT1("Region size would overflow into kernel-memory\n");
682 return STATUS_INVALID_PARAMETER_4;
683 }
684
685 /* Make sure there's a size specified */
686 if (!PRegionSize)
687 {
688 DPRINT1("Region size is invalid (zero)\n");
689 return STATUS_INVALID_PARAMETER_4;
690 }
691
692 /* Check if this is for the current process */
693 if (ProcessHandle == NtCurrentProcess())
694 {
695 /* We already have the current process, no need to go through Ob */
696 Process = CurrentProcess;
697 }
698 else
699 {
700 /* Reference the handle for correct permissions */
701 Status = ObReferenceObjectByHandle(ProcessHandle,
702 PROCESS_VM_OPERATION,
703 PsProcessType,
704 PreviousMode,
705 (PVOID*)&Process,
706 NULL);
707 if (!NT_SUCCESS(Status)) return Status;
708
709 /* Check if not running in the current process */
710 if (CurrentProcess != Process)
711 {
712 /* Attach to it */
713 KeStackAttachProcess(&Process->Pcb, &ApcState);
714 Attached = TRUE;
715 }
716 }
717
718 /* Check for large page allocations */
719 if (AllocationType & MEM_LARGE_PAGES)
720 {
721 /* The lock memory privilege is required */
722 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
723 {
724 /* Fail without it */
725 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
726 if (Attached) KeUnstackDetachProcess(&ApcState);
727 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
728 return STATUS_PRIVILEGE_NOT_HELD;
729 }
730 }
731
732 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
733 RegionSize = PAGE_ROUND_UP((ULONG_PTR)PBaseAddress + PRegionSize) -
734 PAGE_ROUND_DOWN(PBaseAddress);
735
736
737 /*
738 * Copy on Write is reserved for system use. This case is a certain failure
739 * but there may be other cases...needs more testing
740 */
741 if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
742 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
743 {
744 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
745 if (Attached) KeUnstackDetachProcess(&ApcState);
746 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
747 return STATUS_INVALID_PAGE_PROTECTION;
748 }
749
750 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
751 DPRINT("Type %x\n", Type);
752
753 AddressSpace = &Process->Vm;
754 MmLockAddressSpace(AddressSpace);
755
756 if (PBaseAddress != 0)
757 {
758 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
759
760 if (MemoryArea != NULL)
761 {
762 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
763 (ULONG_PTR)MemoryArea->StartingAddress;
764
765 if (((ULONG_PTR)BaseAddress + RegionSize) > (ULONG_PTR)MemoryArea->EndingAddress)
766 {
767 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
768 (ULONG_PTR)BaseAddress + RegionSize, MemoryArea->EndingAddress);
769
770 MmUnlockAddressSpace(AddressSpace);
771 if (Attached) KeUnstackDetachProcess(&ApcState);
772 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
773 return STATUS_MEMORY_NOT_ALLOCATED;
774 }
775
776 if (AllocationType == MEM_RESET)
777 {
778 if (MmIsPagePresent(Process, BaseAddress))
779 {
780 /* FIXME: mark pages as not modified */
781 }
782 else
783 {
784 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
785 }
786
787 MmUnlockAddressSpace(AddressSpace);
788 if (Attached) KeUnstackDetachProcess(&ApcState);
789 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
790
791 /* MEM_RESET does not modify any attributes of region */
792 return STATUS_SUCCESS;
793 }
794
795 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
796 MemoryAreaLength >= RegionSize)
797 {
798 Status = MmAlterRegion(AddressSpace,
799 MemoryArea->StartingAddress,
800 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
801 BaseAddress, RegionSize,
802 Type, Protect, MmModifyAttributes);
803 MmUnlockAddressSpace(AddressSpace);
804 if (Attached) KeUnstackDetachProcess(&ApcState);
805 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
806 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
807
808 /* Give the caller rounded BaseAddress and area length */
809 if (NT_SUCCESS(Status))
810 {
811 *UBaseAddress = BaseAddress;
812 *URegionSize = RegionSize;
813 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
814 }
815
816 return(Status);
817 }
818 else if (MemoryAreaLength >= RegionSize)
819 {
820 /* Region list initialized? */
821 if (MemoryArea->Data.SectionData.RegionListHead.Flink)
822 {
823 Status = MmAlterRegion(AddressSpace,
824 MemoryArea->StartingAddress,
825 &MemoryArea->Data.SectionData.RegionListHead,
826 BaseAddress, RegionSize,
827 Type, Protect, MmModifyAttributes);
828 }
829 else
830 {
831 Status = STATUS_ACCESS_VIOLATION;
832 }
833
834 MmUnlockAddressSpace(AddressSpace);
835 if (Attached) KeUnstackDetachProcess(&ApcState);
836 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
837 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
838
839 /* Give the caller rounded BaseAddress and area length */
840 if (NT_SUCCESS(Status))
841 {
842 *UBaseAddress = BaseAddress;
843 *URegionSize = RegionSize;
844 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
845 }
846
847 return(Status);
848 }
849 else
850 {
851 MmUnlockAddressSpace(AddressSpace);
852 if (Attached) KeUnstackDetachProcess(&ApcState);
853 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
854 return(STATUS_UNSUCCESSFUL);
855 }
856 }
857 }
858
859 Status = MmCreateMemoryArea(AddressSpace,
860 MEMORY_AREA_VIRTUAL_MEMORY,
861 &BaseAddress,
862 RegionSize,
863 Protect,
864 &MemoryArea,
865 PBaseAddress != 0,
866 AllocationType & MEM_TOP_DOWN,
867 BoundaryAddressMultiple);
868 if (!NT_SUCCESS(Status))
869 {
870 MmUnlockAddressSpace(AddressSpace);
871 if (Attached) KeUnstackDetachProcess(&ApcState);
872 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
873 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
874 return(Status);
875 }
876
877 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
878 (ULONG_PTR)MemoryArea->StartingAddress;
879
880 MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
881 MemoryAreaLength, Type, Protect);
882
883 if ((AllocationType & MEM_COMMIT) &&
884 (Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
885 {
886 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
887 MmReserveSwapPages(nPages);
888 }
889
890 MmUnlockAddressSpace(AddressSpace);
891 if (Attached) KeUnstackDetachProcess(&ApcState);
892 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
893
894 *UBaseAddress = BaseAddress;
895 *URegionSize = MemoryAreaLength;
896 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
897
898 return(STATUS_SUCCESS);
899 }
900
901 static VOID
902 MmFreeVirtualMemoryPage(PVOID Context,
903 MEMORY_AREA* MemoryArea,
904 PVOID Address,
905 PFN_NUMBER Page,
906 SWAPENTRY SwapEntry,
907 BOOLEAN Dirty)
908 {
909 PEPROCESS Process = (PEPROCESS)Context;
910
911 if (Page != 0)
912 {
913 SWAPENTRY SavedSwapEntry;
914 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
915 if (SavedSwapEntry != 0)
916 {
917 MmFreeSwapPage(SavedSwapEntry);
918 MmSetSavedSwapEntryPage(Page, 0);
919 }
920 MmDeleteRmap(Page, Process, Address);
921 MmReleasePageMemoryConsumer(MC_USER, Page);
922 }
923 else if (SwapEntry != 0)
924 {
925 MmFreeSwapPage(SwapEntry);
926 }
927 }
928
929 VOID
930 NTAPI
931 MmFreeVirtualMemory(PEPROCESS Process,
932 PMEMORY_AREA MemoryArea)
933 {
934 PLIST_ENTRY current_entry;
935 PMM_REGION current;
936 ULONG i;
937
938 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
939 MemoryArea);
940
941 /* Mark this memory area as about to be deleted. */
942 MemoryArea->DeleteInProgress = TRUE;
943
944 /*
945 * Wait for any ongoing paging operations. Notice that since we have
946 * flagged this memory area as deleted no more page ops will be added.
947 */
948 if (MemoryArea->PageOpCount > 0)
949 {
950 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
951 (ULONG_PTR)MemoryArea->StartingAddress;
952 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
953
954 for (i = 0; i < nPages && MemoryArea->PageOpCount != 0; ++i)
955 {
956 PMM_PAGEOP PageOp;
957 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
958 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
959 NULL, 0);
960 if (PageOp != NULL)
961 {
962 NTSTATUS Status;
963 MmUnlockAddressSpace(&Process->Vm);
964 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
965 0,
966 KernelMode,
967 FALSE,
968 NULL);
969 if (Status != STATUS_SUCCESS)
970 {
971 DPRINT1("Failed to wait for page op\n");
972 KeBugCheck(MEMORY_MANAGEMENT);
973 }
974 MmLockAddressSpace(&Process->Vm);
975 MmReleasePageOp(PageOp);
976 }
977 }
978 }
979
980 /* Free all the individual segments. */
981 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
982 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
983 {
984 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
985 current_entry = current_entry->Flink;
986 ExFreePool(current);
987 }
988
989 /* Actually free the memory area. */
990 MmFreeMemoryArea(&Process->Vm,
991 MemoryArea,
992 MmFreeVirtualMemoryPage,
993 (PVOID)Process);
994 }
995
996 /*
997 * @implemented
998 */
999 NTSTATUS NTAPI
1000 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
1001 IN PVOID* UBaseAddress,
1002 IN PSIZE_T URegionSize,
1003 IN ULONG FreeType)
1004 /*
1005 * FUNCTION: Frees a range of virtual memory
1006 * ARGUMENTS:
1007 * ProcessHandle = Points to the process that allocated the virtual
1008 * memory
1009 * BaseAddress = Points to the memory address, rounded down to a
1010 * multiple of the pagesize
1011 * RegionSize = Limits the range to free, rounded up to a multiple of
1012 * the paging size
1013 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
1014 * RETURNS: Status
1015 */
1016 {
1017 MEMORY_AREA* MemoryArea;
1018 NTSTATUS Status;
1019 PEPROCESS Process;
1020 PMMSUPPORT AddressSpace;
1021 PVOID BaseAddress = NULL, PBaseAddress;
1022 SIZE_T RegionSize = 0, PRegionSize;
1023 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1024 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
1025 KAPC_STATE ApcState;
1026 BOOLEAN Attached = FALSE;
1027 PAGED_CODE();
1028
1029 /* Only two flags are supported */
1030 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
1031 {
1032 DPRINT1("Invalid FreeType\n");
1033 return STATUS_INVALID_PARAMETER_4;
1034 }
1035
1036 /* Check if no flag was used, or if both flags were used */
1037 if (!((FreeType & (MEM_DECOMMIT | MEM_RELEASE))) ||
1038 ((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == (MEM_DECOMMIT | MEM_RELEASE)))
1039 {
1040 DPRINT1("Invalid FreeType combination\n");
1041 return STATUS_INVALID_PARAMETER_4;
1042 }
1043
1044 /* Enter SEH */
1045 _SEH2_TRY
1046 {
1047 /* Check for user-mode parameters */
1048 if (PreviousMode != KernelMode)
1049 {
1050 /* Make sure they are writeable */
1051 ProbeForWritePointer(UBaseAddress);
1052 ProbeForWriteUlong(URegionSize);
1053 }
1054
1055 /* Capture their values */
1056 PBaseAddress = *UBaseAddress;
1057 PRegionSize = *URegionSize;
1058 }
1059 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1060 {
1061 /* Return the exception code */
1062 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1063 }
1064 _SEH2_END;
1065
1066 /* Make sure the allocation isn't past the user area */
1067 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
1068 {
1069 DPRINT1("Virtual free base above User Space\n");
1070 return STATUS_INVALID_PARAMETER_2;
1071 }
1072
1073 /* Make sure the allocation wouldn't overflow past the user area */
1074 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
1075 {
1076 DPRINT1("Region size would overflow into kernel-memory\n");
1077 return STATUS_INVALID_PARAMETER_3;
1078 }
1079
1080 /* Check if this is for the current process */
1081 if (ProcessHandle == NtCurrentProcess())
1082 {
1083 /* We already have the current process, no need to go through Ob */
1084 Process = CurrentProcess;
1085 }
1086 else
1087 {
1088 /* Reference the handle for correct permissions */
1089 Status = ObReferenceObjectByHandle(ProcessHandle,
1090 PROCESS_VM_OPERATION,
1091 PsProcessType,
1092 PreviousMode,
1093 (PVOID*)&Process,
1094 NULL);
1095 if (!NT_SUCCESS(Status)) return Status;
1096
1097 /* Check if not running in the current process */
1098 if (CurrentProcess != Process)
1099 {
1100 /* Attach to it */
1101 KeStackAttachProcess(&Process->Pcb, &ApcState);
1102 Attached = TRUE;
1103 }
1104 }
1105
1106 BaseAddress = (PVOID)PAGE_ROUND_DOWN((PBaseAddress));
1107
1108 AddressSpace = &Process->Vm;
1109
1110 MmLockAddressSpace(AddressSpace);
1111 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1112 if (MemoryArea == NULL)
1113 {
1114 DPRINT1("Unable to find memory area from address 0x%p\n", BaseAddress);
1115 Status = STATUS_UNABLE_TO_FREE_VM;
1116 goto unlock_deref_and_return;
1117 }
1118
1119 if (PRegionSize != 0)
1120 {
1121 /* Use the region the caller wanted, rounded to whole pages */
1122 RegionSize = PAGE_ROUND_UP((ULONG_PTR)(PBaseAddress) + (PRegionSize)) -
1123 PAGE_ROUND_DOWN((PBaseAddress));
1124 }
1125 else
1126 {
1127 /* The caller wanted the whole memory area */
1128 RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
1129 (ULONG_PTR)MemoryArea->StartingAddress;
1130 }
1131
1132 switch (FreeType)
1133 {
1134 case MEM_RELEASE:
1135 /* MEM_RELEASE must be used with the exact base and length
1136 * that was returned by NtAllocateVirtualMemory */
1137
1138 /* Verify the base address is correct */
1139 if (MemoryArea->StartingAddress != BaseAddress)
1140 {
1141 DPRINT1("Base address is illegal for MEM_RELEASE (0x%p != 0x%p)\n",
1142 MemoryArea->StartingAddress,
1143 BaseAddress);
1144 Status = STATUS_UNABLE_TO_FREE_VM;
1145 goto unlock_deref_and_return;
1146 }
1147
1148 /* Verify the region size is correct */
1149 if ((ULONG_PTR)MemoryArea->StartingAddress + RegionSize !=
1150 (ULONG_PTR)MemoryArea->EndingAddress)
1151 {
1152 DPRINT1("Region size is illegal for MEM_RELEASE (0x%x)\n", RegionSize);
1153 Status = STATUS_UNABLE_TO_FREE_VM;
1154 //goto unlock_deref_and_return;
1155 }
1156
1157 if (MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
1158 {
1159 DPRINT1("Memory area is not VM\n");
1160 Status = STATUS_UNABLE_TO_FREE_VM;
1161 goto unlock_deref_and_return;
1162 }
1163
1164 MmFreeVirtualMemory(Process, MemoryArea);
1165 Status = STATUS_SUCCESS;
1166 break;
1167
1168 case MEM_DECOMMIT:
1169 if ((ULONG_PTR)BaseAddress + RegionSize >
1170 (ULONG_PTR)MemoryArea->EndingAddress)
1171 {
1172 DPRINT1("Invald base address (0x%p) and region size (0x%x) for MEM_DECOMMIT\n",
1173 BaseAddress, RegionSize);
1174 Status = STATUS_UNABLE_TO_FREE_VM;
1175 goto unlock_deref_and_return;
1176 }
1177 Status = MmAlterRegion(AddressSpace,
1178 MemoryArea->StartingAddress,
1179 (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) ?
1180 &MemoryArea->Data.SectionData.RegionListHead :
1181 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1182 BaseAddress,
1183 RegionSize,
1184 MEM_RESERVE,
1185 PAGE_NOACCESS,
1186 MmModifyAttributes);
1187 if (!NT_SUCCESS(Status))
1188 {
1189 DPRINT1("MmAlterRegion failed with status 0x%x\n", Status);
1190 Status = STATUS_UNABLE_TO_FREE_VM;
1191 goto unlock_deref_and_return;
1192 }
1193 break;
1194
1195 default:
1196 Status = STATUS_NOT_IMPLEMENTED;
1197 goto unlock_deref_and_return;
1198 }
1199
1200 unlock_deref_and_return:
1201 MmUnlockAddressSpace(AddressSpace);
1202
1203 /* Copy rounded values back in success case */
1204 if (NT_SUCCESS(Status))
1205 {
1206 /* Enter SEH */
1207 _SEH2_TRY
1208 {
1209 *UBaseAddress = BaseAddress;
1210 *URegionSize = RegionSize;
1211 }
1212 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1213 {
1214 Status = _SEH2_GetExceptionCode();
1215 DPRINT1("Failed to copy values back! (Status: 0x%x)\n", Status);
1216 }
1217 _SEH2_END;
1218 }
1219
1220 if (Attached) KeUnstackDetachProcess(&ApcState);
1221 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
1222
1223 return(Status);
1224 }
1225
1226 NTSTATUS
1227 NTAPI
1228 MmProtectAnonMem(PMMSUPPORT AddressSpace,
1229 PMEMORY_AREA MemoryArea,
1230 PVOID BaseAddress,
1231 SIZE_T Length,
1232 ULONG Protect,
1233 PULONG OldProtect)
1234 {
1235 PMM_REGION Region;
1236 NTSTATUS Status = STATUS_SUCCESS;
1237 ULONG_PTR LengthCount = 0;
1238
1239 /* Search all Regions in MemoryArea up to Length */
1240 /* Every Region up to Length must be committed for success */
1241 for (;;)
1242 {
1243 Region = MmFindRegion(MemoryArea->StartingAddress,
1244 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1245 (PVOID)((ULONG_PTR)BaseAddress + LengthCount), NULL);
1246
1247 /* If a Region was found and it is committed */
1248 if ((Region) && (Region->Type == MEM_COMMIT))
1249 {
1250 LengthCount += Region->Length;
1251 if (Length <= LengthCount) break;
1252 continue;
1253 }
1254 /* If Region was found and it is not commited */
1255 else if (Region)
1256 {
1257 Status = STATUS_NOT_COMMITTED;
1258 break;
1259 }
1260 /* If no Region was found at all */
1261 else if (LengthCount == 0)
1262 {
1263 Status = STATUS_INVALID_ADDRESS;
1264 break;
1265 }
1266 }
1267
1268 if (NT_SUCCESS(Status))
1269 {
1270 *OldProtect = Region->Protect;
1271 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1272 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1273 BaseAddress, Length, Region->Type, Protect,
1274 MmModifyAttributes);
1275 }
1276
1277 return (Status);
1278 }
1279
1280 NTSTATUS NTAPI
1281 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
1282 PVOID Address,
1283 PMEMORY_BASIC_INFORMATION Info,
1284 PSIZE_T ResultLength)
1285 {
1286 PMM_REGION Region;
1287 PVOID RegionBase = NULL;
1288
1289 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1290
1291 Region = MmFindRegion(MemoryArea->StartingAddress,
1292 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1293 Address, &RegionBase);
1294 Info->BaseAddress = RegionBase;
1295 Info->AllocationBase = MemoryArea->StartingAddress;
1296 Info->AllocationProtect = MemoryArea->Protect;
1297 Info->RegionSize = Region->Length;
1298 Info->State = Region->Type;
1299 Info->Protect = Region->Protect;
1300 Info->Type = MEM_PRIVATE;
1301
1302 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1303 return(STATUS_SUCCESS);
1304 }
1305
1306 /* EOF */