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