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