- Remove unused and broken MmSplitMemoryArea function.
[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.34 2004/12/19 16:16:57 navaraf 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 <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->BaseAddress,
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 Type;
529 NTSTATUS Status;
530 PMADDRESS_SPACE AddressSpace;
531 PVOID BaseAddress;
532 ULONG RegionSize;
533 PVOID PBaseAddress;
534 ULONG PRegionSize;
535 PHYSICAL_ADDRESS BoundaryAddressMultiple;
536
537 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
538 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
539 *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
540 Protect);
541
542 /*
543 * Check the validity of the parameters
544 */
545 if ((Protect & PAGE_FLAGS_VALID_FROM_USER_MODE) != Protect)
546 {
547 return(STATUS_INVALID_PAGE_PROTECTION);
548 }
549 if ((AllocationType & (MEM_COMMIT | MEM_RESERVE)) == 0)
550 {
551 return(STATUS_INVALID_PARAMETER);
552 }
553
554 PBaseAddress = *UBaseAddress;
555 PRegionSize = *URegionSize;
556 BoundaryAddressMultiple.QuadPart = 0;
557
558 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
559 RegionSize = PAGE_ROUND_UP(PBaseAddress + PRegionSize) -
560 PAGE_ROUND_DOWN(PBaseAddress);
561
562 Status = ObReferenceObjectByHandle(ProcessHandle,
563 PROCESS_VM_OPERATION,
564 NULL,
565 UserMode,
566 (PVOID*)(&Process),
567 NULL);
568 if (!NT_SUCCESS(Status))
569 {
570 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
571 return(Status);
572 }
573
574 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
575 DPRINT("Type %x\n", Type);
576
577 AddressSpace = &Process->AddressSpace;
578 MmLockAddressSpace(AddressSpace);
579
580 if (PBaseAddress != 0)
581 {
582 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
583 BaseAddress);
584
585 if (MemoryArea != NULL &&
586 MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
587 MemoryArea->Length >= RegionSize)
588 {
589 Status =
590 MmAlterRegion(AddressSpace,
591 MemoryArea->BaseAddress,
592 &MemoryArea->Data.VirtualMemoryData.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 if (MemoryArea != NULL && MemoryArea->Length >= RegionSize)
601 {
602 Status =
603 MmAlterRegion(AddressSpace,
604 MemoryArea->BaseAddress,
605 &MemoryArea->Data.SectionData.RegionListHead,
606 BaseAddress, RegionSize,
607 Type, Protect, MmModifyAttributes);
608 MmUnlockAddressSpace(AddressSpace);
609 ObDereferenceObject(Process);
610 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
611 return(Status);
612 }
613 else if (MemoryArea != NULL)
614 {
615 MmUnlockAddressSpace(AddressSpace);
616 ObDereferenceObject(Process);
617 return(STATUS_UNSUCCESSFUL);
618 }
619 }
620
621 Status = MmCreateMemoryArea(Process,
622 AddressSpace,
623 MEMORY_AREA_VIRTUAL_MEMORY,
624 &BaseAddress,
625 RegionSize,
626 Protect,
627 &MemoryArea,
628 PBaseAddress != 0,
629 (AllocationType & MEM_TOP_DOWN),
630 BoundaryAddressMultiple);
631 if (!NT_SUCCESS(Status))
632 {
633 MmUnlockAddressSpace(AddressSpace);
634 ObDereferenceObject(Process);
635 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
636 return(Status);
637 }
638 MmInitialiseRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
639 MemoryArea->Length, Type, Protect);
640
641 if ((AllocationType & MEM_COMMIT) &&
642 ((Protect & PAGE_READWRITE) ||
643 (Protect & PAGE_EXECUTE_READWRITE)))
644 {
645 MmReserveSwapPages(MemoryArea->Length);
646 }
647
648 *UBaseAddress = BaseAddress;
649 *URegionSize = MemoryArea->Length;
650 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
651
652 MmUnlockAddressSpace(AddressSpace);
653 ObDereferenceObject(Process);
654 return(STATUS_SUCCESS);
655 }
656
657 VOID STATIC
658 MmFreeVirtualMemoryPage(PVOID Context,
659 MEMORY_AREA* MemoryArea,
660 PVOID Address,
661 PFN_TYPE Page,
662 SWAPENTRY SwapEntry,
663 BOOLEAN Dirty)
664 {
665 PEPROCESS Process = (PEPROCESS)Context;
666
667 if (Page != 0)
668 {
669 SWAPENTRY SavedSwapEntry;
670 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
671 if (SavedSwapEntry != 0)
672 {
673 MmFreeSwapPage(SavedSwapEntry);
674 MmSetSavedSwapEntryPage(Page, 0);
675 }
676 MmDeleteRmap(Page, Process, Address);
677 MmReleasePageMemoryConsumer(MC_USER, Page);
678 }
679 else if (SwapEntry != 0)
680 {
681 MmFreeSwapPage(SwapEntry);
682 }
683 }
684
685 VOID
686 MmFreeVirtualMemory(PEPROCESS Process,
687 PMEMORY_AREA MemoryArea)
688 {
689 PLIST_ENTRY current_entry;
690 PMM_REGION current;
691 ULONG i;
692
693 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
694 MemoryArea);
695
696 /* Mark this memory area as about to be deleted. */
697 MemoryArea->DeleteInProgress = TRUE;
698
699 /*
700 * Wait for any ongoing paging operations. Notice that since we have
701 * flagged this memory area as deleted no more page ops will be added.
702 */
703 if (MemoryArea->PageOpCount > 0)
704 {
705 for (i = 0; i < PAGE_ROUND_UP(MemoryArea->Length) / PAGE_SIZE; i++)
706 {
707 PMM_PAGEOP PageOp;
708
709 if (MemoryArea->PageOpCount == 0)
710 {
711 break;
712 }
713
714 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
715 (char*)MemoryArea->BaseAddress + (i * PAGE_SIZE),
716 NULL, 0);
717 if (PageOp != NULL)
718 {
719 NTSTATUS Status;
720 MmUnlockAddressSpace(&Process->AddressSpace);
721 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
722 0,
723 KernelMode,
724 FALSE,
725 NULL);
726 if (Status != STATUS_SUCCESS)
727 {
728 DPRINT1("Failed to wait for page op\n");
729 KEBUGCHECK(0);
730 }
731 MmLockAddressSpace(&Process->AddressSpace);
732 MmReleasePageOp(PageOp);
733 }
734 }
735 }
736
737 /* Free all the individual segments. */
738 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
739 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
740 {
741 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
742 current_entry = current_entry->Flink;
743 ExFreePool(current);
744 }
745
746 /* Actually free the memory area. */
747 MmFreeMemoryArea(&Process->AddressSpace,
748 MemoryArea->BaseAddress,
749 0,
750 MmFreeVirtualMemoryPage,
751 (PVOID)Process);
752 }
753
754 /*
755 * @implemented
756 */
757 NTSTATUS STDCALL
758 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
759 IN PVOID* PBaseAddress,
760 IN PULONG PRegionSize,
761 IN ULONG FreeType)
762 /*
763 * FUNCTION: Frees a range of virtual memory
764 * ARGUMENTS:
765 * ProcessHandle = Points to the process that allocated the virtual
766 * memory
767 * BaseAddress = Points to the memory address, rounded down to a
768 * multiple of the pagesize
769 * RegionSize = Limits the range to free, rounded up to a multiple of
770 * the paging size
771 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
772 * RETURNS: Status
773 */
774 {
775 MEMORY_AREA* MemoryArea;
776 NTSTATUS Status;
777 PEPROCESS Process;
778 PMADDRESS_SPACE AddressSpace;
779 PVOID BaseAddress;
780 ULONG RegionSize;
781
782 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
783 "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
784 *PRegionSize,FreeType);
785
786 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
787 RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
788 PAGE_ROUND_DOWN((*PBaseAddress));
789
790 Status = ObReferenceObjectByHandle(ProcessHandle,
791 PROCESS_VM_OPERATION,
792 PsProcessType,
793 UserMode,
794 (PVOID*)(&Process),
795 NULL);
796 if (!NT_SUCCESS(Status))
797 {
798 return(Status);
799 }
800
801 AddressSpace = &Process->AddressSpace;
802
803 MmLockAddressSpace(AddressSpace);
804 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
805 BaseAddress);
806 if (MemoryArea == NULL)
807 {
808 MmUnlockAddressSpace(AddressSpace);
809 ObDereferenceObject(Process);
810 return(STATUS_UNSUCCESSFUL);
811 }
812
813 switch (FreeType)
814 {
815 case MEM_RELEASE:
816 /* We can only free a memory area in one step. */
817 if (MemoryArea->BaseAddress != BaseAddress ||
818 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
819 {
820 MmUnlockAddressSpace(AddressSpace);
821 ObDereferenceObject(Process);
822 return(STATUS_UNSUCCESSFUL);
823 }
824 MmFreeVirtualMemory(Process, MemoryArea);
825 MmUnlockAddressSpace(AddressSpace);
826 ObDereferenceObject(Process);
827 return(STATUS_SUCCESS);
828
829 case MEM_DECOMMIT:
830 Status =
831 MmAlterRegion(AddressSpace,
832 MemoryArea->BaseAddress,
833 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
834 BaseAddress,
835 RegionSize,
836 MEM_RESERVE,
837 PAGE_NOACCESS,
838 MmModifyAttributes);
839 MmUnlockAddressSpace(AddressSpace);
840 ObDereferenceObject(Process);
841 return(Status);
842 }
843 MmUnlockAddressSpace(AddressSpace);
844 ObDereferenceObject(Process);
845 return(STATUS_NOT_IMPLEMENTED);
846 }
847
848 NTSTATUS
849 MmProtectAnonMem(PMADDRESS_SPACE AddressSpace,
850 PMEMORY_AREA MemoryArea,
851 PVOID BaseAddress,
852 ULONG Length,
853 ULONG Protect,
854 PULONG OldProtect)
855 {
856 PMM_REGION Region;
857 NTSTATUS Status;
858
859 Region = MmFindRegion(MemoryArea->BaseAddress,
860 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
861 BaseAddress, NULL);
862 *OldProtect = Region->Protect;
863 Status = MmAlterRegion(AddressSpace, MemoryArea->BaseAddress,
864 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
865 BaseAddress, Length, Region->Type, Protect,
866 MmModifyAttributes);
867 return(Status);
868 }
869
870 NTSTATUS STDCALL
871 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
872 PVOID Address,
873 PMEMORY_BASIC_INFORMATION Info,
874 PULONG ResultLength)
875 {
876 PMM_REGION Region;
877 PVOID RegionBase;
878
879 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
880
881 Region = MmFindRegion(MemoryArea->BaseAddress,
882 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
883 Address, &RegionBase);
884 Info->BaseAddress = RegionBase;
885 Info->AllocationBase = MemoryArea->BaseAddress;
886 Info->AllocationProtect = MemoryArea->Attributes;
887 Info->RegionSize = (char*)RegionBase + Region->Length - (char*)Info->BaseAddress;
888 Info->State = Region->Type;
889 Info->Protect = Region->Protect;
890 Info->Type = MEM_PRIVATE;
891
892 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
893 return(STATUS_SUCCESS);
894 }
895
896 /* EOF */