Lock always the address space if we changing the virtual mapping.
[reactos.git] / reactos / ntoskrnl / mm / anonmem.c
1 /* $Id$
2 *
3 * Copyright (C) 2002-2005 ReactOS Team (and the authors from the programmers section)
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 *
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/anonmem.c
22 * PURPOSE: Implementing anonymous memory.
23 *
24 * PROGRAMMERS: David Welch
25 * Hartmut Birr
26 * Casper Hornstrup
27 * KJK::Hyperion
28 * Ge van Geldorp
29 * Eric Kohl
30 * Royce Mitchell III
31 * Aleksey Bragin
32 * Jason Filby
33 * Art Yerkes
34 * Gunnar Andre' Dalsnes
35 * Filip Navara
36 * Thomas Weidenmueller
37 * Alex Ionescu
38 * Trevor McCort
39 * Steven Edwards
40 */
41
42 /* INCLUDE *****************************************************************/
43
44 #include <ntoskrnl.h>
45 #define NDEBUG
46 #include <internal/debug.h>
47
48 /* FUNCTIONS *****************************************************************/
49
50 NTSTATUS
51 NTAPI
52 MmWritePageVirtualMemory(PMADDRESS_SPACE AddressSpace,
53 PMEMORY_AREA MemoryArea,
54 PVOID Address,
55 PMM_PAGEOP PageOp)
56 {
57 SWAPENTRY SwapEntry;
58 PFN_TYPE Page;
59 NTSTATUS Status;
60
61 /*
62 * Check for paging out from a deleted virtual memory area.
63 */
64 if (MemoryArea->DeleteInProgress)
65 {
66 PageOp->Status = STATUS_UNSUCCESSFUL;
67 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
68 MmReleasePageOp(PageOp);
69 return(STATUS_UNSUCCESSFUL);
70 }
71
72 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
73
74 /*
75 * Get that the page actually is dirty.
76 */
77 if (!MmIsDirtyPage(AddressSpace->Process, Address))
78 {
79 PageOp->Status = STATUS_SUCCESS;
80 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
81 MmReleasePageOp(PageOp);
82 return(STATUS_SUCCESS);
83 }
84
85 /*
86 * Speculatively set the mapping to clean.
87 */
88 MmSetCleanPage(AddressSpace->Process, Address);
89
90 /*
91 * If necessary, allocate an entry in the paging file for this page
92 */
93 SwapEntry = MmGetSavedSwapEntryPage(Page);
94 if (SwapEntry == 0)
95 {
96 SwapEntry = MmAllocSwapPage();
97 if (SwapEntry == 0)
98 {
99 MmSetDirtyPage(AddressSpace->Process, Address);
100 PageOp->Status = STATUS_PAGEFILE_QUOTA_EXCEEDED;
101 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
102 MmReleasePageOp(PageOp);
103 return(STATUS_PAGEFILE_QUOTA_EXCEEDED);
104 }
105 }
106
107 /*
108 * Write the page to the pagefile
109 */
110 Status = MmWriteToSwapPage(SwapEntry, Page);
111 if (!NT_SUCCESS(Status))
112 {
113 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
114 Status);
115 MmSetDirtyPage(AddressSpace->Process, Address);
116 PageOp->Status = STATUS_UNSUCCESSFUL;
117 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
118 MmReleasePageOp(PageOp);
119 return(STATUS_UNSUCCESSFUL);
120 }
121
122 /*
123 * Otherwise we have succeeded.
124 */
125 MmSetSavedSwapEntryPage(Page, SwapEntry);
126 PageOp->Status = STATUS_SUCCESS;
127 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
128 MmReleasePageOp(PageOp);
129 return(STATUS_SUCCESS);
130 }
131
132 NTSTATUS
133 NTAPI
134 MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace,
135 PMEMORY_AREA MemoryArea,
136 PVOID Address,
137 PMM_PAGEOP PageOp)
138 {
139 PFN_TYPE Page;
140 BOOL WasDirty;
141 SWAPENTRY SwapEntry;
142 NTSTATUS Status;
143
144 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
145 Address, AddressSpace->Process->UniqueProcessId);
146
147 /*
148 * Check for paging out from a deleted virtual memory area.
149 */
150 if (MemoryArea->DeleteInProgress)
151 {
152 PageOp->Status = STATUS_UNSUCCESSFUL;
153 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
154 MmReleasePageOp(PageOp);
155 return(STATUS_UNSUCCESSFUL);
156 }
157
158 /*
159 * Disable the virtual mapping.
160 */
161 MmDisableVirtualMapping(AddressSpace->Process, Address,
162 &WasDirty, &Page);
163
164 if (Page == 0)
165 {
166 KEBUGCHECK(0);
167 }
168
169 /*
170 * Paging out non-dirty data is easy.
171 */
172 if (!WasDirty)
173 {
174 MmLockAddressSpace(AddressSpace);
175 MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);
176 MmDeleteAllRmaps(Page, NULL, NULL);
177 if ((SwapEntry = MmGetSavedSwapEntryPage(Page)) != 0)
178 {
179 MmCreatePageFileMapping(AddressSpace->Process, Address, SwapEntry);
180 MmSetSavedSwapEntryPage(Page, 0);
181 }
182 MmUnlockAddressSpace(AddressSpace);
183 MmReleasePageMemoryConsumer(MC_USER, Page);
184 PageOp->Status = STATUS_SUCCESS;
185 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
186 MmReleasePageOp(PageOp);
187 return(STATUS_SUCCESS);
188 }
189
190 /*
191 * If necessary, allocate an entry in the paging file for this page
192 */
193 SwapEntry = MmGetSavedSwapEntryPage(Page);
194 if (SwapEntry == 0)
195 {
196 SwapEntry = MmAllocSwapPage();
197 if (SwapEntry == 0)
198 {
199 MmShowOutOfSpaceMessagePagingFile();
200 MmEnableVirtualMapping(AddressSpace->Process, Address);
201 PageOp->Status = STATUS_UNSUCCESSFUL;
202 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
203 MmReleasePageOp(PageOp);
204 return(STATUS_PAGEFILE_QUOTA);
205 }
206 }
207
208 /*
209 * Write the page to the pagefile
210 */
211 Status = MmWriteToSwapPage(SwapEntry, Page);
212 if (!NT_SUCCESS(Status))
213 {
214 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
215 Status);
216 MmEnableVirtualMapping(AddressSpace->Process, Address);
217 PageOp->Status = STATUS_UNSUCCESSFUL;
218 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
219 MmReleasePageOp(PageOp);
220 return(STATUS_UNSUCCESSFUL);
221 }
222
223 /*
224 * Otherwise we have succeeded, free the page
225 */
226 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page << PAGE_SHIFT);
227 MmLockAddressSpace(AddressSpace);
228 MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);
229 MmCreatePageFileMapping(AddressSpace->Process, Address, SwapEntry);
230 MmUnlockAddressSpace(AddressSpace);
231 MmDeleteAllRmaps(Page, NULL, NULL);
232 MmSetSavedSwapEntryPage(Page, 0);
233 MmReleasePageMemoryConsumer(MC_USER, Page);
234 PageOp->Status = STATUS_SUCCESS;
235 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
236 MmReleasePageOp(PageOp);
237 return(STATUS_SUCCESS);
238 }
239
240 NTSTATUS
241 NTAPI
242 MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace,
243 MEMORY_AREA* MemoryArea,
244 PVOID Address,
245 BOOLEAN Locked)
246 /*
247 * FUNCTION: Move data into memory to satisfy a page not present fault
248 * ARGUMENTS:
249 * AddressSpace = Address space within which the fault occurred
250 * MemoryArea = The memory area within which the fault occurred
251 * Address = The absolute address of fault
252 * RETURNS: Status
253 * NOTES: This function is called with the address space lock held.
254 */
255 {
256 PFN_TYPE Page;
257 NTSTATUS Status;
258 PMM_REGION Region;
259 PMM_PAGEOP PageOp;
260
261 /*
262 * There is a window between taking the page fault and locking the
263 * address space when another thread could load the page so we check
264 * that.
265 */
266 if (MmIsPagePresent(NULL, Address))
267 {
268 if (Locked)
269 {
270 MmLockPage(MmGetPfnForProcess(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 if (Region->Type == MEM_RESERVE || Region->Protect == PAGE_NOACCESS)
290 {
291 return(STATUS_ACCESS_VIOLATION);
292 }
293
294 /*
295 * Get or create a page operation
296 */
297 PageOp = MmGetPageOp(MemoryArea, AddressSpace->Process->UniqueProcessId,
298 (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
299 MM_PAGEOP_PAGEIN, FALSE);
300 if (PageOp == NULL)
301 {
302 DPRINT1("MmGetPageOp failed");
303 KEBUGCHECK(0);
304 }
305
306 /*
307 * Check if someone else is already handling this fault, if so wait
308 * for them
309 */
310 if (PageOp->Thread != PsGetCurrentThread())
311 {
312 MmUnlockAddressSpace(AddressSpace);
313 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
314 0,
315 KernelMode,
316 FALSE,
317 NULL);
318 /*
319 * Check for various strange conditions
320 */
321 if (Status != STATUS_SUCCESS)
322 {
323 DPRINT1("Failed to wait for page op\n");
324 KEBUGCHECK(0);
325 }
326 if (PageOp->Status == STATUS_PENDING)
327 {
328 DPRINT1("Woke for page op before completion\n");
329 KEBUGCHECK(0);
330 }
331 /*
332 * If this wasn't a pagein then we need to restart the handling
333 */
334 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
335 {
336 MmLockAddressSpace(AddressSpace);
337 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
338 MmReleasePageOp(PageOp);
339 return(STATUS_MM_RESTART_OPERATION);
340 }
341 /*
342 * If the thread handling this fault has failed then we don't retry
343 */
344 if (!NT_SUCCESS(PageOp->Status))
345 {
346 MmLockAddressSpace(AddressSpace);
347 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
348 Status = PageOp->Status;
349 MmReleasePageOp(PageOp);
350 return(Status);
351 }
352 MmLockAddressSpace(AddressSpace);
353 if (Locked)
354 {
355 MmLockPage(MmGetPfnForProcess(NULL, Address));
356 }
357 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
358 MmReleasePageOp(PageOp);
359 return(STATUS_SUCCESS);
360 }
361
362 /*
363 * Try to allocate a page
364 */
365 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
366 if (Status == STATUS_NO_MEMORY)
367 {
368 MmUnlockAddressSpace(AddressSpace);
369 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
370 MmLockAddressSpace(AddressSpace);
371 }
372 if (!NT_SUCCESS(Status))
373 {
374 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
375 KEBUGCHECK(0);
376 }
377
378 /*
379 * Handle swapped out pages.
380 */
381 if (MmIsPageSwapEntry(NULL, Address))
382 {
383 SWAPENTRY SwapEntry;
384
385 MmDeletePageFileMapping(AddressSpace->Process, Address, &SwapEntry);
386 Status = MmReadFromSwapPage(SwapEntry, Page);
387 if (!NT_SUCCESS(Status))
388 {
389 KEBUGCHECK(0);
390 }
391 MmSetSavedSwapEntryPage(Page, SwapEntry);
392 }
393
394 /*
395 * Set the page. If we fail because we are out of memory then
396 * try again
397 */
398 Status = MmCreateVirtualMapping(AddressSpace->Process,
399 (PVOID)PAGE_ROUND_DOWN(Address),
400 Region->Protect,
401 &Page,
402 1);
403 while (Status == STATUS_NO_MEMORY)
404 {
405 MmUnlockAddressSpace(AddressSpace);
406 Status = MmCreateVirtualMapping(AddressSpace->Process,
407 Address,
408 Region->Protect,
409 &Page,
410 1);
411 MmLockAddressSpace(AddressSpace);
412 }
413 if (!NT_SUCCESS(Status))
414 {
415 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
416 KEBUGCHECK(0);
417 return(Status);
418 }
419
420 /*
421 * Add the page to the process's working set
422 */
423 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAGE_ROUND_DOWN(Address));
424
425 /*
426 * Finish the operation
427 */
428 if (Locked)
429 {
430 MmLockPage(Page);
431 }
432 PageOp->Status = STATUS_SUCCESS;
433 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
434 MmReleasePageOp(PageOp);
435 return(STATUS_SUCCESS);
436 }
437
438 VOID STATIC
439 MmModifyAttributes(PMADDRESS_SPACE AddressSpace,
440 PVOID BaseAddress,
441 ULONG RegionSize,
442 ULONG OldType,
443 ULONG OldProtect,
444 ULONG NewType,
445 ULONG NewProtect)
446 /*
447 * FUNCTION: Modify the attributes of a memory region
448 */
449 {
450 /*
451 * If we are switching a previously committed region to reserved then
452 * free any allocated pages within the region
453 */
454 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
455 {
456 ULONG i;
457
458 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
459 {
460 PFN_TYPE Page;
461
462 if (MmIsPageSwapEntry(AddressSpace->Process,
463 (char*)BaseAddress + (i * PAGE_SIZE)))
464 {
465 SWAPENTRY SwapEntry;
466
467 MmDeletePageFileMapping(AddressSpace->Process,
468 (char*)BaseAddress + (i * PAGE_SIZE),
469 &SwapEntry);
470 MmFreeSwapPage(SwapEntry);
471 }
472 else
473 {
474 MmDeleteVirtualMapping(AddressSpace->Process,
475 (char*)BaseAddress + (i*PAGE_SIZE),
476 FALSE, NULL, &Page);
477 if (Page != 0)
478 {
479 SWAPENTRY SavedSwapEntry;
480 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
481 if (SavedSwapEntry != 0)
482 {
483 MmFreeSwapPage(SavedSwapEntry);
484 MmSetSavedSwapEntryPage(Page, 0);
485 }
486 MmDeleteRmap(Page, AddressSpace->Process,
487 (char*)BaseAddress + (i * PAGE_SIZE));
488 MmReleasePageMemoryConsumer(MC_USER, Page);
489 }
490 }
491 }
492 }
493
494 /*
495 * If we are changing the protection attributes of a committed region then
496 * alter the attributes for any allocated pages within the region
497 */
498 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
499 OldProtect != NewProtect)
500 {
501 ULONG i;
502
503 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
504 {
505 if (MmIsPagePresent(AddressSpace->Process,
506 (char*)BaseAddress + (i*PAGE_SIZE)))
507 {
508 MmSetPageProtect(AddressSpace->Process,
509 (char*)BaseAddress + (i*PAGE_SIZE),
510 NewProtect);
511 }
512 }
513 }
514 }
515
516 /*
517 * @implemented
518 */
519 NTSTATUS STDCALL
520 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
521 IN OUT PVOID* UBaseAddress,
522 IN ULONG ZeroBits,
523 IN OUT PULONG URegionSize,
524 IN ULONG AllocationType,
525 IN ULONG Protect)
526 /*
527 * FUNCTION: Allocates a block of virtual memory in the process address space
528 * ARGUMENTS:
529 * ProcessHandle = The handle of the process which owns the virtual memory
530 * BaseAddress = A pointer to the virtual memory allocated. If you
531 * supply a non zero value the system will try to
532 * allocate the memory at the address supplied. It round
533 * it down to a multiple of the page size.
534 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
535 * that must be zero, ensuring that the memory will be
536 * allocated at a address below a certain value.
537 * RegionSize = The number of bytes to allocate
538 * AllocationType = Indicates the type of virtual memory you like to
539 * allocated, can be a combination of MEM_COMMIT,
540 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
541 * Protect = Indicates the protection type of the pages allocated, can be
542 * a combination of PAGE_READONLY, PAGE_READWRITE,
543 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
544 * PAGE_NOACCESS
545 * RETURNS: Status
546 */
547 {
548 PEPROCESS Process;
549 MEMORY_AREA* MemoryArea;
550 ULONG_PTR MemoryAreaLength;
551 ULONG Type;
552 NTSTATUS Status;
553 PMADDRESS_SPACE AddressSpace;
554 PVOID BaseAddress;
555 ULONG RegionSize;
556 PVOID PBaseAddress;
557 ULONG PRegionSize;
558 PHYSICAL_ADDRESS BoundaryAddressMultiple;
559
560 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
561 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
562 *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
563 Protect);
564
565 /* Check for valid protection flags */
566 if ((Protect & PAGE_FLAGS_VALID_FROM_USER_MODE) != Protect)
567 {
568 DPRINT1("Invalid page protection\n");
569 return STATUS_INVALID_PAGE_PROTECTION;
570 }
571
572 /* Check for valid Zero bits */
573 if (ZeroBits > 21)
574 {
575 DPRINT1("Too many zero bits\n");
576 return STATUS_INVALID_PARAMETER_3;
577 }
578
579 /* Check for valid Allocation Types */
580 if ((AllocationType &~ (MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
581 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
582 {
583 DPRINT1("Invalid Allocation Type\n");
584 return STATUS_INVALID_PARAMETER_5;
585 }
586
587 /* Check for at least one of these Allocation Types to be set */
588 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
589 {
590 DPRINT1("No memory allocation base type\n");
591 return STATUS_INVALID_PARAMETER_5;
592 }
593
594 /* MEM_RESET is an exclusive flag, make sure that is valid too */
595 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
596 {
597 DPRINT1("MEM_RESET used illegaly\n");
598 return STATUS_INVALID_PARAMETER_5;
599 }
600
601 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
602 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
603 {
604 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
605 return STATUS_INVALID_PARAMETER_5;
606 }
607
608 /* MEM_PHYSICAL can only be used with MEM_RESERVE, and can only be R/W */
609 if (AllocationType & MEM_PHYSICAL)
610 {
611 /* First check for MEM_RESERVE exclusivity */
612 if (AllocationType != (MEM_RESERVE | MEM_PHYSICAL))
613 {
614 DPRINT1("MEM_PHYSICAL used with other flags then MEM_RESERVE or"
615 "MEM_RESERVE was not present at all\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_PAGE_PROTECTION;
624 }
625 }
626
627 PBaseAddress = *UBaseAddress;
628 PRegionSize = *URegionSize;
629 BoundaryAddressMultiple.QuadPart = 0;
630
631 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
632 RegionSize = PAGE_ROUND_UP(PBaseAddress + PRegionSize) -
633 PAGE_ROUND_DOWN(PBaseAddress);
634
635 /*
636 * We've captured and calculated the data, now do more checks
637 * Yes, MmCreateMemoryArea does similar checks, but they don't return
638 * the right status codes that a caller of this routine would expect.
639 */
640 if (BaseAddress >= MM_HIGHEST_USER_ADDRESS)
641 {
642 DPRINT1("Virtual allocation above User Space\n");
643 return STATUS_INVALID_PARAMETER_2;
644 }
645 if (!RegionSize)
646 {
647 DPRINT1("Region size is invalid\n");
648 return STATUS_INVALID_PARAMETER_4;
649 }
650 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)BaseAddress) < RegionSize)
651 {
652 DPRINT1("Region size would overflow into kernel-memory\n");
653 return STATUS_INVALID_PARAMETER_4;
654 }
655
656 /*
657 * Copy on Write is reserved for system use. This case is a certain failure
658 * but there may be other cases...needs more testing
659 */
660 if ((!BaseAddress || (AllocationType & MEM_RESERVE)) &&
661 ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY)))
662 {
663 DPRINT1("Copy on write is not supported by VirtualAlloc\n");
664 return STATUS_INVALID_PAGE_PROTECTION;
665 }
666
667
668 Status = ObReferenceObjectByHandle(ProcessHandle,
669 PROCESS_VM_OPERATION,
670 NULL,
671 UserMode,
672 (PVOID*)(&Process),
673 NULL);
674 if (!NT_SUCCESS(Status))
675 {
676 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
677 return(Status);
678 }
679
680 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
681 DPRINT("Type %x\n", Type);
682
683 AddressSpace = &Process->AddressSpace;
684 MmLockAddressSpace(AddressSpace);
685
686 if (PBaseAddress != 0)
687 {
688 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
689
690 if (MemoryArea != NULL)
691 {
692 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
693 (ULONG_PTR)MemoryArea->StartingAddress;
694 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
695 MemoryAreaLength >= RegionSize)
696 {
697 Status =
698 MmAlterRegion(AddressSpace,
699 MemoryArea->StartingAddress,
700 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
701 BaseAddress, RegionSize,
702 Type, Protect, MmModifyAttributes);
703 MmUnlockAddressSpace(AddressSpace);
704 ObDereferenceObject(Process);
705 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
706 return(Status);
707 }
708 else if (MemoryAreaLength >= RegionSize)
709 {
710 Status =
711 MmAlterRegion(AddressSpace,
712 MemoryArea->StartingAddress,
713 &MemoryArea->Data.SectionData.RegionListHead,
714 BaseAddress, RegionSize,
715 Type, Protect, MmModifyAttributes);
716 MmUnlockAddressSpace(AddressSpace);
717 ObDereferenceObject(Process);
718 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
719 return(Status);
720 }
721 else
722 {
723 MmUnlockAddressSpace(AddressSpace);
724 ObDereferenceObject(Process);
725 return(STATUS_UNSUCCESSFUL);
726 }
727 }
728 }
729
730 Status = MmCreateMemoryArea(Process,
731 AddressSpace,
732 MEMORY_AREA_VIRTUAL_MEMORY,
733 &BaseAddress,
734 RegionSize,
735 Protect,
736 &MemoryArea,
737 PBaseAddress != 0,
738 (AllocationType & MEM_TOP_DOWN) == MEM_TOP_DOWN,
739 BoundaryAddressMultiple);
740 if (!NT_SUCCESS(Status))
741 {
742 MmUnlockAddressSpace(AddressSpace);
743 ObDereferenceObject(Process);
744 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
745 return(Status);
746 }
747
748 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
749 (ULONG_PTR)MemoryArea->StartingAddress;
750
751 MmInitialiseRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
752 MemoryAreaLength, Type, Protect);
753
754 if ((AllocationType & MEM_COMMIT) &&
755 ((Protect & PAGE_READWRITE) ||
756 (Protect & PAGE_EXECUTE_READWRITE)))
757 {
758 MmReserveSwapPages(MemoryAreaLength);
759 }
760
761 *UBaseAddress = BaseAddress;
762 *URegionSize = MemoryAreaLength;
763 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
764
765 MmUnlockAddressSpace(AddressSpace);
766 ObDereferenceObject(Process);
767 return(STATUS_SUCCESS);
768 }
769
770 VOID STATIC
771 MmFreeVirtualMemoryPage(PVOID Context,
772 MEMORY_AREA* MemoryArea,
773 PVOID Address,
774 PFN_TYPE Page,
775 SWAPENTRY SwapEntry,
776 BOOLEAN Dirty)
777 {
778 PEPROCESS Process = (PEPROCESS)Context;
779
780 if (Page != 0)
781 {
782 SWAPENTRY SavedSwapEntry;
783 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
784 if (SavedSwapEntry != 0)
785 {
786 MmFreeSwapPage(SavedSwapEntry);
787 MmSetSavedSwapEntryPage(Page, 0);
788 }
789 MmDeleteRmap(Page, Process, Address);
790 MmReleasePageMemoryConsumer(MC_USER, Page);
791 }
792 else if (SwapEntry != 0)
793 {
794 MmFreeSwapPage(SwapEntry);
795 }
796 }
797
798 VOID
799 NTAPI
800 MmFreeVirtualMemory(PEPROCESS Process,
801 PMEMORY_AREA MemoryArea)
802 {
803 PLIST_ENTRY current_entry;
804 PMM_REGION current;
805 ULONG i;
806
807 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
808 MemoryArea);
809
810 /* Mark this memory area as about to be deleted. */
811 MemoryArea->DeleteInProgress = TRUE;
812
813 /*
814 * Wait for any ongoing paging operations. Notice that since we have
815 * flagged this memory area as deleted no more page ops will be added.
816 */
817 if (MemoryArea->PageOpCount > 0)
818 {
819 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
820 (ULONG_PTR)MemoryArea->StartingAddress;
821
822 /* FiN TODO: Optimize loop counter! */
823 for (i = 0; i < PAGE_ROUND_UP(MemoryAreaLength) / PAGE_SIZE; i++)
824 {
825 PMM_PAGEOP PageOp;
826
827 if (MemoryArea->PageOpCount == 0)
828 {
829 break;
830 }
831
832 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
833 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
834 NULL, 0);
835 if (PageOp != NULL)
836 {
837 NTSTATUS Status;
838 MmUnlockAddressSpace(&Process->AddressSpace);
839 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
840 0,
841 KernelMode,
842 FALSE,
843 NULL);
844 if (Status != STATUS_SUCCESS)
845 {
846 DPRINT1("Failed to wait for page op\n");
847 KEBUGCHECK(0);
848 }
849 MmLockAddressSpace(&Process->AddressSpace);
850 MmReleasePageOp(PageOp);
851 }
852 }
853 }
854
855 /* Free all the individual segments. */
856 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
857 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
858 {
859 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
860 current_entry = current_entry->Flink;
861 ExFreePool(current);
862 }
863
864 /* Actually free the memory area. */
865 MmFreeMemoryArea(&Process->AddressSpace,
866 MemoryArea,
867 MmFreeVirtualMemoryPage,
868 (PVOID)Process);
869 }
870
871 /*
872 * @implemented
873 */
874 NTSTATUS STDCALL
875 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
876 IN PVOID* PBaseAddress,
877 IN PULONG PRegionSize,
878 IN ULONG FreeType)
879 /*
880 * FUNCTION: Frees a range of virtual memory
881 * ARGUMENTS:
882 * ProcessHandle = Points to the process that allocated the virtual
883 * memory
884 * BaseAddress = Points to the memory address, rounded down to a
885 * multiple of the pagesize
886 * RegionSize = Limits the range to free, rounded up to a multiple of
887 * the paging size
888 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
889 * RETURNS: Status
890 */
891 {
892 MEMORY_AREA* MemoryArea;
893 NTSTATUS Status;
894 PEPROCESS Process;
895 PMADDRESS_SPACE AddressSpace;
896 PVOID BaseAddress;
897 ULONG RegionSize;
898
899 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
900 "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
901 *PRegionSize,FreeType);
902
903 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
904 RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
905 PAGE_ROUND_DOWN((*PBaseAddress));
906
907 Status = ObReferenceObjectByHandle(ProcessHandle,
908 PROCESS_VM_OPERATION,
909 PsProcessType,
910 UserMode,
911 (PVOID*)(&Process),
912 NULL);
913 if (!NT_SUCCESS(Status))
914 {
915 return(Status);
916 }
917
918 AddressSpace = &Process->AddressSpace;
919
920 MmLockAddressSpace(AddressSpace);
921 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
922 if (MemoryArea == NULL)
923 {
924 MmUnlockAddressSpace(AddressSpace);
925 ObDereferenceObject(Process);
926 return(STATUS_UNSUCCESSFUL);
927 }
928
929 switch (FreeType)
930 {
931 case MEM_RELEASE:
932 /* We can only free a memory area in one step. */
933 if (MemoryArea->StartingAddress != BaseAddress ||
934 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
935 {
936 MmUnlockAddressSpace(AddressSpace);
937 ObDereferenceObject(Process);
938 return(STATUS_UNSUCCESSFUL);
939 }
940 MmFreeVirtualMemory(Process, MemoryArea);
941 MmUnlockAddressSpace(AddressSpace);
942 ObDereferenceObject(Process);
943 return(STATUS_SUCCESS);
944
945 case MEM_DECOMMIT:
946 Status =
947 MmAlterRegion(AddressSpace,
948 MemoryArea->StartingAddress,
949 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
950 BaseAddress,
951 RegionSize,
952 MEM_RESERVE,
953 PAGE_NOACCESS,
954 MmModifyAttributes);
955 MmUnlockAddressSpace(AddressSpace);
956 ObDereferenceObject(Process);
957 return(Status);
958 }
959 MmUnlockAddressSpace(AddressSpace);
960 ObDereferenceObject(Process);
961 return(STATUS_NOT_IMPLEMENTED);
962 }
963
964 NTSTATUS
965 NTAPI
966 MmProtectAnonMem(PMADDRESS_SPACE AddressSpace,
967 PMEMORY_AREA MemoryArea,
968 PVOID BaseAddress,
969 ULONG Length,
970 ULONG Protect,
971 PULONG OldProtect)
972 {
973 PMM_REGION Region;
974 NTSTATUS Status;
975
976 Region = MmFindRegion(MemoryArea->StartingAddress,
977 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
978 BaseAddress, NULL);
979 *OldProtect = Region->Protect;
980 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
981 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
982 BaseAddress, Length, Region->Type, Protect,
983 MmModifyAttributes);
984 return(Status);
985 }
986
987 NTSTATUS STDCALL
988 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
989 PVOID Address,
990 PMEMORY_BASIC_INFORMATION Info,
991 PULONG ResultLength)
992 {
993 PMM_REGION Region;
994 PVOID RegionBase;
995
996 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
997
998 Region = MmFindRegion(MemoryArea->StartingAddress,
999 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
1000 Address, &RegionBase);
1001 Info->BaseAddress = RegionBase;
1002 Info->AllocationBase = MemoryArea->StartingAddress;
1003 Info->AllocationProtect = MemoryArea->Attributes;
1004 Info->RegionSize = Region->Length;
1005 Info->State = Region->Type;
1006 Info->Protect = Region->Protect;
1007 Info->Type = MEM_PRIVATE;
1008
1009 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1010 return(STATUS_SUCCESS);
1011 }
1012
1013 /* EOF */