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