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