Proper cleanup after processes
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
1 /* $Id: virtual.c,v 1.41 2001/03/08 22:06:02 dwelch Exp $
2 *
3 * COPYRIGHT: See COPYING in the top directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/mm/virtual.c
6 * PURPOSE: implementing the Virtualxxx section of the win32 api
7 * PROGRAMMER: David Welch
8 * UPDATE HISTORY:
9 * 09/4/98: Created
10 * 10/6/98: Corrections from Fatahi (i_fatahi@hotmail.com)
11 * 30/9/98: Implemented ZwxxxVirtualMemory functions
12 */
13
14 /* INCLUDE *****************************************************************/
15
16 #include <ddk/ntddk.h>
17 #include <internal/mm.h>
18 #include <internal/mmhal.h>
19 #include <internal/ob.h>
20 #include <internal/io.h>
21 #include <internal/ps.h>
22 #include <internal/pool.h>
23
24 #define NDEBUG
25 #include <internal/debug.h>
26
27 /* TYPES *********************************************************************/
28
29 typedef struct _MM_SEGMENT
30 {
31 ULONG Type;
32 ULONG Protect;
33 ULONG Length;
34 LIST_ENTRY SegmentListEntry;
35 } MM_SEGMENT, *PMM_SEGMENT;
36
37 /* GLOBALS *******************************************************************/
38
39 #define TAG_MM_SEGMENT TAG('M', 'S', 'E', 'G')
40
41 /* FUNCTIONS *****************************************************************/
42
43 PMM_SEGMENT
44 MmGetSegmentForAddress(PMEMORY_AREA MArea,
45 PVOID Address,
46 PVOID* PCurrentAddress)
47 /*
48 * FUNCTION: Get the segment corresponding to a particular memory area and
49 * address.
50 * ARGUMENTS:
51 * MArea (IN) = The memory area
52 * Address (IN) = The address to get the segment for
53 * PCurrentAddress (OUT) = The start of the segment
54 * RETURNS:
55 * The corresponding segment or NULL if an error occurred
56 */
57 {
58 PVOID CurrentAddress;
59 PMM_SEGMENT CurrentSegment;
60 PLIST_ENTRY Current;
61
62 if (Address < MArea->BaseAddress ||
63 Address >= (MArea->BaseAddress + MArea->Length))
64 {
65 KeBugCheck(0);
66 *PCurrentAddress = NULL;
67 return(NULL);
68 }
69
70 Current = MArea->Data.VirtualMemoryData.SegmentListHead.Flink;
71 CurrentAddress = MArea->BaseAddress;
72 while (Current != &MArea->Data.VirtualMemoryData.SegmentListHead)
73 {
74 CurrentSegment = CONTAINING_RECORD(Current,
75 MM_SEGMENT,
76 SegmentListEntry);
77 if (Address >= CurrentAddress &&
78 Address < (CurrentAddress + CurrentSegment->Length))
79 {
80 *PCurrentAddress = CurrentAddress;
81 return(CurrentSegment);
82 }
83 CurrentAddress = CurrentAddress + CurrentSegment->Length;
84 Current = Current->Flink;
85 }
86 KeBugCheck(0);
87 return(NULL);
88 }
89
90 NTSTATUS
91 MmWritePageVirtualMemory(PMADDRESS_SPACE AddressSpace,
92 PMEMORY_AREA MArea,
93 PVOID Address)
94 {
95 SWAPENTRY se;
96 ULONG Flags;
97 PHYSICAL_ADDRESS PhysicalAddress;
98 PMDL Mdl;
99 NTSTATUS Status;
100
101 /*
102 * FIXME: What should we do if an i/o operation is pending on
103 * this page
104 */
105
106 /*
107 * If the memory area is readonly then there is nothing to do
108 */
109 if (MArea->Attributes & PAGE_READONLY ||
110 MArea->Attributes & PAGE_EXECUTE_READ)
111 {
112 return(STATUS_SUCCESS);
113 }
114 /*
115 * Set the page to readonly. This ensures the current contents aren't
116 * modified while we are writing it to swap.
117 */
118 MmSetPageProtect(AddressSpace->Process,
119 Address,
120 PAGE_READONLY);
121 /*
122 * If the page isn't dirty then there is nothing to do.
123 */
124 if (!MmIsPageDirty(AddressSpace->Process, Address))
125 {
126 MmSetPageProtect(AddressSpace->Process,
127 Address,
128 MArea->Attributes);
129 return(STATUS_SUCCESS);
130 }
131 PhysicalAddress = MmGetPhysicalAddress(Address);
132 /*
133 * If we haven't already allocated a swap entry for this page
134 * then allocate one
135 */
136 if ((se = MmGetSavedSwapEntryPage((PVOID)PhysicalAddress.u.LowPart)) != 0)
137 {
138 se = MmAllocSwapPage();
139 if (se == 0)
140 {
141 MmSetPageProtect(AddressSpace->Process,
142 Address,
143 MArea->Attributes);
144 return(STATUS_UNSUCCESSFUL);
145 }
146 MmSetSavedSwapEntryPage((PVOID)PhysicalAddress.u.LowPart, se);
147 }
148 /*
149 * Set the flags so other threads will know what we are doing
150 */
151 Flags = MmGetFlagsPage((PVOID)PhysicalAddress.u.LowPart);
152 Flags = Flags | MM_PHYSICAL_PAGE_MPW_PENDING;
153 MmSetFlagsPage((PVOID)PhysicalAddress.u.LowPart, Flags);
154 /*
155 * Build an mdl to hold the page for writeout
156 */
157 Mdl = MmCreateMdl(NULL, NULL, PAGESIZE);
158 MmBuildMdlFromPages(Mdl, (PULONG)&PhysicalAddress.u.LowPart);
159 /*
160 * Unlock the address space and write out the page to swap.
161 */
162 MmUnlockAddressSpace(AddressSpace);
163 Status = MmWriteToSwapPage(se, Mdl);
164 /*
165 * Cleanup
166 */
167 MmLockAddressSpace(AddressSpace);
168 Flags = MmGetFlagsPage((PVOID)PhysicalAddress.u.LowPart);
169 Flags = Flags & (~MM_PHYSICAL_PAGE_MPW_PENDING);
170 MmSetFlagsPage((PVOID)PhysicalAddress.u.LowPart,Flags);
171 /*
172 * If we successfully wrote the page then reset the dirty bit
173 */
174 if (NT_SUCCESS(Status))
175 {
176 MmSetCleanPage(AddressSpace->Process, Address);
177 }
178 return(Status);
179 }
180
181
182 ULONG MmPageOutVirtualMemory(PMADDRESS_SPACE AddressSpace,
183 PMEMORY_AREA MemoryArea,
184 PVOID Address,
185 PBOOLEAN Ul)
186 {
187 PHYSICAL_ADDRESS PhysicalAddress;
188
189 if ((MemoryArea->Attributes & PAGE_READONLY) ||
190 (MemoryArea->Attributes & PAGE_EXECUTE_READ) ||
191 !MmIsPageDirty(PsGetCurrentProcess(), Address))
192 {
193 PhysicalAddress = MmGetPhysicalAddress(Address);
194
195 MmRemovePageFromWorkingSet(AddressSpace->Process,
196 Address);
197 MmDeleteVirtualMapping(PsGetCurrentProcess(), Address, FALSE);
198 MmDereferencePage((PVOID)PhysicalAddress.u.LowPart);
199 *Ul = TRUE;
200 return(1);
201 }
202 *Ul = FALSE;
203 return(0);
204 }
205
206 NTSTATUS
207 MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace,
208 MEMORY_AREA* MemoryArea,
209 PVOID Address,
210 BOOLEAN Locked)
211 /*
212 * FUNCTION: Move data into memory to satisfy a page not present fault
213 * ARGUMENTS:
214 * AddressSpace = Address space within which the fault occurred
215 * MemoryArea = The memory area within which the fault occurred
216 * Address = The absolute address of fault
217 * RETURNS: Status
218 * NOTES: This function is called with the address space lock held.
219 */
220 {
221 PVOID Page;
222 NTSTATUS Status;
223 PMM_SEGMENT Segment;
224 PVOID CurrentAddress;
225
226 /*
227 * Get the segment corresponding to the virtual address
228 */
229 Segment = MmGetSegmentForAddress(MemoryArea, Address, &CurrentAddress);
230 if (Segment == NULL)
231 {
232 return(STATUS_UNSUCCESSFUL);
233 }
234 if (Segment->Type == MEM_RESERVE)
235 {
236 return(STATUS_UNSUCCESSFUL);
237 }
238
239 if (MmIsPagePresent(NULL, Address))
240 {
241 if (Locked)
242 {
243 MmLockPage((PVOID)MmGetPhysicalAddressForProcess(NULL, Address));
244 }
245 return(STATUS_SUCCESS);
246 }
247
248 /*
249 * Try to allocate a page
250 */
251 Page = MmAllocPage(0);
252 while (Page == NULL)
253 {
254 MmUnlockAddressSpace(AddressSpace);
255 MmWaitForFreePages();
256 MmLockAddressSpace(AddressSpace);
257 if (MmIsPagePresent(NULL, Address))
258 {
259 if (Locked)
260 {
261 MmLockPage((PVOID)MmGetPhysicalAddressForProcess(NULL,
262 Address));
263 }
264 return(STATUS_SUCCESS);
265 }
266 Page = MmAllocPage(0);
267 }
268
269 /*
270 * Add the page to the process's working set
271 */
272 MmAddPageToWorkingSet(PsGetCurrentProcess(),
273 (PVOID)PAGE_ROUND_DOWN(Address));
274
275 /*
276 * Set the page. If we fail because we are out of memory then
277 * try again
278 */
279 Status = MmCreateVirtualMapping(PsGetCurrentProcess(),
280 Address,
281 MemoryArea->Attributes,
282 (ULONG)Page);
283 while (Status == STATUS_NO_MEMORY)
284 {
285 MmUnlockAddressSpace(AddressSpace);
286 MmWaitForFreePages();
287 MmLockAddressSpace(AddressSpace);
288 if (MmIsPagePresent(NULL, Address))
289 {
290 if (Locked)
291 {
292 MmLockPage((PVOID)MmGetPhysicalAddressForProcess(NULL,
293 Address));
294 }
295 MmDereferencePage(Page);
296 return(STATUS_SUCCESS);
297 }
298 Status = MmCreateVirtualMapping(PsGetCurrentProcess(),
299 Address,
300 MemoryArea->Attributes,
301 (ULONG)Page);
302 }
303 if (!NT_SUCCESS(Status))
304 {
305 return(Status);
306 }
307 else
308 {
309 if (Locked)
310 {
311 MmLockPage((PVOID)MmGetPhysicalAddressForProcess(NULL, Address));
312 }
313 return(STATUS_SUCCESS);
314 }
315 }
316
317 VOID STATIC
318 MmModifyAttributes(PMADDRESS_SPACE AddressSpace,
319 PVOID BaseAddress,
320 ULONG RegionSize,
321 ULONG OldType,
322 ULONG OldProtect,
323 ULONG NewType,
324 ULONG NewProtect)
325 /*
326 * FUNCTION: Modify the attributes of a memory region
327 */
328 {
329 /*
330 * If we are switching a previously committed region to reserved then
331 * free any allocated pages within the region
332 */
333 if (NewType == MEM_RESERVE && OldType == MEM_COMMIT)
334 {
335 ULONG i;
336
337 for (i=0; i <= (RegionSize/PAGESIZE); i++)
338 {
339 LARGE_INTEGER PhysicalAddr;
340
341 PhysicalAddr = MmGetPhysicalAddress(BaseAddress + (i*PAGESIZE));
342 MmDeleteVirtualMapping(AddressSpace->Process,
343 BaseAddress + (i*PAGESIZE),
344 FALSE);
345 if (PhysicalAddr.u.LowPart != 0)
346 {
347 MmRemovePageFromWorkingSet(AddressSpace->Process,
348 BaseAddress + (i*PAGESIZE));
349 MmDereferencePage((PVOID)(ULONG)(PhysicalAddr.u.LowPart));
350 }
351 }
352 }
353
354 /*
355 * If we are changing the protection attributes of a committed region then
356 * alter the attributes for any allocated pages within the region
357 */
358 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
359 OldProtect != NewProtect)
360 {
361 ULONG i;
362
363 for (i=0; i <= (RegionSize/PAGESIZE); i++)
364 {
365 if (MmIsPagePresent(AddressSpace->Process,
366 BaseAddress + (i*PAGESIZE)))
367 {
368 MmSetPageProtect(AddressSpace->Process,
369 BaseAddress + (i*PAGESIZE),
370 NewProtect);
371 }
372 }
373 }
374 }
375
376 VOID STATIC
377 InsertAfterEntry(PLIST_ENTRY Previous,
378 PLIST_ENTRY Entry)
379 /*
380 * FUNCTION: Insert a list entry after another entry in the list
381 */
382 {
383 Previous->Flink->Blink = Entry;
384
385 Entry->Flink = Previous->Flink;
386 Entry->Blink = Previous;
387
388 Previous->Flink = Entry;
389 }
390
391 #if 0
392 VOID STATIC
393 MmDumpSegmentsMemoryArea(PMEMORY_AREA MemoryArea)
394 {
395 PVOID CurrentAddress;
396 PLIST_ENTRY CurrentEntry;
397 PMM_SEGMENT CurrentSegment;
398 PLIST_ENTRY ListHead;
399
400 CurrentEntry = MemoryArea->Data.VirtualMemoryData.SegmentListHead.Flink;
401 ListHead = &MemoryArea->Data.VirtualMemoryData.SegmentListHead;
402
403 CurrentAddress = MemoryArea->BaseAddress;
404 while (CurrentEntry != ListHead)
405 {
406 CurrentSegment = CONTAINING_RECORD(CurrentEntry,
407 MM_SEGMENT,
408 SegmentListEntry);
409
410 DbgPrint("0x%x 0x%x %d %d\n",
411 CurrentAddress,
412 CurrentSegment->Length,
413 CurrentSegment->Type,
414 CurrentSegment->Protect);
415
416 CurrentAddress = CurrentAddress + CurrentSegment->Length;
417 CurrentEntry = CurrentEntry->Flink;
418 }
419 }
420 #endif
421
422 NTSTATUS
423 MmSplitSegment(PMADDRESS_SPACE AddressSpace,
424 PMEMORY_AREA MemoryArea,
425 PVOID RegionAddress,
426 ULONG RegionLength,
427 ULONG Type,
428 ULONG Protect,
429 PMM_SEGMENT FirstSegment,
430 PVOID FirstAddress)
431 /*
432 * FUNCTION: Split a memory segment internally
433 */
434 {
435 PMM_SEGMENT NewTopSegment;
436 PMM_SEGMENT RegionSegment;
437 ULONG OldType;
438 ULONG OldProtect;
439 ULONG OldLength;
440
441 /*
442 * Save the type and protection and length of the current segment
443 */
444 OldType = FirstSegment->Type;
445 OldProtect = FirstSegment->Protect;
446 OldLength = FirstSegment->Length;
447
448 /*
449 * If the segment is already of the right type and protection then
450 * there is nothing to do.
451 */
452 if (FirstSegment->Type == Type && FirstSegment->Protect == Protect)
453 {
454 return(STATUS_SUCCESS);
455 }
456
457 /*
458 * Allocate the segment we might need here because if the allocation
459 * fails below it will be difficult to undo what we've done already.
460 */
461 NewTopSegment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SEGMENT),
462 TAG_MM_SEGMENT);
463 if (NewTopSegment == NULL)
464 {
465 return(STATUS_NO_MEMORY);
466 }
467
468 if (FirstAddress < RegionAddress)
469 {
470 /*
471 * If the region to be affected starts at a higher address than
472 * the current segment then create a new segment for the
473 * affected portion
474 */
475 RegionSegment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SEGMENT),
476 TAG_MM_SEGMENT);
477 if (RegionSegment == NULL)
478 {
479 ExFreePool(NewTopSegment);
480 return(STATUS_NO_MEMORY);
481 }
482
483 RegionSegment->Type = Type;
484 RegionSegment->Protect = Protect;
485 RegionSegment->Length = RegionLength;
486
487 FirstSegment->Length = RegionAddress - FirstAddress;
488
489 InsertAfterEntry(&FirstSegment->SegmentListEntry,
490 &RegionSegment->SegmentListEntry);
491 }
492 else
493 {
494 /*
495 * Otherwise just set its type and protection and length
496 */
497
498 FirstSegment->Type = Type;
499 FirstSegment->Protect = Protect;
500 FirstSegment->Length = RegionLength;
501
502 RegionSegment = FirstSegment;
503 }
504
505 if ((FirstAddress + OldLength) > (RegionAddress + RegionLength))
506 {
507 /*
508 * If the top of the current segment extends after the affected
509 * region then create a segment for the unaffected portion
510 */
511
512 NewTopSegment->Type = OldType;
513 NewTopSegment->Protect = OldProtect;
514 NewTopSegment->Length = (FirstAddress + OldLength) -
515 (RegionAddress + RegionLength);
516
517 InsertAfterEntry(&RegionSegment->SegmentListEntry,
518 &NewTopSegment->SegmentListEntry);
519 }
520 else
521 {
522 ExFreePool(NewTopSegment);
523 NewTopSegment = NULL;
524 }
525
526 /*
527 * Actually set the type and protection of the affected region
528 */
529 MmModifyAttributes(AddressSpace,
530 RegionAddress,
531 RegionLength,
532 OldType,
533 OldProtect,
534 Type,
535 Protect);
536 return(STATUS_SUCCESS);
537 }
538
539 NTSTATUS MmGatherSegment(PMADDRESS_SPACE AddressSpace,
540 PMEMORY_AREA MemoryArea,
541 PVOID RegionAddress,
542 ULONG RegionLength,
543 ULONG Type,
544 ULONG Protect,
545 PMM_SEGMENT FirstSegment,
546 PVOID FirstAddress)
547 /*
548 * FUNCTION: Do a virtual memory operation that will effect several
549 * memory segments.
550 * ARGUMENTS:
551 * AddressSpace (IN) = Address space to affect
552 * MemoryArea (IN) = Memory area to affect
553 * BaseAddress (IN) = Base address of the region to affect
554 * RegionSize (IN) = Size of the region to affect
555 * Type (IN) = New type of the region
556 * Protect (IN) = New protection of the region
557 * CurrentSegment (IN) = First segment intersecting with the region
558 * CurrentAddress (IN) = Start address of the first segment
559 * interesting with the region
560 * RETURNS: Status
561 */
562 {
563 PMM_SEGMENT RegionSegment;
564 PVOID CurrentAddress;
565 ULONG RemainingLength;
566 PLIST_ENTRY CurrentEntry;
567 PLIST_ENTRY ListHead;
568 PMM_SEGMENT CurrentSegment;
569
570 if (FirstAddress < RegionAddress)
571 {
572 /*
573 * If a portion of the first segment is not covered by the region then
574 * we need to split it into two segments
575 */
576
577 RegionSegment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SEGMENT),
578 TAG_MM_SEGMENT);
579 if (RegionSegment == NULL)
580 {
581 return(STATUS_NO_MEMORY);
582 }
583
584 RegionSegment->Type = Type;
585 RegionSegment->Protect = Protect;
586 RegionSegment->Length = (FirstAddress + FirstSegment->Length) -
587 RegionAddress;
588
589 FirstSegment->Length = RegionAddress - FirstAddress;
590
591 InsertAfterEntry(&FirstSegment->SegmentListEntry,
592 &RegionSegment->SegmentListEntry);
593
594 MmModifyAttributes(AddressSpace,
595 RegionAddress,
596 RegionSegment->Length,
597 FirstSegment->Type,
598 FirstSegment->Protect,
599 Type,
600 Protect);
601
602 CurrentAddress = FirstAddress + FirstSegment->Length +
603 RegionSegment->Length;
604 }
605 else
606 {
607 /*
608 * Otherwise just change the attributes of the segment
609 */
610
611 ULONG OldType;
612 ULONG OldProtect;
613
614 OldType = FirstSegment->Type;
615 OldProtect = FirstSegment->Protect;
616
617 FirstSegment->Type = Type;
618 FirstSegment->Protect = Protect;
619
620 RegionSegment = FirstSegment;
621
622 MmModifyAttributes(AddressSpace,
623 RegionAddress,
624 FirstSegment->Length,
625 OldType,
626 OldProtect,
627 Type,
628 Protect);
629
630 CurrentAddress = FirstAddress + RegionSegment->Length;
631 }
632
633 /*
634 * Change the attributes of all the complete segments lying inside the
635 * affected region
636 */
637 RemainingLength = RegionLength - RegionSegment->Length;
638 CurrentEntry = RegionSegment->SegmentListEntry.Flink;
639 CurrentSegment = CONTAINING_RECORD(CurrentEntry,
640 MM_SEGMENT,
641 SegmentListEntry);
642 ListHead = &MemoryArea->Data.VirtualMemoryData.SegmentListHead;
643
644 while (CurrentEntry != ListHead && RemainingLength > 0)
645 {
646 ULONG OldType;
647 ULONG OldProtect;
648 ULONG OldLength;
649
650 /*
651 * If this segment will not be completely covered by the
652 * affected region then break
653 */
654 if (CurrentSegment->Length > RemainingLength)
655 {
656 break;
657 }
658
659 OldType = CurrentSegment->Type;
660 OldProtect = CurrentSegment->Protect;
661 OldLength = CurrentSegment->Length;
662
663 /*
664 * Extend the length of the previous segment to cover this one
665 */
666 RegionSegment->Length = RegionSegment->Length + OldLength;
667 RemainingLength = RemainingLength - OldLength;
668 CurrentAddress = CurrentAddress + OldLength;
669 CurrentEntry = CurrentEntry->Flink;
670
671 /*
672 * Remove the current segment from the list
673 */
674 RemoveEntryList(&CurrentSegment->SegmentListEntry);
675 ExFreePool(CurrentSegment);
676
677 MmModifyAttributes(AddressSpace,
678 CurrentAddress,
679 OldLength,
680 OldType,
681 OldProtect,
682 Type,
683 Protect);
684
685 CurrentSegment = CONTAINING_RECORD(CurrentEntry,
686 MM_SEGMENT,
687 SegmentListEntry);
688 }
689
690 /*
691 * If we've run off the top of the memory area then bug check
692 */
693 if (CurrentEntry == ListHead && RemainingLength > 0)
694 {
695 KeBugCheck(0);
696 }
697
698 /*
699 * We've only affected a portion of a segment then split it in two
700 */
701 if (RemainingLength > 0)
702 {
703 CurrentSegment->Length = CurrentSegment->Length - RemainingLength;
704
705 RegionSegment->Length = RegionSegment->Length + RemainingLength;
706
707 MmModifyAttributes(AddressSpace,
708 CurrentAddress,
709 RemainingLength,
710 CurrentSegment->Type,
711 CurrentSegment->Protect,
712 Type,
713 Protect);
714 }
715
716 return(STATUS_SUCCESS);
717 }
718
719 NTSTATUS MmComplexVirtualMemoryOperation(PMADDRESS_SPACE AddressSpace,
720 PMEMORY_AREA MemoryArea,
721 PVOID BaseAddress,
722 ULONG RegionSize,
723 ULONG Type,
724 ULONG Protect)
725 {
726 PMM_SEGMENT CurrentSegment;
727 PVOID CurrentAddress;
728
729 CurrentSegment = MmGetSegmentForAddress(MemoryArea,
730 BaseAddress,
731 &CurrentAddress);
732 if (CurrentSegment == NULL)
733 {
734 KeBugCheck(0);
735 }
736
737 if (BaseAddress >= CurrentAddress &&
738 (BaseAddress + RegionSize) <= (CurrentAddress + CurrentSegment->Length))
739 {
740 return((MmSplitSegment(AddressSpace,
741 MemoryArea,
742 BaseAddress,
743 RegionSize,
744 Type,
745 Protect,
746 CurrentSegment,
747 CurrentAddress)));
748 }
749 else
750 {
751 return((MmGatherSegment(AddressSpace,
752 MemoryArea,
753 BaseAddress,
754 RegionSize,
755 Type,
756 Protect,
757 CurrentSegment,
758 CurrentAddress)));
759 }
760 }
761
762
763 NTSTATUS STDCALL
764 NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
765 IN OUT PVOID* UBaseAddress,
766 IN ULONG ZeroBits,
767 IN OUT PULONG URegionSize,
768 IN ULONG AllocationType,
769 IN ULONG Protect)
770 /*
771 * FUNCTION: Allocates a block of virtual memory in the process address space
772 * ARGUMENTS:
773 * ProcessHandle = The handle of the process which owns the virtual memory
774 * BaseAddress = A pointer to the virtual memory allocated. If you
775 * supply a non zero value the system will try to
776 * allocate the memory at the address supplied. It round
777 * it down to a multiple of the page size.
778 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
779 * that must be zero, ensuring that the memory will be
780 * allocated at a address below a certain value.
781 * RegionSize = The number of bytes to allocate
782 * AllocationType = Indicates the type of virtual memory you like to
783 * allocated, can be one of the values : MEM_COMMIT,
784 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN
785 * Protect = Indicates the protection type of the pages allocated, can be
786 * a combination of PAGE_READONLY, PAGE_READWRITE,
787 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
788 * PAGE_NOACCESS
789 * REMARKS:
790 * This function maps to the win32 VirtualAllocEx. Virtual memory is
791 * process based so the protocol starts with a ProcessHandle. I
792 * splitted the functionality of obtaining the actual address and
793 * specifying the start address in two parameters ( BaseAddress and
794 * StartAddress ) The NumberOfBytesAllocated specify the range and the
795 * AllocationType and ProctectionType map to the other two parameters.
796 * RETURNS: Status
797 */
798 {
799 PEPROCESS Process;
800 MEMORY_AREA* MemoryArea;
801 ULONG Type;
802 NTSTATUS Status;
803 PMADDRESS_SPACE AddressSpace;
804 PMM_SEGMENT Segment;
805 PVOID BaseAddress;
806 ULONG RegionSize;
807 PVOID PBaseAddress;
808 ULONG PRegionSize;
809
810 DPRINT("NtAllocateVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
811 "ZeroBits %d, *RegionSize %x, AllocationType %x, Protect %x)\n",
812 ProcessHandle,*BaseAddress,ZeroBits,*RegionSize,AllocationType,
813 Protect);
814
815 /*
816 * Check the validity of the parameters
817 */
818 if ((Protect & PAGE_FLAGS_VALID_FROM_USER_MODE) != Protect)
819 {
820 return(STATUS_INVALID_PAGE_PROTECTION);
821 }
822 PBaseAddress = *UBaseAddress;
823 PRegionSize = *URegionSize;
824
825
826 BaseAddress = (PVOID)PAGE_ROUND_DOWN(PBaseAddress);
827 RegionSize = PAGE_ROUND_UP(PBaseAddress + PRegionSize) -
828 PAGE_ROUND_DOWN(PBaseAddress);
829
830 Status = ObReferenceObjectByHandle(ProcessHandle,
831 PROCESS_VM_OPERATION,
832 NULL,
833 UserMode,
834 (PVOID*)(&Process),
835 NULL);
836 if (!NT_SUCCESS(Status))
837 {
838 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
839 return(Status);
840 }
841
842 if (AllocationType & MEM_RESERVE)
843 {
844 Type = MEM_RESERVE;
845 }
846 else
847 {
848 Type = MEM_COMMIT;
849 }
850
851 AddressSpace = &Process->AddressSpace;
852 MmLockAddressSpace(AddressSpace);
853
854 if (BaseAddress != 0)
855 {
856 MemoryArea = MmOpenMemoryAreaByAddress(&Process->AddressSpace,
857 BaseAddress);
858
859 if (MemoryArea != NULL &&
860 MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
861 MemoryArea->Length >= RegionSize)
862 {
863 Status = MmComplexVirtualMemoryOperation(AddressSpace,
864 MemoryArea,
865 BaseAddress,
866 RegionSize,
867 Type,
868 Protect);
869 /* FIXME: Reserve/dereserve swap pages */
870 MmUnlockAddressSpace(AddressSpace);
871 ObDereferenceObject(Process);
872 return(Status);
873 }
874 else if (MemoryArea != NULL)
875 {
876 MmUnlockAddressSpace(AddressSpace);
877 ObDereferenceObject(Process);
878 return(STATUS_UNSUCCESSFUL);
879 }
880 }
881
882 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SEGMENT),
883 TAG_MM_SEGMENT);
884 if (Segment == NULL)
885 {
886 MmUnlockAddressSpace(AddressSpace);
887 ObDereferenceObject(Process);
888 return(STATUS_UNSUCCESSFUL);
889 }
890
891 Status = MmCreateMemoryArea(Process,
892 &Process->AddressSpace,
893 MEMORY_AREA_VIRTUAL_MEMORY,
894 &BaseAddress,
895 RegionSize,
896 Protect,
897 &MemoryArea);
898
899 if (!NT_SUCCESS(Status))
900 {
901 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
902 MmUnlockAddressSpace(AddressSpace);
903 ObDereferenceObject(Process);
904 return(Status);
905 }
906
907 InitializeListHead(&MemoryArea->Data.VirtualMemoryData.SegmentListHead);
908
909 Segment->Type = Type;
910 Segment->Protect = Protect;
911 Segment->Length = RegionSize;
912 InsertTailList(&MemoryArea->Data.VirtualMemoryData.SegmentListHead,
913 &Segment->SegmentListEntry);
914
915 DPRINT("*BaseAddress %x\n",*BaseAddress);
916 if ((AllocationType & MEM_COMMIT) &&
917 ((Protect & PAGE_READWRITE) ||
918 (Protect & PAGE_EXECUTE_READWRITE)))
919 {
920 MmReserveSwapPages(RegionSize);
921 }
922
923 *UBaseAddress = BaseAddress;
924 *URegionSize = RegionSize;
925
926 MmUnlockAddressSpace(AddressSpace);
927 ObDereferenceObject(Process);
928 return(STATUS_SUCCESS);
929 }
930
931
932 NTSTATUS STDCALL
933 NtFlushVirtualMemory(IN HANDLE ProcessHandle,
934 IN PVOID BaseAddress,
935 IN ULONG NumberOfBytesToFlush,
936 OUT PULONG NumberOfBytesFlushed OPTIONAL)
937 /*
938 * FUNCTION: Flushes virtual memory to file
939 * ARGUMENTS:
940 * ProcessHandle = Points to the process that allocated the virtual
941 * memory
942 * BaseAddress = Points to the memory address
943 * NumberOfBytesToFlush = Limits the range to flush,
944 * NumberOfBytesFlushed = Actual number of bytes flushed
945 * RETURNS: Status
946 */
947 {
948 UNIMPLEMENTED;
949 }
950
951 VOID STATIC
952 MmFreeVirtualMemoryPage(PVOID Context, PVOID Address, ULONG PhysicalAddr)
953 {
954 PEPROCESS Process = (PEPROCESS)Context;
955
956 if (PhysicalAddr != 0)
957 {
958 MmRemovePageFromWorkingSet(Process, Address);
959 MmDereferencePage((PVOID)PhysicalAddr);
960 }
961 }
962
963 NTSTATUS STDCALL
964 NtFreeVirtualMemory(IN HANDLE ProcessHandle,
965 IN PVOID* PBaseAddress,
966 IN PULONG PRegionSize,
967 IN ULONG FreeType)
968 /*
969 * FUNCTION: Frees a range of virtual memory
970 * ARGUMENTS:
971 * ProcessHandle = Points to the process that allocated the virtual
972 * memory
973 * BaseAddress = Points to the memory address, rounded down to a
974 * multiple of the pagesize
975 * RegionSize = Limits the range to free, rounded up to a multiple of
976 * the paging size
977 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
978 * RETURNS: Status
979 */
980 {
981 MEMORY_AREA* MemoryArea;
982 NTSTATUS Status;
983 PEPROCESS Process;
984 PMADDRESS_SPACE AddressSpace;
985 PVOID BaseAddress;
986 ULONG RegionSize;
987
988 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
989 "*RegionSize %x, FreeType %x)\n",ProcessHandle,*BaseAddress,
990 *RegionSize,FreeType);
991
992 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
993 RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
994 PAGE_ROUND_DOWN((*PBaseAddress));
995
996 Status = ObReferenceObjectByHandle(ProcessHandle,
997 PROCESS_VM_OPERATION,
998 PsProcessType,
999 UserMode,
1000 (PVOID*)(&Process),
1001 NULL);
1002 if (!NT_SUCCESS(Status))
1003 {
1004 return(Status);
1005 }
1006
1007 AddressSpace = &Process->AddressSpace;
1008
1009 MmLockAddressSpace(AddressSpace);
1010 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
1011 BaseAddress);
1012 if (MemoryArea == NULL)
1013 {
1014 MmUnlockAddressSpace(AddressSpace);
1015 ObDereferenceObject(Process);
1016 return(STATUS_UNSUCCESSFUL);
1017 }
1018
1019 switch (FreeType)
1020 {
1021 case MEM_RELEASE:
1022 if (MemoryArea->BaseAddress != BaseAddress)
1023 {
1024 MmUnlockAddressSpace(AddressSpace);
1025 ObDereferenceObject(Process);
1026 return(STATUS_UNSUCCESSFUL);
1027 }
1028 #if 0
1029 if ((MemoryArea->Type == MEMORY_AREA_COMMIT) &&
1030 ((MemoryArea->Attributes & PAGE_READWRITE) ||
1031 (MemoryArea->Attributes & PAGE_EXECUTE_READWRITE)))
1032 {
1033 MmDereserveSwapPages(PAGE_ROUND_UP(MemoryArea->Length));
1034 }
1035 #endif
1036
1037 MmFreeMemoryArea(&Process->AddressSpace,
1038 BaseAddress,
1039 0,
1040 MmFreeVirtualMemoryPage,
1041 (PVOID)Process);
1042 MmUnlockAddressSpace(AddressSpace);
1043 ObDereferenceObject(Process);
1044 return(STATUS_SUCCESS);
1045
1046 case MEM_DECOMMIT:
1047 Status = MmComplexVirtualMemoryOperation(AddressSpace,
1048 MemoryArea,
1049 BaseAddress,
1050 RegionSize,
1051 MEM_RESERVE,
1052 PAGE_NOACCESS);
1053 MmUnlockAddressSpace(AddressSpace);
1054 ObDereferenceObject(Process);
1055 return(Status);
1056 }
1057 MmUnlockAddressSpace(AddressSpace);
1058 ObDereferenceObject(Process);
1059 return(STATUS_NOT_IMPLEMENTED);
1060 }
1061
1062
1063 NTSTATUS STDCALL
1064 NtLockVirtualMemory(HANDLE ProcessHandle,
1065 PVOID BaseAddress,
1066 ULONG NumberOfBytesToLock,
1067 PULONG NumberOfBytesLocked)
1068 {
1069 UNIMPLEMENTED;
1070 }
1071
1072
1073 VOID
1074 MmChangeAreaProtection(PEPROCESS Process,
1075 PVOID BaseAddress,
1076 ULONG Length,
1077 ULONG Protect)
1078 {
1079 ULONG i;
1080
1081 for (i=0; i<(Length/PAGESIZE); i++)
1082 {
1083 if (MmIsPagePresent(Process, BaseAddress + (i*PAGESIZE)))
1084 {
1085 MmSetPageProtect(Process,
1086 BaseAddress + (i*PAGESIZE),
1087 Protect);
1088 }
1089 }
1090 }
1091
1092
1093 NTSTATUS STDCALL NtProtectVirtualMemory(IN HANDLE ProcessHandle,
1094 IN PVOID BaseAddress,
1095 IN ULONG NumberOfBytesToProtect,
1096 IN ULONG NewAccessProtection,
1097 OUT PULONG OldAccessProtection)
1098 {
1099 PMEMORY_AREA MemoryArea;
1100 PEPROCESS Process;
1101 NTSTATUS Status;
1102 PMADDRESS_SPACE AddressSpace;
1103
1104 Status = ObReferenceObjectByHandle(ProcessHandle,
1105 PROCESS_VM_OPERATION,
1106 PsProcessType,
1107 UserMode,
1108 (PVOID*)(&Process),
1109 NULL);
1110 if (Status != STATUS_SUCCESS)
1111 {
1112 DPRINT("NtProtectVirtualMemory() = %x\n",Status);
1113 return(Status);
1114 }
1115
1116 AddressSpace = &Process->AddressSpace;
1117
1118 MmLockAddressSpace(AddressSpace);
1119 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
1120 BaseAddress);
1121 if (MemoryArea == NULL)
1122 {
1123 DPRINT("NtProtectVirtualMemory() = %x\n",STATUS_UNSUCCESSFUL);
1124 MmUnlockAddressSpace(AddressSpace);
1125 ObDereferenceObject(Process);
1126 return(STATUS_UNSUCCESSFUL);
1127 }
1128
1129 *OldAccessProtection = MemoryArea->Attributes;
1130
1131 if (MemoryArea->BaseAddress == BaseAddress &&
1132 MemoryArea->Length == NumberOfBytesToProtect)
1133 {
1134 MemoryArea->Attributes = NewAccessProtection;
1135 }
1136 else
1137 {
1138 MemoryArea = MmSplitMemoryArea(Process,
1139 &Process->AddressSpace,
1140 MemoryArea,
1141 BaseAddress,
1142 NumberOfBytesToProtect,
1143 MemoryArea->Type,
1144 NewAccessProtection);
1145 }
1146 MmChangeAreaProtection(Process,
1147 BaseAddress,
1148 NumberOfBytesToProtect,
1149 NewAccessProtection);
1150 MmUnlockAddressSpace(AddressSpace);
1151 ObDereferenceObject(Process);
1152 return(STATUS_SUCCESS);
1153 }
1154
1155
1156 NTSTATUS STDCALL NtQueryVirtualMemory (IN HANDLE ProcessHandle,
1157 IN PVOID Address,
1158 IN CINT VirtualMemoryInformationClass,
1159 OUT PVOID VirtualMemoryInformation,
1160 IN ULONG Length,
1161 OUT PULONG ResultLength)
1162 {
1163 NTSTATUS Status;
1164 PEPROCESS Process;
1165 MEMORY_AREA* MemoryArea;
1166
1167 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
1168 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
1169 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
1170 VirtualMemoryInformationClass,VirtualMemoryInformation,
1171 Length,ResultLength);
1172
1173 switch(VirtualMemoryInformationClass)
1174 {
1175 case MemoryBasicInformation:
1176 {
1177 PMEMORY_BASIC_INFORMATION Info =
1178 (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
1179 PMADDRESS_SPACE AddressSpace;
1180
1181 if (Length < sizeof(MEMORY_BASIC_INFORMATION))
1182 {
1183 ObDereferenceObject(Process);
1184 return STATUS_INFO_LENGTH_MISMATCH;
1185 }
1186
1187 if (ResultLength)
1188 {
1189 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1190 }
1191
1192 Status = ObReferenceObjectByHandle(ProcessHandle,
1193 PROCESS_QUERY_INFORMATION,
1194 NULL,
1195 UserMode,
1196 (PVOID*)(&Process),
1197 NULL);
1198
1199 if (!NT_SUCCESS(Status))
1200 {
1201 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
1202 return(Status);
1203 }
1204
1205 AddressSpace = &Process->AddressSpace;
1206 MmLockAddressSpace(AddressSpace);
1207 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
1208 Address);
1209
1210 if (MemoryArea == NULL)
1211 {
1212 Info->State = MEM_FREE;
1213 DPRINT("Virtual memory at %p is free.\n", Address);
1214 MmUnlockAddressSpace(AddressSpace);
1215 ObDereferenceObject(Process);
1216 return (STATUS_SUCCESS);
1217 }
1218
1219 #if 0
1220 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
1221 {
1222 Info->State = MEM_COMMIT;
1223 }
1224 else
1225 {
1226 Info->State = MEM_RESERVE;
1227 }
1228 #endif
1229
1230 Info->BaseAddress = MemoryArea->BaseAddress;
1231 Info->RegionSize = MemoryArea->Length;
1232
1233 DPRINT("BaseAddress %p, RegionSize %x State %x\n",
1234 Info->BaseAddress, Info->RegionSize, Info->State);
1235
1236 MmUnlockAddressSpace(AddressSpace);
1237 ObDereferenceObject(Process);
1238 return STATUS_SUCCESS;
1239 }
1240 break;
1241 }
1242
1243 return STATUS_INVALID_INFO_CLASS;
1244 }
1245
1246
1247 NTSTATUS STDCALL
1248 NtReadVirtualMemory(IN HANDLE ProcessHandle,
1249 IN PVOID BaseAddress,
1250 OUT PVOID Buffer,
1251 IN ULONG NumberOfBytesToRead,
1252 OUT PULONG NumberOfBytesRead)
1253 {
1254 NTSTATUS Status;
1255 PMDL Mdl;
1256 PVOID SystemAddress;
1257 PEPROCESS Process;
1258
1259 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1260 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress,
1261 Buffer,NumberOfBytesToRead);
1262
1263 Status = ObReferenceObjectByHandle(ProcessHandle,
1264 PROCESS_VM_WRITE,
1265 NULL,
1266 UserMode,
1267 (PVOID*)(&Process),
1268 NULL);
1269 if (Status != STATUS_SUCCESS)
1270 {
1271 return(Status);
1272 }
1273
1274 Mdl = MmCreateMdl(NULL,
1275 Buffer,
1276 NumberOfBytesToRead);
1277 MmProbeAndLockPages(Mdl,
1278 UserMode,
1279 IoWriteAccess);
1280
1281 KeAttachProcess(Process);
1282
1283 SystemAddress = MmGetSystemAddressForMdl(Mdl);
1284 memcpy(SystemAddress, BaseAddress, NumberOfBytesToRead);
1285
1286 KeDetachProcess();
1287
1288 ObDereferenceObject(Process);
1289
1290 *NumberOfBytesRead = NumberOfBytesToRead;
1291 return(STATUS_SUCCESS);
1292 }
1293
1294
1295 NTSTATUS STDCALL
1296 NtUnlockVirtualMemory(HANDLE ProcessHandle,
1297 PVOID BaseAddress,
1298 ULONG NumberOfBytesToUnlock,
1299 PULONG NumberOfBytesUnlocked OPTIONAL)
1300 {
1301 UNIMPLEMENTED;
1302 }
1303
1304
1305 NTSTATUS STDCALL
1306 NtWriteVirtualMemory(IN HANDLE ProcessHandle,
1307 IN PVOID BaseAddress,
1308 IN PVOID Buffer,
1309 IN ULONG NumberOfBytesToWrite,
1310 OUT PULONG NumberOfBytesWritten)
1311 {
1312 NTSTATUS Status;
1313 PMDL Mdl;
1314 PVOID SystemAddress;
1315 PEPROCESS Process;
1316
1317 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1318 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
1319 Buffer,NumberOfBytesToWrite);
1320
1321 Status = ObReferenceObjectByHandle(ProcessHandle,
1322 PROCESS_VM_WRITE,
1323 NULL,
1324 UserMode,
1325 (PVOID*)(&Process),
1326 NULL);
1327 if (Status != STATUS_SUCCESS)
1328 {
1329 return(Status);
1330 }
1331
1332 Mdl = MmCreateMdl(NULL,
1333 Buffer,
1334 NumberOfBytesToWrite);
1335 MmProbeAndLockPages(Mdl,
1336 UserMode,
1337 IoReadAccess);
1338
1339 KeAttachProcess(Process);
1340
1341 DPRINT("Attached to process copying memory\n");
1342
1343 SystemAddress = MmGetSystemAddressForMdl(Mdl);
1344 memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
1345
1346 DPRINT("Done copy\n");
1347
1348 KeDetachProcess();
1349
1350 ObDereferenceObject(Process);
1351
1352 *NumberOfBytesWritten = NumberOfBytesToWrite;
1353
1354 DPRINT("Finished NtWriteVirtualMemory()\n");
1355
1356 return(STATUS_SUCCESS);
1357 }
1358
1359
1360 DWORD
1361 STDCALL
1362 MmSecureVirtualMemory (
1363 DWORD Unknown0,
1364 DWORD Unknown1,
1365 DWORD Unknown2
1366 )
1367 {
1368 UNIMPLEMENTED;
1369 return 0;
1370 }
1371
1372
1373 VOID
1374 STDCALL
1375 MmUnsecureVirtualMemory (
1376 DWORD Unknown0
1377 )
1378 {
1379 UNIMPLEMENTED;
1380 }
1381
1382 /* EOF */