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