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