[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / mm / anonmem.c
1 /*
2 * Copyright (C) 2002-2005 ReactOS Team (and the authors from the programmers section)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/anonmem.c
21 * PURPOSE: Implementing anonymous memory.
22 *
23 * PROGRAMMERS: David Welch
24 * Casper Hornstrup
25 * KJK::Hyperion
26 * Ge van Geldorp
27 * Eric Kohl
28 * Royce Mitchell III
29 * Aleksey Bragin
30 * Jason Filby
31 * Art Yerkes
32 * Gunnar Andre' Dalsnes
33 * Filip Navara
34 * Thomas Weidenmueller
35 * Alex Ionescu
36 * Trevor McCort
37 * Steven Edwards
38 */
39
40 /* INCLUDE *****************************************************************/
41
42 #include <ntoskrnl.h>
43 #define NDEBUG
44 #include <debug.h>
45
46 #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 /*
166 * FUNCTION: Move data into memory to satisfy a page not present fault
167 * ARGUMENTS:
168 * AddressSpace = Address space within which the fault occurred
169 * MemoryArea = The memory area within which the fault occurred
170 * Address = The absolute address of fault
171 * RETURNS: Status
172 * NOTES: This function is called with the address space lock held.
173 */
174 {
175 PFN_NUMBER Page;
176 NTSTATUS Status;
177 PMM_REGION Region;
178 PMM_PAGEOP PageOp;
179 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
180
181 /*
182 * There is a window between taking the page fault and locking the
183 * address space when another thread could load the page so we check
184 * that.
185 */
186 if (MmIsPagePresent(Process, Address))
187 {
188 return(STATUS_SUCCESS);
189 }
190
191 /*
192 * Check for the virtual memory area being deleted.
193 */
194 if (MemoryArea->DeleteInProgress)
195 {
196 return(STATUS_UNSUCCESSFUL);
197 }
198
199 /*
200 * Get the segment corresponding to the virtual address
201 */
202 Region = MmFindRegion(MemoryArea->StartingAddress,
203 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
204 Address, NULL);
205
206 if (Region->Type == MEM_RESERVE || Region->Protect == PAGE_NOACCESS)
207 {
208 return(STATUS_ACCESS_VIOLATION);
209 }
210
211 /*
212 * FIXME
213 */
214 if (Region->Protect & PAGE_GUARD)
215 {
216 return(STATUS_GUARD_PAGE_VIOLATION);
217 }
218
219 /*
220 * Get or create a page operation
221 */
222 PageOp = MmGetPageOp(MemoryArea, Process->UniqueProcessId,
223 (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
224 MM_PAGEOP_PAGEIN, FALSE);
225 if (PageOp == NULL)
226 {
227 DPRINT1("MmGetPageOp failed");
228 KeBugCheck(MEMORY_MANAGEMENT);
229 }
230
231 /*
232 * Check if someone else is already handling this fault, if so wait
233 * for them
234 */
235 if (PageOp->Thread != PsGetCurrentThread())
236 {
237 MmUnlockAddressSpace(AddressSpace);
238 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
239 0,
240 KernelMode,
241 FALSE,
242 NULL);
243 /*
244 * Check for various strange conditions
245 */
246 if (Status != STATUS_SUCCESS)
247 {
248 DPRINT1("Failed to wait for page op\n");
249 KeBugCheck(MEMORY_MANAGEMENT);
250 }
251 if (PageOp->Status == STATUS_PENDING)
252 {
253 DPRINT1("Woke for page op before completion\n");
254 KeBugCheck(MEMORY_MANAGEMENT);
255 }
256 /*
257 * If this wasn't a pagein then we need to restart the handling
258 */
259 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
260 {
261 MmLockAddressSpace(AddressSpace);
262 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
263 MmReleasePageOp(PageOp);
264 return(STATUS_MM_RESTART_OPERATION);
265 }
266 /*
267 * If the thread handling this fault has failed then we don't retry
268 */
269 if (!NT_SUCCESS(PageOp->Status))
270 {
271 MmLockAddressSpace(AddressSpace);
272 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
273 Status = PageOp->Status;
274 MmReleasePageOp(PageOp);
275 return(Status);
276 }
277 MmLockAddressSpace(AddressSpace);
278 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
279 MmReleasePageOp(PageOp);
280 return(STATUS_SUCCESS);
281 }
282
283 /*
284 * Try to allocate a page
285 */
286 MI_SET_USAGE(MI_USAGE_VAD);
287 MI_SET_PROCESS2(Process->ImageFileName);
288 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
289 if (Status == STATUS_NO_MEMORY)
290 {
291 MmUnlockAddressSpace(AddressSpace);
292 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
293 MmLockAddressSpace(AddressSpace);
294 }
295 if (!NT_SUCCESS(Status))
296 {
297 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
298 KeBugCheck(MEMORY_MANAGEMENT);
299 }
300
301 /*
302 * Handle swapped out pages.
303 */
304 if (MmIsPageSwapEntry(Process, Address))
305 {
306 SWAPENTRY SwapEntry;
307
308 MmDeletePageFileMapping(Process, Address, &SwapEntry);
309 Status = MmReadFromSwapPage(SwapEntry, Page);
310 if (!NT_SUCCESS(Status))
311 {
312 KeBugCheck(MEMORY_MANAGEMENT);
313 }
314 MmSetSavedSwapEntryPage(Page, SwapEntry);
315 }
316
317 /*
318 * Set the page. If we fail because we are out of memory then
319 * try again
320 */
321 Status = MmCreateVirtualMapping(Process,
322 (PVOID)PAGE_ROUND_DOWN(Address),
323 Region->Protect,
324 &Page,
325 1);
326 while (Status == STATUS_NO_MEMORY)
327 {
328 MmUnlockAddressSpace(AddressSpace);
329 Status = MmCreateVirtualMapping(Process,
330 (PVOID)PAGE_ROUND_DOWN(Address),
331 Region->Protect,
332 &Page,
333 1);
334 MmLockAddressSpace(AddressSpace);
335 }
336 if (!NT_SUCCESS(Status))
337 {
338 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
339 KeBugCheck(MEMORY_MANAGEMENT);
340 return(Status);
341 }
342
343 /*
344 * Add the page to the process's working set
345 */
346 MmInsertRmap(Page, Process, (PVOID)PAGE_ROUND_DOWN(Address));
347
348 /*
349 * Finish the operation
350 */
351 PageOp->Status = STATUS_SUCCESS;
352 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
353 MmReleasePageOp(PageOp);
354 return(STATUS_SUCCESS);
355 }
356
357 static VOID
358 MmModifyAttributes(PMMSUPPORT AddressSpace,
359 PVOID BaseAddress,
360 SIZE_T RegionSize,
361 ULONG OldType,
362 ULONG OldProtect,
363 ULONG NewType,
364 ULONG NewProtect)
365 /*
366 * FUNCTION: Modify the attributes of a memory region
367 */
368 {
369 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
370
371 /*
372 * If we are switching a previously committed region to reserved then
373 * free any allocated pages within the region
374 */
375 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
376 {
377 ULONG i;
378
379 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
380 {
381 PFN_NUMBER Page;
382
383 if (MmIsPageSwapEntry(Process,
384 (char*)BaseAddress + (i * PAGE_SIZE)))
385 {
386 SWAPENTRY SwapEntry;
387
388 MmDeletePageFileMapping(Process,
389 (char*)BaseAddress + (i * PAGE_SIZE),
390 &SwapEntry);
391 MmFreeSwapPage(SwapEntry);
392 }
393 else
394 {
395 MmDeleteVirtualMapping(Process,
396 (char*)BaseAddress + (i*PAGE_SIZE),
397 FALSE, NULL, &Page);
398 if (Page != 0)
399 {
400 SWAPENTRY SavedSwapEntry;
401 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
402 if (SavedSwapEntry != 0)
403 {
404 MmFreeSwapPage(SavedSwapEntry);
405 MmSetSavedSwapEntryPage(Page, 0);
406 }
407 MmDeleteRmap(Page, Process,
408 (char*)BaseAddress + (i * PAGE_SIZE));
409 MmReleasePageMemoryConsumer(MC_USER, Page);
410 }
411 }
412 }
413 }
414
415 /*
416 * If we are changing the protection attributes of a committed region then
417 * alter the attributes for any allocated pages within the region
418 */
419 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
420 OldProtect != NewProtect)
421 {
422 ULONG i;
423
424 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
425 {
426 if (MmIsPagePresent(Process,
427 (char*)BaseAddress + (i*PAGE_SIZE)))
428 {
429 MmSetPageProtect(Process,
430 (char*)BaseAddress + (i*PAGE_SIZE),
431 NewProtect);
432 }
433 }
434 }
435 }
436
437 NTSTATUS NTAPI
438 MiProtectVirtualMemory(IN PEPROCESS Process,
439 IN OUT PVOID *BaseAddress,
440 IN OUT PSIZE_T NumberOfBytesToProtect,
441 IN ULONG NewAccessProtection,
442 OUT PULONG OldAccessProtection OPTIONAL)
443 {
444 PMEMORY_AREA MemoryArea;
445 PMMSUPPORT AddressSpace;
446 ULONG OldAccessProtection_;
447 NTSTATUS Status;
448
449 *NumberOfBytesToProtect =
450 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
451 PAGE_ROUND_DOWN(*BaseAddress);
452 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
453
454 AddressSpace = &Process->Vm;
455
456 MmLockAddressSpace(AddressSpace);
457 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
458 if (MemoryArea == NULL)
459 {
460 MmUnlockAddressSpace(AddressSpace);
461 return STATUS_UNSUCCESSFUL;
462 }
463
464 if (OldAccessProtection == NULL)
465 OldAccessProtection = &OldAccessProtection_;
466
467 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
468 {
469 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
470 *NumberOfBytesToProtect, NewAccessProtection,
471 OldAccessProtection);
472 }
473 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
474 {
475 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
476 *NumberOfBytesToProtect,
477 NewAccessProtection,
478 OldAccessProtection);
479 }
480 else
481 {
482 /* FIXME: Should we return failure or success in this case? */
483 Status = STATUS_CONFLICTING_ADDRESSES;
484 }
485
486 MmUnlockAddressSpace(AddressSpace);
487
488 return Status;
489 }
490
491 /*
492 * @implemented
493 */
494 NTSTATUS
495 NTAPI
496 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
497 IN OUT PVOID* UBaseAddress,
498 IN ULONG_PTR ZeroBits,
499 IN OUT PSIZE_T URegionSize,
500 IN ULONG AllocationType,
501 IN ULONG Protect)
502 {
503 PEPROCESS Process;
504 MEMORY_AREA* MemoryArea;
505 ULONG_PTR MemoryAreaLength;
506 ULONG Type;
507 NTSTATUS Status;
508 PMMSUPPORT AddressSpace;
509 PVOID BaseAddress;
510 ULONG RegionSize;
511 PVOID PBaseAddress;
512 ULONG_PTR PRegionSize;
513 PHYSICAL_ADDRESS BoundaryAddressMultiple;
514 PEPROCESS CurrentProcess = PsGetCurrentProcess();
515 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
516 KAPC_STATE ApcState;
517 ULONG ProtectionMask;
518 BOOLEAN Attached = FALSE;
519 BoundaryAddressMultiple.QuadPart = 0;
520 PAGED_CODE();
521
522 /* Check for valid Zero bits */
523 if (ZeroBits > 21)
524 {
525 DPRINT1("Too many zero bits\n");
526 return STATUS_INVALID_PARAMETER_3;
527 }
528
529 /* Check for valid Allocation Types */
530 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
531 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
532 {
533 DPRINT1("Invalid Allocation Type\n");
534 return STATUS_INVALID_PARAMETER_5;
535 }
536
537 /* Check for at least one of these Allocation Types to be set */
538 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
539 {
540 DPRINT1("No memory allocation base type\n");
541 return STATUS_INVALID_PARAMETER_5;
542 }
543
544 /* MEM_RESET is an exclusive flag, make sure that is valid too */
545 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
546 {
547 DPRINT1("Invalid use of MEM_RESET\n");
548 return STATUS_INVALID_PARAMETER_5;
549 }
550
551 /* Check if large pages are being used */
552 if (AllocationType & MEM_LARGE_PAGES)
553 {
554 /* Large page allocations MUST be committed */
555 if (!(AllocationType & MEM_COMMIT))
556 {
557 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
558 return STATUS_INVALID_PARAMETER_5;
559 }
560
561 /* These flags are not allowed with large page allocations */
562 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
563 {
564 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
565 return STATUS_INVALID_PARAMETER_5;
566 }
567 }
568
569 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
570 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
571 {
572 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
573 return STATUS_INVALID_PARAMETER_5;
574 }
575
576 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
577 if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
578 {
579 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
580 return STATUS_INVALID_PARAMETER_5;
581 }
582
583 /* Check for valid MEM_PHYSICAL usage */
584 if (AllocationType & MEM_PHYSICAL)
585 {
586 /* Only these flags are allowed with MEM_PHYSIAL */
587 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
588 {
589 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
590 return STATUS_INVALID_PARAMETER_5;
591 }
592
593 /* Then make sure PAGE_READWRITE is used */
594 if (Protect != PAGE_READWRITE)
595 {
596 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
597 return STATUS_INVALID_PARAMETER_6;
598 }
599 }
600
601 /* Calculate the protection mask and make sure it's valid */
602 ProtectionMask = MiMakeProtectionMask(Protect);
603 if (ProtectionMask == MM_INVALID_PROTECTION)
604 {
605 DPRINT1("Invalid protection mask\n");
606 return STATUS_INVALID_PAGE_PROTECTION;
607 }
608
609 /* Enter SEH */
610 _SEH2_TRY
611 {
612 /* Check for user-mode parameters */
613 if (PreviousMode != KernelMode)
614 {
615 /* Make sure they are writable */
616 ProbeForWritePointer(UBaseAddress);
617 ProbeForWriteUlong(URegionSize);
618 }
619
620 /* Capture their values */
621 PBaseAddress = *UBaseAddress;
622 PRegionSize = *URegionSize;
623 }
624 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
625 {
626 /* Return the exception code */
627 _SEH2_YIELD(return _SEH2_GetExceptionCode());
628 }
629 _SEH2_END;
630
631 /* Make sure the allocation isn't past the VAD area */
632 if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
633 {
634 DPRINT1("Virtual allocation base above User Space\n");
635 return STATUS_INVALID_PARAMETER_2;
636 }
637
638 /* Make sure the allocation wouldn't overflow past the VAD area */
639 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
640 {
641 DPRINT1("Region size would overflow into kernel-memory\n");
642 return STATUS_INVALID_PARAMETER_4;
643 }
644
645 /* Make sure there's a size specified */
646 if (!PRegionSize)
647 {
648 DPRINT1("Region size is invalid (zero)\n");
649 return STATUS_INVALID_PARAMETER_4;
650 }
651
652 /* Check if this is for the current process */
653 if (ProcessHandle == NtCurrentProcess())
654 {
655 /* We already have the current process, no need to go through Ob */
656 Process = CurrentProcess;
657 }
658 else
659 {
660 /* Reference the handle for correct permissions */
661 Status = ObReferenceObjectByHandle(ProcessHandle,
662 PROCESS_VM_OPERATION,
663 PsProcessType,
664 PreviousMode,
665 (PVOID*)&Process,
666 NULL);
667 if (!NT_SUCCESS(Status)) return Status;
668
669 /* Check if not running in the current process */
670 if (CurrentProcess != Process)
671 {
672 /* Attach to it */
673 KeStackAttachProcess(&Process->Pcb, &ApcState);
674 Attached = TRUE;
675 }
676 }
677
678 /* Check for large page allocations */
679 if (AllocationType & MEM_LARGE_PAGES)
680 {
681 /* The lock memory privilege is required */
682 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
683 {
684 /* Fail without it */
685 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
686 if (Attached) KeUnstackDetachProcess(&ApcState);
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 if (Attached) KeUnstackDetachProcess(&ApcState);
706 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
707 return STATUS_INVALID_PAGE_PROTECTION;
708 }
709
710 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
711 DPRINT("Type %x\n", Type);
712
713 AddressSpace = &Process->Vm;
714 MmLockAddressSpace(AddressSpace);
715
716 if (PBaseAddress != 0)
717 {
718 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
719
720 if (MemoryArea != NULL)
721 {
722 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
723 (ULONG_PTR)MemoryArea->StartingAddress;
724
725 if (((ULONG_PTR)BaseAddress + RegionSize) > (ULONG_PTR)MemoryArea->EndingAddress)
726 {
727 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
728 (ULONG_PTR)BaseAddress + RegionSize, MemoryArea->EndingAddress);
729
730 MmUnlockAddressSpace(AddressSpace);
731 if (Attached) KeUnstackDetachProcess(&ApcState);
732 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
733 return STATUS_MEMORY_NOT_ALLOCATED;
734 }
735
736 if (AllocationType == MEM_RESET)
737 {
738 if (MmIsPagePresent(Process, BaseAddress))
739 {
740 /* FIXME: mark pages as not modified */
741 }
742 else
743 {
744 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
745 }
746
747 MmUnlockAddressSpace(AddressSpace);
748 if (Attached) KeUnstackDetachProcess(&ApcState);
749 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
750
751 /* MEM_RESET does not modify any attributes of region */
752 return STATUS_SUCCESS;
753 }
754
755 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
756 MemoryAreaLength >= RegionSize)
757 {
758 Status = MmAlterRegion(AddressSpace,
759 MemoryArea->StartingAddress,
760 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
761 BaseAddress, RegionSize,
762 Type, Protect, MmModifyAttributes);
763 MmUnlockAddressSpace(AddressSpace);
764 if (Attached) KeUnstackDetachProcess(&ApcState);
765 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
766 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
767
768 /* Give the caller rounded BaseAddress and area length */
769 if (NT_SUCCESS(Status))
770 {
771 *UBaseAddress = BaseAddress;
772 *URegionSize = RegionSize;
773 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
774 }
775
776 return(Status);
777 }
778 else if (MemoryAreaLength >= RegionSize)
779 {
780 /* Region list initialized? */
781 if (MemoryArea->Data.SectionData.RegionListHead.Flink)
782 {
783 Status = 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 SIZE_T 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 = MmAlterRegion(AddressSpace,
1097 MemoryArea->StartingAddress,
1098 (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) ?
1099 &MemoryArea->Data.SectionData.RegionListHead :
1100 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1101 BaseAddress,
1102 RegionSize,
1103 MEM_RESERVE,
1104 PAGE_NOACCESS,
1105 MmModifyAttributes);
1106 goto unlock_deref_and_return;
1107 }
1108
1109 Status = STATUS_NOT_IMPLEMENTED;
1110
1111 unlock_deref_and_return:
1112
1113 MmUnlockAddressSpace(AddressSpace);
1114 if (Attached) KeUnstackDetachProcess(&ApcState);
1115 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
1116
1117 return(Status);
1118 }
1119
1120 NTSTATUS
1121 NTAPI
1122 MmProtectAnonMem(PMMSUPPORT AddressSpace,
1123 PMEMORY_AREA MemoryArea,
1124 PVOID BaseAddress,
1125 SIZE_T Length,
1126 ULONG Protect,
1127 PULONG OldProtect)
1128 {
1129 PMM_REGION Region;
1130 NTSTATUS Status = STATUS_SUCCESS;
1131 ULONG_PTR LengthCount = 0;
1132
1133 /* Search all Regions in MemoryArea up to Length */
1134 /* Every Region up to Length must be committed for success */
1135 for (;;)
1136 {
1137 Region = MmFindRegion(MemoryArea->StartingAddress,
1138 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1139 (PVOID)((ULONG_PTR)BaseAddress + LengthCount), NULL);
1140
1141 /* If a Region was found and it is committed */
1142 if ((Region) && (Region->Type == MEM_COMMIT))
1143 {
1144 LengthCount += Region->Length;
1145 if (Length <= LengthCount) break;
1146 continue;
1147 }
1148 /* If Region was found and it is not commited */
1149 else if (Region)
1150 {
1151 Status = STATUS_NOT_COMMITTED;
1152 break;
1153 }
1154 /* If no Region was found at all */
1155 else if (LengthCount == 0)
1156 {
1157 Status = STATUS_INVALID_ADDRESS;
1158 break;
1159 }
1160 }
1161
1162 if (NT_SUCCESS(Status))
1163 {
1164 *OldProtect = Region->Protect;
1165 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1166 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1167 BaseAddress, Length, Region->Type, Protect,
1168 MmModifyAttributes);
1169 }
1170
1171 return (Status);
1172 }
1173
1174 NTSTATUS NTAPI
1175 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
1176 PVOID Address,
1177 PMEMORY_BASIC_INFORMATION Info,
1178 PSIZE_T ResultLength)
1179 {
1180 PMM_REGION Region;
1181 PVOID RegionBase = NULL;
1182
1183 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1184
1185 Region = MmFindRegion(MemoryArea->StartingAddress,
1186 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1187 Address, &RegionBase);
1188 Info->BaseAddress = RegionBase;
1189 Info->AllocationBase = MemoryArea->StartingAddress;
1190 Info->AllocationProtect = MemoryArea->Protect;
1191 Info->RegionSize = Region->Length;
1192 Info->State = Region->Type;
1193 Info->Protect = Region->Protect;
1194 Info->Type = MEM_PRIVATE;
1195
1196 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1197 return(STATUS_SUCCESS);
1198 }
1199
1200 /* EOF */