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