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