- Remove some public APIs from internal headers.
[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 BOOLEAN 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(AddressSpace,
731 MEMORY_AREA_VIRTUAL_MEMORY,
732 &BaseAddress,
733 RegionSize,
734 Protect,
735 &MemoryArea,
736 PBaseAddress != 0,
737 AllocationType & MEM_TOP_DOWN,
738 BoundaryAddressMultiple);
739 if (!NT_SUCCESS(Status))
740 {
741 MmUnlockAddressSpace(AddressSpace);
742 ObDereferenceObject(Process);
743 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
744 return(Status);
745 }
746
747 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
748 (ULONG_PTR)MemoryArea->StartingAddress;
749
750 MmInitializeRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
751 MemoryAreaLength, Type, Protect);
752
753 if ((AllocationType & MEM_COMMIT) &&
754 ((Protect & PAGE_READWRITE) ||
755 (Protect & PAGE_EXECUTE_READWRITE)))
756 {
757 MmReserveSwapPages(MemoryAreaLength);
758 }
759
760 *UBaseAddress = BaseAddress;
761 *URegionSize = MemoryAreaLength;
762 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
763
764 MmUnlockAddressSpace(AddressSpace);
765 ObDereferenceObject(Process);
766 return(STATUS_SUCCESS);
767 }
768
769 VOID STATIC
770 MmFreeVirtualMemoryPage(PVOID Context,
771 MEMORY_AREA* MemoryArea,
772 PVOID Address,
773 PFN_TYPE Page,
774 SWAPENTRY SwapEntry,
775 BOOLEAN Dirty)
776 {
777 PEPROCESS Process = (PEPROCESS)Context;
778
779 if (Page != 0)
780 {
781 SWAPENTRY SavedSwapEntry;
782 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
783 if (SavedSwapEntry != 0)
784 {
785 MmFreeSwapPage(SavedSwapEntry);
786 MmSetSavedSwapEntryPage(Page, 0);
787 }
788 MmDeleteRmap(Page, Process, Address);
789 MmReleasePageMemoryConsumer(MC_USER, Page);
790 }
791 else if (SwapEntry != 0)
792 {
793 MmFreeSwapPage(SwapEntry);
794 }
795 }
796
797 VOID
798 NTAPI
799 MmFreeVirtualMemory(PEPROCESS Process,
800 PMEMORY_AREA MemoryArea)
801 {
802 PLIST_ENTRY current_entry;
803 PMM_REGION current;
804 ULONG i;
805
806 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
807 MemoryArea);
808
809 /* Mark this memory area as about to be deleted. */
810 MemoryArea->DeleteInProgress = TRUE;
811
812 /*
813 * Wait for any ongoing paging operations. Notice that since we have
814 * flagged this memory area as deleted no more page ops will be added.
815 */
816 if (MemoryArea->PageOpCount > 0)
817 {
818 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
819 (ULONG_PTR)MemoryArea->StartingAddress;
820
821 /* FiN TODO: Optimize loop counter! */
822 for (i = 0; i < PAGE_ROUND_UP(MemoryAreaLength) / PAGE_SIZE; i++)
823 {
824 PMM_PAGEOP PageOp;
825
826 if (MemoryArea->PageOpCount == 0)
827 {
828 break;
829 }
830
831 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
832 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
833 NULL, 0);
834 if (PageOp != NULL)
835 {
836 NTSTATUS Status;
837 MmUnlockAddressSpace(&Process->AddressSpace);
838 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
839 0,
840 KernelMode,
841 FALSE,
842 NULL);
843 if (Status != STATUS_SUCCESS)
844 {
845 DPRINT1("Failed to wait for page op\n");
846 KEBUGCHECK(0);
847 }
848 MmLockAddressSpace(&Process->AddressSpace);
849 MmReleasePageOp(PageOp);
850 }
851 }
852 }
853
854 /* Free all the individual segments. */
855 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
856 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
857 {
858 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
859 current_entry = current_entry->Flink;
860 ExFreePool(current);
861 }
862
863 /* Actually free the memory area. */
864 MmFreeMemoryArea(&Process->AddressSpace,
865 MemoryArea,
866 MmFreeVirtualMemoryPage,
867 (PVOID)Process);
868 }
869
870 /*
871 * @implemented
872 */
873 NTSTATUS STDCALL
874 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
875 IN PVOID* PBaseAddress,
876 IN PULONG PRegionSize,
877 IN ULONG FreeType)
878 /*
879 * FUNCTION: Frees a range of virtual memory
880 * ARGUMENTS:
881 * ProcessHandle = Points to the process that allocated the virtual
882 * memory
883 * BaseAddress = Points to the memory address, rounded down to a
884 * multiple of the pagesize
885 * RegionSize = Limits the range to free, rounded up to a multiple of
886 * the paging size
887 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
888 * RETURNS: Status
889 */
890 {
891 MEMORY_AREA* MemoryArea;
892 NTSTATUS Status;
893 PEPROCESS Process;
894 PMADDRESS_SPACE AddressSpace;
895 PVOID BaseAddress;
896 ULONG RegionSize;
897
898 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
899 "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
900 *PRegionSize,FreeType);
901
902 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
903 RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
904 PAGE_ROUND_DOWN((*PBaseAddress));
905
906 Status = ObReferenceObjectByHandle(ProcessHandle,
907 PROCESS_VM_OPERATION,
908 PsProcessType,
909 UserMode,
910 (PVOID*)(&Process),
911 NULL);
912 if (!NT_SUCCESS(Status))
913 {
914 return(Status);
915 }
916
917 AddressSpace = &Process->AddressSpace;
918
919 MmLockAddressSpace(AddressSpace);
920 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
921 if (MemoryArea == NULL)
922 {
923 MmUnlockAddressSpace(AddressSpace);
924 ObDereferenceObject(Process);
925 return(STATUS_UNSUCCESSFUL);
926 }
927
928 switch (FreeType)
929 {
930 case MEM_RELEASE:
931 /* We can only free a memory area in one step. */
932 if (MemoryArea->StartingAddress != BaseAddress ||
933 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
934 {
935 MmUnlockAddressSpace(AddressSpace);
936 ObDereferenceObject(Process);
937 return(STATUS_UNSUCCESSFUL);
938 }
939 MmFreeVirtualMemory(Process, MemoryArea);
940 MmUnlockAddressSpace(AddressSpace);
941 ObDereferenceObject(Process);
942 return(STATUS_SUCCESS);
943
944 case MEM_DECOMMIT:
945 Status =
946 MmAlterRegion(AddressSpace,
947 MemoryArea->StartingAddress,
948 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
949 BaseAddress,
950 RegionSize,
951 MEM_RESERVE,
952 PAGE_NOACCESS,
953 MmModifyAttributes);
954 MmUnlockAddressSpace(AddressSpace);
955 ObDereferenceObject(Process);
956 return(Status);
957 }
958 MmUnlockAddressSpace(AddressSpace);
959 ObDereferenceObject(Process);
960 return(STATUS_NOT_IMPLEMENTED);
961 }
962
963 NTSTATUS
964 NTAPI
965 MmProtectAnonMem(PMADDRESS_SPACE AddressSpace,
966 PMEMORY_AREA MemoryArea,
967 PVOID BaseAddress,
968 ULONG Length,
969 ULONG Protect,
970 PULONG OldProtect)
971 {
972 PMM_REGION Region;
973 NTSTATUS Status;
974
975 Region = MmFindRegion(MemoryArea->StartingAddress,
976 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
977 BaseAddress, NULL);
978 *OldProtect = Region->Protect;
979 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
980 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
981 BaseAddress, Length, Region->Type, Protect,
982 MmModifyAttributes);
983 return(Status);
984 }
985
986 NTSTATUS STDCALL
987 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
988 PVOID Address,
989 PMEMORY_BASIC_INFORMATION Info,
990 PULONG ResultLength)
991 {
992 PMM_REGION Region;
993 PVOID RegionBase = NULL;
994
995 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
996
997 Region = MmFindRegion(MemoryArea->StartingAddress,
998 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
999 Address, &RegionBase);
1000 Info->BaseAddress = RegionBase;
1001 Info->AllocationBase = MemoryArea->StartingAddress;
1002 Info->AllocationProtect = MemoryArea->Protect;
1003 Info->RegionSize = Region->Length;
1004 Info->State = Region->Type;
1005 Info->Protect = Region->Protect;
1006 Info->Type = MEM_PRIVATE;
1007
1008 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1009 return(STATUS_SUCCESS);
1010 }
1011
1012 /* EOF */