Sync to trunk head (r42241)
[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.
554 * RETURNS: Status
555 */
556 {
557 PEPROCESS Process;
558 MEMORY_AREA* MemoryArea;
559 ULONG_PTR MemoryAreaLength;
560 ULONG Type;
561 NTSTATUS Status;
562 PMMSUPPORT AddressSpace;
563 PVOID BaseAddress;
564 ULONG RegionSize;
565 PVOID PBaseAddress;
566 ULONG PRegionSize;
567 ULONG MemProtection;
568 PHYSICAL_ADDRESS BoundaryAddressMultiple;
569 KPROCESSOR_MODE PreviousMode;
570
571 PAGED_CODE();
572
573 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
574 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
575 *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
576 Protect);
577
578 /* Check for valid protection flags */
579 MemProtection = Protect & ~(PAGE_GUARD|PAGE_NOCACHE);
580 if (MemProtection != PAGE_NOACCESS &&
581 MemProtection != PAGE_READONLY &&
582 MemProtection != PAGE_READWRITE &&
583 MemProtection != PAGE_WRITECOPY &&
584 MemProtection != PAGE_EXECUTE &&
585 MemProtection != PAGE_EXECUTE_READ &&
586 MemProtection != PAGE_EXECUTE_READWRITE &&
587 MemProtection != PAGE_EXECUTE_WRITECOPY)
588 {
589 DPRINT1("Invalid page protection\n");
590 return STATUS_INVALID_PAGE_PROTECTION;
591 }
592
593 /* Check for valid Zero bits */
594 if (ZeroBits > 21)
595 {
596 DPRINT1("Too many zero bits\n");
597 return STATUS_INVALID_PARAMETER_3;
598 }
599
600 /* Check for valid Allocation Types */
601 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
602 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
603 {
604 DPRINT1("Invalid Allocation Type\n");
605 return STATUS_INVALID_PARAMETER_5;
606 }
607
608 /* Check for at least one of these Allocation Types to be set */
609 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
610 {
611 DPRINT1("No memory allocation base type\n");
612 return STATUS_INVALID_PARAMETER_5;
613 }
614
615 /* MEM_RESET is an exclusive flag, make sure that is valid too */
616 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
617 {
618 DPRINT1("Invalid use of MEM_RESET\n");
619 return STATUS_INVALID_PARAMETER_5;
620 }
621
622 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
623 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
624 {
625 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
626 return STATUS_INVALID_PARAMETER_5;
627 }
628
629 /* MEM_PHYSICAL can only be used with MEM_RESERVE, and can only be R/W */
630 if (AllocationType & MEM_PHYSICAL)
631 {
632 /* First check for MEM_RESERVE exclusivity */
633 if (AllocationType != (MEM_RESERVE | MEM_PHYSICAL))
634 {
635 DPRINT1("MEM_PHYSICAL used with other flags then MEM_RESERVE or"
636 "MEM_RESERVE was not present at all\n");
637 return STATUS_INVALID_PARAMETER_5;
638 }
639
640 /* Then make sure PAGE_READWRITE is used */
641 if (Protect != PAGE_READWRITE)
642 {
643 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
644 return STATUS_INVALID_PAGE_PROTECTION;
645 }
646 }
647
648 PreviousMode = KeGetPreviousMode();
649
650 _SEH2_TRY
651 {
652 if (PreviousMode != KernelMode)
653 {
654 ProbeForWritePointer(UBaseAddress);
655 ProbeForWriteUlong(URegionSize);
656 }
657 PBaseAddress = *UBaseAddress;
658 PRegionSize = *URegionSize;
659 }
660 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
661 {
662 /* Get the exception code */
663 Status = _SEH2_GetExceptionCode();
664 _SEH2_YIELD(return Status);
665 }
666 _SEH2_END;
667
668 BoundaryAddressMultiple.QuadPart = 0;
669
670 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
671 RegionSize = PAGE_ROUND_UP((ULONG_PTR)PBaseAddress + PRegionSize) -
672 PAGE_ROUND_DOWN(PBaseAddress);
673
674 /*
675 * We've captured and calculated the data, now do more checks
676 * Yes, MmCreateMemoryArea does similar checks, but they don't return
677 * the right status codes that a caller of this routine would expect.
678 */
679 if ((ULONG_PTR)BaseAddress >= USER_SHARED_DATA)
680 {
681 DPRINT1("Virtual allocation base above User Space\n");
682 return STATUS_INVALID_PARAMETER_2;
683 }
684 if (!RegionSize)
685 {
686 DPRINT1("Region size is invalid (zero)\n");
687 return STATUS_INVALID_PARAMETER_4;
688 }
689 if ((USER_SHARED_DATA - (ULONG_PTR)BaseAddress) < RegionSize)
690 {
691 DPRINT1("Region size would overflow into kernel-memory\n");
692 return STATUS_INVALID_PARAMETER_4;
693 }
694
695 /*
696 * Copy on Write is reserved for system use. This case is a certain failure
697 * but there may be other cases...needs more testing
698 */
699 if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
700 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
701 {
702 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
703 return STATUS_INVALID_PAGE_PROTECTION;
704 }
705
706
707 Status = ObReferenceObjectByHandle(ProcessHandle,
708 PROCESS_VM_OPERATION,
709 PsProcessType,
710 PreviousMode,
711 (PVOID*)(&Process),
712 NULL);
713 if (!NT_SUCCESS(Status))
714 {
715 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
716 return(Status);
717 }
718
719 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
720 DPRINT("Type %x\n", Type);
721
722 AddressSpace = &Process->Vm;
723 MmLockAddressSpace(AddressSpace);
724
725 if (PBaseAddress != 0)
726 {
727 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
728
729 if (MemoryArea != NULL)
730 {
731 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
732 (ULONG_PTR)MemoryArea->StartingAddress;
733 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
734 MemoryAreaLength >= RegionSize)
735 {
736 Status =
737 MmAlterRegion(AddressSpace,
738 MemoryArea->StartingAddress,
739 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
740 BaseAddress, RegionSize,
741 Type, Protect, MmModifyAttributes);
742 MmUnlockAddressSpace(AddressSpace);
743 ObDereferenceObject(Process);
744 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
745
746 /* Give the caller rounded BaseAddress and area length */
747 if (NT_SUCCESS(Status))
748 {
749 *UBaseAddress = BaseAddress;
750 *URegionSize = RegionSize;
751 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
752 }
753
754 return(Status);
755 }
756 else if (MemoryAreaLength >= RegionSize)
757 {
758 /* Region list initialized? */
759 if (MemoryArea->Data.SectionData.RegionListHead.Flink)
760 {
761 Status =
762 MmAlterRegion(AddressSpace,
763 MemoryArea->StartingAddress,
764 &MemoryArea->Data.SectionData.RegionListHead,
765 BaseAddress, RegionSize,
766 Type, Protect, MmModifyAttributes);
767 }
768 else
769 {
770 Status = STATUS_ACCESS_VIOLATION;
771 }
772
773 MmUnlockAddressSpace(AddressSpace);
774 ObDereferenceObject(Process);
775 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
776
777 /* Give the caller rounded BaseAddress and area length */
778 if (NT_SUCCESS(Status))
779 {
780 *UBaseAddress = BaseAddress;
781 *URegionSize = RegionSize;
782 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
783 }
784
785 return(Status);
786 }
787 else
788 {
789 MmUnlockAddressSpace(AddressSpace);
790 ObDereferenceObject(Process);
791 return(STATUS_UNSUCCESSFUL);
792 }
793 }
794 }
795
796 Status = MmCreateMemoryArea(AddressSpace,
797 MEMORY_AREA_VIRTUAL_MEMORY,
798 &BaseAddress,
799 RegionSize,
800 Protect,
801 &MemoryArea,
802 PBaseAddress != 0,
803 AllocationType & MEM_TOP_DOWN,
804 BoundaryAddressMultiple);
805 if (!NT_SUCCESS(Status))
806 {
807 MmUnlockAddressSpace(AddressSpace);
808 ObDereferenceObject(Process);
809 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
810 return(Status);
811 }
812
813 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
814 (ULONG_PTR)MemoryArea->StartingAddress;
815
816 MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
817 MemoryAreaLength, Type, Protect);
818
819 if ((AllocationType & MEM_COMMIT) &&
820 (Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
821 {
822 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
823 MmReserveSwapPages(nPages);
824 }
825
826 *UBaseAddress = BaseAddress;
827 *URegionSize = MemoryAreaLength;
828 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
829
830 MmUnlockAddressSpace(AddressSpace);
831 ObDereferenceObject(Process);
832 return(STATUS_SUCCESS);
833 }
834
835 static VOID
836 MmFreeVirtualMemoryPage(PVOID Context,
837 MEMORY_AREA* MemoryArea,
838 PVOID Address,
839 PFN_TYPE Page,
840 SWAPENTRY SwapEntry,
841 BOOLEAN Dirty)
842 {
843 PEPROCESS Process = (PEPROCESS)Context;
844
845 if (Page != 0)
846 {
847 SWAPENTRY SavedSwapEntry;
848 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
849 if (SavedSwapEntry != 0)
850 {
851 MmFreeSwapPage(SavedSwapEntry);
852 MmSetSavedSwapEntryPage(Page, 0);
853 }
854 MmDeleteRmap(Page, Process, Address);
855 MmReleasePageMemoryConsumer(MC_USER, Page);
856 }
857 else if (SwapEntry != 0)
858 {
859 MmFreeSwapPage(SwapEntry);
860 }
861 }
862
863 VOID
864 NTAPI
865 MmFreeVirtualMemory(PEPROCESS Process,
866 PMEMORY_AREA MemoryArea)
867 {
868 PLIST_ENTRY current_entry;
869 PMM_REGION current;
870 ULONG i;
871
872 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
873 MemoryArea);
874
875 /* Mark this memory area as about to be deleted. */
876 MemoryArea->DeleteInProgress = TRUE;
877
878 /*
879 * Wait for any ongoing paging operations. Notice that since we have
880 * flagged this memory area as deleted no more page ops will be added.
881 */
882 if (MemoryArea->PageOpCount > 0)
883 {
884 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
885 (ULONG_PTR)MemoryArea->StartingAddress;
886 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
887
888 for (i = 0; i < nPages && MemoryArea->PageOpCount != 0; ++i)
889 {
890 PMM_PAGEOP PageOp;
891 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
892 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
893 NULL, 0);
894 if (PageOp != NULL)
895 {
896 NTSTATUS Status;
897 MmUnlockAddressSpace(&Process->Vm);
898 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
899 0,
900 KernelMode,
901 FALSE,
902 NULL);
903 if (Status != STATUS_SUCCESS)
904 {
905 DPRINT1("Failed to wait for page op\n");
906 KeBugCheck(MEMORY_MANAGEMENT);
907 }
908 MmLockAddressSpace(&Process->Vm);
909 MmReleasePageOp(PageOp);
910 }
911 }
912 }
913
914 /* Free all the individual segments. */
915 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
916 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
917 {
918 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
919 current_entry = current_entry->Flink;
920 ExFreePool(current);
921 }
922
923 /* Actually free the memory area. */
924 MmFreeMemoryArea(&Process->Vm,
925 MemoryArea,
926 MmFreeVirtualMemoryPage,
927 (PVOID)Process);
928 }
929
930 /*
931 * @implemented
932 */
933 NTSTATUS NTAPI
934 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
935 IN PVOID* PBaseAddress,
936 IN PSIZE_T PRegionSize,
937 IN ULONG FreeType)
938 /*
939 * FUNCTION: Frees a range of virtual memory
940 * ARGUMENTS:
941 * ProcessHandle = Points to the process that allocated the virtual
942 * memory
943 * BaseAddress = Points to the memory address, rounded down to a
944 * multiple of the pagesize
945 * RegionSize = Limits the range to free, rounded up to a multiple of
946 * the paging size
947 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
948 * RETURNS: Status
949 */
950 {
951 MEMORY_AREA* MemoryArea;
952 NTSTATUS Status = STATUS_SUCCESS;
953 PEPROCESS Process;
954 PMMSUPPORT AddressSpace;
955 PVOID BaseAddress;
956 ULONG RegionSize;
957
958 PAGED_CODE();
959
960 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
961 "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
962 *PRegionSize,FreeType);
963
964 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
965 {
966 DPRINT1("Invalid FreeType\n");
967 return STATUS_INVALID_PARAMETER_4;
968 }
969
970 if(ExGetPreviousMode() != KernelMode)
971 {
972 _SEH2_TRY
973 {
974 /* Probe user pointers */
975 ProbeForWriteSize_t(PRegionSize);
976 ProbeForWritePointer(PBaseAddress);
977 }
978 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
979 {
980 /* Get exception code */
981 Status = _SEH2_GetExceptionCode();
982 }
983 _SEH2_END;
984 if (!NT_SUCCESS(Status)) return Status;
985 }
986
987 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
988 RegionSize = PAGE_ROUND_UP((ULONG_PTR)(*PBaseAddress) + (*PRegionSize)) -
989 PAGE_ROUND_DOWN((*PBaseAddress));
990
991 Status = ObReferenceObjectByHandle(ProcessHandle,
992 PROCESS_VM_OPERATION,
993 PsProcessType,
994 UserMode,
995 (PVOID*)(&Process),
996 NULL);
997 if (!NT_SUCCESS(Status))
998 {
999 return(Status);
1000 }
1001
1002 AddressSpace = &Process->Vm;
1003
1004 MmLockAddressSpace(AddressSpace);
1005 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1006 if (MemoryArea == NULL)
1007 {
1008 Status = STATUS_UNSUCCESSFUL;
1009 goto unlock_deref_and_return;
1010 }
1011
1012 switch (FreeType)
1013 {
1014 case MEM_RELEASE:
1015 /* We can only free a memory area in one step. */
1016 if (MemoryArea->StartingAddress != BaseAddress ||
1017 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
1018 {
1019 Status = STATUS_UNSUCCESSFUL;
1020 goto unlock_deref_and_return;
1021 }
1022
1023 MmFreeVirtualMemory(Process, MemoryArea);
1024 Status = STATUS_SUCCESS;
1025 goto unlock_deref_and_return;
1026
1027 case MEM_DECOMMIT:
1028 Status =
1029 MmAlterRegion(AddressSpace,
1030 MemoryArea->StartingAddress,
1031 (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) ?
1032 &MemoryArea->Data.SectionData.RegionListHead :
1033 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1034 BaseAddress,
1035 RegionSize,
1036 MEM_RESERVE,
1037 PAGE_NOACCESS,
1038 MmModifyAttributes);
1039 goto unlock_deref_and_return;
1040 }
1041
1042 Status = STATUS_NOT_IMPLEMENTED;
1043
1044 unlock_deref_and_return:
1045
1046 MmUnlockAddressSpace(AddressSpace);
1047 ObDereferenceObject(Process);
1048
1049 return(Status);
1050 }
1051
1052 NTSTATUS
1053 NTAPI
1054 MmProtectAnonMem(PMMSUPPORT AddressSpace,
1055 PMEMORY_AREA MemoryArea,
1056 PVOID BaseAddress,
1057 ULONG Length,
1058 ULONG Protect,
1059 PULONG OldProtect)
1060 {
1061 PMM_REGION Region;
1062 NTSTATUS Status = STATUS_SUCCESS;
1063 ULONG LengthCount = 0;
1064
1065 /* Search all Regions in MemoryArea up to Length */
1066 /* Every Region up to Length must be committed for success */
1067 for (;;)
1068 {
1069 Region = MmFindRegion(MemoryArea->StartingAddress,
1070 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1071 (PVOID)((ULONG_PTR)BaseAddress + (ULONG_PTR)LengthCount), NULL);
1072
1073 /* If a Region was found and it is committed */
1074 if ((Region) && (Region->Type == MEM_COMMIT))
1075 {
1076 LengthCount += Region->Length;
1077 if (Length <= LengthCount) break;
1078 continue;
1079 }
1080 /* If Region was found and it is not commited */
1081 else if (Region)
1082 {
1083 Status = STATUS_NOT_COMMITTED;
1084 break;
1085 }
1086 /* If no Region was found at all */
1087 else if (LengthCount == 0)
1088 {
1089 Status = STATUS_INVALID_ADDRESS;
1090 break;
1091 }
1092 }
1093
1094 if (NT_SUCCESS(Status))
1095 {
1096 *OldProtect = Region->Protect;
1097 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1098 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1099 BaseAddress, Length, Region->Type, Protect,
1100 MmModifyAttributes);
1101 }
1102
1103 return (Status);
1104 }
1105
1106 NTSTATUS NTAPI
1107 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
1108 PVOID Address,
1109 PMEMORY_BASIC_INFORMATION Info,
1110 PSIZE_T ResultLength)
1111 {
1112 PMM_REGION Region;
1113 PVOID RegionBase = NULL;
1114
1115 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1116
1117 Region = MmFindRegion(MemoryArea->StartingAddress,
1118 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1119 Address, &RegionBase);
1120 Info->BaseAddress = RegionBase;
1121 Info->AllocationBase = MemoryArea->StartingAddress;
1122 Info->AllocationProtect = MemoryArea->Protect;
1123 Info->RegionSize = Region->Length;
1124 Info->State = Region->Type;
1125 Info->Protect = Region->Protect;
1126 Info->Type = MEM_PRIVATE;
1127
1128 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1129 return(STATUS_SUCCESS);
1130 }
1131
1132 /* EOF */