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