[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 PMM_PAGEOP PageOp;
424 PMEMORY_AREA MArea;
425 char* addr = (char*)BaseAddress;
426
427 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
428 {
429 MArea = MmLocateMemoryAreaByAddress(AddressSpace, addr);
430 do
431 {
432 PageOp = MmGetPageOp(MArea, Process->UniqueProcessId, addr,
433 NULL, 0, MM_PAGEOP_CHANGEPROTECT, TRUE);
434 } while(PageOp == NULL);
435
436 /* Should we enable/disable virtual mapping? */
437 if((NewProtect & PAGE_NOACCESS) &&
438 !(OldProtect & PAGE_NOACCESS) &&
439 (MmIsPagePresent(Process, addr)))
440 {
441 /* Set other flags if any */
442 if(NewProtect != PAGE_NOACCESS)
443 MmSetPageProtect(Process, addr, NewProtect & ~PAGE_NOACCESS);
444 MmDisableVirtualMapping(Process, addr, NULL, NULL);
445 }
446 else if((OldProtect & PAGE_NOACCESS) && !(NewProtect & PAGE_NOACCESS))
447 {
448 MmEnableVirtualMapping(Process, addr);
449 }
450
451 /* Set new protection flags */
452 if(MmIsPagePresent(Process, addr))
453 {
454 MmSetPageProtect(Process, addr, NewProtect);
455 }
456 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
457 MmReleasePageOp(PageOp);
458 addr += PAGE_SIZE;
459 }
460 }
461 }
462
463 NTSTATUS NTAPI
464 MiProtectVirtualMemory(IN PEPROCESS Process,
465 IN OUT PVOID *BaseAddress,
466 IN OUT PSIZE_T NumberOfBytesToProtect,
467 IN ULONG NewAccessProtection,
468 OUT PULONG OldAccessProtection OPTIONAL)
469 {
470 PMEMORY_AREA MemoryArea;
471 PMMSUPPORT AddressSpace;
472 ULONG OldAccessProtection_;
473 NTSTATUS Status;
474
475 *NumberOfBytesToProtect =
476 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
477 PAGE_ROUND_DOWN(*BaseAddress);
478 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
479
480 AddressSpace = &Process->Vm;
481
482 MmLockAddressSpace(AddressSpace);
483 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
484 if (MemoryArea == NULL)
485 {
486 MmUnlockAddressSpace(AddressSpace);
487 return STATUS_UNSUCCESSFUL;
488 }
489
490 if (OldAccessProtection == NULL)
491 OldAccessProtection = &OldAccessProtection_;
492
493 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
494 {
495 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
496 *NumberOfBytesToProtect, NewAccessProtection,
497 OldAccessProtection);
498 }
499 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
500 {
501 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
502 *NumberOfBytesToProtect,
503 NewAccessProtection,
504 OldAccessProtection);
505 }
506 else
507 {
508 /* FIXME: Should we return failure or success in this case? */
509 Status = STATUS_CONFLICTING_ADDRESSES;
510 }
511
512 MmUnlockAddressSpace(AddressSpace);
513
514 return Status;
515 }
516
517 /*
518 * @implemented
519 */
520 NTSTATUS
521 NTAPI
522 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
523 IN OUT PVOID* UBaseAddress,
524 IN ULONG_PTR ZeroBits,
525 IN OUT PSIZE_T URegionSize,
526 IN ULONG AllocationType,
527 IN ULONG Protect)
528 {
529 PEPROCESS Process;
530 MEMORY_AREA* MemoryArea;
531 ULONG_PTR MemoryAreaLength;
532 ULONG Type;
533 NTSTATUS Status;
534 PMMSUPPORT AddressSpace;
535 PVOID BaseAddress;
536 ULONG RegionSize;
537 PVOID PBaseAddress;
538 ULONG_PTR PRegionSize;
539 PHYSICAL_ADDRESS BoundaryAddressMultiple;
540 PEPROCESS CurrentProcess = PsGetCurrentProcess();
541 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
542 KAPC_STATE ApcState;
543 ULONG ProtectionMask;
544 BOOLEAN Attached = FALSE;
545 BoundaryAddressMultiple.QuadPart = 0;
546 PAGED_CODE();
547
548 /* Check for valid Zero bits */
549 if (ZeroBits > 21)
550 {
551 DPRINT1("Too many zero bits\n");
552 return STATUS_INVALID_PARAMETER_3;
553 }
554
555 /* Check for valid Allocation Types */
556 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
557 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
558 {
559 DPRINT1("Invalid Allocation Type\n");
560 return STATUS_INVALID_PARAMETER_5;
561 }
562
563 /* Check for at least one of these Allocation Types to be set */
564 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
565 {
566 DPRINT1("No memory allocation base type\n");
567 return STATUS_INVALID_PARAMETER_5;
568 }
569
570 /* MEM_RESET is an exclusive flag, make sure that is valid too */
571 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
572 {
573 DPRINT1("Invalid use of MEM_RESET\n");
574 return STATUS_INVALID_PARAMETER_5;
575 }
576
577 /* Check if large pages are being used */
578 if (AllocationType & MEM_LARGE_PAGES)
579 {
580 /* Large page allocations MUST be committed */
581 if (!(AllocationType & MEM_COMMIT))
582 {
583 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n");
584 return STATUS_INVALID_PARAMETER_5;
585 }
586
587 /* These flags are not allowed with large page allocations */
588 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH))
589 {
590 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n");
591 return STATUS_INVALID_PARAMETER_5;
592 }
593 }
594
595 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
596 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
597 {
598 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
599 return STATUS_INVALID_PARAMETER_5;
600 }
601
602 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
603 if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
604 {
605 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
606 return STATUS_INVALID_PARAMETER_5;
607 }
608
609 /* Check for valid MEM_PHYSICAL usage */
610 if (AllocationType & MEM_PHYSICAL)
611 {
612 /* Only these flags are allowed with MEM_PHYSIAL */
613 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
614 {
615 DPRINT1("Using illegal flags with MEM_PHYSICAL\n");
616 return STATUS_INVALID_PARAMETER_5;
617 }
618
619 /* Then make sure PAGE_READWRITE is used */
620 if (Protect != PAGE_READWRITE)
621 {
622 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
623 return STATUS_INVALID_PARAMETER_6;
624 }
625 }
626
627 /* Calculate the protection mask and make sure it's valid */
628 ProtectionMask = MiMakeProtectionMask(Protect);
629 if (ProtectionMask == MM_INVALID_PROTECTION)
630 {
631 DPRINT1("Invalid protection mask\n");
632 return STATUS_INVALID_PAGE_PROTECTION;
633 }
634
635 /* Enter SEH */
636 _SEH2_TRY
637 {
638 /* Check for user-mode parameters */
639 if (PreviousMode != KernelMode)
640 {
641 /* Make sure they are writable */
642 ProbeForWritePointer(UBaseAddress);
643 ProbeForWriteUlong(URegionSize);
644 }
645
646 /* Capture their values */
647 PBaseAddress = *UBaseAddress;
648 PRegionSize = *URegionSize;
649 }
650 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
651 {
652 /* Return the exception code */
653 _SEH2_YIELD(return _SEH2_GetExceptionCode());
654 }
655 _SEH2_END;
656
657 /* Make sure the allocation isn't past the VAD area */
658 if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
659 {
660 DPRINT1("Virtual allocation base above User Space\n");
661 return STATUS_INVALID_PARAMETER_2;
662 }
663
664 /* Make sure the allocation wouldn't overflow past the VAD area */
665 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize)
666 {
667 DPRINT1("Region size would overflow into kernel-memory\n");
668 return STATUS_INVALID_PARAMETER_4;
669 }
670
671 /* Make sure there's a size specified */
672 if (!PRegionSize)
673 {
674 DPRINT1("Region size is invalid (zero)\n");
675 return STATUS_INVALID_PARAMETER_4;
676 }
677
678 /* Check if this is for the current process */
679 if (ProcessHandle == NtCurrentProcess())
680 {
681 /* We already have the current process, no need to go through Ob */
682 Process = CurrentProcess;
683 }
684 else
685 {
686 /* Reference the handle for correct permissions */
687 Status = ObReferenceObjectByHandle(ProcessHandle,
688 PROCESS_VM_OPERATION,
689 PsProcessType,
690 PreviousMode,
691 (PVOID*)&Process,
692 NULL);
693 if (!NT_SUCCESS(Status)) return Status;
694
695 /* Check if not running in the current process */
696 if (CurrentProcess != Process)
697 {
698 /* Attach to it */
699 KeStackAttachProcess(&Process->Pcb, &ApcState);
700 Attached = TRUE;
701 }
702 }
703
704 /* Check for large page allocations */
705 if (AllocationType & MEM_LARGE_PAGES)
706 {
707 /* The lock memory privilege is required */
708 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))
709 {
710 /* Fail without it */
711 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n");
712 if (Attached) KeUnstackDetachProcess(&ApcState);
713 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
714 return STATUS_PRIVILEGE_NOT_HELD;
715 }
716 }
717
718 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
719 RegionSize = PAGE_ROUND_UP((ULONG_PTR)PBaseAddress + PRegionSize) -
720 PAGE_ROUND_DOWN(PBaseAddress);
721
722
723 /*
724 * Copy on Write is reserved for system use. This case is a certain failure
725 * but there may be other cases...needs more testing
726 */
727 if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
728 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)))
729 {
730 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
731 if (Attached) KeUnstackDetachProcess(&ApcState);
732 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
733 return STATUS_INVALID_PAGE_PROTECTION;
734 }
735
736 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
737 DPRINT("Type %x\n", Type);
738
739 AddressSpace = &Process->Vm;
740 MmLockAddressSpace(AddressSpace);
741
742 if (PBaseAddress != 0)
743 {
744 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
745
746 if (MemoryArea != NULL)
747 {
748 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
749 (ULONG_PTR)MemoryArea->StartingAddress;
750
751 if (((ULONG_PTR)BaseAddress + RegionSize) > (ULONG_PTR)MemoryArea->EndingAddress)
752 {
753 DPRINT("BaseAddress + RegionSize %x is larger than MemoryArea's EndingAddress %x\n",
754 (ULONG_PTR)BaseAddress + RegionSize, MemoryArea->EndingAddress);
755
756 MmUnlockAddressSpace(AddressSpace);
757 if (Attached) KeUnstackDetachProcess(&ApcState);
758 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
759 return STATUS_MEMORY_NOT_ALLOCATED;
760 }
761
762 if (AllocationType == MEM_RESET)
763 {
764 if (MmIsPagePresent(Process, BaseAddress))
765 {
766 /* FIXME: mark pages as not modified */
767 }
768 else
769 {
770 /* FIXME: if pages are in paging file discard them and bring in pages of zeros */
771 }
772
773 MmUnlockAddressSpace(AddressSpace);
774 if (Attached) KeUnstackDetachProcess(&ApcState);
775 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
776
777 /* MEM_RESET does not modify any attributes of region */
778 return STATUS_SUCCESS;
779 }
780
781 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
782 MemoryAreaLength >= RegionSize)
783 {
784 Status = MmAlterRegion(AddressSpace,
785 MemoryArea->StartingAddress,
786 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
787 BaseAddress, RegionSize,
788 Type, Protect, MmModifyAttributes);
789 MmUnlockAddressSpace(AddressSpace);
790 if (Attached) KeUnstackDetachProcess(&ApcState);
791 if (ProcessHandle != NtCurrentProcess()) 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 if (MemoryAreaLength >= RegionSize)
805 {
806 /* Region list initialized? */
807 if (MemoryArea->Data.SectionData.RegionListHead.Flink)
808 {
809 Status = MmAlterRegion(AddressSpace,
810 MemoryArea->StartingAddress,
811 &MemoryArea->Data.SectionData.RegionListHead,
812 BaseAddress, RegionSize,
813 Type, Protect, MmModifyAttributes);
814 }
815 else
816 {
817 Status = STATUS_ACCESS_VIOLATION;
818 }
819
820 MmUnlockAddressSpace(AddressSpace);
821 if (Attached) KeUnstackDetachProcess(&ApcState);
822 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
823 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
824
825 /* Give the caller rounded BaseAddress and area length */
826 if (NT_SUCCESS(Status))
827 {
828 *UBaseAddress = BaseAddress;
829 *URegionSize = RegionSize;
830 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
831 }
832
833 return(Status);
834 }
835 else
836 {
837 MmUnlockAddressSpace(AddressSpace);
838 if (Attached) KeUnstackDetachProcess(&ApcState);
839 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
840 return(STATUS_UNSUCCESSFUL);
841 }
842 }
843 }
844
845 Status = MmCreateMemoryArea(AddressSpace,
846 MEMORY_AREA_VIRTUAL_MEMORY,
847 &BaseAddress,
848 RegionSize,
849 Protect,
850 &MemoryArea,
851 PBaseAddress != 0,
852 AllocationType & MEM_TOP_DOWN,
853 BoundaryAddressMultiple);
854 if (!NT_SUCCESS(Status))
855 {
856 MmUnlockAddressSpace(AddressSpace);
857 if (Attached) KeUnstackDetachProcess(&ApcState);
858 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
859 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
860 return(Status);
861 }
862
863 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
864 (ULONG_PTR)MemoryArea->StartingAddress;
865
866 MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
867 MemoryAreaLength, Type, Protect);
868
869 if ((AllocationType & MEM_COMMIT) &&
870 (Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
871 {
872 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
873 MmReserveSwapPages(nPages);
874 }
875
876 MmUnlockAddressSpace(AddressSpace);
877 if (Attached) KeUnstackDetachProcess(&ApcState);
878 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
879
880 *UBaseAddress = BaseAddress;
881 *URegionSize = MemoryAreaLength;
882 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
883
884 return(STATUS_SUCCESS);
885 }
886
887 static VOID
888 MmFreeVirtualMemoryPage(PVOID Context,
889 MEMORY_AREA* MemoryArea,
890 PVOID Address,
891 PFN_NUMBER Page,
892 SWAPENTRY SwapEntry,
893 BOOLEAN Dirty)
894 {
895 PEPROCESS Process = (PEPROCESS)Context;
896
897 if (Page != 0)
898 {
899 SWAPENTRY SavedSwapEntry;
900 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
901 if (SavedSwapEntry != 0)
902 {
903 MmFreeSwapPage(SavedSwapEntry);
904 MmSetSavedSwapEntryPage(Page, 0);
905 }
906 MmDeleteRmap(Page, Process, Address);
907 MmReleasePageMemoryConsumer(MC_USER, Page);
908 }
909 else if (SwapEntry != 0)
910 {
911 MmFreeSwapPage(SwapEntry);
912 }
913 }
914
915 VOID
916 NTAPI
917 MmFreeVirtualMemory(PEPROCESS Process,
918 PMEMORY_AREA MemoryArea)
919 {
920 PLIST_ENTRY current_entry;
921 PMM_REGION current;
922 ULONG i;
923
924 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
925 MemoryArea);
926
927 /* Mark this memory area as about to be deleted. */
928 MemoryArea->DeleteInProgress = TRUE;
929
930 /*
931 * Wait for any ongoing paging operations. Notice that since we have
932 * flagged this memory area as deleted no more page ops will be added.
933 */
934 if (MemoryArea->PageOpCount > 0)
935 {
936 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
937 (ULONG_PTR)MemoryArea->StartingAddress;
938 const ULONG nPages = PAGE_ROUND_UP(MemoryAreaLength) >> PAGE_SHIFT;
939
940 for (i = 0; i < nPages && MemoryArea->PageOpCount != 0; ++i)
941 {
942 PMM_PAGEOP PageOp;
943 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
944 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
945 NULL, 0);
946 if (PageOp != NULL)
947 {
948 NTSTATUS Status;
949 MmUnlockAddressSpace(&Process->Vm);
950 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
951 0,
952 KernelMode,
953 FALSE,
954 NULL);
955 if (Status != STATUS_SUCCESS)
956 {
957 DPRINT1("Failed to wait for page op\n");
958 KeBugCheck(MEMORY_MANAGEMENT);
959 }
960 MmLockAddressSpace(&Process->Vm);
961 MmReleasePageOp(PageOp);
962 }
963 }
964 }
965
966 /* Free all the individual segments. */
967 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
968 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
969 {
970 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
971 current_entry = current_entry->Flink;
972 ExFreePool(current);
973 }
974
975 /* Actually free the memory area. */
976 MmFreeMemoryArea(&Process->Vm,
977 MemoryArea,
978 MmFreeVirtualMemoryPage,
979 (PVOID)Process);
980 }
981
982 /*
983 * @implemented
984 */
985 NTSTATUS NTAPI
986 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
987 IN PVOID* UBaseAddress,
988 IN PSIZE_T URegionSize,
989 IN ULONG FreeType)
990 /*
991 * FUNCTION: Frees a range of virtual memory
992 * ARGUMENTS:
993 * ProcessHandle = Points to the process that allocated the virtual
994 * memory
995 * BaseAddress = Points to the memory address, rounded down to a
996 * multiple of the pagesize
997 * RegionSize = Limits the range to free, rounded up to a multiple of
998 * the paging size
999 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
1000 * RETURNS: Status
1001 */
1002 {
1003 MEMORY_AREA* MemoryArea;
1004 NTSTATUS Status;
1005 PEPROCESS Process;
1006 PMMSUPPORT AddressSpace;
1007 PVOID BaseAddress, PBaseAddress;
1008 SIZE_T RegionSize, PRegionSize;
1009 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1010 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
1011 KAPC_STATE ApcState;
1012 BOOLEAN Attached = FALSE;
1013 PAGED_CODE();
1014
1015 /* Only two flags are supported */
1016 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT)))
1017 {
1018 DPRINT1("Invalid FreeType\n");
1019 return STATUS_INVALID_PARAMETER_4;
1020 }
1021
1022 /* Check if no flag was used, or if both flags were used */
1023 if (!((FreeType & (MEM_DECOMMIT | MEM_RELEASE))) ||
1024 ((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == (MEM_DECOMMIT | MEM_RELEASE)))
1025 {
1026 DPRINT1("Invalid FreeType combination\n");
1027 return STATUS_INVALID_PARAMETER_4;
1028 }
1029
1030 /* Enter SEH */
1031 _SEH2_TRY
1032 {
1033 /* Check for user-mode parameters */
1034 if (PreviousMode != KernelMode)
1035 {
1036 /* Make sure they are writable */
1037 ProbeForWritePointer(UBaseAddress);
1038 ProbeForWriteUlong(URegionSize);
1039 }
1040
1041 /* Capture their values */
1042 PBaseAddress = *UBaseAddress;
1043 PRegionSize = *URegionSize;
1044 }
1045 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1046 {
1047 /* Return the exception code */
1048 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1049 }
1050 _SEH2_END;
1051
1052 /* Make sure the allocation isn't past the user area */
1053 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS)
1054 {
1055 DPRINT1("Virtual free base above User Space\n");
1056 return STATUS_INVALID_PARAMETER_2;
1057 }
1058
1059 /* Make sure the allocation wouldn't overflow past the user area */
1060 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize)
1061 {
1062 DPRINT1("Region size would overflow into kernel-memory\n");
1063 return STATUS_INVALID_PARAMETER_3;
1064 }
1065
1066 /* Check if this is for the current process */
1067 if (ProcessHandle == NtCurrentProcess())
1068 {
1069 /* We already have the current process, no need to go through Ob */
1070 Process = CurrentProcess;
1071 }
1072 else
1073 {
1074 /* Reference the handle for correct permissions */
1075 Status = ObReferenceObjectByHandle(ProcessHandle,
1076 PROCESS_VM_OPERATION,
1077 PsProcessType,
1078 PreviousMode,
1079 (PVOID*)&Process,
1080 NULL);
1081 if (!NT_SUCCESS(Status)) return Status;
1082
1083 /* Check if not running in the current process */
1084 if (CurrentProcess != Process)
1085 {
1086 /* Attach to it */
1087 KeStackAttachProcess(&Process->Pcb, &ApcState);
1088 Attached = TRUE;
1089 }
1090 }
1091
1092 BaseAddress = (PVOID)PAGE_ROUND_DOWN((PBaseAddress));
1093
1094 AddressSpace = &Process->Vm;
1095
1096 MmLockAddressSpace(AddressSpace);
1097 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1098 if (MemoryArea == NULL)
1099 {
1100 DPRINT1("Unable to find memory area from address 0x%p\n", BaseAddress);
1101 Status = STATUS_UNABLE_TO_FREE_VM;
1102 goto unlock_deref_and_return;
1103 }
1104
1105 if (PRegionSize != 0)
1106 {
1107 /* Use the region the caller wanted, rounded to whole pages */
1108 RegionSize = PAGE_ROUND_UP((ULONG_PTR)(PBaseAddress) + (PRegionSize)) -
1109 PAGE_ROUND_DOWN((PBaseAddress));
1110 }
1111 else
1112 {
1113 /* The caller wanted the whole memory area */
1114 RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
1115 (ULONG_PTR)MemoryArea->StartingAddress;
1116 }
1117
1118 switch (FreeType)
1119 {
1120 case MEM_RELEASE:
1121 /* MEM_RELEASE must be used with the exact base and length
1122 * that was returned by NtAllocateVirtualMemory */
1123
1124 /* Verify the base address is correct */
1125 if (MemoryArea->StartingAddress != BaseAddress)
1126 {
1127 DPRINT1("Base address is illegal for MEM_RELEASE (0x%p != 0x%p)\n",
1128 MemoryArea->StartingAddress,
1129 BaseAddress);
1130 Status = STATUS_UNABLE_TO_FREE_VM;
1131 goto unlock_deref_and_return;
1132 }
1133
1134 /* Verify the region size is correct */
1135 if ((ULONG_PTR)MemoryArea->StartingAddress + RegionSize !=
1136 (ULONG_PTR)MemoryArea->EndingAddress)
1137 {
1138 DPRINT1("Region size is illegal for MEM_RELEASE (0x%x)\n", RegionSize);
1139 Status = STATUS_UNABLE_TO_FREE_VM;
1140 //goto unlock_deref_and_return;
1141 }
1142
1143 if (MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
1144 {
1145 DPRINT1("Memory area is not VM\n");
1146 Status = STATUS_UNABLE_TO_FREE_VM;
1147 goto unlock_deref_and_return;
1148 }
1149
1150 MmFreeVirtualMemory(Process, MemoryArea);
1151 Status = STATUS_SUCCESS;
1152 break;
1153
1154 case MEM_DECOMMIT:
1155 if ((ULONG_PTR)BaseAddress + RegionSize >
1156 (ULONG_PTR)MemoryArea->EndingAddress)
1157 {
1158 DPRINT1("Invald base address (0x%p) and region size (0x%x) for MEM_DECOMMIT\n",
1159 BaseAddress, RegionSize);
1160 Status = STATUS_UNABLE_TO_FREE_VM;
1161 goto unlock_deref_and_return;
1162 }
1163 Status = MmAlterRegion(AddressSpace,
1164 MemoryArea->StartingAddress,
1165 (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) ?
1166 &MemoryArea->Data.SectionData.RegionListHead :
1167 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1168 BaseAddress,
1169 RegionSize,
1170 MEM_RESERVE,
1171 PAGE_NOACCESS,
1172 MmModifyAttributes);
1173 if (!NT_SUCCESS(Status))
1174 {
1175 DPRINT1("MmAlterRegion failed with status 0x%x\n", Status);
1176 Status = STATUS_UNABLE_TO_FREE_VM;
1177 goto unlock_deref_and_return;
1178 }
1179 break;
1180
1181 default:
1182 Status = STATUS_NOT_IMPLEMENTED;
1183 goto unlock_deref_and_return;
1184 }
1185
1186 /* Copy rounded values back in success case */
1187 *UBaseAddress = BaseAddress;
1188 *URegionSize = RegionSize;
1189
1190 unlock_deref_and_return:
1191 MmUnlockAddressSpace(AddressSpace);
1192 if (Attached) KeUnstackDetachProcess(&ApcState);
1193 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
1194
1195 return(Status);
1196 }
1197
1198 NTSTATUS
1199 NTAPI
1200 MmProtectAnonMem(PMMSUPPORT AddressSpace,
1201 PMEMORY_AREA MemoryArea,
1202 PVOID BaseAddress,
1203 SIZE_T Length,
1204 ULONG Protect,
1205 PULONG OldProtect)
1206 {
1207 PMM_REGION Region;
1208 NTSTATUS Status = STATUS_SUCCESS;
1209 ULONG_PTR LengthCount = 0;
1210
1211 /* Search all Regions in MemoryArea up to Length */
1212 /* Every Region up to Length must be committed for success */
1213 for (;;)
1214 {
1215 Region = MmFindRegion(MemoryArea->StartingAddress,
1216 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1217 (PVOID)((ULONG_PTR)BaseAddress + LengthCount), NULL);
1218
1219 /* If a Region was found and it is committed */
1220 if ((Region) && (Region->Type == MEM_COMMIT))
1221 {
1222 LengthCount += Region->Length;
1223 if (Length <= LengthCount) break;
1224 continue;
1225 }
1226 /* If Region was found and it is not commited */
1227 else if (Region)
1228 {
1229 Status = STATUS_NOT_COMMITTED;
1230 break;
1231 }
1232 /* If no Region was found at all */
1233 else if (LengthCount == 0)
1234 {
1235 Status = STATUS_INVALID_ADDRESS;
1236 break;
1237 }
1238 }
1239
1240 if (NT_SUCCESS(Status))
1241 {
1242 *OldProtect = Region->Protect;
1243 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1244 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1245 BaseAddress, Length, Region->Type, Protect,
1246 MmModifyAttributes);
1247 }
1248
1249 return (Status);
1250 }
1251
1252 NTSTATUS NTAPI
1253 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
1254 PVOID Address,
1255 PMEMORY_BASIC_INFORMATION Info,
1256 PSIZE_T ResultLength)
1257 {
1258 PMM_REGION Region;
1259 PVOID RegionBase = NULL;
1260
1261 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
1262
1263 Region = MmFindRegion(MemoryArea->StartingAddress,
1264 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1265 Address, &RegionBase);
1266 Info->BaseAddress = RegionBase;
1267 Info->AllocationBase = MemoryArea->StartingAddress;
1268 Info->AllocationProtect = MemoryArea->Protect;
1269 Info->RegionSize = Region->Length;
1270 Info->State = Region->Type;
1271 Info->Protect = Region->Protect;
1272 Info->Type = MEM_PRIVATE;
1273
1274 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1275 return(STATUS_SUCCESS);
1276 }
1277
1278 /* EOF */