merge trunk head (37902)
[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 /* FUNCTIONS *****************************************************************/
47
48 NTSTATUS
49 NTAPI
50 MmWritePageVirtualMemory(PMM_AVL_TABLE AddressSpace,
51 PMEMORY_AREA MemoryArea,
52 PVOID Address,
53 PMM_PAGEOP PageOp)
54 {
55 SWAPENTRY SwapEntry;
56 PFN_TYPE Page;
57 NTSTATUS Status;
58 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
59
60 /*
61 * Check for paging out from a deleted virtual memory area.
62 */
63 if (MemoryArea->DeleteInProgress)
64 {
65 PageOp->Status = STATUS_UNSUCCESSFUL;
66 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
67 MmReleasePageOp(PageOp);
68 return(STATUS_UNSUCCESSFUL);
69 }
70
71 Page = MmGetPfnForProcess(Process, Address);
72
73 /*
74 * Get that the page actually is dirty.
75 */
76 if (!MmIsDirtyPage(Process, Address))
77 {
78 PageOp->Status = STATUS_SUCCESS;
79 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
80 MmReleasePageOp(PageOp);
81 return(STATUS_SUCCESS);
82 }
83
84 /*
85 * Speculatively set the mapping to clean.
86 */
87 MmSetCleanPage(Process, Address);
88
89 /*
90 * If necessary, allocate an entry in the paging file for this page
91 */
92 SwapEntry = MmGetSavedSwapEntryPage(Page);
93 if (SwapEntry == 0)
94 {
95 SwapEntry = MmAllocSwapPage();
96 if (SwapEntry == 0)
97 {
98 MmSetDirtyPage(Process, Address);
99 PageOp->Status = STATUS_PAGEFILE_QUOTA_EXCEEDED;
100 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
101 MmReleasePageOp(PageOp);
102 return(STATUS_PAGEFILE_QUOTA_EXCEEDED);
103 }
104 }
105
106 /*
107 * Write the page to the pagefile
108 */
109 Status = MmWriteToSwapPage(SwapEntry, Page);
110 if (!NT_SUCCESS(Status))
111 {
112 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
113 Status);
114 MmSetDirtyPage(Process, Address);
115 PageOp->Status = STATUS_UNSUCCESSFUL;
116 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
117 MmReleasePageOp(PageOp);
118 return(STATUS_UNSUCCESSFUL);
119 }
120
121 /*
122 * Otherwise we have succeeded.
123 */
124 MmSetSavedSwapEntryPage(Page, SwapEntry);
125 PageOp->Status = STATUS_SUCCESS;
126 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
127 MmReleasePageOp(PageOp);
128 return(STATUS_SUCCESS);
129 }
130
131 NTSTATUS
132 NTAPI
133 MmPageOutVirtualMemory(PMM_AVL_TABLE AddressSpace,
134 PMEMORY_AREA MemoryArea,
135 PVOID Address,
136 PMM_PAGEOP PageOp)
137 {
138 PFN_TYPE Page;
139 BOOLEAN WasDirty;
140 SWAPENTRY SwapEntry;
141 NTSTATUS Status;
142 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
143
144 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
145 Address, Process->UniqueProcessId);
146
147 /*
148 * Check for paging out from a deleted virtual memory area.
149 */
150 if (MemoryArea->DeleteInProgress)
151 {
152 PageOp->Status = STATUS_UNSUCCESSFUL;
153 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
154 MmReleasePageOp(PageOp);
155 return(STATUS_UNSUCCESSFUL);
156 }
157
158 /*
159 * Disable the virtual mapping.
160 */
161 MmDisableVirtualMapping(Process, Address,
162 &WasDirty, &Page);
163
164 if (Page == 0)
165 {
166 ASSERT(FALSE);
167 }
168
169 /*
170 * Paging out non-dirty data is easy.
171 */
172 if (!WasDirty)
173 {
174 MmLockAddressSpace(AddressSpace);
175 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
176 MmDeleteAllRmaps(Page, NULL, NULL);
177 if ((SwapEntry = MmGetSavedSwapEntryPage(Page)) != 0)
178 {
179 MmCreatePageFileMapping(Process, Address, SwapEntry);
180 MmSetSavedSwapEntryPage(Page, 0);
181 }
182 MmUnlockAddressSpace(AddressSpace);
183 MmReleasePageMemoryConsumer(MC_USER, Page);
184 PageOp->Status = STATUS_SUCCESS;
185 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
186 MmReleasePageOp(PageOp);
187 return(STATUS_SUCCESS);
188 }
189
190 /*
191 * If necessary, allocate an entry in the paging file for this page
192 */
193 SwapEntry = MmGetSavedSwapEntryPage(Page);
194 if (SwapEntry == 0)
195 {
196 SwapEntry = MmAllocSwapPage();
197 if (SwapEntry == 0)
198 {
199 MmShowOutOfSpaceMessagePagingFile();
200 MmEnableVirtualMapping(Process, Address);
201 PageOp->Status = STATUS_UNSUCCESSFUL;
202 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
203 MmReleasePageOp(PageOp);
204 return(STATUS_PAGEFILE_QUOTA);
205 }
206 }
207
208 /*
209 * Write the page to the pagefile
210 */
211 Status = MmWriteToSwapPage(SwapEntry, Page);
212 if (!NT_SUCCESS(Status))
213 {
214 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
215 Status);
216 MmEnableVirtualMapping(Process, Address);
217 PageOp->Status = STATUS_UNSUCCESSFUL;
218 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
219 MmReleasePageOp(PageOp);
220 return(STATUS_UNSUCCESSFUL);
221 }
222
223 /*
224 * Otherwise we have succeeded, free the page
225 */
226 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page << PAGE_SHIFT);
227 MmLockAddressSpace(AddressSpace);
228 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
229 MmCreatePageFileMapping(Process, Address, SwapEntry);
230 MmUnlockAddressSpace(AddressSpace);
231 MmDeleteAllRmaps(Page, NULL, NULL);
232 MmSetSavedSwapEntryPage(Page, 0);
233 MmReleasePageMemoryConsumer(MC_USER, Page);
234 PageOp->Status = STATUS_SUCCESS;
235 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
236 MmReleasePageOp(PageOp);
237 return(STATUS_SUCCESS);
238 }
239
240 NTSTATUS
241 NTAPI
242 MmNotPresentFaultVirtualMemory(PMM_AVL_TABLE AddressSpace,
243 MEMORY_AREA* MemoryArea,
244 PVOID Address,
245 BOOLEAN Locked)
246 /*
247 * FUNCTION: Move data into memory to satisfy a page not present fault
248 * ARGUMENTS:
249 * AddressSpace = Address space within which the fault occurred
250 * MemoryArea = The memory area within which the fault occurred
251 * Address = The absolute address of fault
252 * RETURNS: Status
253 * NOTES: This function is called with the address space lock held.
254 */
255 {
256 PFN_TYPE Page;
257 NTSTATUS Status;
258 PMM_REGION Region;
259 PMM_PAGEOP PageOp;
260 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
261
262 /*
263 * There is a window between taking the page fault and locking the
264 * address space when another thread could load the page so we check
265 * that.
266 */
267 if (MmIsPagePresent(NULL, Address))
268 {
269 if (Locked)
270 {
271 MmLockPage(MmGetPfnForProcess(NULL, Address));
272 }
273 return(STATUS_SUCCESS);
274 }
275
276 /*
277 * Check for the virtual memory area being deleted.
278 */
279 if (MemoryArea->DeleteInProgress)
280 {
281 return(STATUS_UNSUCCESSFUL);
282 }
283
284 /*
285 * Get the segment corresponding to the virtual address
286 */
287 Region = MmFindRegion(MemoryArea->StartingAddress,
288 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
289 Address, NULL);
290 if (Region->Type == MEM_RESERVE || Region->Protect == PAGE_NOACCESS)
291 {
292 return(STATUS_ACCESS_VIOLATION);
293 }
294
295 /*
296 * Get or create a page operation
297 */
298 PageOp = MmGetPageOp(MemoryArea, Process->UniqueProcessId,
299 (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
300 MM_PAGEOP_PAGEIN, FALSE);
301 if (PageOp == NULL)
302 {
303 DPRINT1("MmGetPageOp failed");
304 ASSERT(FALSE);
305 }
306
307 /*
308 * Check if someone else is already handling this fault, if so wait
309 * for them
310 */
311 if (PageOp->Thread != PsGetCurrentThread())
312 {
313 MmUnlockAddressSpace(AddressSpace);
314 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
315 0,
316 KernelMode,
317 FALSE,
318 NULL);
319 /*
320 * Check for various strange conditions
321 */
322 if (Status != STATUS_SUCCESS)
323 {
324 DPRINT1("Failed to wait for page op\n");
325 ASSERT(FALSE);
326 }
327 if (PageOp->Status == STATUS_PENDING)
328 {
329 DPRINT1("Woke for page op before completion\n");
330 ASSERT(FALSE);
331 }
332 /*
333 * If this wasn't a pagein then we need to restart the handling
334 */
335 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
336 {
337 MmLockAddressSpace(AddressSpace);
338 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
339 MmReleasePageOp(PageOp);
340 return(STATUS_MM_RESTART_OPERATION);
341 }
342 /*
343 * If the thread handling this fault has failed then we don't retry
344 */
345 if (!NT_SUCCESS(PageOp->Status))
346 {
347 MmLockAddressSpace(AddressSpace);
348 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
349 Status = PageOp->Status;
350 MmReleasePageOp(PageOp);
351 return(Status);
352 }
353 MmLockAddressSpace(AddressSpace);
354 if (Locked)
355 {
356 MmLockPage(MmGetPfnForProcess(NULL, Address));
357 }
358 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
359 MmReleasePageOp(PageOp);
360 return(STATUS_SUCCESS);
361 }
362
363 /*
364 * Try to allocate a page
365 */
366 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
367 if (Status == STATUS_NO_MEMORY)
368 {
369 MmUnlockAddressSpace(AddressSpace);
370 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
371 MmLockAddressSpace(AddressSpace);
372 }
373 if (!NT_SUCCESS(Status))
374 {
375 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
376 ASSERT(FALSE);
377 }
378
379 /*
380 * Handle swapped out pages.
381 */
382 if (MmIsPageSwapEntry(NULL, Address))
383 {
384 SWAPENTRY SwapEntry;
385
386 MmDeletePageFileMapping(Process, Address, &SwapEntry);
387 Status = MmReadFromSwapPage(SwapEntry, Page);
388 if (!NT_SUCCESS(Status))
389 {
390 ASSERT(FALSE);
391 }
392 MmSetSavedSwapEntryPage(Page, SwapEntry);
393 }
394
395 /*
396 * Set the page. If we fail because we are out of memory then
397 * try again
398 */
399 Status = MmCreateVirtualMapping(Process,
400 (PVOID)PAGE_ROUND_DOWN(Address),
401 Region->Protect,
402 &Page,
403 1);
404 while (Status == STATUS_NO_MEMORY)
405 {
406 MmUnlockAddressSpace(AddressSpace);
407 Status = MmCreateVirtualMapping(Process,
408 Address,
409 Region->Protect,
410 &Page,
411 1);
412 MmLockAddressSpace(AddressSpace);
413 }
414 if (!NT_SUCCESS(Status))
415 {
416 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
417 ASSERT(FALSE);
418 return(Status);
419 }
420
421 /*
422 * Add the page to the process's working set
423 */
424 MmInsertRmap(Page, Process, (PVOID)PAGE_ROUND_DOWN(Address));
425
426 /*
427 * Finish the operation
428 */
429 if (Locked)
430 {
431 MmLockPage(Page);
432 }
433 PageOp->Status = STATUS_SUCCESS;
434 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
435 MmReleasePageOp(PageOp);
436 return(STATUS_SUCCESS);
437 }
438
439 static VOID
440 MmModifyAttributes(PMM_AVL_TABLE AddressSpace,
441 PVOID BaseAddress,
442 ULONG RegionSize,
443 ULONG OldType,
444 ULONG OldProtect,
445 ULONG NewType,
446 ULONG NewProtect)
447 /*
448 * FUNCTION: Modify the attributes of a memory region
449 */
450 {
451 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
452
453 /*
454 * If we are switching a previously committed region to reserved then
455 * free any allocated pages within the region
456 */
457 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
458 {
459 ULONG i;
460
461 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
462 {
463 PFN_TYPE Page;
464
465 if (MmIsPageSwapEntry(Process,
466 (char*)BaseAddress + (i * PAGE_SIZE)))
467 {
468 SWAPENTRY SwapEntry;
469
470 MmDeletePageFileMapping(Process,
471 (char*)BaseAddress + (i * PAGE_SIZE),
472 &SwapEntry);
473 MmFreeSwapPage(SwapEntry);
474 }
475 else
476 {
477 MmDeleteVirtualMapping(Process,
478 (char*)BaseAddress + (i*PAGE_SIZE),
479 FALSE, NULL, &Page);
480 if (Page != 0)
481 {
482 SWAPENTRY SavedSwapEntry;
483 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
484 if (SavedSwapEntry != 0)
485 {
486 MmFreeSwapPage(SavedSwapEntry);
487 MmSetSavedSwapEntryPage(Page, 0);
488 }
489 MmDeleteRmap(Page, Process,
490 (char*)BaseAddress + (i * PAGE_SIZE));
491 MmReleasePageMemoryConsumer(MC_USER, Page);
492 }
493 }
494 }
495 }
496
497 /*
498 * If we are changing the protection attributes of a committed region then
499 * alter the attributes for any allocated pages within the region
500 */
501 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
502 OldProtect != NewProtect)
503 {
504 ULONG i;
505
506 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
507 {
508 if (MmIsPagePresent(Process,
509 (char*)BaseAddress + (i*PAGE_SIZE)))
510 {
511 MmSetPageProtect(Process,
512 (char*)BaseAddress + (i*PAGE_SIZE),
513 NewProtect);
514 }
515 }
516 }
517 }
518
519 /*
520 * @implemented
521 */
522 NTSTATUS NTAPI
523 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
524 IN OUT PVOID* UBaseAddress,
525 IN ULONG_PTR ZeroBits,
526 IN OUT PSIZE_T URegionSize,
527 IN ULONG AllocationType,
528 IN ULONG Protect)
529 /*
530 * FUNCTION: Allocates a block of virtual memory in the process address space
531 * ARGUMENTS:
532 * ProcessHandle = The handle of the process which owns the virtual memory
533 * BaseAddress = A pointer to the virtual memory allocated. If you
534 * supply a non zero value the system will try to
535 * allocate the memory at the address supplied. It round
536 * it down to a multiple of the page size.
537 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
538 * that must be zero, ensuring that the memory will be
539 * allocated at a address below a certain value.
540 * RegionSize = The number of bytes to allocate
541 * AllocationType = Indicates the type of virtual memory you like to
542 * allocated, can be a combination of MEM_COMMIT,
543 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
544 * Protect = Indicates the protection type of the pages allocated, can be
545 * a combination of PAGE_READONLY, PAGE_READWRITE,
546 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
547 * PAGE_NOACCESS
548 * RETURNS: Status
549 */
550 {
551 PEPROCESS Process;
552 MEMORY_AREA* MemoryArea;
553 ULONG_PTR MemoryAreaLength;
554 ULONG Type;
555 NTSTATUS Status;
556 PMM_AVL_TABLE AddressSpace;
557 PVOID BaseAddress;
558 ULONG RegionSize;
559 PVOID PBaseAddress;
560 ULONG PRegionSize;
561 PHYSICAL_ADDRESS BoundaryAddressMultiple;
562 KPROCESSOR_MODE PreviousMode;
563
564 PAGED_CODE();
565
566 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
567 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
568 *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
569 Protect);
570
571 /* Check for valid protection flags */
572 if ((Protect & PAGE_FLAGS_VALID_FROM_USER_MODE) != Protect)
573 {
574 DPRINT1("Invalid page protection\n");
575 return STATUS_INVALID_PAGE_PROTECTION;
576 }
577
578 /* Check for valid Zero bits */
579 if (ZeroBits > 21)
580 {
581 DPRINT1("Too many zero bits\n");
582 return STATUS_INVALID_PARAMETER_3;
583 }
584
585 /* Check for valid Allocation Types */
586 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
587 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
588 {
589 DPRINT1("Invalid Allocation Type\n");
590 return STATUS_INVALID_PARAMETER_5;
591 }
592
593 /* Check for at least one of these Allocation Types to be set */
594 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
595 {
596 DPRINT1("No memory allocation base type\n");
597 return STATUS_INVALID_PARAMETER_5;
598 }
599
600 /* MEM_RESET is an exclusive flag, make sure that is valid too */
601 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
602 {
603 DPRINT1("Invalid use of MEM_RESET\n");
604 return STATUS_INVALID_PARAMETER_5;
605 }
606
607 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
608 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
609 {
610 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
611 return STATUS_INVALID_PARAMETER_5;
612 }
613
614 /* MEM_PHYSICAL can only be used with MEM_RESERVE, and can only be R/W */
615 if (AllocationType & MEM_PHYSICAL)
616 {
617 /* First check for MEM_RESERVE exclusivity */
618 if (AllocationType != (MEM_RESERVE | MEM_PHYSICAL))
619 {
620 DPRINT1("MEM_PHYSICAL used with other flags then MEM_RESERVE or"
621 "MEM_RESERVE was not present at all\n");
622 return STATUS_INVALID_PARAMETER_5;
623 }
624
625 /* Then make sure PAGE_READWRITE is used */
626 if (Protect != PAGE_READWRITE)
627 {
628 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
629 return STATUS_INVALID_PAGE_PROTECTION;
630 }
631 }
632
633 PreviousMode = KeGetPreviousMode();
634
635 _SEH2_TRY
636 {
637 if (PreviousMode != KernelMode)
638 {
639 ProbeForWritePointer(UBaseAddress);
640 ProbeForWriteUlong(URegionSize);
641 }
642 PBaseAddress = *UBaseAddress;
643 PRegionSize = *URegionSize;
644 }
645 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
646 {
647 /* Get the exception code */
648 Status = _SEH2_GetExceptionCode();
649 _SEH2_YIELD(return Status);
650 }
651 _SEH2_END;
652
653 BoundaryAddressMultiple.QuadPart = 0;
654
655 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
656 RegionSize = PAGE_ROUND_UP((ULONG_PTR)PBaseAddress + PRegionSize) -
657 PAGE_ROUND_DOWN(PBaseAddress);
658
659 /*
660 * We've captured and calculated the data, now do more checks
661 * Yes, MmCreateMemoryArea does similar checks, but they don't return
662 * the right status codes that a caller of this routine would expect.
663 */
664 if ((ULONG_PTR)BaseAddress >= USER_SHARED_DATA)
665 {
666 DPRINT1("Virtual allocation base above User Space\n");
667 return STATUS_INVALID_PARAMETER_2;
668 }
669 if (!RegionSize)
670 {
671 DPRINT1("Region size is invalid (zero)\n");
672 return STATUS_INVALID_PARAMETER_4;
673 }
674 if ((USER_SHARED_DATA - (ULONG_PTR)BaseAddress) < RegionSize)
675 {
676 DPRINT1("Region size would overflow into kernel-memory\n");
677 return STATUS_INVALID_PARAMETER_4;
678 }
679
680 /*
681 * Copy on Write is reserved for system use. This case is a certain failure
682 * but there may be other cases...needs more testing
683 */
684 if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
685 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
686 {
687 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
688 return STATUS_INVALID_PAGE_PROTECTION;
689 }
690
691
692 Status = ObReferenceObjectByHandle(ProcessHandle,
693 PROCESS_VM_OPERATION,
694 PsProcessType,
695 PreviousMode,
696 (PVOID*)(&Process),
697 NULL);
698 if (!NT_SUCCESS(Status))
699 {
700 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
701 return(Status);
702 }
703
704 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
705 DPRINT("Type %x\n", Type);
706
707 AddressSpace = &Process->VadRoot;
708 MmLockAddressSpace(AddressSpace);
709
710 if (PBaseAddress != 0)
711 {
712 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
713
714 if (MemoryArea != NULL)
715 {
716 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
717 (ULONG_PTR)MemoryArea->StartingAddress;
718 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
719 MemoryAreaLength >= RegionSize)
720 {
721 Status =
722 MmAlterRegion(AddressSpace,
723 MemoryArea->StartingAddress,
724 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
725 BaseAddress, RegionSize,
726 Type, Protect, MmModifyAttributes);
727 MmUnlockAddressSpace(AddressSpace);
728 ObDereferenceObject(Process);
729 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
730
731 /* Give the caller rounded BaseAddress and area length */
732 if (NT_SUCCESS(Status))
733 {
734 *UBaseAddress = BaseAddress;
735 *URegionSize = RegionSize;
736 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
737 }
738
739 return(Status);
740 }
741 else if (MemoryAreaLength >= RegionSize)
742 {
743 /* Region list initialized? */
744 if (MemoryArea->Data.SectionData.RegionListHead.Flink)
745 {
746 Status =
747 MmAlterRegion(AddressSpace,
748 MemoryArea->StartingAddress,
749 &MemoryArea->Data.SectionData.RegionListHead,
750 BaseAddress, RegionSize,
751 Type, Protect, MmModifyAttributes);
752 }
753 else
754 {
755 Status = STATUS_ACCESS_VIOLATION;
756 }
757
758 MmUnlockAddressSpace(AddressSpace);
759 ObDereferenceObject(Process);
760 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
761
762 /* Give the caller rounded BaseAddress and area length */
763 if (NT_SUCCESS(Status))
764 {
765 *UBaseAddress = BaseAddress;
766 *URegionSize = RegionSize;
767 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
768 }
769
770 return(Status);
771 }
772 else
773 {
774 MmUnlockAddressSpace(AddressSpace);
775 ObDereferenceObject(Process);
776 return(STATUS_UNSUCCESSFUL);
777 }
778 }
779 }
780
781 Status = MmCreateMemoryArea(AddressSpace,
782 MEMORY_AREA_VIRTUAL_MEMORY,
783 &BaseAddress,
784 RegionSize,
785 Protect,
786 &MemoryArea,
787 PBaseAddress != 0,
788 AllocationType & MEM_TOP_DOWN,
789 BoundaryAddressMultiple);
790 if (!NT_SUCCESS(Status))
791 {
792 MmUnlockAddressSpace(AddressSpace);
793 ObDereferenceObject(Process);
794 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
795 return(Status);
796 }
797
798 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
799 (ULONG_PTR)MemoryArea->StartingAddress;
800
801 MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
802 MemoryAreaLength, Type, Protect);
803
804 if ((AllocationType & MEM_COMMIT) &&
805 (Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
806 {
807 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
808 MmReserveSwapPages(nPages);
809 }
810
811 *UBaseAddress = BaseAddress;
812 *URegionSize = MemoryAreaLength;
813 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
814
815 MmUnlockAddressSpace(AddressSpace);
816 ObDereferenceObject(Process);
817 return(STATUS_SUCCESS);
818 }
819
820 static VOID
821 MmFreeVirtualMemoryPage(PVOID Context,
822 MEMORY_AREA* MemoryArea,
823 PVOID Address,
824 PFN_TYPE Page,
825 SWAPENTRY SwapEntry,
826 BOOLEAN Dirty)
827 {
828 PEPROCESS Process = (PEPROCESS)Context;
829
830 if (Page != 0)
831 {
832 SWAPENTRY SavedSwapEntry;
833 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
834 if (SavedSwapEntry != 0)
835 {
836 MmFreeSwapPage(SavedSwapEntry);
837 MmSetSavedSwapEntryPage(Page, 0);
838 }
839 MmDeleteRmap(Page, Process, Address);
840 MmReleasePageMemoryConsumer(MC_USER, Page);
841 }
842 else if (SwapEntry != 0)
843 {
844 MmFreeSwapPage(SwapEntry);
845 }
846 }
847
848 VOID
849 NTAPI
850 MmFreeVirtualMemory(PEPROCESS Process,
851 PMEMORY_AREA MemoryArea)
852 {
853 PLIST_ENTRY current_entry;
854 PMM_REGION current;
855 ULONG i;
856
857 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
858 MemoryArea);
859
860 /* Mark this memory area as about to be deleted. */
861 MemoryArea->DeleteInProgress = TRUE;
862
863 /*
864 * Wait for any ongoing paging operations. Notice that since we have
865 * flagged this memory area as deleted no more page ops will be added.
866 */
867 if (MemoryArea->PageOpCount > 0)
868 {
869 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
870 (ULONG_PTR)MemoryArea->StartingAddress;
871 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
872
873 for (i = 0; i < nPages && MemoryArea->PageOpCount != 0; ++i)
874 {
875 PMM_PAGEOP PageOp;
876 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
877 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
878 NULL, 0);
879 if (PageOp != NULL)
880 {
881 NTSTATUS Status;
882 MmUnlockAddressSpace(&Process->VadRoot);
883 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
884 0,
885 KernelMode,
886 FALSE,
887 NULL);
888 if (Status != STATUS_SUCCESS)
889 {
890 DPRINT1("Failed to wait for page op\n");
891 ASSERT(FALSE);
892 }
893 MmLockAddressSpace(&Process->VadRoot);
894 MmReleasePageOp(PageOp);
895 }
896 }
897 }
898
899 /* Free all the individual segments. */
900 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
901 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
902 {
903 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
904 current_entry = current_entry->Flink;
905 ExFreePool(current);
906 }
907
908 /* Actually free the memory area. */
909 MmFreeMemoryArea(&Process->VadRoot,
910 MemoryArea,
911 MmFreeVirtualMemoryPage,
912 (PVOID)Process);
913 }
914
915 /*
916 * @implemented
917 */
918 NTSTATUS NTAPI
919 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
920 IN PVOID* PBaseAddress,
921 IN PSIZE_T PRegionSize,
922 IN ULONG FreeType)
923 /*
924 * FUNCTION: Frees a range of virtual memory
925 * ARGUMENTS:
926 * ProcessHandle = Points to the process that allocated the virtual
927 * memory
928 * BaseAddress = Points to the memory address, rounded down to a
929 * multiple of the pagesize
930 * RegionSize = Limits the range to free, rounded up to a multiple of
931 * the paging size
932 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
933 * RETURNS: Status
934 */
935 {
936 MEMORY_AREA* MemoryArea;
937 NTSTATUS Status;
938 PEPROCESS Process;
939 PMM_AVL_TABLE AddressSpace;
940 PVOID BaseAddress;
941 ULONG RegionSize;
942
943 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
944 "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
945 *PRegionSize,FreeType);
946
947 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
948 RegionSize = PAGE_ROUND_UP((ULONG_PTR)(*PBaseAddress) + (*PRegionSize)) -
949 PAGE_ROUND_DOWN((*PBaseAddress));
950
951 Status = ObReferenceObjectByHandle(ProcessHandle,
952 PROCESS_VM_OPERATION,
953 PsProcessType,
954 UserMode,
955 (PVOID*)(&Process),
956 NULL);
957 if (!NT_SUCCESS(Status))
958 {
959 return(Status);
960 }
961
962 AddressSpace = &Process->VadRoot;
963
964 MmLockAddressSpace(AddressSpace);
965 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
966 if (MemoryArea == NULL)
967 {
968 Status = STATUS_UNSUCCESSFUL;
969 goto unlock_deref_and_return;
970 }
971
972 switch (FreeType)
973 {
974 case MEM_RELEASE:
975 /* We can only free a memory area in one step. */
976 if (MemoryArea->StartingAddress != BaseAddress ||
977 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
978 {
979 Status = STATUS_UNSUCCESSFUL;
980 goto unlock_deref_and_return;
981 }
982
983 MmFreeVirtualMemory(Process, MemoryArea);
984 Status = STATUS_SUCCESS;
985 goto unlock_deref_and_return;
986
987 case MEM_DECOMMIT:
988 Status =
989 MmAlterRegion(AddressSpace,
990 MemoryArea->StartingAddress,
991 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
992 BaseAddress,
993 RegionSize,
994 MEM_RESERVE,
995 PAGE_NOACCESS,
996 MmModifyAttributes);
997 goto unlock_deref_and_return;
998 }
999
1000 Status = STATUS_NOT_IMPLEMENTED;
1001
1002 unlock_deref_and_return:
1003
1004 MmUnlockAddressSpace(AddressSpace);
1005 ObDereferenceObject(Process);
1006
1007 return(Status);
1008 }
1009
1010 NTSTATUS
1011 NTAPI
1012 MmProtectAnonMem(PMM_AVL_TABLE AddressSpace,
1013 PMEMORY_AREA MemoryArea,
1014 PVOID BaseAddress,
1015 ULONG Length,
1016 ULONG Protect,
1017 PULONG OldProtect)
1018 {
1019 PMM_REGION Region;
1020 NTSTATUS Status;
1021
1022 Region = MmFindRegion(MemoryArea->StartingAddress,
1023 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1024 BaseAddress, NULL);
1025 *OldProtect = Region->Protect;
1026 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1027 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1028 BaseAddress, Length, Region->Type, Protect,
1029 MmModifyAttributes);
1030 return(Status);
1031 }
1032
1033 NTSTATUS NTAPI
1034 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
1035 PVOID Address,
1036 PMEMORY_BASIC_INFORMATION Info,
1037 PSIZE_T ResultLength)
1038 {
1039 PMM_REGION Region;
1040 PVOID RegionBase = NULL;
1041
1042 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1043
1044 Region = MmFindRegion(MemoryArea->StartingAddress,
1045 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1046 Address, &RegionBase);
1047 Info->BaseAddress = RegionBase;
1048 Info->AllocationBase = MemoryArea->StartingAddress;
1049 Info->AllocationProtect = MemoryArea->Protect;
1050 Info->RegionSize = Region->Length;
1051 Info->State = Region->Type;
1052 Info->Protect = Region->Protect;
1053 Info->Type = MEM_PRIVATE;
1054
1055 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1056 return(STATUS_SUCCESS);
1057 }
1058
1059 /* EOF */