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