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