merge 37282 from amd64-branch:
[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(PMMSUPPORT 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(PMMSUPPORT 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(MEMORY_MANAGEMENT);
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(PMMSUPPORT 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
291 if (Region->Type == MEM_RESERVE || Region->Protect == PAGE_NOACCESS)
292 {
293 return(STATUS_ACCESS_VIOLATION);
294 }
295
296 /*
297 * FIXME
298 */
299 if (Region->Protect & PAGE_GUARD)
300 {
301 return(STATUS_GUARD_PAGE_VIOLATION);
302 }
303
304 /*
305 * Get or create a page operation
306 */
307 PageOp = MmGetPageOp(MemoryArea, Process->UniqueProcessId,
308 (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
309 MM_PAGEOP_PAGEIN, FALSE);
310 if (PageOp == NULL)
311 {
312 DPRINT1("MmGetPageOp failed");
313 KeBugCheck(MEMORY_MANAGEMENT);
314 }
315
316 /*
317 * Check if someone else is already handling this fault, if so wait
318 * for them
319 */
320 if (PageOp->Thread != PsGetCurrentThread())
321 {
322 MmUnlockAddressSpace(AddressSpace);
323 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
324 0,
325 KernelMode,
326 FALSE,
327 NULL);
328 /*
329 * Check for various strange conditions
330 */
331 if (Status != STATUS_SUCCESS)
332 {
333 DPRINT1("Failed to wait for page op\n");
334 KeBugCheck(MEMORY_MANAGEMENT);
335 }
336 if (PageOp->Status == STATUS_PENDING)
337 {
338 DPRINT1("Woke for page op before completion\n");
339 KeBugCheck(MEMORY_MANAGEMENT);
340 }
341 /*
342 * If this wasn't a pagein then we need to restart the handling
343 */
344 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
345 {
346 MmLockAddressSpace(AddressSpace);
347 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
348 MmReleasePageOp(PageOp);
349 return(STATUS_MM_RESTART_OPERATION);
350 }
351 /*
352 * If the thread handling this fault has failed then we don't retry
353 */
354 if (!NT_SUCCESS(PageOp->Status))
355 {
356 MmLockAddressSpace(AddressSpace);
357 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
358 Status = PageOp->Status;
359 MmReleasePageOp(PageOp);
360 return(Status);
361 }
362 MmLockAddressSpace(AddressSpace);
363 if (Locked)
364 {
365 MmLockPage(MmGetPfnForProcess(NULL, Address));
366 }
367 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
368 MmReleasePageOp(PageOp);
369 return(STATUS_SUCCESS);
370 }
371
372 /*
373 * Try to allocate a page
374 */
375 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
376 if (Status == STATUS_NO_MEMORY)
377 {
378 MmUnlockAddressSpace(AddressSpace);
379 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
380 MmLockAddressSpace(AddressSpace);
381 }
382 if (!NT_SUCCESS(Status))
383 {
384 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
385 KeBugCheck(MEMORY_MANAGEMENT);
386 }
387
388 /*
389 * Handle swapped out pages.
390 */
391 if (MmIsPageSwapEntry(NULL, Address))
392 {
393 SWAPENTRY SwapEntry;
394
395 MmDeletePageFileMapping(Process, Address, &SwapEntry);
396 Status = MmReadFromSwapPage(SwapEntry, Page);
397 if (!NT_SUCCESS(Status))
398 {
399 KeBugCheck(MEMORY_MANAGEMENT);
400 }
401 MmSetSavedSwapEntryPage(Page, SwapEntry);
402 }
403
404 /*
405 * Set the page. If we fail because we are out of memory then
406 * try again
407 */
408 Status = MmCreateVirtualMapping(Process,
409 (PVOID)PAGE_ROUND_DOWN(Address),
410 Region->Protect,
411 &Page,
412 1);
413 while (Status == STATUS_NO_MEMORY)
414 {
415 MmUnlockAddressSpace(AddressSpace);
416 Status = MmCreateVirtualMapping(Process,
417 Address,
418 Region->Protect,
419 &Page,
420 1);
421 MmLockAddressSpace(AddressSpace);
422 }
423 if (!NT_SUCCESS(Status))
424 {
425 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
426 KeBugCheck(MEMORY_MANAGEMENT);
427 return(Status);
428 }
429
430 /*
431 * Add the page to the process's working set
432 */
433 MmInsertRmap(Page, Process, (PVOID)PAGE_ROUND_DOWN(Address));
434
435 /*
436 * Finish the operation
437 */
438 if (Locked)
439 {
440 MmLockPage(Page);
441 }
442 PageOp->Status = STATUS_SUCCESS;
443 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
444 MmReleasePageOp(PageOp);
445 return(STATUS_SUCCESS);
446 }
447
448 static VOID
449 MmModifyAttributes(PMMSUPPORT AddressSpace,
450 PVOID BaseAddress,
451 ULONG RegionSize,
452 ULONG OldType,
453 ULONG OldProtect,
454 ULONG NewType,
455 ULONG NewProtect)
456 /*
457 * FUNCTION: Modify the attributes of a memory region
458 */
459 {
460 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
461
462 /*
463 * If we are switching a previously committed region to reserved then
464 * free any allocated pages within the region
465 */
466 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
467 {
468 ULONG i;
469
470 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
471 {
472 PFN_TYPE Page;
473
474 if (MmIsPageSwapEntry(Process,
475 (char*)BaseAddress + (i * PAGE_SIZE)))
476 {
477 SWAPENTRY SwapEntry;
478
479 MmDeletePageFileMapping(Process,
480 (char*)BaseAddress + (i * PAGE_SIZE),
481 &SwapEntry);
482 MmFreeSwapPage(SwapEntry);
483 }
484 else
485 {
486 MmDeleteVirtualMapping(Process,
487 (char*)BaseAddress + (i*PAGE_SIZE),
488 FALSE, NULL, &Page);
489 if (Page != 0)
490 {
491 SWAPENTRY SavedSwapEntry;
492 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
493 if (SavedSwapEntry != 0)
494 {
495 MmFreeSwapPage(SavedSwapEntry);
496 MmSetSavedSwapEntryPage(Page, 0);
497 }
498 MmDeleteRmap(Page, Process,
499 (char*)BaseAddress + (i * PAGE_SIZE));
500 MmReleasePageMemoryConsumer(MC_USER, Page);
501 }
502 }
503 }
504 }
505
506 /*
507 * If we are changing the protection attributes of a committed region then
508 * alter the attributes for any allocated pages within the region
509 */
510 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
511 OldProtect != NewProtect)
512 {
513 ULONG i;
514
515 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
516 {
517 if (MmIsPagePresent(Process,
518 (char*)BaseAddress + (i*PAGE_SIZE)))
519 {
520 MmSetPageProtect(Process,
521 (char*)BaseAddress + (i*PAGE_SIZE),
522 NewProtect);
523 }
524 }
525 }
526 }
527
528 /*
529 * @implemented
530 */
531 NTSTATUS NTAPI
532 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
533 IN OUT PVOID* UBaseAddress,
534 IN ULONG_PTR ZeroBits,
535 IN OUT PSIZE_T URegionSize,
536 IN ULONG AllocationType,
537 IN ULONG Protect)
538 /*
539 * FUNCTION: Allocates a block of virtual memory in the process address space
540 * ARGUMENTS:
541 * ProcessHandle = The handle of the process which owns the virtual memory
542 * BaseAddress = A pointer to the virtual memory allocated. If you
543 * supply a non zero value the system will try to
544 * allocate the memory at the address supplied. It round
545 * it down to a multiple of the page size.
546 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
547 * that must be zero, ensuring that the memory will be
548 * allocated at a address below a certain value.
549 * RegionSize = The number of bytes to allocate
550 * AllocationType = Indicates the type of virtual memory you like to
551 * allocated, can be a combination of MEM_COMMIT,
552 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
553 * Protect = Indicates the protection type of the pages allocated, can be
554 * a combination of PAGE_READONLY, PAGE_READWRITE,
555 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
556 * PAGE_NOACCESS
557 * RETURNS: Status
558 */
559 {
560 PEPROCESS Process;
561 MEMORY_AREA* MemoryArea;
562 ULONG_PTR MemoryAreaLength;
563 ULONG Type;
564 NTSTATUS Status;
565 PMMSUPPORT AddressSpace;
566 PVOID BaseAddress;
567 ULONG RegionSize;
568 PVOID PBaseAddress;
569 ULONG PRegionSize;
570 PHYSICAL_ADDRESS BoundaryAddressMultiple;
571 KPROCESSOR_MODE PreviousMode;
572
573 PAGED_CODE();
574
575 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
576 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
577 *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
578 Protect);
579
580 /* Check for valid protection flags */
581 if (!Protect || Protect & ~PAGE_FLAGS_VALID_FROM_USER_MODE)
582 {
583 DPRINT1("Invalid page protection\n");
584 return STATUS_INVALID_PAGE_PROTECTION;
585 }
586
587 /* Check for valid Zero bits */
588 if (ZeroBits > 21)
589 {
590 DPRINT1("Too many zero bits\n");
591 return STATUS_INVALID_PARAMETER_3;
592 }
593
594 /* Check for valid Allocation Types */
595 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
596 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
597 {
598 DPRINT1("Invalid Allocation Type\n");
599 return STATUS_INVALID_PARAMETER_5;
600 }
601
602 /* Check for at least one of these Allocation Types to be set */
603 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
604 {
605 DPRINT1("No memory allocation base type\n");
606 return STATUS_INVALID_PARAMETER_5;
607 }
608
609 /* MEM_RESET is an exclusive flag, make sure that is valid too */
610 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
611 {
612 DPRINT1("Invalid use of MEM_RESET\n");
613 return STATUS_INVALID_PARAMETER_5;
614 }
615
616 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
617 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
618 {
619 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
620 return STATUS_INVALID_PARAMETER_5;
621 }
622
623 /* MEM_PHYSICAL can only be used with MEM_RESERVE, and can only be R/W */
624 if (AllocationType & MEM_PHYSICAL)
625 {
626 /* First check for MEM_RESERVE exclusivity */
627 if (AllocationType != (MEM_RESERVE | MEM_PHYSICAL))
628 {
629 DPRINT1("MEM_PHYSICAL used with other flags then MEM_RESERVE or"
630 "MEM_RESERVE was not present at all\n");
631 return STATUS_INVALID_PARAMETER_5;
632 }
633
634 /* Then make sure PAGE_READWRITE is used */
635 if (Protect != PAGE_READWRITE)
636 {
637 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
638 return STATUS_INVALID_PAGE_PROTECTION;
639 }
640 }
641
642 PreviousMode = KeGetPreviousMode();
643
644 _SEH2_TRY
645 {
646 if (PreviousMode != KernelMode)
647 {
648 ProbeForWritePointer(UBaseAddress);
649 ProbeForWriteUlong(URegionSize);
650 }
651 PBaseAddress = *UBaseAddress;
652 PRegionSize = *URegionSize;
653 }
654 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
655 {
656 /* Get the exception code */
657 Status = _SEH2_GetExceptionCode();
658 _SEH2_YIELD(return Status);
659 }
660 _SEH2_END;
661
662 BoundaryAddressMultiple.QuadPart = 0;
663
664 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
665 RegionSize = PAGE_ROUND_UP((ULONG_PTR)PBaseAddress + PRegionSize) -
666 PAGE_ROUND_DOWN(PBaseAddress);
667
668 /*
669 * We've captured and calculated the data, now do more checks
670 * Yes, MmCreateMemoryArea does similar checks, but they don't return
671 * the right status codes that a caller of this routine would expect.
672 */
673 if ((ULONG_PTR)BaseAddress >= USER_SHARED_DATA)
674 {
675 DPRINT1("Virtual allocation base above User Space\n");
676 return STATUS_INVALID_PARAMETER_2;
677 }
678 if (!RegionSize)
679 {
680 DPRINT1("Region size is invalid (zero)\n");
681 return STATUS_INVALID_PARAMETER_4;
682 }
683 if ((USER_SHARED_DATA - (ULONG_PTR)BaseAddress) < RegionSize)
684 {
685 DPRINT1("Region size would overflow into kernel-memory\n");
686 return STATUS_INVALID_PARAMETER_4;
687 }
688
689 /*
690 * Copy on Write is reserved for system use. This case is a certain failure
691 * but there may be other cases...needs more testing
692 */
693 if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
694 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
695 {
696 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
697 return STATUS_INVALID_PAGE_PROTECTION;
698 }
699
700
701 Status = ObReferenceObjectByHandle(ProcessHandle,
702 PROCESS_VM_OPERATION,
703 PsProcessType,
704 PreviousMode,
705 (PVOID*)(&Process),
706 NULL);
707 if (!NT_SUCCESS(Status))
708 {
709 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
710 return(Status);
711 }
712
713 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
714 DPRINT("Type %x\n", Type);
715
716 AddressSpace = &Process->Vm;
717 MmLockAddressSpace(AddressSpace);
718
719 if (PBaseAddress != 0)
720 {
721 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
722
723 if (MemoryArea != NULL)
724 {
725 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
726 (ULONG_PTR)MemoryArea->StartingAddress;
727 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
728 MemoryAreaLength >= RegionSize)
729 {
730 Status =
731 MmAlterRegion(AddressSpace,
732 MemoryArea->StartingAddress,
733 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
734 BaseAddress, RegionSize,
735 Type, Protect, MmModifyAttributes);
736 MmUnlockAddressSpace(AddressSpace);
737 ObDereferenceObject(Process);
738 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
739
740 /* Give the caller rounded BaseAddress and area length */
741 if (NT_SUCCESS(Status))
742 {
743 *UBaseAddress = BaseAddress;
744 *URegionSize = RegionSize;
745 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
746 }
747
748 return(Status);
749 }
750 else if (MemoryAreaLength >= RegionSize)
751 {
752 /* Region list initialized? */
753 if (MemoryArea->Data.SectionData.RegionListHead.Flink)
754 {
755 Status =
756 MmAlterRegion(AddressSpace,
757 MemoryArea->StartingAddress,
758 &MemoryArea->Data.SectionData.RegionListHead,
759 BaseAddress, RegionSize,
760 Type, Protect, MmModifyAttributes);
761 }
762 else
763 {
764 Status = STATUS_ACCESS_VIOLATION;
765 }
766
767 MmUnlockAddressSpace(AddressSpace);
768 ObDereferenceObject(Process);
769 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
770
771 /* Give the caller rounded BaseAddress and area length */
772 if (NT_SUCCESS(Status))
773 {
774 *UBaseAddress = BaseAddress;
775 *URegionSize = RegionSize;
776 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
777 }
778
779 return(Status);
780 }
781 else
782 {
783 MmUnlockAddressSpace(AddressSpace);
784 ObDereferenceObject(Process);
785 return(STATUS_UNSUCCESSFUL);
786 }
787 }
788 }
789
790 Status = MmCreateMemoryArea(AddressSpace,
791 MEMORY_AREA_VIRTUAL_MEMORY,
792 &BaseAddress,
793 RegionSize,
794 Protect,
795 &MemoryArea,
796 PBaseAddress != 0,
797 AllocationType & MEM_TOP_DOWN,
798 BoundaryAddressMultiple);
799 if (!NT_SUCCESS(Status))
800 {
801 MmUnlockAddressSpace(AddressSpace);
802 ObDereferenceObject(Process);
803 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
804 return(Status);
805 }
806
807 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
808 (ULONG_PTR)MemoryArea->StartingAddress;
809
810 MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
811 MemoryAreaLength, Type, Protect);
812
813 if ((AllocationType & MEM_COMMIT) &&
814 (Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
815 {
816 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
817 MmReserveSwapPages(nPages);
818 }
819
820 *UBaseAddress = BaseAddress;
821 *URegionSize = MemoryAreaLength;
822 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
823
824 MmUnlockAddressSpace(AddressSpace);
825 ObDereferenceObject(Process);
826 return(STATUS_SUCCESS);
827 }
828
829 static VOID
830 MmFreeVirtualMemoryPage(PVOID Context,
831 MEMORY_AREA* MemoryArea,
832 PVOID Address,
833 PFN_TYPE Page,
834 SWAPENTRY SwapEntry,
835 BOOLEAN Dirty)
836 {
837 PEPROCESS Process = (PEPROCESS)Context;
838
839 if (Page != 0)
840 {
841 SWAPENTRY SavedSwapEntry;
842 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
843 if (SavedSwapEntry != 0)
844 {
845 MmFreeSwapPage(SavedSwapEntry);
846 MmSetSavedSwapEntryPage(Page, 0);
847 }
848 MmDeleteRmap(Page, Process, Address);
849 MmReleasePageMemoryConsumer(MC_USER, Page);
850 }
851 else if (SwapEntry != 0)
852 {
853 MmFreeSwapPage(SwapEntry);
854 }
855 }
856
857 VOID
858 NTAPI
859 MmFreeVirtualMemory(PEPROCESS Process,
860 PMEMORY_AREA MemoryArea)
861 {
862 PLIST_ENTRY current_entry;
863 PMM_REGION current;
864 ULONG i;
865
866 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
867 MemoryArea);
868
869 /* Mark this memory area as about to be deleted. */
870 MemoryArea->DeleteInProgress = TRUE;
871
872 /*
873 * Wait for any ongoing paging operations. Notice that since we have
874 * flagged this memory area as deleted no more page ops will be added.
875 */
876 if (MemoryArea->PageOpCount > 0)
877 {
878 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
879 (ULONG_PTR)MemoryArea->StartingAddress;
880 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
881
882 for (i = 0; i < nPages && MemoryArea->PageOpCount != 0; ++i)
883 {
884 PMM_PAGEOP PageOp;
885 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
886 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
887 NULL, 0);
888 if (PageOp != NULL)
889 {
890 NTSTATUS Status;
891 MmUnlockAddressSpace(&Process->Vm);
892 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
893 0,
894 KernelMode,
895 FALSE,
896 NULL);
897 if (Status != STATUS_SUCCESS)
898 {
899 DPRINT1("Failed to wait for page op\n");
900 KeBugCheck(MEMORY_MANAGEMENT);
901 }
902 MmLockAddressSpace(&Process->Vm);
903 MmReleasePageOp(PageOp);
904 }
905 }
906 }
907
908 /* Free all the individual segments. */
909 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
910 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
911 {
912 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
913 current_entry = current_entry->Flink;
914 ExFreePool(current);
915 }
916
917 /* Actually free the memory area. */
918 MmFreeMemoryArea(&Process->Vm,
919 MemoryArea,
920 MmFreeVirtualMemoryPage,
921 (PVOID)Process);
922 }
923
924 /*
925 * @implemented
926 */
927 NTSTATUS NTAPI
928 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
929 IN PVOID* PBaseAddress,
930 IN PSIZE_T PRegionSize,
931 IN ULONG FreeType)
932 /*
933 * FUNCTION: Frees a range of virtual memory
934 * ARGUMENTS:
935 * ProcessHandle = Points to the process that allocated the virtual
936 * memory
937 * BaseAddress = Points to the memory address, rounded down to a
938 * multiple of the pagesize
939 * RegionSize = Limits the range to free, rounded up to a multiple of
940 * the paging size
941 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
942 * RETURNS: Status
943 */
944 {
945 MEMORY_AREA* MemoryArea;
946 NTSTATUS Status;
947 PEPROCESS Process;
948 PMMSUPPORT AddressSpace;
949 PVOID BaseAddress;
950 ULONG RegionSize;
951
952 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
953 "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
954 *PRegionSize,FreeType);
955
956 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
957 {
958 DPRINT1("Invalid FreeType\n");
959 return STATUS_INVALID_PARAMETER_4;
960 }
961
962 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
963 RegionSize = PAGE_ROUND_UP((ULONG_PTR)(*PBaseAddress) + (*PRegionSize)) -
964 PAGE_ROUND_DOWN((*PBaseAddress));
965
966 Status = ObReferenceObjectByHandle(ProcessHandle,
967 PROCESS_VM_OPERATION,
968 PsProcessType,
969 UserMode,
970 (PVOID*)(&Process),
971 NULL);
972 if (!NT_SUCCESS(Status))
973 {
974 return(Status);
975 }
976
977 AddressSpace = &Process->Vm;
978
979 MmLockAddressSpace(AddressSpace);
980 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
981 if (MemoryArea == NULL)
982 {
983 Status = STATUS_UNSUCCESSFUL;
984 goto unlock_deref_and_return;
985 }
986
987 switch (FreeType)
988 {
989 case MEM_RELEASE:
990 /* We can only free a memory area in one step. */
991 if (MemoryArea->StartingAddress != BaseAddress ||
992 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
993 {
994 Status = STATUS_UNSUCCESSFUL;
995 goto unlock_deref_and_return;
996 }
997
998 MmFreeVirtualMemory(Process, MemoryArea);
999 Status = STATUS_SUCCESS;
1000 goto unlock_deref_and_return;
1001
1002 case MEM_DECOMMIT:
1003 Status =
1004 MmAlterRegion(AddressSpace,
1005 MemoryArea->StartingAddress,
1006 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1007 BaseAddress,
1008 RegionSize,
1009 MEM_RESERVE,
1010 PAGE_NOACCESS,
1011 MmModifyAttributes);
1012 goto unlock_deref_and_return;
1013 }
1014
1015 Status = STATUS_NOT_IMPLEMENTED;
1016
1017 unlock_deref_and_return:
1018
1019 MmUnlockAddressSpace(AddressSpace);
1020 ObDereferenceObject(Process);
1021
1022 return(Status);
1023 }
1024
1025 NTSTATUS
1026 NTAPI
1027 MmProtectAnonMem(PMMSUPPORT AddressSpace,
1028 PMEMORY_AREA MemoryArea,
1029 PVOID BaseAddress,
1030 ULONG Length,
1031 ULONG Protect,
1032 PULONG OldProtect)
1033 {
1034 PMM_REGION Region;
1035 NTSTATUS Status;
1036
1037 Region = MmFindRegion(MemoryArea->StartingAddress,
1038 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1039 BaseAddress, NULL);
1040 if (Region->Type == MEM_COMMIT)
1041 {
1042 /* FIXME: check if the whole range is committed
1043 * before altering the memory */
1044 *OldProtect = Region->Protect;
1045 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1046 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1047 BaseAddress, Length, Region->Type, Protect,
1048 MmModifyAttributes);
1049 }
1050 else
1051 {
1052 Status = STATUS_NOT_COMMITTED;
1053 }
1054 return(Status);
1055 }
1056
1057 NTSTATUS NTAPI
1058 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
1059 PVOID Address,
1060 PMEMORY_BASIC_INFORMATION Info,
1061 PULONG ResultLength)
1062 {
1063 PMM_REGION Region;
1064 PVOID RegionBase = NULL;
1065
1066 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1067
1068 Region = MmFindRegion(MemoryArea->StartingAddress,
1069 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1070 Address, &RegionBase);
1071 Info->BaseAddress = RegionBase;
1072 Info->AllocationBase = MemoryArea->StartingAddress;
1073 Info->AllocationProtect = MemoryArea->Protect;
1074 Info->RegionSize = Region->Length;
1075 Info->State = Region->Type;
1076 Info->Protect = Region->Protect;
1077 Info->Type = MEM_PRIVATE;
1078
1079 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1080 return(STATUS_SUCCESS);
1081 }
1082
1083 /* EOF */