6d21202527e685defbc5795e29ec59cd53c0d6be
[reactos.git] / reactos / ntoskrnl / mm / anonmem.c
1 /* $Id$
2 *
3 * Copyright (C) 2002-2005 ReactOS Team (and the authors from the programmers section)
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 *
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/anonmem.c
22 * PURPOSE: Implementing anonymous memory.
23 *
24 * PROGRAMMERS: David Welch
25 * Hartmut Birr
26 * Casper Hornstrup
27 * KJK::Hyperion
28 * Ge van Geldorp
29 * Eric Kohl
30 * Royce Mitchell III
31 * Aleksey Bragin
32 * Jason Filby
33 * Art Yerkes
34 * Gunnar Andre' Dalsnes
35 * Filip Navara
36 * Thomas Weidenmueller
37 * Alex Ionescu
38 * Trevor McCort
39 * Steven Edwards
40 */
41
42 /* INCLUDE *****************************************************************/
43
44 #include <ntoskrnl.h>
45 #define NDEBUG
46 #include <internal/debug.h>
47
48 /* FUNCTIONS *****************************************************************/
49
50 NTSTATUS
51 MmWritePageVirtualMemory(PMADDRESS_SPACE AddressSpace,
52 PMEMORY_AREA MemoryArea,
53 PVOID Address,
54 PMM_PAGEOP PageOp)
55 {
56 SWAPENTRY SwapEntry;
57 PFN_TYPE Page;
58 NTSTATUS Status;
59
60 /*
61 * Check for paging out from a deleted virtual memory area.
62 */
63 if (MemoryArea->DeleteInProgress)
64 {
65 PageOp->Status = STATUS_UNSUCCESSFUL;
66 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
67 MmReleasePageOp(PageOp);
68 return(STATUS_UNSUCCESSFUL);
69 }
70
71 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
72
73 /*
74 * Get that the page actually is dirty.
75 */
76 if (!MmIsDirtyPage(AddressSpace->Process, Address))
77 {
78 PageOp->Status = STATUS_SUCCESS;
79 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
80 MmReleasePageOp(PageOp);
81 return(STATUS_SUCCESS);
82 }
83
84 /*
85 * Speculatively set the mapping to clean.
86 */
87 MmSetCleanPage(AddressSpace->Process, Address);
88
89 /*
90 * If necessary, allocate an entry in the paging file for this page
91 */
92 SwapEntry = MmGetSavedSwapEntryPage(Page);
93 if (SwapEntry == 0)
94 {
95 SwapEntry = MmAllocSwapPage();
96 if (SwapEntry == 0)
97 {
98 MmSetDirtyPage(AddressSpace->Process, Address);
99 PageOp->Status = STATUS_PAGEFILE_QUOTA_EXCEEDED;
100 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
101 MmReleasePageOp(PageOp);
102 return(STATUS_PAGEFILE_QUOTA_EXCEEDED);
103 }
104 }
105
106 /*
107 * Write the page to the pagefile
108 */
109 Status = MmWriteToSwapPage(SwapEntry, Page);
110 if (!NT_SUCCESS(Status))
111 {
112 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
113 Status);
114 MmSetDirtyPage(AddressSpace->Process, Address);
115 PageOp->Status = STATUS_UNSUCCESSFUL;
116 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
117 MmReleasePageOp(PageOp);
118 return(STATUS_UNSUCCESSFUL);
119 }
120
121 /*
122 * Otherwise we have succeeded.
123 */
124 MmSetSavedSwapEntryPage(Page, SwapEntry);
125 PageOp->Status = STATUS_SUCCESS;
126 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
127 MmReleasePageOp(PageOp);
128 return(STATUS_SUCCESS);
129 }
130
131 NTSTATUS
132 MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace,
133 PMEMORY_AREA MemoryArea,
134 PVOID Address,
135 PMM_PAGEOP PageOp)
136 {
137 PFN_TYPE Page;
138 BOOL WasDirty;
139 SWAPENTRY SwapEntry;
140 NTSTATUS Status;
141
142 DPRINT("MmPageOutVirtualMemory(Address 0x%.8X) PID %d\n",
143 Address, AddressSpace->Process->UniqueProcessId);
144
145 /*
146 * Check for paging out from a deleted virtual memory area.
147 */
148 if (MemoryArea->DeleteInProgress)
149 {
150 PageOp->Status = STATUS_UNSUCCESSFUL;
151 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
152 MmReleasePageOp(PageOp);
153 return(STATUS_UNSUCCESSFUL);
154 }
155
156 /*
157 * Disable the virtual mapping.
158 */
159 MmDisableVirtualMapping(AddressSpace->Process, Address,
160 &WasDirty, &Page);
161
162 if (Page == 0)
163 {
164 KEBUGCHECK(0);
165 }
166
167 /*
168 * Paging out non-dirty data is easy.
169 */
170 if (!WasDirty)
171 {
172 MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);
173 MmDeleteAllRmaps(Page, NULL, NULL);
174 if ((SwapEntry = MmGetSavedSwapEntryPage(Page)) != 0)
175 {
176 MmCreatePageFileMapping(AddressSpace->Process, Address, SwapEntry);
177 MmSetSavedSwapEntryPage(Page, 0);
178 }
179 MmReleasePageMemoryConsumer(MC_USER, Page);
180 PageOp->Status = STATUS_SUCCESS;
181 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
182 MmReleasePageOp(PageOp);
183 return(STATUS_SUCCESS);
184 }
185
186 /*
187 * If necessary, allocate an entry in the paging file for this page
188 */
189 SwapEntry = MmGetSavedSwapEntryPage(Page);
190 if (SwapEntry == 0)
191 {
192 SwapEntry = MmAllocSwapPage();
193 if (SwapEntry == 0)
194 {
195 MmShowOutOfSpaceMessagePagingFile();
196 MmEnableVirtualMapping(AddressSpace->Process, Address);
197 PageOp->Status = STATUS_UNSUCCESSFUL;
198 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
199 MmReleasePageOp(PageOp);
200 return(STATUS_PAGEFILE_QUOTA);
201 }
202 }
203
204 /*
205 * Write the page to the pagefile
206 */
207 Status = MmWriteToSwapPage(SwapEntry, Page);
208 if (!NT_SUCCESS(Status))
209 {
210 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
211 Status);
212 MmEnableVirtualMapping(AddressSpace->Process, Address);
213 PageOp->Status = STATUS_UNSUCCESSFUL;
214 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
215 MmReleasePageOp(PageOp);
216 return(STATUS_UNSUCCESSFUL);
217 }
218
219 /*
220 * Otherwise we have succeeded, free the page
221 */
222 DPRINT("MM: Swapped out virtual memory page 0x%.8X!\n", Page << PAGE_SHIFT);
223 MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);
224 MmCreatePageFileMapping(AddressSpace->Process, Address, SwapEntry);
225 MmDeleteAllRmaps(Page, NULL, NULL);
226 MmSetSavedSwapEntryPage(Page, 0);
227 MmReleasePageMemoryConsumer(MC_USER, Page);
228 PageOp->Status = STATUS_SUCCESS;
229 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
230 MmReleasePageOp(PageOp);
231 return(STATUS_SUCCESS);
232 }
233
234 NTSTATUS
235 MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace,
236 MEMORY_AREA* MemoryArea,
237 PVOID Address,
238 BOOLEAN Locked)
239 /*
240 * FUNCTION: Move data into memory to satisfy a page not present fault
241 * ARGUMENTS:
242 * AddressSpace = Address space within which the fault occurred
243 * MemoryArea = The memory area within which the fault occurred
244 * Address = The absolute address of fault
245 * RETURNS: Status
246 * NOTES: This function is called with the address space lock held.
247 */
248 {
249 PFN_TYPE Page;
250 NTSTATUS Status;
251 PMM_REGION Region;
252 PMM_PAGEOP PageOp;
253
254 /*
255 * There is a window between taking the page fault and locking the
256 * address space when another thread could load the page so we check
257 * that.
258 */
259 if (MmIsPagePresent(NULL, Address))
260 {
261 if (Locked)
262 {
263 MmLockPage(MmGetPfnForProcess(NULL, Address));
264 }
265 return(STATUS_SUCCESS);
266 }
267
268 /*
269 * Check for the virtual memory area being deleted.
270 */
271 if (MemoryArea->DeleteInProgress)
272 {
273 return(STATUS_UNSUCCESSFUL);
274 }
275
276 /*
277 * Get the segment corresponding to the virtual address
278 */
279 Region = MmFindRegion(MemoryArea->StartingAddress,
280 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
281 Address, NULL);
282 if (Region->Type == MEM_RESERVE || Region->Protect == PAGE_NOACCESS)
283 {
284 return(STATUS_ACCESS_VIOLATION);
285 }
286
287 /*
288 * Get or create a page operation
289 */
290 PageOp = MmGetPageOp(MemoryArea, AddressSpace->Process->UniqueProcessId,
291 (PVOID)PAGE_ROUND_DOWN(Address), NULL, 0,
292 MM_PAGEOP_PAGEIN, FALSE);
293 if (PageOp == NULL)
294 {
295 DPRINT1("MmGetPageOp failed");
296 KEBUGCHECK(0);
297 }
298
299 /*
300 * Check if someone else is already handling this fault, if so wait
301 * for them
302 */
303 if (PageOp->Thread != PsGetCurrentThread())
304 {
305 MmUnlockAddressSpace(AddressSpace);
306 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
307 0,
308 KernelMode,
309 FALSE,
310 NULL);
311 /*
312 * Check for various strange conditions
313 */
314 if (Status != STATUS_SUCCESS)
315 {
316 DPRINT1("Failed to wait for page op\n");
317 KEBUGCHECK(0);
318 }
319 if (PageOp->Status == STATUS_PENDING)
320 {
321 DPRINT1("Woke for page op before completion\n");
322 KEBUGCHECK(0);
323 }
324 /*
325 * If this wasn't a pagein then we need to restart the handling
326 */
327 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
328 {
329 MmLockAddressSpace(AddressSpace);
330 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
331 MmReleasePageOp(PageOp);
332 return(STATUS_MM_RESTART_OPERATION);
333 }
334 /*
335 * If the thread handling this fault has failed then we don't retry
336 */
337 if (!NT_SUCCESS(PageOp->Status))
338 {
339 MmLockAddressSpace(AddressSpace);
340 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
341 Status = PageOp->Status;
342 MmReleasePageOp(PageOp);
343 return(Status);
344 }
345 MmLockAddressSpace(AddressSpace);
346 if (Locked)
347 {
348 MmLockPage(MmGetPfnForProcess(NULL, Address));
349 }
350 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
351 MmReleasePageOp(PageOp);
352 return(STATUS_SUCCESS);
353 }
354
355 /*
356 * Try to allocate a page
357 */
358 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
359 if (Status == STATUS_NO_MEMORY)
360 {
361 MmUnlockAddressSpace(AddressSpace);
362 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
363 MmLockAddressSpace(AddressSpace);
364 }
365 if (!NT_SUCCESS(Status))
366 {
367 DPRINT1("MmRequestPageMemoryConsumer failed, status = %x\n", Status);
368 KEBUGCHECK(0);
369 }
370
371 /*
372 * Handle swapped out pages.
373 */
374 if (MmIsPageSwapEntry(NULL, Address))
375 {
376 SWAPENTRY SwapEntry;
377
378 MmDeletePageFileMapping(AddressSpace->Process, Address, &SwapEntry);
379 Status = MmReadFromSwapPage(SwapEntry, Page);
380 if (!NT_SUCCESS(Status))
381 {
382 KEBUGCHECK(0);
383 }
384 MmSetSavedSwapEntryPage(Page, SwapEntry);
385 }
386
387 /*
388 * Set the page. If we fail because we are out of memory then
389 * try again
390 */
391 Status = MmCreateVirtualMapping(AddressSpace->Process,
392 (PVOID)PAGE_ROUND_DOWN(Address),
393 Region->Protect,
394 &Page,
395 1);
396 while (Status == STATUS_NO_MEMORY)
397 {
398 MmUnlockAddressSpace(AddressSpace);
399 Status = MmCreateVirtualMapping(AddressSpace->Process,
400 Address,
401 Region->Protect,
402 &Page,
403 1);
404 MmLockAddressSpace(AddressSpace);
405 }
406 if (!NT_SUCCESS(Status))
407 {
408 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
409 KEBUGCHECK(0);
410 return(Status);
411 }
412
413 /*
414 * Add the page to the process's working set
415 */
416 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAGE_ROUND_DOWN(Address));
417
418 /*
419 * Finish the operation
420 */
421 if (Locked)
422 {
423 MmLockPage(Page);
424 }
425 PageOp->Status = STATUS_SUCCESS;
426 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
427 MmReleasePageOp(PageOp);
428 return(STATUS_SUCCESS);
429 }
430
431 VOID STATIC
432 MmModifyAttributes(PMADDRESS_SPACE AddressSpace,
433 PVOID BaseAddress,
434 ULONG RegionSize,
435 ULONG OldType,
436 ULONG OldProtect,
437 ULONG NewType,
438 ULONG NewProtect)
439 /*
440 * FUNCTION: Modify the attributes of a memory region
441 */
442 {
443 /*
444 * If we are switching a previously committed region to reserved then
445 * free any allocated pages within the region
446 */
447 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
448 {
449 ULONG i;
450
451 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
452 {
453 PFN_TYPE Page;
454
455 if (MmIsPageSwapEntry(AddressSpace->Process,
456 (char*)BaseAddress + (i * PAGE_SIZE)))
457 {
458 SWAPENTRY SwapEntry;
459
460 MmDeletePageFileMapping(AddressSpace->Process,
461 (char*)BaseAddress + (i * PAGE_SIZE),
462 &SwapEntry);
463 MmFreeSwapPage(SwapEntry);
464 }
465 else
466 {
467 MmDeleteVirtualMapping(AddressSpace->Process,
468 (char*)BaseAddress + (i*PAGE_SIZE),
469 FALSE, NULL, &Page);
470 if (Page != 0)
471 {
472 SWAPENTRY SavedSwapEntry;
473 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
474 if (SavedSwapEntry != 0)
475 {
476 MmFreeSwapPage(SavedSwapEntry);
477 MmSetSavedSwapEntryPage(Page, 0);
478 }
479 MmDeleteRmap(Page, AddressSpace->Process,
480 (char*)BaseAddress + (i * PAGE_SIZE));
481 MmReleasePageMemoryConsumer(MC_USER, Page);
482 }
483 }
484 }
485 }
486
487 /*
488 * If we are changing the protection attributes of a committed region then
489 * alter the attributes for any allocated pages within the region
490 */
491 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
492 OldProtect != NewProtect)
493 {
494 ULONG i;
495
496 for (i=0; i < PAGE_ROUND_UP(RegionSize)/PAGE_SIZE; i++)
497 {
498 if (MmIsPagePresent(AddressSpace->Process,
499 (char*)BaseAddress + (i*PAGE_SIZE)))
500 {
501 MmSetPageProtect(AddressSpace->Process,
502 (char*)BaseAddress + (i*PAGE_SIZE),
503 NewProtect);
504 }
505 }
506 }
507 }
508
509 /*
510 * @implemented
511 */
512 NTSTATUS STDCALL
513 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
514 IN OUT PVOID* UBaseAddress,
515 IN ULONG ZeroBits,
516 IN OUT PULONG URegionSize,
517 IN ULONG AllocationType,
518 IN ULONG Protect)
519 /*
520 * FUNCTION: Allocates a block of virtual memory in the process address space
521 * ARGUMENTS:
522 * ProcessHandle = The handle of the process which owns the virtual memory
523 * BaseAddress = A pointer to the virtual memory allocated. If you
524 * supply a non zero value the system will try to
525 * allocate the memory at the address supplied. It round
526 * it down to a multiple of the page size.
527 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
528 * that must be zero, ensuring that the memory will be
529 * allocated at a address below a certain value.
530 * RegionSize = The number of bytes to allocate
531 * AllocationType = Indicates the type of virtual memory you like to
532 * allocated, can be a combination of MEM_COMMIT,
533 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN.
534 * Protect = Indicates the protection type of the pages allocated, can be
535 * a combination of PAGE_READONLY, PAGE_READWRITE,
536 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
537 * PAGE_NOACCESS
538 * RETURNS: Status
539 */
540 {
541 PEPROCESS Process;
542 MEMORY_AREA* MemoryArea;
543 ULONG_PTR MemoryAreaLength;
544 ULONG Type;
545 NTSTATUS Status;
546 PMADDRESS_SPACE AddressSpace;
547 PVOID BaseAddress;
548 ULONG RegionSize;
549 PVOID PBaseAddress;
550 ULONG PRegionSize;
551 PHYSICAL_ADDRESS BoundaryAddressMultiple;
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 /* Check for valid protection flags */
559 if ((Protect & PAGE_FLAGS_VALID_FROM_USER_MODE) != Protect)
560 {
561 DPRINT1("Invalid page protection\n");
562 return STATUS_INVALID_PAGE_PROTECTION;
563 }
564
565 /* Check for valid Allocation Types */
566 if ((AllocationType &~ (MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
567 MEM_TOP_DOWN | MEM_WRITE_WATCH)))
568 {
569 DPRINT1("Invalid Allocation Type\n");
570 return STATUS_INVALID_PARAMETER_5;
571 }
572
573 /* Check for at least one of these Allocation Types to be set */
574 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET)))
575 {
576 DPRINT1("No memory allocation base type\n");
577 return STATUS_INVALID_PARAMETER_5;
578 }
579
580 /* MEM_RESET is an exclusive flag, make sure that is valid too */
581 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET))
582 {
583 DPRINT1("MEM_RESET used illegaly\n");
584 return STATUS_INVALID_PARAMETER_5;
585 }
586
587 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */
588 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE))
589 {
590 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
591 return STATUS_INVALID_PARAMETER_5;
592 }
593
594 /* MEM_PHYSICAL can only be used with MEM_RESERVE, and can only be R/W */
595 if (AllocationType & MEM_PHYSICAL)
596 {
597 /* First check for MEM_RESERVE exclusivity */
598 if (AllocationType != (MEM_RESERVE | MEM_PHYSICAL))
599 {
600 DPRINT1("MEM_PHYSICAL used with other flags then MEM_RESERVE or"
601 "MEM_RESERVE was not present at all\n");
602 return STATUS_INVALID_PARAMETER_5;
603 }
604
605 /* Then make sure PAGE_READWRITE is used */
606 if (Protect != PAGE_READWRITE)
607 {
608 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n");
609 return STATUS_INVALID_PAGE_PROTECTION;
610 }
611 }
612
613 PBaseAddress = *UBaseAddress;
614 PRegionSize = *URegionSize;
615 BoundaryAddressMultiple.QuadPart = 0;
616
617 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
618 RegionSize = PAGE_ROUND_UP(PBaseAddress + PRegionSize) -
619 PAGE_ROUND_DOWN(PBaseAddress);
620
621 /*
622 * We've captured and calculated the data, now do more checks
623 * Yes, MmCreateMemoryArea does similar checks, but they don't return
624 * the right status codes that a caller of this routine would expect.
625 */
626 if (BaseAddress >= MM_HIGHEST_USER_ADDRESS)
627 {
628 DPRINT1("Virtual allocation above User Space\n");
629 return STATUS_INVALID_PARAMETER_2;
630 }
631 if (!RegionSize)
632 {
633 DPRINT1("Region size is invalid\n");
634 return STATUS_INVALID_PARAMETER_4;
635 }
636 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)BaseAddress) < RegionSize)
637 {
638 DPRINT1("Region size would overflow into kernel-memory\n");
639 return STATUS_INVALID_PARAMETER_4;
640 }
641
642 Status = ObReferenceObjectByHandle(ProcessHandle,
643 PROCESS_VM_OPERATION,
644 NULL,
645 UserMode,
646 (PVOID*)(&Process),
647 NULL);
648 if (!NT_SUCCESS(Status))
649 {
650 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
651 return(Status);
652 }
653
654 Type = (AllocationType & MEM_COMMIT) ? MEM_COMMIT : MEM_RESERVE;
655 DPRINT("Type %x\n", Type);
656
657 AddressSpace = &Process->AddressSpace;
658 MmLockAddressSpace(AddressSpace);
659
660 if (PBaseAddress != 0)
661 {
662 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
663
664 if (MemoryArea != NULL)
665 {
666 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
667 (ULONG_PTR)MemoryArea->StartingAddress;
668 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
669 MemoryAreaLength >= RegionSize)
670 {
671 Status =
672 MmAlterRegion(AddressSpace,
673 MemoryArea->StartingAddress,
674 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
675 BaseAddress, RegionSize,
676 Type, Protect, MmModifyAttributes);
677 MmUnlockAddressSpace(AddressSpace);
678 ObDereferenceObject(Process);
679 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
680 return(Status);
681 }
682 else if (MemoryAreaLength >= RegionSize)
683 {
684 Status =
685 MmAlterRegion(AddressSpace,
686 MemoryArea->StartingAddress,
687 &MemoryArea->Data.SectionData.RegionListHead,
688 BaseAddress, RegionSize,
689 Type, Protect, MmModifyAttributes);
690 MmUnlockAddressSpace(AddressSpace);
691 ObDereferenceObject(Process);
692 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
693 return(Status);
694 }
695 else
696 {
697 MmUnlockAddressSpace(AddressSpace);
698 ObDereferenceObject(Process);
699 return(STATUS_UNSUCCESSFUL);
700 }
701 }
702 }
703
704 Status = MmCreateMemoryArea(Process,
705 AddressSpace,
706 MEMORY_AREA_VIRTUAL_MEMORY,
707 &BaseAddress,
708 RegionSize,
709 Protect,
710 &MemoryArea,
711 PBaseAddress != 0,
712 (AllocationType & MEM_TOP_DOWN) == MEM_TOP_DOWN,
713 BoundaryAddressMultiple);
714 if (!NT_SUCCESS(Status))
715 {
716 MmUnlockAddressSpace(AddressSpace);
717 ObDereferenceObject(Process);
718 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
719 return(Status);
720 }
721
722 MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
723 (ULONG_PTR)MemoryArea->StartingAddress;
724
725 MmInitialiseRegion(&MemoryArea->Data.VirtualMemoryData.RegionListHead,
726 MemoryAreaLength, Type, Protect);
727
728 if ((AllocationType & MEM_COMMIT) &&
729 ((Protect & PAGE_READWRITE) ||
730 (Protect & PAGE_EXECUTE_READWRITE)))
731 {
732 MmReserveSwapPages(MemoryAreaLength);
733 }
734
735 *UBaseAddress = BaseAddress;
736 *URegionSize = MemoryAreaLength;
737 DPRINT("*UBaseAddress %x *URegionSize %x\n", BaseAddress, RegionSize);
738
739 MmUnlockAddressSpace(AddressSpace);
740 ObDereferenceObject(Process);
741 return(STATUS_SUCCESS);
742 }
743
744 VOID STATIC
745 MmFreeVirtualMemoryPage(PVOID Context,
746 MEMORY_AREA* MemoryArea,
747 PVOID Address,
748 PFN_TYPE Page,
749 SWAPENTRY SwapEntry,
750 BOOLEAN Dirty)
751 {
752 PEPROCESS Process = (PEPROCESS)Context;
753
754 if (Page != 0)
755 {
756 SWAPENTRY SavedSwapEntry;
757 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
758 if (SavedSwapEntry != 0)
759 {
760 MmFreeSwapPage(SavedSwapEntry);
761 MmSetSavedSwapEntryPage(Page, 0);
762 }
763 MmDeleteRmap(Page, Process, Address);
764 MmReleasePageMemoryConsumer(MC_USER, Page);
765 }
766 else if (SwapEntry != 0)
767 {
768 MmFreeSwapPage(SwapEntry);
769 }
770 }
771
772 VOID
773 MmFreeVirtualMemory(PEPROCESS Process,
774 PMEMORY_AREA MemoryArea)
775 {
776 PLIST_ENTRY current_entry;
777 PMM_REGION current;
778 ULONG i;
779
780 DPRINT("MmFreeVirtualMemory(Process %p MemoryArea %p)\n", Process,
781 MemoryArea);
782
783 /* Mark this memory area as about to be deleted. */
784 MemoryArea->DeleteInProgress = TRUE;
785
786 /*
787 * Wait for any ongoing paging operations. Notice that since we have
788 * flagged this memory area as deleted no more page ops will be added.
789 */
790 if (MemoryArea->PageOpCount > 0)
791 {
792 ULONG_PTR MemoryAreaLength = (ULONG_PTR)MemoryArea->EndingAddress -
793 (ULONG_PTR)MemoryArea->StartingAddress;
794
795 /* FiN TODO: Optimize loop counter! */
796 for (i = 0; i < PAGE_ROUND_UP(MemoryAreaLength) / PAGE_SIZE; i++)
797 {
798 PMM_PAGEOP PageOp;
799
800 if (MemoryArea->PageOpCount == 0)
801 {
802 break;
803 }
804
805 PageOp = MmCheckForPageOp(MemoryArea, Process->UniqueProcessId,
806 (PVOID)((ULONG_PTR)MemoryArea->StartingAddress + (i * PAGE_SIZE)),
807 NULL, 0);
808 if (PageOp != NULL)
809 {
810 NTSTATUS Status;
811 MmUnlockAddressSpace(&Process->AddressSpace);
812 Status = KeWaitForSingleObject(&PageOp->CompletionEvent,
813 0,
814 KernelMode,
815 FALSE,
816 NULL);
817 if (Status != STATUS_SUCCESS)
818 {
819 DPRINT1("Failed to wait for page op\n");
820 KEBUGCHECK(0);
821 }
822 MmLockAddressSpace(&Process->AddressSpace);
823 MmReleasePageOp(PageOp);
824 }
825 }
826 }
827
828 /* Free all the individual segments. */
829 current_entry = MemoryArea->Data.VirtualMemoryData.RegionListHead.Flink;
830 while (current_entry != &MemoryArea->Data.VirtualMemoryData.RegionListHead)
831 {
832 current = CONTAINING_RECORD(current_entry, MM_REGION, RegionListEntry);
833 current_entry = current_entry->Flink;
834 ExFreePool(current);
835 }
836
837 /* Actually free the memory area. */
838 MmFreeMemoryArea(&Process->AddressSpace,
839 MemoryArea,
840 MmFreeVirtualMemoryPage,
841 (PVOID)Process);
842 }
843
844 /*
845 * @implemented
846 */
847 NTSTATUS STDCALL
848 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
849 IN PVOID* PBaseAddress,
850 IN PULONG PRegionSize,
851 IN ULONG FreeType)
852 /*
853 * FUNCTION: Frees a range of virtual memory
854 * ARGUMENTS:
855 * ProcessHandle = Points to the process that allocated the virtual
856 * memory
857 * BaseAddress = Points to the memory address, rounded down to a
858 * multiple of the pagesize
859 * RegionSize = Limits the range to free, rounded up to a multiple of
860 * the paging size
861 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
862 * RETURNS: Status
863 */
864 {
865 MEMORY_AREA* MemoryArea;
866 NTSTATUS Status;
867 PEPROCESS Process;
868 PMADDRESS_SPACE AddressSpace;
869 PVOID BaseAddress;
870 ULONG RegionSize;
871
872 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *PBaseAddress %x, "
873 "*PRegionSize %x, FreeType %x)\n",ProcessHandle,*PBaseAddress,
874 *PRegionSize,FreeType);
875
876 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
877 RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
878 PAGE_ROUND_DOWN((*PBaseAddress));
879
880 Status = ObReferenceObjectByHandle(ProcessHandle,
881 PROCESS_VM_OPERATION,
882 PsProcessType,
883 UserMode,
884 (PVOID*)(&Process),
885 NULL);
886 if (!NT_SUCCESS(Status))
887 {
888 return(Status);
889 }
890
891 AddressSpace = &Process->AddressSpace;
892
893 MmLockAddressSpace(AddressSpace);
894 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
895 if (MemoryArea == NULL)
896 {
897 MmUnlockAddressSpace(AddressSpace);
898 ObDereferenceObject(Process);
899 return(STATUS_UNSUCCESSFUL);
900 }
901
902 switch (FreeType)
903 {
904 case MEM_RELEASE:
905 /* We can only free a memory area in one step. */
906 if (MemoryArea->StartingAddress != BaseAddress ||
907 MemoryArea->Type != MEMORY_AREA_VIRTUAL_MEMORY)
908 {
909 MmUnlockAddressSpace(AddressSpace);
910 ObDereferenceObject(Process);
911 return(STATUS_UNSUCCESSFUL);
912 }
913 MmFreeVirtualMemory(Process, MemoryArea);
914 MmUnlockAddressSpace(AddressSpace);
915 ObDereferenceObject(Process);
916 return(STATUS_SUCCESS);
917
918 case MEM_DECOMMIT:
919 Status =
920 MmAlterRegion(AddressSpace,
921 MemoryArea->StartingAddress,
922 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
923 BaseAddress,
924 RegionSize,
925 MEM_RESERVE,
926 PAGE_NOACCESS,
927 MmModifyAttributes);
928 MmUnlockAddressSpace(AddressSpace);
929 ObDereferenceObject(Process);
930 return(Status);
931 }
932 MmUnlockAddressSpace(AddressSpace);
933 ObDereferenceObject(Process);
934 return(STATUS_NOT_IMPLEMENTED);
935 }
936
937 NTSTATUS
938 MmProtectAnonMem(PMADDRESS_SPACE AddressSpace,
939 PMEMORY_AREA MemoryArea,
940 PVOID BaseAddress,
941 ULONG Length,
942 ULONG Protect,
943 PULONG OldProtect)
944 {
945 PMM_REGION Region;
946 NTSTATUS Status;
947
948 Region = MmFindRegion(MemoryArea->StartingAddress,
949 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
950 BaseAddress, NULL);
951 *OldProtect = Region->Protect;
952 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
953 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
954 BaseAddress, Length, Region->Type, Protect,
955 MmModifyAttributes);
956 return(Status);
957 }
958
959 NTSTATUS STDCALL
960 MmQueryAnonMem(PMEMORY_AREA MemoryArea,
961 PVOID Address,
962 PMEMORY_BASIC_INFORMATION Info,
963 PULONG ResultLength)
964 {
965 PMM_REGION Region;
966 PVOID RegionBase;
967
968 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
969
970 Region = MmFindRegion(MemoryArea->StartingAddress,
971 &MemoryArea->Data.VirtualMemoryData.RegionListHead,
972 Address, &RegionBase);
973 Info->BaseAddress = RegionBase;
974 Info->AllocationBase = MemoryArea->StartingAddress;
975 Info->AllocationProtect = MemoryArea->Attributes;
976 Info->RegionSize = (char*)RegionBase + Region->Length - (char*)Info->BaseAddress;
977 Info->State = Region->Type;
978 Info->Protect = Region->Protect;
979 Info->Type = MEM_PRIVATE;
980
981 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
982 return(STATUS_SUCCESS);
983 }
984
985 /* EOF */