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