sync with trunk head (34904)
[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 <internal/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 KEBUGCHECK(0);
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 KEBUGCHECK(0);
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 KEBUGCHECK(0);
326 }
327 if (PageOp->Status == STATUS_PENDING)
328 {
329 DPRINT1("Woke for page op before completion\n");
330 KEBUGCHECK(0);
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 KEBUGCHECK(0);
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 KEBUGCHECK(0);
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 KEBUGCHECK(0);
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 VOID static
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 STDCALL
523 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
524 IN OUT PVOID* UBaseAddress,
525 IN ULONG 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 _SEH_TRY
636 {
637 if (PreviousMode != KernelMode)
638 {
639 ProbeForWritePointer(UBaseAddress);
640 ProbeForWriteUlong(URegionSize);
641 }
642 PBaseAddress = *UBaseAddress;
643 PRegionSize = *URegionSize;
644 }
645 _SEH_HANDLE
646 {
647 /* Get the exception code */
648 Status = _SEH_GetExceptionCode();
649 _SEH_YIELD(return Status);
650 }
651 _SEH_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 return(Status);
731 }
732 else if (MemoryAreaLength >= RegionSize)
733 {
734 /* Region list initialized? */
735 if (MemoryArea->Data.SectionData.RegionListHead.Flink)
736 {
737 Status =
738 MmAlterRegion(AddressSpace,
739 MemoryArea->StartingAddress,
740 &MemoryArea->Data.SectionData.RegionListHead,
741 BaseAddress, RegionSize,
742 Type, Protect, MmModifyAttributes);
743 }
744 else
745 {
746 Status = STATUS_ACCESS_VIOLATION;
747 }
748
749 MmUnlockAddressSpace(AddressSpace);
750 ObDereferenceObject(Process);
751 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
752 return(Status);
753 }
754 else
755 {
756 MmUnlockAddressSpace(AddressSpace);
757 ObDereferenceObject(Process);
758 return(STATUS_UNSUCCESSFUL);
759 }
760 }
761 }
762
763 Status = MmCreateMemoryArea(AddressSpace,
764 MEMORY_AREA_VIRTUAL_MEMORY,
765 &BaseAddress,
766 RegionSize,
767 Protect,
768 &MemoryArea,
769 PBaseAddress != 0,
770 AllocationType & MEM_TOP_DOWN,
771 BoundaryAddressMultiple);
772 if (!NT_SUCCESS(Status))
773 {
774 MmUnlockAddressSpace(AddressSpace);
775 ObDereferenceObject(Process);
776 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
777 return(Status);
778 }
779
780 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
781 (ULONG_PTR)MemoryArea->StartingAddress;
782
783 MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
784 MemoryAreaLength, Type, Protect);
785
786 if ((AllocationType & MEM_COMMIT) &&
787 (Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
788 {
789 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
790 MmReserveSwapPages(nPages);
791 }
792
793 *UBaseAddress = BaseAddress;
794 *URegionSize = MemoryAreaLength;
795 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
796
797 MmUnlockAddressSpace(AddressSpace);
798 ObDereferenceObject(Process);
799 return(STATUS_SUCCESS);
800 }
801
802 VOID static
803 MmFreeVirtualMemoryPage(PVOID Context,
804 MEMORY_AREA* MemoryArea,
805 PVOID Address,
806 PFN_TYPE Page,
807 SWAPENTRY SwapEntry,
808 BOOLEAN Dirty)
809 {
810 PEPROCESS Process = (PEPROCESS)Context;
811
812 if (Page != 0)
813 {
814 SWAPENTRY SavedSwapEntry;
815 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
816 if (SavedSwapEntry != 0)
817 {
818 MmFreeSwapPage(SavedSwapEntry);
819 MmSetSavedSwapEntryPage(Page, 0);
820 }
821 MmDeleteRmap(Page, Process, Address);
822 MmReleasePageMemoryConsumer(MC_USER, Page);
823 }
824 else if (SwapEntry != 0)
825 {
826 MmFreeSwapPage(SwapEntry);
827 }
828 }
829
830 VOID
831 NTAPI
832 MmFreeVirtualMemory(PEPROCESS Process,
833 PMEMORY_AREA MemoryArea)
834 {
835 PLIST_ENTRY current_entry;
836 PMM_REGION current;
837 ULONG i;
838
839 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
840 MemoryArea);
841
842 /* Mark this memory area as about to be deleted. */
843 MemoryArea->DeleteInProgress = TRUE;
844
845 /*
846 * Wait for any ongoing paging operations. Notice that since we have
847 * flagged this memory area as deleted no more page ops will be added.
848 */
849 if (MemoryArea->PageOpCount > 0)
850 {
851 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
852 (ULONG_PTR)MemoryArea->StartingAddress;
853 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
854
855 for (i = 0; i < nPages && MemoryArea->PageOpCount != 0; ++i)
856 {
857 PMM_PAGEOP PageOp;
858 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
859 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
860 NULL, 0);
861 if (PageOp != NULL)
862 {
863 NTSTATUS Status;
864 MmUnlockAddressSpace(&Process->VadRoot);
865 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
866 0,
867 KernelMode,
868 FALSE,
869 NULL);
870 if (Status != STATUS_SUCCESS)
871 {
872 DPRINT1("Failed to wait for page op\n");
873 KEBUGCHECK(0);
874 }
875 MmLockAddressSpace(&Process->VadRoot);
876 MmReleasePageOp(PageOp);
877 }
878 }
879 }
880
881 /* Free all the individual segments. */
882 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
883 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
884 {
885 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
886 current_entry = current_entry->Flink;
887 ExFreePool(current);
888 }
889
890 /* Actually free the memory area. */
891 MmFreeMemoryArea(&Process->VadRoot,
892 MemoryArea,
893 MmFreeVirtualMemoryPage,
894 (PVOID)Process);
895 }
896
897 /*
898 * @implemented
899 */
900 NTSTATUS STDCALL
901 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
902 IN PVOID* PBaseAddress,
903 IN PSIZE_T PRegionSize,
904 IN ULONG FreeType)
905 /*
906 * FUNCTION: Frees a range of virtual memory
907 * ARGUMENTS:
908 * ProcessHandle = Points to the process that allocated the virtual
909 * memory
910 * BaseAddress = Points to the memory address, rounded down to a
911 * multiple of the pagesize
912 * RegionSize = Limits the range to free, rounded up to a multiple of
913 * the paging size
914 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
915 * RETURNS: Status
916 */
917 {
918 MEMORY_AREA* MemoryArea;
919 NTSTATUS Status;
920 PEPROCESS Process;
921 PMM_AVL_TABLE AddressSpace;
922 PVOID BaseAddress;
923 ULONG RegionSize;
924
925 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
926 "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
927 *PRegionSize,FreeType);
928
929 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
930 RegionSize = PAGE_ROUND_UP((ULONG_PTR)(*PBaseAddress) + (*PRegionSize)) -
931 PAGE_ROUND_DOWN((*PBaseAddress));
932
933 Status = ObReferenceObjectByHandle(ProcessHandle,
934 PROCESS_VM_OPERATION,
935 PsProcessType,
936 UserMode,
937 (PVOID*)(&Process),
938 NULL);
939 if (!NT_SUCCESS(Status))
940 {
941 return(Status);
942 }
943
944 AddressSpace = &Process->VadRoot;
945
946 MmLockAddressSpace(AddressSpace);
947 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
948 if (MemoryArea == NULL)
949 {
950 Status = STATUS_UNSUCCESSFUL;
951 goto unlock_deref_and_return;
952 }
953
954 switch (FreeType)
955 {
956 case MEM_RELEASE:
957 /* We can only free a memory area in one step. */
958 if (MemoryArea->StartingAddress != BaseAddress ||
959 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
960 {
961 Status = STATUS_UNSUCCESSFUL;
962 goto unlock_deref_and_return;
963 }
964
965 MmFreeVirtualMemory(Process, MemoryArea);
966 Status = STATUS_SUCCESS;
967 goto unlock_deref_and_return;
968
969 case MEM_DECOMMIT:
970 Status =
971 MmAlterRegion(AddressSpace,
972 MemoryArea->StartingAddress,
973 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
974 BaseAddress,
975 RegionSize,
976 MEM_RESERVE,
977 PAGE_NOACCESS,
978 MmModifyAttributes);
979 goto unlock_deref_and_return;
980 }
981
982 Status = STATUS_NOT_IMPLEMENTED;
983
984 unlock_deref_and_return:
985
986 MmUnlockAddressSpace(AddressSpace);
987 ObDereferenceObject(Process);
988
989 return(Status);
990 }
991
992 NTSTATUS
993 NTAPI
994 MmProtectAnonMem(PMM_AVL_TABLE AddressSpace,
995 PMEMORY_AREA MemoryArea,
996 PVOID BaseAddress,
997 ULONG Length,
998 ULONG Protect,
999 PULONG OldProtect)
1000 {
1001 PMM_REGION Region;
1002 NTSTATUS Status;
1003
1004 Region = MmFindRegion(MemoryArea->StartingAddress,
1005 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1006 BaseAddress, NULL);
1007 *OldProtect = Region->Protect;
1008 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1009 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1010 BaseAddress, Length, Region->Type, Protect,
1011 MmModifyAttributes);
1012 return(Status);
1013 }
1014
1015 NTSTATUS STDCALL
1016 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
1017 PVOID Address,
1018 PMEMORY_BASIC_INFORMATION Info,
1019 PULONG ResultLength)
1020 {
1021 PMM_REGION Region;
1022 PVOID RegionBase = NULL;
1023
1024 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1025
1026 Region = MmFindRegion(MemoryArea->StartingAddress,
1027 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1028 Address, &RegionBase);
1029 Info->BaseAddress = RegionBase;
1030 Info->AllocationBase = MemoryArea->StartingAddress;
1031 Info->AllocationProtect = MemoryArea->Protect;
1032 Info->RegionSize = Region->Length;
1033 Info->State = Region->Type;
1034 Info->Protect = Region->Protect;
1035 Info->Type = MEM_PRIVATE;
1036
1037 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1038 return(STATUS_SUCCESS);
1039 }
1040
1041 /* EOF */