- Implemented NtQueryVirtualMemory for unallocated regions.
[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.29 2004/07/10 17:01:03 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 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 PhysicalAddress =
63 MmGetPhysicalAddressForProcess(AddressSpace->Process, Address);
64
65 /*
66 * Get that the page actually is dirty.
67 */
68 if (!MmIsDirtyPage(MemoryArea->Process, Address))
69 {
70 PageOp->Status = STATUS_SUCCESS;
71 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
72 MmReleasePageOp(PageOp);
73 return(STATUS_SUCCESS);
74 }
75
76 /*
77 * Speculatively set the mapping to clean.
78 */
79 MmSetCleanPage(MemoryArea->Process, Address);
80
81 /*
82 * If necessary, allocate an entry in the paging file for this page
83 */
84 SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress);
85 if (SwapEntry == 0)
86 {
87 SwapEntry = MmAllocSwapPage();
88 if (SwapEntry == 0)
89 {
90 MmSetDirtyPage(MemoryArea->Process, Address);
91 PageOp->Status = STATUS_PAGEFILE_QUOTA_EXCEEDED;
92 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
93 MmReleasePageOp(PageOp);
94 return(STATUS_PAGEFILE_QUOTA_EXCEEDED);
95 }
96 }
97
98 /*
99 * Write the page to the pagefile
100 */
101 Status = MmWriteToSwapPage(SwapEntry, &PhysicalAddress);
102 if (!NT_SUCCESS(Status))
103 {
104 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
105 Status);
106 MmSetDirtyPage(MemoryArea->Process, Address);
107 PageOp->Status = STATUS_UNSUCCESSFUL;
108 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
109 MmReleasePageOp(PageOp);
110 return(STATUS_UNSUCCESSFUL);
111 }
112
113 /*
114 * Otherwise we have succeeded.
115 */
116 MmSetSavedSwapEntryPage(PhysicalAddress, SwapEntry);
117 PageOp->Status = STATUS_SUCCESS;
118 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
119 MmReleasePageOp(PageOp);
120 return(STATUS_SUCCESS);
121 }
122
123 NTSTATUS
124 MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace,
125 PMEMORY_AREA MemoryArea,
126 PVOID Address,
127 PMM_PAGEOP PageOp)
128 {
129 PHYSICAL_ADDRESS PhysicalAddress;
130 BOOL WasDirty;
131 SWAPENTRY SwapEntry;
132 NTSTATUS Status;
133
134 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
135 Address, MemoryArea->Process->UniqueProcessId);
136
137 /*
138 * Check for paging out from a deleted virtual memory area.
139 */
140 if (MemoryArea->DeleteInProgress)
141 {
142 PageOp->Status = STATUS_UNSUCCESSFUL;
143 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
144 MmReleasePageOp(PageOp);
145 return(STATUS_UNSUCCESSFUL);
146 }
147
148 /*
149 * Disable the virtual mapping.
150 */
151 MmDisableVirtualMapping(MemoryArea->Process, Address,
152 &WasDirty, &PhysicalAddress);
153
154 if (PhysicalAddress.QuadPart == 0)
155 {
156 KEBUGCHECK(0);
157 }
158
159 /*
160 * Paging out non-dirty data is easy.
161 */
162 if (!WasDirty)
163 {
164 MmDeleteVirtualMapping(MemoryArea->Process, Address, FALSE, NULL, NULL);
165 MmDeleteAllRmaps(PhysicalAddress, NULL, NULL);
166 if ((SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress)) != 0)
167 {
168 MmCreatePageFileMapping(MemoryArea->Process, Address, SwapEntry);
169 MmSetSavedSwapEntryPage(PhysicalAddress, 0);
170 }
171 MmReleasePageMemoryConsumer(MC_USER, PhysicalAddress);
172 PageOp->Status = STATUS_SUCCESS;
173 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
174 MmReleasePageOp(PageOp);
175 return(STATUS_SUCCESS);
176 }
177
178 /*
179 * If necessary, allocate an entry in the paging file for this page
180 */
181 SwapEntry = MmGetSavedSwapEntryPage(PhysicalAddress);
182 if (SwapEntry == 0)
183 {
184 SwapEntry = MmAllocSwapPage();
185 if (SwapEntry == 0)
186 {
187 MmShowOutOfSpaceMessagePagingFile();
188 MmEnableVirtualMapping(MemoryArea->Process, Address);
189 PageOp->Status = STATUS_UNSUCCESSFUL;
190 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
191 MmReleasePageOp(PageOp);
192 return(STATUS_PAGEFILE_QUOTA);
193 }
194 }
195
196 /*
197 * Write the page to the pagefile
198 */
199 Status = MmWriteToSwapPage(SwapEntry, &PhysicalAddress);
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 || Region->Protect == PAGE_NOACCESS)
275 {
276 return(STATUS_ACCESS_VIOLATION);
277 }
278
279 /*
280 * Get or create a page operation
281 */
282 PageOp = MmGetPageOp(MemoryArea, (ULONG)MemoryArea->Process->UniqueProcessId,
283 (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
284 MM_PAGEOP_PAGEIN, FALSE);
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 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
323 MmReleasePageOp(PageOp);
324 return(STATUS_MM_RESTART_OPERATION);
325 }
326 /*
327 * If the thread handling this fault has failed then we don't retry
328 */
329 if (!NT_SUCCESS(PageOp->Status))
330 {
331 MmLockAddressSpace(AddressSpace);
332 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
333 Status = PageOp->Status;
334 MmReleasePageOp(PageOp);
335 return(Status);
336 }
337 MmLockAddressSpace(AddressSpace);
338 if (Locked)
339 {
340 MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
341 }
342 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
343 MmReleasePageOp(PageOp);
344 return(STATUS_SUCCESS);
345 }
346
347 /*
348 * Try to allocate a page
349 */
350 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
351 if (Status == STATUS_NO_MEMORY)
352 {
353 MmUnlockAddressSpace(AddressSpace);
354 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
355 MmLockAddressSpace(AddressSpace);
356 }
357 if (!NT_SUCCESS(Status))
358 {
359 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
360 KEBUGCHECK(0);
361 }
362
363 /*
364 * Handle swapped out pages.
365 */
366 if (MmIsPageSwapEntry(NULL, Address))
367 {
368 SWAPENTRY SwapEntry;
369
370 MmDeletePageFileMapping(MemoryArea->Process, Address, &SwapEntry);
371 Status = MmReadFromSwapPage(SwapEntry, &Page);
372 if (!NT_SUCCESS(Status))
373 {
374 KEBUGCHECK(0);
375 }
376 MmSetSavedSwapEntryPage(Page, SwapEntry);
377 }
378
379 /*
380 * Set the page. If we fail because we are out of memory then
381 * try again
382 */
383 Status = MmCreateVirtualMapping(MemoryArea->Process,
384 (PVOID)PAGE_ROUND_DOWN(Address),
385 Region->Protect,
386 Page,
387 FALSE);
388 while (Status == STATUS_NO_MEMORY)
389 {
390 MmUnlockAddressSpace(AddressSpace);
391 Status = MmCreateVirtualMapping(MemoryArea->Process,
392 Address,
393 Region->Protect,
394 Page,
395 TRUE);
396 MmLockAddressSpace(AddressSpace);
397 }
398 if (!NT_SUCCESS(Status))
399 {
400 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
401 KEBUGCHECK(0);
402 return(Status);
403 }
404
405 /*
406 * Add the page to the process's working set
407 */
408 MmInsertRmap(Page, MemoryArea->Process, (PVOID)PAGE_ROUND_DOWN(Address));
409
410 /*
411 * Finish the operation
412 */
413 if (Locked)
414 {
415 MmLockPage(MmGetPhysicalAddressForProcess(NULL, Address));
416 }
417 PageOp->Status = STATUS_SUCCESS;
418 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
419 MmReleasePageOp(PageOp);
420 return(STATUS_SUCCESS);
421 }
422
423 VOID STATIC
424 MmModifyAttributes(PMADDRESS_SPACE AddressSpace,
425 PVOID BaseAddress,
426 ULONG RegionSize,
427 ULONG OldType,
428 ULONG OldProtect,
429 ULONG NewType,
430 ULONG NewProtect)
431 /*
432 * FUNCTION: Modify the attributes of a memory region
433 */
434 {
435 /*
436 * If we are switching a previously committed region to reserved then
437 * free any allocated pages within the region
438 */
439 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
440 {
441 ULONG i;
442
443 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
444 {
445 LARGE_INTEGER PhysicalAddr;
446
447 if (MmIsPageSwapEntry(AddressSpace->Process,
448 (char*)BaseAddress + (i * PAGE_SIZE)))
449 {
450 SWAPENTRY SwapEntry;
451
452 MmDeletePageFileMapping(AddressSpace->Process,
453 (char*)BaseAddress + (i * PAGE_SIZE),
454 &SwapEntry);
455 MmFreeSwapPage(SwapEntry);
456 }
457 else
458 {
459 MmDeleteVirtualMapping(AddressSpace->Process,
460 (char*)BaseAddress + (i*PAGE_SIZE),
461 FALSE, NULL, &PhysicalAddr);
462 if (PhysicalAddr.QuadPart != 0)
463 {
464 SWAPENTRY SavedSwapEntry;
465 SavedSwapEntry = MmGetSavedSwapEntryPage(PhysicalAddr);
466 if (SavedSwapEntry != 0)
467 {
468 MmFreeSwapPage(SavedSwapEntry);
469 MmSetSavedSwapEntryPage(PhysicalAddr, 0);
470 }
471 MmDeleteRmap(PhysicalAddr, AddressSpace->Process,
472 (char*)BaseAddress + (i * PAGE_SIZE));
473 MmReleasePageMemoryConsumer(MC_USER, PhysicalAddr);
474 }
475 }
476 }
477 }
478
479 /*
480 * If we are changing the protection attributes of a committed region then
481 * alter the attributes for any allocated pages within the region
482 */
483 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
484 OldProtect != NewProtect)
485 {
486 ULONG i;
487
488 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
489 {
490 if (MmIsPagePresent(AddressSpace->Process,
491 (char*)BaseAddress + (i*PAGE_SIZE)))
492 {
493 MmSetPageProtect(AddressSpace->Process,
494 (char*)BaseAddress + (i*PAGE_SIZE),
495 NewProtect);
496 }
497 }
498 }
499 }
500
501 /*
502 * @implemented
503 */
504 NTSTATUS STDCALL
505 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
506 IN OUT PVOID* UBaseAddress,
507 IN ULONG ZeroBits,
508 IN OUT PULONG URegionSize,
509 IN ULONG AllocationType,
510 IN ULONG Protect)
511 /*
512 * FUNCTION: Allocates a block of virtual memory in the process address space
513 * ARGUMENTS:
514 * ProcessHandle = The handle of the process which owns the virtual memory
515 * BaseAddress = A pointer to the virtual memory allocated. If you
516 * supply a non zero value the system will try to
517 * allocate the memory at the address supplied. It round
518 * it down to a multiple of the page size.
519 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
520 * that must be zero, ensuring that the memory will be
521 * allocated at a address below a certain value.
522 * RegionSize = The number of bytes to allocate
523 * AllocationType = Indicates the type of virtual memory you like to
524 * allocated, can be a combination of MEM_COMMIT,
525 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
526 * Protect = Indicates the protection type of the pages allocated, can be
527 * a combination of PAGE_READONLY, PAGE_READWRITE,
528 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
529 * PAGE_NOACCESS
530 * RETURNS: Status
531 */
532 {
533 PEPROCESS Process;
534 MEMORY_AREA* MemoryArea;
535 ULONG Type;
536 NTSTATUS Status;
537 PMADDRESS_SPACE AddressSpace;
538 PVOID BaseAddress;
539 ULONG RegionSize;
540 PVOID PBaseAddress;
541 ULONG PRegionSize;
542 PHYSICAL_ADDRESS BoundaryAddressMultiple;
543
544 DPRINT("NtAllocateVirtualMemory(*UBaseAddress %x, "
545 "ZeroBits %d, *URegionSize %x, AllocationType %x, Protect %x)\n",
546 *UBaseAddress,ZeroBits,*URegionSize,AllocationType,
547 Protect);
548
549 /*
550 * Check the validity of the parameters
551 */
552 if ((Protect & PAGE_FLAGS_VALID_FROM_USER_MODE) != Protect)
553 {
554 return(STATUS_INVALID_PAGE_PROTECTION);
555 }
556 if ((AllocationType & (MEM_COMMIT | MEM_RESERVE)) == 0)
557 {
558 return(STATUS_INVALID_PARAMETER);
559 }
560
561 PBaseAddress = *UBaseAddress;
562 PRegionSize = *URegionSize;
563 BoundaryAddressMultiple.QuadPart = 0;
564
565 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
566 RegionSize = PAGE_ROUND_UP(PBaseAddress + PRegionSize) -
567 PAGE_ROUND_DOWN(PBaseAddress);
568
569 Status = ObReferenceObjectByHandle(ProcessHandle,
570 PROCESS_VM_OPERATION,
571 NULL,
572 UserMode,
573 (PVOID*)(&Process),
574 NULL);
575 if (!NT_SUCCESS(Status))
576 {
577 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
578 return(Status);
579 }
580
581 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
582 DPRINT("Type %x\n", Type);
583
584 AddressSpace = &Process->AddressSpace;
585 MmLockAddressSpace(AddressSpace);
586
587 if (PBaseAddress != 0)
588 {
589 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
590 BaseAddress);
591
592 if (MemoryArea != NULL &&
593 MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
594 MemoryArea->Length >= RegionSize)
595 {
596 Status =
597 MmAlterRegion(AddressSpace,
598 MemoryArea->BaseAddress,
599 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
600 BaseAddress, RegionSize,
601 Type, Protect, MmModifyAttributes);
602 MmUnlockAddressSpace(AddressSpace);
603 ObDereferenceObject(Process);
604 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
605 return(Status);
606 }
607 else if (MemoryArea != NULL && MemoryArea->Length >= RegionSize)
608 {
609 Status =
610 MmAlterRegion(AddressSpace,
611 MemoryArea->BaseAddress,
612 &MemoryArea->Data.SectionData.RegionListHead,
613 BaseAddress, RegionSize,
614 Type, Protect, MmModifyAttributes);
615 MmUnlockAddressSpace(AddressSpace);
616 ObDereferenceObject(Process);
617 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
618 return(Status);
619 }
620 else if (MemoryArea != NULL)
621 {
622 MmUnlockAddressSpace(AddressSpace);
623 ObDereferenceObject(Process);
624 return(STATUS_UNSUCCESSFUL);
625 }
626 }
627
628 Status = MmCreateMemoryArea(Process,
629 AddressSpace,
630 MEMORY_AREA_VIRTUAL_MEMORY,
631 &BaseAddress,
632 RegionSize,
633 Protect,
634 &MemoryArea,
635 PBaseAddress != 0,
636 (AllocationType & MEM_TOP_DOWN),
637 BoundaryAddressMultiple);
638 if (!NT_SUCCESS(Status))
639 {
640 MmUnlockAddressSpace(AddressSpace);
641 ObDereferenceObject(Process);
642 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
643 return(Status);
644 }
645 MmInitialiseRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
646 RegionSize, Type, Protect);
647
648 if ((AllocationType & MEM_COMMIT) &&
649 ((Protect & PAGE_READWRITE) ||
650 (Protect & PAGE_EXECUTE_READWRITE)))
651 {
652 MmReserveSwapPages(RegionSize);
653 }
654
655 *UBaseAddress = BaseAddress;
656 *URegionSize = RegionSize;
657 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
658
659 MmUnlockAddressSpace(AddressSpace);
660 ObDereferenceObject(Process);
661 return(STATUS_SUCCESS);
662 }
663
664 VOID STATIC
665 MmFreeVirtualMemoryPage(PVOID Context,
666 MEMORY_AREA* MemoryArea,
667 PVOID Address,
668 PHYSICAL_ADDRESS PhysicalAddr,
669 SWAPENTRY SwapEntry,
670 BOOLEAN Dirty)
671 {
672 PEPROCESS Process = (PEPROCESS)Context;
673
674 if (PhysicalAddr.QuadPart != 0)
675 {
676 SWAPENTRY SavedSwapEntry;
677 SavedSwapEntry = MmGetSavedSwapEntryPage(PhysicalAddr);
678 if (SavedSwapEntry != 0)
679 {
680 MmFreeSwapPage(SavedSwapEntry);
681 MmSetSavedSwapEntryPage(PhysicalAddr, 0);
682 }
683 MmDeleteRmap(PhysicalAddr, Process, Address);
684 MmReleasePageMemoryConsumer(MC_USER, PhysicalAddr);
685 }
686 else if (SwapEntry != 0)
687 {
688 MmFreeSwapPage(SwapEntry);
689 }
690 }
691
692 VOID
693 MmFreeVirtualMemory(PEPROCESS Process,
694 PMEMORY_AREA MemoryArea)
695 {
696 PLIST_ENTRY current_entry;
697 PMM_REGION current;
698 ULONG i;
699
700 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
701 MemoryArea);
702
703 /* Mark this memory area as about to be deleted. */
704 MemoryArea->DeleteInProgress = TRUE;
705
706 /*
707 * Wait for any ongoing paging operations. Notice that since we have
708 * flagged this memory area as deleted no more page ops will be added.
709 */
710 if (MemoryArea->PageOpCount > 0)
711 {
712 for (i = 0; i < PAGE_ROUND_UP(MemoryArea->Length) / PAGE_SIZE; i++)
713 {
714 PMM_PAGEOP PageOp;
715
716 if (MemoryArea->PageOpCount == 0)
717 {
718 break;
719 }
720
721 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
722 (char*)MemoryArea->BaseAddress + (i * PAGE_SIZE),
723 NULL, 0);
724 if (PageOp != NULL)
725 {
726 NTSTATUS Status;
727 MmUnlockAddressSpace(&Process->AddressSpace);
728 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
729 0,
730 KernelMode,
731 FALSE,
732 NULL);
733 if (Status != STATUS_SUCCESS)
734 {
735 DPRINT1("Failed to wait for page op\n");
736 KEBUGCHECK(0);
737 }
738 MmLockAddressSpace(&Process->AddressSpace);
739 MmReleasePageOp(PageOp);
740 }
741 }
742 }
743
744 /* Free all the individual segments. */
745 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
746 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
747 {
748 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
749 current_entry = current_entry->Flink;
750 ExFreePool(current);
751 }
752
753 /* Actually free the memory area. */
754 MmFreeMemoryArea(&Process->AddressSpace,
755 MemoryArea->BaseAddress,
756 0,
757 MmFreeVirtualMemoryPage,
758 (PVOID)Process);
759 }
760
761 /*
762 * @unimplemented
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->BaseAddress = RegionBase;
891 Info->AllocationBase = MemoryArea->BaseAddress;
892 Info->AllocationProtect = MemoryArea->Attributes;
893 Info->RegionSize = (char*)RegionBase + Region->Length - (char*)Info->BaseAddress;
894 Info->State = Region->Type;
895 Info->Protect = Region->Protect;
896 Info->Type = MEM_PRIVATE;
897
898 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
899 return(STATUS_SUCCESS);
900 }
901
902 /* EOF */