* Sync with recent trunk (r52669).
[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 #define MODULE_INVOLVED_IN_ARM3
47 #include "ARM3/miarm.h"
48
49 /* FUNCTIONS *****************************************************************/
50
51 NTSTATUS
52 NTAPI
53 MmPageOutVirtualMemory(PMMSUPPORT AddressSpace,
54 PMEMORY_AREA MemoryArea,
55 PVOID Address,
56 PMM_PAGEOP PageOp)
57 {
58 PFN_NUMBER Page;
59 BOOLEAN WasDirty;
60 SWAPENTRY SwapEntry;
61 NTSTATUS Status;
62 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
63
64 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
65 Address, Process->UniqueProcessId);
66
67 /*
68 * Check for paging out from a deleted virtual memory area.
69 */
70 if (MemoryArea->DeleteInProgress)
71 {
72 PageOp->Status = STATUS_UNSUCCESSFUL;
73 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
74 MmReleasePageOp(PageOp);
75 return(STATUS_UNSUCCESSFUL);
76 }
77
78 /*
79 * Disable the virtual mapping.
80 */
81 MmDisableVirtualMapping(Process, Address,
82 &WasDirty, &Page);
83
84 if (Page == 0)
85 {
86 KeBugCheck(MEMORY_MANAGEMENT);
87 }
88
89 /*
90 * Paging out non-dirty data is easy.
91 */
92 if (!WasDirty)
93 {
94 MmLockAddressSpace(AddressSpace);
95 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
96 MmDeleteAllRmaps(Page, NULL, NULL);
97 if ((SwapEntry = MmGetSavedSwapEntryPage(Page)) != 0)
98 {
99 MmCreatePageFileMapping(Process, Address, SwapEntry);
100 MmSetSavedSwapEntryPage(Page, 0);
101 }
102 MmUnlockAddressSpace(AddressSpace);
103 MmReleasePageMemoryConsumer(MC_USER, Page);
104 PageOp->Status = STATUS_SUCCESS;
105 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
106 MmReleasePageOp(PageOp);
107 return(STATUS_SUCCESS);
108 }
109
110 /*
111 * If necessary, allocate an entry in the paging file for this page
112 */
113 SwapEntry = MmGetSavedSwapEntryPage(Page);
114 if (SwapEntry == 0)
115 {
116 SwapEntry = MmAllocSwapPage();
117 if (SwapEntry == 0)
118 {
119 MmShowOutOfSpaceMessagePagingFile();
120 MmEnableVirtualMapping(Process, Address);
121 PageOp->Status = STATUS_UNSUCCESSFUL;
122 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
123 MmReleasePageOp(PageOp);
124 return(STATUS_PAGEFILE_QUOTA);
125 }
126 }
127
128 /*
129 * Write the page to the pagefile
130 */
131 Status = MmWriteToSwapPage(SwapEntry, Page);
132 if (!NT_SUCCESS(Status))
133 {
134 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
135 Status);
136 MmEnableVirtualMapping(Process, Address);
137 PageOp->Status = STATUS_UNSUCCESSFUL;
138 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
139 MmReleasePageOp(PageOp);
140 return(STATUS_UNSUCCESSFUL);
141 }
142
143 /*
144 * Otherwise we have succeeded, free the page
145 */
146 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page << PAGE_SHIFT);
147 MmLockAddressSpace(AddressSpace);
148 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
149 MmCreatePageFileMapping(Process, Address, SwapEntry);
150 MmUnlockAddressSpace(AddressSpace);
151 MmDeleteAllRmaps(Page, NULL, NULL);
152 MmSetSavedSwapEntryPage(Page, 0);
153 MmReleasePageMemoryConsumer(MC_USER, Page);
154 PageOp->Status = STATUS_SUCCESS;
155 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
156 MmReleasePageOp(PageOp);
157 return(STATUS_SUCCESS);
158 }
159
160 NTSTATUS
161 NTAPI
162 MmNotPresentFaultVirtualMemory(PMMSUPPORT AddressSpace,
163 MEMORY_AREA* MemoryArea,
164 PVOID Address,
165 BOOLEAN Locked)
166 /*
167 * FUNCTION: Move data into memory to satisfy a page not present fault
168 * ARGUMENTS:
169 * AddressSpace = Address space within which the fault occurred
170 * MemoryArea = The memory area within which the fault occurred
171 * Address = The absolute address of fault
172 * RETURNS: Status
173 * NOTES: This function is called with the address space lock held.
174 */
175 {
176 PFN_NUMBER Page;
177 NTSTATUS Status;
178 PMM_REGION Region;
179 PMM_PAGEOP PageOp;
180 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
181
182 /*
183 * There is a window between taking the page fault and locking the
184 * address space when another thread could load the page so we check
185 * that.
186 */
187 if (MmIsPagePresent(NULL, Address))
188 {
189 return(STATUS_SUCCESS);
190 }
191
192 /*
193 * Check for the virtual memory area being deleted.
194 */
195 if (MemoryArea->DeleteInProgress)
196 {
197 return(STATUS_UNSUCCESSFUL);
198 }
199
200 /*
201 * Get the segment corresponding to the virtual address
202 */
203 Region = MmFindRegion(MemoryArea->StartingAddress,
204 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
205 Address, NULL);
206
207 if (Region->Type == MEM_RESERVE || Region->Protect == PAGE_NOACCESS)
208 {
209 return(STATUS_ACCESS_VIOLATION);
210 }
211
212 /*
213 * FIXME
214 */
215 if (Region->Protect & PAGE_GUARD)
216 {
217 return(STATUS_GUARD_PAGE_VIOLATION);
218 }
219
220 /*
221 * Get or create a page operation
222 */
223 PageOp = MmGetPageOp(MemoryArea, Process->UniqueProcessId,
224 (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
225 MM_PAGEOP_PAGEIN, FALSE);
226 if (PageOp == NULL)
227 {
228 DPRINT1("MmGetPageOp failed");
229 KeBugCheck(MEMORY_MANAGEMENT);
230 }
231
232 /*
233 * Check if someone else is already handling this fault, if so wait
234 * for them
235 */
236 if (PageOp->Thread != PsGetCurrentThread())
237 {
238 MmUnlockAddressSpace(AddressSpace);
239 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
240 0,
241 KernelMode,
242 FALSE,
243 NULL);
244 /*
245 * Check for various strange conditions
246 */
247 if (Status != STATUS_SUCCESS)
248 {
249 DPRINT1("Failed to wait for page op\n");
250 KeBugCheck(MEMORY_MANAGEMENT);
251 }
252 if (PageOp->Status == STATUS_PENDING)
253 {
254 DPRINT1("Woke for page op before completion\n");
255 KeBugCheck(MEMORY_MANAGEMENT);
256 }
257 /*
258 * If this wasn't a pagein then we need to restart the handling
259 */
260 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
261 {
262 MmLockAddressSpace(AddressSpace);
263 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
264 MmReleasePageOp(PageOp);
265 return(STATUS_MM_RESTART_OPERATION);
266 }
267 /*
268 * If the thread handling this fault has failed then we don't retry
269 */
270 if (!NT_SUCCESS(PageOp->Status))
271 {
272 MmLockAddressSpace(AddressSpace);
273 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
274 Status = PageOp->Status;
275 MmReleasePageOp(PageOp);
276 return(Status);
277 }
278 MmLockAddressSpace(AddressSpace);
279 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
280 MmReleasePageOp(PageOp);
281 return(STATUS_SUCCESS);
282 }
283
284 /*
285 * Try to allocate a page
286 */
287 MI_SET_USAGE(MI_USAGE_VAD);
288 MI_SET_PROCESS2(Process->ImageFileName);
289 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
290 if (Status == STATUS_NO_MEMORY)
291 {
292 MmUnlockAddressSpace(AddressSpace);
293 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
294 MmLockAddressSpace(AddressSpace);
295 }
296 if (!NT_SUCCESS(Status))
297 {
298 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
299 KeBugCheck(MEMORY_MANAGEMENT);
300 }
301
302 /*
303 * Handle swapped out pages.
304 */
305 if (MmIsPageSwapEntry(NULL, Address))
306 {
307 SWAPENTRY SwapEntry;
308
309 MmDeletePageFileMapping(Process, Address, &SwapEntry);
310 Status = MmReadFromSwapPage(SwapEntry, Page);
311 if (!NT_SUCCESS(Status))
312 {
313 KeBugCheck(MEMORY_MANAGEMENT);
314 }
315 MmSetSavedSwapEntryPage(Page, SwapEntry);
316 }
317
318 /*
319 * Set the page. If we fail because we are out of memory then
320 * try again
321 */
322 Status = MmCreateVirtualMapping(Process,
323 (PVOID)PAGE_ROUND_DOWN(Address),
324 Region->Protect,
325 &Page,
326 1);
327 while (Status == STATUS_NO_MEMORY)
328 {
329 MmUnlockAddressSpace(AddressSpace);
330 Status = MmCreateVirtualMapping(Process,
331 Address,
332 Region->Protect,
333 &Page,
334 1);
335 MmLockAddressSpace(AddressSpace);
336 }
337 if (!NT_SUCCESS(Status))
338 {
339 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
340 KeBugCheck(MEMORY_MANAGEMENT);
341 return(Status);
342 }
343
344 /*
345 * Add the page to the process's working set
346 */
347 MmInsertRmap(Page, Process, (PVOID)PAGE_ROUND_DOWN(Address));
348
349 /*
350 * Finish the operation
351 */
352 PageOp->Status = STATUS_SUCCESS;
353 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
354 MmReleasePageOp(PageOp);
355 return(STATUS_SUCCESS);
356 }
357
358 static VOID
359 MmModifyAttributes(PMMSUPPORT AddressSpace,
360 PVOID BaseAddress,
361 ULONG RegionSize,
362 ULONG OldType,
363 ULONG OldProtect,
364 ULONG NewType,
365 ULONG NewProtect)
366 /*
367 * FUNCTION: Modify the attributes of a memory region
368 */
369 {
370 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
371
372 /*
373 * If we are switching a previously committed region to reserved then
374 * free any allocated pages within the region
375 */
376 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
377 {
378 ULONG i;
379
380 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
381 {
382 PFN_NUMBER Page;
383
384 if (MmIsPageSwapEntry(Process,
385 (char*)BaseAddress + (i * PAGE_SIZE)))
386 {
387 SWAPENTRY SwapEntry;
388
389 MmDeletePageFileMapping(Process,
390 (char*)BaseAddress + (i * PAGE_SIZE),
391 &SwapEntry);
392 MmFreeSwapPage(SwapEntry);
393 }
394 else
395 {
396 MmDeleteVirtualMapping(Process,
397 (char*)BaseAddress + (i*PAGE_SIZE),
398 FALSE, NULL, &Page);
399 if (Page != 0)
400 {
401 SWAPENTRY SavedSwapEntry;
402 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
403 if (SavedSwapEntry != 0)
404 {
405 MmFreeSwapPage(SavedSwapEntry);
406 MmSetSavedSwapEntryPage(Page, 0);
407 }
408 MmDeleteRmap(Page, Process,
409 (char*)BaseAddress + (i * PAGE_SIZE));
410 MmReleasePageMemoryConsumer(MC_USER, Page);
411 }
412 }
413 }
414 }
415
416 /*
417 * If we are changing the protection attributes of a committed region then
418 * alter the attributes for any allocated pages within the region
419 */
420 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
421 OldProtect != NewProtect)
422 {
423 ULONG i;
424
425 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
426 {
427 if (MmIsPagePresent(Process,
428 (char*)BaseAddress + (i*PAGE_SIZE)))
429 {
430 MmSetPageProtect(Process,
431 (char*)BaseAddress + (i*PAGE_SIZE),
432 NewProtect);
433 }
434 }
435 }
436 }
437
438 NTSTATUS NTAPI
439 MiProtectVirtualMemory(IN PEPROCESS Process,
440 IN OUT PVOID *BaseAddress,
441 IN OUT PSIZE_T NumberOfBytesToProtect,
442 IN ULONG NewAccessProtection,
443 OUT PULONG OldAccessProtection OPTIONAL)
444 {
445 PMEMORY_AREA MemoryArea;
446 PMMSUPPORT AddressSpace;
447 ULONG OldAccessProtection_;
448 NTSTATUS Status;
449
450 *NumberOfBytesToProtect =
451 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
452 PAGE_ROUND_DOWN(*BaseAddress);
453 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
454
455 AddressSpace = &Process->Vm;
456
457 MmLockAddressSpace(AddressSpace);
458 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
459 if (MemoryArea == NULL)
460 {
461 MmUnlockAddressSpace(AddressSpace);
462 return STATUS_UNSUCCESSFUL;
463 }
464
465 if (OldAccessProtection == NULL)
466 OldAccessProtection = &OldAccessProtection_;
467
468 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
469 {
470 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
471 *NumberOfBytesToProtect, NewAccessProtection,
472 OldAccessProtection);
473 }
474 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
475 {
476 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
477 *NumberOfBytesToProtect,
478 NewAccessProtection,
479 OldAccessProtection);
480 }
481 else
482 {
483 /* FIXME: Should we return failure or success in this case? */
484 Status = STATUS_CONFLICTING_ADDRESSES;
485 }
486
487 MmUnlockAddressSpace(AddressSpace);
488
489 return Status;
490 }
491
492 /*
493 * @implemented
494 */
495 NTSTATUS
496 NTAPI
497 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
498 IN OUT PVOID* UBaseAddress,
499 IN ULONG_PTR ZeroBits,
500 IN OUT PSIZE_T URegionSize,
501 IN ULONG AllocationType,
502 IN ULONG Protect)
503 {
504 PEPROCESS Process;
505 MEMORY_AREA* MemoryArea;
506 ULONG_PTR MemoryAreaLength;
507 ULONG Type;
508 NTSTATUS Status;
509 PMMSUPPORT AddressSpace;
510 PVOID BaseAddress;
511 ULONG RegionSize;
512 PVOID PBaseAddress;
513 ULONG PRegionSize;
514 PHYSICAL_ADDRESS BoundaryAddressMultiple;
515 PEPROCESS CurrentProcess = PsGetCurrentProcess();
516 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
517 KAPC_STATE ApcState;
518 ULONG ProtectionMask;
519 BOOLEAN Attached = FALSE;
520 BoundaryAddressMultiple.QuadPart = 0;
521 PAGED_CODE();
522
523 /* Check for valid Zero bits */
524 if (ZeroBits > 21)
525 {
526 DPRINT1("Too many zero bits\n");
527 return STATUS_INVALID_PARAMETER_3;
528 }
529
530 /* Check for valid Allocation Types */
531 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
532 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
533 {
534 DPRINT1("Invalid Allocation Type\n");
535 return STATUS_INVALID_PARAMETER_5;
536 }
537
538 /* Check for at least one of these Allocation Types to be set */
539 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
540 {
541 DPRINT1("No memory allocation base type\n");
542 return STATUS_INVALID_PARAMETER_5;
543 }
544
545 /* MEM_RESET is an exclusive flag, make sure that is valid too */
546 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
547 {
548 DPRINT1("Invalid use of MEM_RESET\n");
549 return STATUS_INVALID_PARAMETER_5;
550 }
551
552 /* Check if large pages are being used */
553 if (AllocationType & MEM_LARGE_PAGES)
554 {
555 /* Large page allocations MUST be committed */
556 if (!(AllocationType & MEM_COMMIT))
557 {
558 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
559 return STATUS_INVALID_PARAMETER_5;
560 }
561
562 /* These flags are not allowed with large page allocations */
563 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
564 {
565 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
566 return STATUS_INVALID_PARAMETER_5;
567 }
568 }
569
570 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
571 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
572 {
573 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
574 return STATUS_INVALID_PARAMETER_5;
575 }
576
577 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
578 if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
579 {
580 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
581 return STATUS_INVALID_PARAMETER_5;
582 }
583
584 /* Check for valid MEM_PHYSICAL usage */
585 if (AllocationType & MEM_PHYSICAL)
586 {
587 /* Only these flags are allowed with MEM_PHYSIAL */
588 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
589 {
590 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
591 return STATUS_INVALID_PARAMETER_5;
592 }
593
594 /* Then make sure PAGE_READWRITE is used */
595 if (Protect != PAGE_READWRITE)
596 {
597 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
598 return STATUS_INVALID_PARAMETER_6;
599 }
600 }
601
602 /* Calculate the protection mask and make sure it's valid */
603 ProtectionMask = MiMakeProtectionMask(Protect);
604 if (ProtectionMask == MM_INVALID_PROTECTION)
605 {
606 DPRINT1("Invalid protection mask\n");
607 return STATUS_INVALID_PAGE_PROTECTION;
608 }
609
610 /* Enter SEH */
611 _SEH2_TRY
612 {
613 /* Check for user-mode parameters */
614 if (PreviousMode != KernelMode)
615 {
616 /* Make sure they are writable */
617 ProbeForWritePointer(UBaseAddress);
618 ProbeForWriteUlong(URegionSize);
619 }
620
621 /* Capture their values */
622 PBaseAddress = *UBaseAddress;
623 PRegionSize = *URegionSize;
624 }
625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
626 {
627 /* Return the exception code */
628 _SEH2_YIELD(return _SEH2_GetExceptionCode());
629 }
630 _SEH2_END;
631
632 /* Make sure the allocation isn't past the VAD area */
633 if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
634 {
635 DPRINT1("Virtual allocation base above User Space\n");
636 return STATUS_INVALID_PARAMETER_2;
637 }
638
639 /* Make sure the allocation wouldn't overflow past the VAD area */
640 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
641 {
642 DPRINT1("Region size would overflow into kernel-memory\n");
643 return STATUS_INVALID_PARAMETER_4;
644 }
645
646 /* Make sure there's a size specified */
647 if (!PRegionSize)
648 {
649 DPRINT1("Region size is invalid (zero)\n");
650 return STATUS_INVALID_PARAMETER_4;
651 }
652
653 /* Check if this is for the current process */
654 if (ProcessHandle == NtCurrentProcess())
655 {
656 /* We already have the current process, no need to go through Ob */
657 Process = CurrentProcess;
658 }
659 else
660 {
661 /* Reference the handle for correct permissions */
662 Status = ObReferenceObjectByHandle(ProcessHandle,
663 PROCESS_VM_OPERATION,
664 PsProcessType,
665 PreviousMode,
666 (PVOID*)&Process,
667 NULL);
668 if (!NT_SUCCESS(Status)) return Status;
669
670 /* Check if not running in the current process */
671 if (CurrentProcess != Process)
672 {
673 /* Attach to it */
674 KeStackAttachProcess(&Process->Pcb, &ApcState);
675 Attached = TRUE;
676 }
677 }
678
679 /* Check for large page allocations */
680 if (AllocationType & MEM_LARGE_PAGES)
681 {
682 /* The lock memory privilege is required */
683 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
684 {
685 /* Fail without it */
686 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
687 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
688 return STATUS_PRIVILEGE_NOT_HELD;
689 }
690 }
691
692 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
693 RegionSize = PAGE_ROUND_UP((ULONG_PTR)PBaseAddress + PRegionSize) -
694 PAGE_ROUND_DOWN(PBaseAddress);
695
696
697 /*
698 * Copy on Write is reserved for system use. This case is a certain failure
699 * but there may be other cases...needs more testing
700 */
701 if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
702 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
703 {
704 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
705 return STATUS_INVALID_PAGE_PROTECTION;
706 }
707
708 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
709 DPRINT("Type %x\n", Type);
710
711 AddressSpace = &Process->Vm;
712 MmLockAddressSpace(AddressSpace);
713
714 if (PBaseAddress != 0)
715 {
716 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
717
718 if (MemoryArea != NULL)
719 {
720 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
721 (ULONG_PTR)MemoryArea->StartingAddress;
722
723 if (((ULONG_PTR)BaseAddress + RegionSize) > (ULONG_PTR)MemoryArea->EndingAddress)
724 {
725 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
726 (ULONG_PTR)BaseAddress + RegionSize, MemoryArea->EndingAddress);
727
728 MmUnlockAddressSpace(AddressSpace);
729 if (Attached) KeUnstackDetachProcess(&ApcState);
730 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
731 return STATUS_MEMORY_NOT_ALLOCATED;
732 }
733
734 if (AllocationType == MEM_RESET)
735 {
736 if (MmIsPagePresent(Process, BaseAddress))
737 {
738 /* FIXME: mark pages as not modified */
739 }
740 else
741 {
742 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
743 }
744
745 MmUnlockAddressSpace(AddressSpace);
746 if (Attached) KeUnstackDetachProcess(&ApcState);
747 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
748
749 /* MEM_RESET does not modify any attributes of region */
750 return STATUS_SUCCESS;
751 }
752
753 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
754 MemoryAreaLength >= RegionSize)
755 {
756 Status =
757 MmAlterRegion(AddressSpace,
758 MemoryArea->StartingAddress,
759 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
760 BaseAddress, RegionSize,
761 Type, Protect, MmModifyAttributes);
762 MmUnlockAddressSpace(AddressSpace);
763 if (Attached) KeUnstackDetachProcess(&ApcState);
764 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
765 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
766
767 /* Give the caller rounded BaseAddress and area length */
768 if (NT_SUCCESS(Status))
769 {
770 *UBaseAddress = BaseAddress;
771 *URegionSize = RegionSize;
772 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
773 }
774
775 return(Status);
776 }
777 else if (MemoryAreaLength >= RegionSize)
778 {
779 /* Region list initialized? */
780 if (MemoryArea->Data.SectionData.RegionListHead.Flink)
781 {
782 Status =
783 MmAlterRegion(AddressSpace,
784 MemoryArea->StartingAddress,
785 &MemoryArea->Data.SectionData.RegionListHead,
786 BaseAddress, RegionSize,
787 Type, Protect, MmModifyAttributes);
788 }
789 else
790 {
791 Status = STATUS_ACCESS_VIOLATION;
792 }
793
794 MmUnlockAddressSpace(AddressSpace);
795 if (Attached) KeUnstackDetachProcess(&ApcState);
796 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
797 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
798
799 /* Give the caller rounded BaseAddress and area length */
800 if (NT_SUCCESS(Status))
801 {
802 *UBaseAddress = BaseAddress;
803 *URegionSize = RegionSize;
804 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
805 }
806
807 return(Status);
808 }
809 else
810 {
811 MmUnlockAddressSpace(AddressSpace);
812 if (Attached) KeUnstackDetachProcess(&ApcState);
813 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
814 return(STATUS_UNSUCCESSFUL);
815 }
816 }
817 }
818
819 Status = MmCreateMemoryArea(AddressSpace,
820 MEMORY_AREA_VIRTUAL_MEMORY,
821 &BaseAddress,
822 RegionSize,
823 Protect,
824 &MemoryArea,
825 PBaseAddress != 0,
826 AllocationType & MEM_TOP_DOWN,
827 BoundaryAddressMultiple);
828 if (!NT_SUCCESS(Status))
829 {
830 MmUnlockAddressSpace(AddressSpace);
831 if (Attached) KeUnstackDetachProcess(&ApcState);
832 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
833 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
834 return(Status);
835 }
836
837 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
838 (ULONG_PTR)MemoryArea->StartingAddress;
839
840 MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
841 MemoryAreaLength, Type, Protect);
842
843 if ((AllocationType & MEM_COMMIT) &&
844 (Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
845 {
846 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
847 MmReserveSwapPages(nPages);
848 }
849
850 MmUnlockAddressSpace(AddressSpace);
851 if (Attached) KeUnstackDetachProcess(&ApcState);
852 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
853
854 *UBaseAddress = BaseAddress;
855 *URegionSize = MemoryAreaLength;
856 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
857
858 return(STATUS_SUCCESS);
859 }
860
861 static VOID
862 MmFreeVirtualMemoryPage(PVOID Context,
863 MEMORY_AREA* MemoryArea,
864 PVOID Address,
865 PFN_NUMBER Page,
866 SWAPENTRY SwapEntry,
867 BOOLEAN Dirty)
868 {
869 PEPROCESS Process = (PEPROCESS)Context;
870
871 if (Page != 0)
872 {
873 SWAPENTRY SavedSwapEntry;
874 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
875 if (SavedSwapEntry != 0)
876 {
877 MmFreeSwapPage(SavedSwapEntry);
878 MmSetSavedSwapEntryPage(Page, 0);
879 }
880 MmDeleteRmap(Page, Process, Address);
881 MmReleasePageMemoryConsumer(MC_USER, Page);
882 }
883 else if (SwapEntry != 0)
884 {
885 MmFreeSwapPage(SwapEntry);
886 }
887 }
888
889 VOID
890 NTAPI
891 MmFreeVirtualMemory(PEPROCESS Process,
892 PMEMORY_AREA MemoryArea)
893 {
894 PLIST_ENTRY current_entry;
895 PMM_REGION current;
896 ULONG i;
897
898 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
899 MemoryArea);
900
901 /* Mark this memory area as about to be deleted. */
902 MemoryArea->DeleteInProgress = TRUE;
903
904 /*
905 * Wait for any ongoing paging operations. Notice that since we have
906 * flagged this memory area as deleted no more page ops will be added.
907 */
908 if (MemoryArea->PageOpCount > 0)
909 {
910 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
911 (ULONG_PTR)MemoryArea->StartingAddress;
912 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
913
914 for (i = 0; i < nPages && MemoryArea->PageOpCount != 0; ++i)
915 {
916 PMM_PAGEOP PageOp;
917 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
918 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
919 NULL, 0);
920 if (PageOp != NULL)
921 {
922 NTSTATUS Status;
923 MmUnlockAddressSpace(&Process->Vm);
924 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
925 0,
926 KernelMode,
927 FALSE,
928 NULL);
929 if (Status != STATUS_SUCCESS)
930 {
931 DPRINT1("Failed to wait for page op\n");
932 KeBugCheck(MEMORY_MANAGEMENT);
933 }
934 MmLockAddressSpace(&Process->Vm);
935 MmReleasePageOp(PageOp);
936 }
937 }
938 }
939
940 /* Free all the individual segments. */
941 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
942 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
943 {
944 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
945 current_entry = current_entry->Flink;
946 ExFreePool(current);
947 }
948
949 /* Actually free the memory area. */
950 MmFreeMemoryArea(&Process->Vm,
951 MemoryArea,
952 MmFreeVirtualMemoryPage,
953 (PVOID)Process);
954 }
955
956 /*
957 * @implemented
958 */
959 NTSTATUS NTAPI
960 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
961 IN PVOID* UBaseAddress,
962 IN PSIZE_T URegionSize,
963 IN ULONG FreeType)
964 /*
965 * FUNCTION: Frees a range of virtual memory
966 * ARGUMENTS:
967 * ProcessHandle = Points to the process that allocated the virtual
968 * memory
969 * BaseAddress = Points to the memory address, rounded down to a
970 * multiple of the pagesize
971 * RegionSize = Limits the range to free, rounded up to a multiple of
972 * the paging size
973 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
974 * RETURNS: Status
975 */
976 {
977 MEMORY_AREA* MemoryArea;
978 NTSTATUS Status;
979 PEPROCESS Process;
980 PMMSUPPORT AddressSpace;
981 PVOID BaseAddress, PBaseAddress;
982 ULONG RegionSize, PRegionSize;
983 PEPROCESS CurrentProcess = PsGetCurrentProcess();
984 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
985 KAPC_STATE ApcState;
986 BOOLEAN Attached = FALSE;
987 PAGED_CODE();
988
989 /* Only two flags are supported */
990 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
991 {
992 DPRINT1("Invalid FreeType\n");
993 return STATUS_INVALID_PARAMETER_4;
994 }
995
996 /* Check if no flag was used, or if both flags were used */
997 if (!((FreeType & (MEM_DECOMMIT | MEM_RELEASE))) ||
998 ((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == (MEM_DECOMMIT | MEM_RELEASE)))
999 {
1000 DPRINT1("Invalid FreeType combination\n");
1001 return STATUS_INVALID_PARAMETER_4;
1002 }
1003
1004 /* Enter SEH */
1005 _SEH2_TRY
1006 {
1007 /* Check for user-mode parameters */
1008 if (PreviousMode != KernelMode)
1009 {
1010 /* Make sure they are writable */
1011 ProbeForWritePointer(UBaseAddress);
1012 ProbeForWriteUlong(URegionSize);
1013 }
1014
1015 /* Capture their values */
1016 PBaseAddress = *UBaseAddress;
1017 PRegionSize = *URegionSize;
1018 }
1019 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1020 {
1021 /* Return the exception code */
1022 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1023 }
1024 _SEH2_END;
1025
1026 /* Make sure the allocation isn't past the user area */
1027 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
1028 {
1029 DPRINT1("Virtual free base above User Space\n");
1030 return STATUS_INVALID_PARAMETER_2;
1031 }
1032
1033 /* Make sure the allocation wouldn't overflow past the user area */
1034 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
1035 {
1036 DPRINT1("Region size would overflow into kernel-memory\n");
1037 return STATUS_INVALID_PARAMETER_3;
1038 }
1039
1040 /* Check if this is for the current process */
1041 if (ProcessHandle == NtCurrentProcess())
1042 {
1043 /* We already have the current process, no need to go through Ob */
1044 Process = CurrentProcess;
1045 }
1046 else
1047 {
1048 /* Reference the handle for correct permissions */
1049 Status = ObReferenceObjectByHandle(ProcessHandle,
1050 PROCESS_VM_OPERATION,
1051 PsProcessType,
1052 PreviousMode,
1053 (PVOID*)&Process,
1054 NULL);
1055 if (!NT_SUCCESS(Status)) return Status;
1056
1057 /* Check if not running in the current process */
1058 if (CurrentProcess != Process)
1059 {
1060 /* Attach to it */
1061 KeStackAttachProcess(&Process->Pcb, &ApcState);
1062 Attached = TRUE;
1063 }
1064 }
1065
1066 BaseAddress = (PVOID)PAGE_ROUND_DOWN((PBaseAddress));
1067 RegionSize = PAGE_ROUND_UP((ULONG_PTR)(PBaseAddress) + (PRegionSize)) -
1068 PAGE_ROUND_DOWN((PBaseAddress));
1069
1070 AddressSpace = &Process->Vm;
1071
1072 MmLockAddressSpace(AddressSpace);
1073 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1074 if (MemoryArea == NULL)
1075 {
1076 Status = STATUS_UNSUCCESSFUL;
1077 goto unlock_deref_and_return;
1078 }
1079
1080 switch (FreeType)
1081 {
1082 case MEM_RELEASE:
1083 /* We can only free a memory area in one step. */
1084 if (MemoryArea->StartingAddress != BaseAddress ||
1085 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
1086 {
1087 Status = STATUS_UNSUCCESSFUL;
1088 goto unlock_deref_and_return;
1089 }
1090
1091 MmFreeVirtualMemory(Process, MemoryArea);
1092 Status = STATUS_SUCCESS;
1093 goto unlock_deref_and_return;
1094
1095 case MEM_DECOMMIT:
1096 Status =
1097 MmAlterRegion(AddressSpace,
1098 MemoryArea->StartingAddress,
1099 (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) ?
1100 &MemoryArea->Data.SectionData.RegionListHead :
1101 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1102 BaseAddress,
1103 RegionSize,
1104 MEM_RESERVE,
1105 PAGE_NOACCESS,
1106 MmModifyAttributes);
1107 goto unlock_deref_and_return;
1108 }
1109
1110 Status = STATUS_NOT_IMPLEMENTED;
1111
1112 unlock_deref_and_return:
1113
1114 MmUnlockAddressSpace(AddressSpace);
1115 if (Attached) KeUnstackDetachProcess(&ApcState);
1116 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
1117
1118 return(Status);
1119 }
1120
1121 NTSTATUS
1122 NTAPI
1123 MmProtectAnonMem(PMMSUPPORT AddressSpace,
1124 PMEMORY_AREA MemoryArea,
1125 PVOID BaseAddress,
1126 ULONG Length,
1127 ULONG Protect,
1128 PULONG OldProtect)
1129 {
1130 PMM_REGION Region;
1131 NTSTATUS Status = STATUS_SUCCESS;
1132 ULONG LengthCount = 0;
1133
1134 /* Search all Regions in MemoryArea up to Length */
1135 /* Every Region up to Length must be committed for success */
1136 for (;;)
1137 {
1138 Region = MmFindRegion(MemoryArea->StartingAddress,
1139 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1140 (PVOID)((ULONG_PTR)BaseAddress + (ULONG_PTR)LengthCount), NULL);
1141
1142 /* If a Region was found and it is committed */
1143 if ((Region) && (Region->Type == MEM_COMMIT))
1144 {
1145 LengthCount += Region->Length;
1146 if (Length <= LengthCount) break;
1147 continue;
1148 }
1149 /* If Region was found and it is not commited */
1150 else if (Region)
1151 {
1152 Status = STATUS_NOT_COMMITTED;
1153 break;
1154 }
1155 /* If no Region was found at all */
1156 else if (LengthCount == 0)
1157 {
1158 Status = STATUS_INVALID_ADDRESS;
1159 break;
1160 }
1161 }
1162
1163 if (NT_SUCCESS(Status))
1164 {
1165 *OldProtect = Region->Protect;
1166 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1167 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1168 BaseAddress, Length, Region->Type, Protect,
1169 MmModifyAttributes);
1170 }
1171
1172 return (Status);
1173 }
1174
1175 NTSTATUS NTAPI
1176 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
1177 PVOID Address,
1178 PMEMORY_BASIC_INFORMATION Info,
1179 PSIZE_T ResultLength)
1180 {
1181 PMM_REGION Region;
1182 PVOID RegionBase = NULL;
1183
1184 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1185
1186 Region = MmFindRegion(MemoryArea->StartingAddress,
1187 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1188 Address, &RegionBase);
1189 Info->BaseAddress = RegionBase;
1190 Info->AllocationBase = MemoryArea->StartingAddress;
1191 Info->AllocationProtect = MemoryArea->Protect;
1192 Info->RegionSize = Region->Length;
1193 Info->State = Region->Type;
1194 Info->Protect = Region->Protect;
1195 Info->Type = MEM_PRIVATE;
1196
1197 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1198 return(STATUS_SUCCESS);
1199 }
1200
1201 /* EOF */