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