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