Added .o -> .sys rule to simplify driver makefiles
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
1 /* $Id: virtual.c,v 1.33 2000/08/18 22:27:03 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 <string.h>
23 #include <internal/string.h>
24
25 #define NDEBUG
26 #include <internal/debug.h>
27
28 /* TYPES *********************************************************************/
29
30 typedef struct _MM_SEGMENT
31 {
32 ULONG Type;
33 ULONG Protect;
34 ULONG Length;
35 LIST_ENTRY SegmentListEntry;
36 } MM_SEGMENT, *PMM_SEGMENT;
37
38 /* FUNCTIONS *****************************************************************/
39
40 PMM_SEGMENT MmGetSegmentForAddress(PMEMORY_AREA MArea,
41 PVOID Address,
42 PVOID* PCurrentAddress)
43 /*
44 * FUNCTION: Get the segment corresponding to a particular memory area and
45 * address.
46 * ARGUMENTS:
47 * MArea (IN) = The memory area
48 * Address (IN) = The address to get the segment for
49 * PCurrentAddress (OUT) = The start of the segment
50 * RETURNS:
51 * The corresponding memory or NULL if an error occurred
52 */
53 {
54 PVOID CurrentAddress;
55 PMM_SEGMENT CurrentSegment;
56 PLIST_ENTRY Current;
57
58 if (Address < MArea->BaseAddress ||
59 Address >= (MArea->BaseAddress + MArea->Length))
60 {
61 KeBugCheck(0);
62 *PCurrentAddress = NULL;
63 return(NULL);
64 }
65
66 Current = MArea->Data.VirtualMemoryData.SegmentListHead.Flink;
67 CurrentAddress = MArea->BaseAddress;
68 while (Current != &MArea->Data.VirtualMemoryData.SegmentListHead)
69 {
70 CurrentSegment = CONTAINING_RECORD(Current,
71 MM_SEGMENT,
72 SegmentListEntry);
73 if (Address >= CurrentAddress &&
74 Address < (CurrentAddress + CurrentSegment->Length))
75 {
76 *PCurrentAddress = CurrentAddress;
77 return(CurrentSegment);
78 }
79 CurrentAddress = CurrentAddress + CurrentSegment->Length;
80 Current = Current->Flink;
81 }
82 KeBugCheck(0);
83 return(NULL);
84 }
85
86 NTSTATUS 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 MmSetPage(PsGetCurrentProcess(),
193 Address,
194 0,
195 0);
196 MmDereferencePage((PVOID)PhysicalAddress.u.LowPart);
197 *Ul = TRUE;
198 return(1);
199 }
200 *Ul = FALSE;
201 return(0);
202 }
203
204 NTSTATUS MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace,
205 MEMORY_AREA* MemoryArea,
206 PVOID Address)
207 /*
208 * FUNCTION: Move data into memory to satisfy a page not present fault
209 * ARGUMENTS:
210 * AddressSpace = Address space within which the fault occurred
211 * MemoryArea = The memory area within which the fault occurred
212 * Address = The absolute address of fault
213 * RETURNS: Status
214 * NOTES: This function is called with the address space lock held.
215 */
216 {
217 PVOID Page;
218 NTSTATUS Status;
219 PMM_SEGMENT Segment;
220 PVOID CurrentAddress;
221
222 Segment = MmGetSegmentForAddress(MemoryArea, Address, &CurrentAddress);
223 if (Segment == NULL)
224 {
225 return(STATUS_UNSUCCESSFUL);
226 }
227 if (Segment->Type == MEM_RESERVE)
228 {
229 return(STATUS_UNSUCCESSFUL);
230 }
231
232 if (MmIsPagePresent(NULL, Address))
233 {
234 return(STATUS_SUCCESS);
235 }
236
237 Page = MmAllocPage(0);
238 while (Page == NULL)
239 {
240 MmUnlockAddressSpace(AddressSpace);
241 MmWaitForFreePages();
242 MmLockAddressSpace(AddressSpace);
243 if (MmIsPagePresent(NULL, Address))
244 {
245 return(STATUS_SUCCESS);
246 }
247 Page = MmAllocPage(0);
248 }
249 Status = MmCreatePageTable(Address);
250 while (!NT_SUCCESS(Status))
251 {
252 MmUnlockAddressSpace(AddressSpace);
253 MmWaitForFreePages();
254 MmLockAddressSpace(AddressSpace);
255 if (MmIsPagePresent(NULL, Address))
256 {
257 MmDereferencePage(Page);
258 return(STATUS_SUCCESS);
259 }
260 Status = MmCreatePageTable(Address);
261 }
262 MmAddPageToWorkingSet(PsGetCurrentProcess(), Address);
263 MmSetPage(PsGetCurrentProcess(),
264 Address,
265 MemoryArea->Attributes,
266 (ULONG)Page);
267
268 return(STATUS_SUCCESS);
269 }
270
271 VOID MmModifyAttributes(PMADDRESS_SPACE AddressSpace,
272 PVOID BaseAddress,
273 ULONG RegionSize,
274 ULONG OldType,
275 ULONG OldProtect,
276 ULONG NewType,
277 ULONG NewProtect)
278 {
279 if (NewType == MEM_RESERVE &&
280 OldType == MEM_COMMIT)
281 {
282 ULONG i;
283
284 for (i=0; i<=(RegionSize/PAGESIZE); i++)
285 {
286 LARGE_INTEGER PhysicalAddr;
287
288 PhysicalAddr = MmGetPhysicalAddress(BaseAddress + (i*PAGESIZE));
289 if (PhysicalAddr.u.LowPart != 0)
290 {
291 MmRemovePageFromWorkingSet(AddressSpace->Process,
292 BaseAddress + (i*PAGESIZE));
293 MmDereferencePage((PVOID)(ULONG)(PhysicalAddr.u.LowPart));
294 }
295 }
296 }
297 if (NewType == MEM_COMMIT && OldType == MEM_COMMIT &&
298 OldProtect != NewProtect)
299 {
300 ULONG i;
301
302 for (i=0; i<(RegionSize/PAGESIZE); i++)
303 {
304 if (MmIsPagePresent(AddressSpace->Process,
305 BaseAddress + (i*PAGESIZE)))
306 {
307 MmSetPageProtect(AddressSpace->Process,
308 BaseAddress + (i*PAGESIZE),
309 NewProtect);
310 }
311 }
312 }
313 }
314
315 VOID InsertAfterEntry(PLIST_ENTRY Previous,
316 PLIST_ENTRY Entry)
317 {
318 Previous->Flink->Blink = Entry;
319
320 Entry->Flink = Previous->Flink;
321 Entry->Blink = Previous;
322
323 Previous->Flink = Entry;
324 }
325
326 NTSTATUS MmSplitSegment(PMADDRESS_SPACE AddressSpace,
327 PMEMORY_AREA MemoryArea,
328 PVOID BaseAddress,
329 ULONG RegionSize,
330 ULONG Type,
331 ULONG Protect,
332 PMM_SEGMENT CurrentSegment,
333 PVOID CurrentAddress)
334 /*
335 * FUNCTION: Split a memory segment internally
336 */
337 {
338 PMM_SEGMENT NewSegment;
339 PMM_SEGMENT NewTopSegment;
340 PMM_SEGMENT PreviousSegment;
341 ULONG OldType;
342 ULONG OldProtect;
343
344 OldType = CurrentSegment->Type;
345 OldProtect = CurrentSegment->Protect;
346
347 NewSegment = ExAllocatePool(NonPagedPool, sizeof(MM_SEGMENT));
348 if (NewSegment == NULL)
349 {
350 return(STATUS_NO_MEMORY);
351 }
352 NewTopSegment = ExAllocatePool(NonPagedPool, sizeof(MM_SEGMENT));
353 if (NewTopSegment == NULL)
354 {
355 ExFreePool(NewSegment);
356 return(STATUS_NO_MEMORY);
357 }
358
359 if (CurrentSegment->Type == Type &&
360 CurrentSegment->Protect == Protect)
361 {
362 return(STATUS_SUCCESS);
363 }
364
365 if (CurrentAddress < BaseAddress)
366 {
367 NewSegment->Type = Type;
368 NewSegment->Protect = Protect;
369 NewSegment->Length = RegionSize;
370
371 CurrentSegment->Length = BaseAddress - CurrentAddress;
372
373 InsertAfterEntry(&CurrentSegment->SegmentListEntry,
374 &NewSegment->SegmentListEntry);
375
376 PreviousSegment = NewSegment;
377 }
378 else
379 {
380 CurrentSegment->Type = Type;
381 CurrentSegment->Protect = Protect;
382
383 PreviousSegment = CurrentSegment;
384
385 ExFreePool(NewSegment);
386 NewSegment = NULL;
387 }
388
389 if ((CurrentAddress + CurrentSegment->Length) > (BaseAddress + RegionSize))
390 {
391 NewTopSegment->Type = OldType;
392 NewTopSegment->Protect = OldProtect;
393 NewTopSegment->Length =
394 (CurrentAddress + CurrentSegment->Length) -
395 (BaseAddress + RegionSize);
396
397 InsertAfterEntry(&PreviousSegment->SegmentListEntry,
398 &NewTopSegment->SegmentListEntry);
399 }
400 else
401 {
402 ExFreePool(NewTopSegment);
403 NewTopSegment = NULL;
404 }
405
406 MmModifyAttributes(AddressSpace, BaseAddress, RegionSize,
407 OldType, OldProtect, Type, Protect);
408 return(STATUS_SUCCESS);
409 }
410
411 NTSTATUS MmGatherSegment(PMADDRESS_SPACE AddressSpace,
412 PMEMORY_AREA MemoryArea,
413 PVOID BaseAddress,
414 ULONG RegionSize,
415 ULONG Type,
416 ULONG Protect,
417 PMM_SEGMENT CurrentSegment,
418 PVOID CurrentAddress)
419 /*
420 * FUNCTION: Do a virtual memory operation that will effect several
421 * memory segments.
422 * ARGUMENTS:
423 * AddressSpace (IN) = Address space to affect
424 * MemoryArea (IN) = Memory area to affect
425 * BaseAddress (IN) = Base address of the region to affect
426 * RegionSize (IN) = Size of the region to affect
427 * Type (IN) = New type of the region
428 * Protect (IN) = New protection of the region
429 * CurrentSegment (IN) = First segment intersecting with the region
430 * CurrentAddress (IN) = Start address of the first segment
431 * interesting with the region
432 * RETURNS: Status
433 */
434 {
435 PMM_SEGMENT NewSegment;
436 PMM_SEGMENT NewTopSegment;
437 PMM_SEGMENT PreviousSegment;
438 PVOID LAddress;
439 ULONG RSize;
440 PLIST_ENTRY CurrentEntry;
441 PLIST_ENTRY ListHead;
442
443 /*
444 * We will need a maximum of two new segments. Allocate them now
445 * because if we fail latter we may not be able to reverse the
446 * what we've already done
447 */
448 NewSegment = ExAllocatePool(NonPagedPool, sizeof(MM_SEGMENT));
449 if (NewSegment == NULL)
450 {
451 return(STATUS_NO_MEMORY);
452 }
453 NewTopSegment = ExAllocatePool(NonPagedPool, sizeof(MM_SEGMENT));
454 if (NewTopSegment == NULL)
455 {
456 ExFreePool(NewSegment);
457 return(STATUS_NO_MEMORY);
458 }
459
460 if (CurrentAddress < BaseAddress)
461 {
462 /*
463 * If a portion of the first segment is not covered by the region then
464 * we need to split it into two segments
465 */
466
467 NewSegment->Type = Type;
468 NewSegment->Protect = Protect;
469 NewSegment->Length = RegionSize;
470
471 CurrentSegment->Length =
472 BaseAddress - CurrentAddress;
473
474 InsertAfterEntry(&CurrentSegment->SegmentListEntry,
475 &NewSegment->SegmentListEntry);
476
477 PreviousSegment = NewSegment;
478
479 MmModifyAttributes(AddressSpace, BaseAddress, NewSegment->Length,
480 CurrentSegment->Type,
481 CurrentSegment->Protect, Type, Protect);
482 }
483 else
484 {
485 /*
486 * Otherwise just change the attributes of the segment
487 */
488
489 ULONG OldType;
490 ULONG OldProtect;
491
492 OldType = CurrentSegment->Type;
493 OldProtect = CurrentSegment->Protect;
494
495 CurrentSegment->Type = Type;
496 CurrentSegment->Protect = Protect;
497
498 PreviousSegment = CurrentSegment;
499
500 ExFreePool(NewSegment);
501 NewSegment = NULL;
502
503 MmModifyAttributes(AddressSpace, BaseAddress, CurrentSegment->Length,
504 OldType, OldProtect, Type, Protect);
505 }
506
507 LAddress = BaseAddress + PreviousSegment->Length;
508 RSize = RegionSize - PreviousSegment->Length;
509 CurrentEntry = PreviousSegment->SegmentListEntry.Flink;
510 ListHead = &MemoryArea->Data.VirtualMemoryData.SegmentListHead;
511
512 while (CurrentEntry != ListHead && RSize > 0)
513 {
514 ULONG OldType;
515 ULONG OldProtect;
516
517 CurrentSegment = CONTAINING_RECORD(CurrentEntry,
518 MM_SEGMENT,
519 SegmentListEntry);
520
521 if (CurrentSegment->Length > RSize)
522 {
523 break;
524 }
525
526 OldType = CurrentSegment->Type;
527 OldProtect = CurrentSegment->Protect;
528 CurrentSegment->Type = Type;
529 CurrentSegment->Protect = Protect;
530
531 MmModifyAttributes(AddressSpace, LAddress, CurrentSegment->Length,
532 OldType, OldProtect, Type, Protect);
533
534 RSize = RSize - CurrentSegment->Length;
535 LAddress = LAddress + CurrentSegment->Length;
536
537 CurrentEntry = CurrentEntry->Flink;
538 }
539
540 if (CurrentEntry == ListHead && RSize > 0)
541 {
542 KeBugCheck(0);
543 }
544
545 if (RSize > 0)
546 {
547 NewTopSegment->Type = CurrentSegment->Type;
548 NewTopSegment->Protect = CurrentSegment->Protect;
549 NewTopSegment->Length = CurrentSegment->Length - RSize;
550
551 CurrentSegment->Length = RSize;
552 CurrentSegment->Type = Type;
553 CurrentSegment->Protect = Protect;
554
555 InsertAfterEntry(&CurrentSegment->SegmentListEntry,
556 &NewTopSegment->SegmentListEntry);
557
558 MmModifyAttributes(AddressSpace, LAddress, RSize,
559 NewTopSegment->Type,
560 NewTopSegment->Protect, Type, Protect);
561 }
562
563 return(STATUS_SUCCESS);
564 }
565
566 NTSTATUS MmComplexVirtualMemoryOperation(PMADDRESS_SPACE AddressSpace,
567 PMEMORY_AREA MemoryArea,
568 PVOID BaseAddress,
569 ULONG RegionSize,
570 ULONG Type,
571 ULONG Protect)
572 {
573 PMM_SEGMENT CurrentSegment;
574 PVOID CurrentAddress;
575
576 CurrentSegment = MmGetSegmentForAddress(MemoryArea,
577 BaseAddress,
578 &CurrentAddress);
579 if (CurrentSegment == NULL)
580 {
581 KeBugCheck(0);
582 }
583
584 if (BaseAddress >= CurrentAddress &&
585 (BaseAddress + RegionSize) <= (CurrentAddress + CurrentSegment->Length))
586 {
587 return((MmSplitSegment(AddressSpace,
588 MemoryArea,
589 BaseAddress,
590 RegionSize,
591 Type,
592 Protect,
593 CurrentSegment,
594 CurrentAddress)));
595 }
596 else
597 {
598 return((MmGatherSegment(AddressSpace,
599 MemoryArea,
600 BaseAddress,
601 RegionSize,
602 Type,
603 Protect,
604 CurrentSegment,
605 CurrentAddress)));
606 }
607 }
608
609
610 NTSTATUS STDCALL NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
611 IN OUT PVOID* PBaseAddress,
612 IN ULONG ZeroBits,
613 IN OUT PULONG PRegionSize,
614 IN ULONG AllocationType,
615 IN ULONG Protect)
616 /*
617 * FUNCTION: Allocates a block of virtual memory in the process address space
618 * ARGUMENTS:
619 * ProcessHandle = The handle of the process which owns the virtual memory
620 * BaseAddress = A pointer to the virtual memory allocated. If you
621 * supply a non zero value the system will try to
622 * allocate the memory at the address supplied. It round
623 * it down to a multiple of the page size.
624 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
625 * that must be zero, ensuring that the memory will be
626 * allocated at a address below a certain value.
627 * RegionSize = The number of bytes to allocate
628 * AllocationType = Indicates the type of virtual memory you like to
629 * allocated, can be one of the values : MEM_COMMIT,
630 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN
631 * Protect = Indicates the protection type of the pages allocated, can be
632 * a combination of PAGE_READONLY, PAGE_READWRITE,
633 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
634 * PAGE_NOACCESS
635 * REMARKS:
636 * This function maps to the win32 VirtualAllocEx. Virtual memory is
637 * process based so the protocol starts with a ProcessHandle. I
638 * splitted the functionality of obtaining the actual address and
639 * specifying the start address in two parameters ( BaseAddress and
640 * StartAddress ) The NumberOfBytesAllocated specify the range and the
641 * AllocationType and ProctectionType map to the other two parameters.
642 * RETURNS: Status
643 */
644 {
645 PEPROCESS Process;
646 MEMORY_AREA* MemoryArea;
647 ULONG Type;
648 NTSTATUS Status;
649 PMADDRESS_SPACE AddressSpace;
650 PMM_SEGMENT Segment;
651 PVOID BaseAddress;
652 ULONG RegionSize;
653
654 DPRINT("NtAllocateVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
655 "ZeroBits %d, *RegionSize %x, AllocationType %x, Protect %x)\n",
656 ProcessHandle,*BaseAddress,ZeroBits,*RegionSize,AllocationType,
657 Protect);
658
659 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
660 RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
661 PAGE_ROUND_DOWN((*PBaseAddress));
662
663 Status = ObReferenceObjectByHandle(ProcessHandle,
664 PROCESS_VM_OPERATION,
665 NULL,
666 UserMode,
667 (PVOID*)(&Process),
668 NULL);
669 if (!NT_SUCCESS(Status))
670 {
671 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
672 return(Status);
673 }
674
675 if (AllocationType & MEM_RESERVE)
676 {
677 Type = MEM_RESERVE;
678 }
679 else
680 {
681 Type = MEM_COMMIT;
682 }
683
684 AddressSpace = &Process->AddressSpace;
685 MmLockAddressSpace(AddressSpace);
686
687 if (BaseAddress != 0)
688 {
689 MemoryArea = MmOpenMemoryAreaByAddress(&Process->AddressSpace,
690 BaseAddress);
691
692 if (MemoryArea != NULL &&
693 MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY &&
694 MemoryArea->Length >= RegionSize)
695 {
696 Status = MmComplexVirtualMemoryOperation(AddressSpace,
697 MemoryArea,
698 BaseAddress,
699 RegionSize,
700 Type,
701 Protect);
702 /* FIXME: Reserve/dereserve swap pages */
703 MmUnlockAddressSpace(AddressSpace);
704 ObDereferenceObject(Process);
705 return(Status);
706 }
707 else if (MemoryArea != NULL)
708 {
709 MmUnlockAddressSpace(AddressSpace);
710 ObDereferenceObject(Process);
711 return(STATUS_UNSUCCESSFUL);
712 }
713 }
714
715 Segment = ExAllocatePool(NonPagedPool, sizeof(MM_SEGMENT));
716 if (Segment == NULL)
717 {
718 MmUnlockAddressSpace(AddressSpace);
719 ObDereferenceObject(Process);
720 return(STATUS_UNSUCCESSFUL);
721 }
722
723 Status = MmCreateMemoryArea(Process,
724 &Process->AddressSpace,
725 MEMORY_AREA_VIRTUAL_MEMORY,
726 &BaseAddress,
727 RegionSize,
728 Protect,
729 &MemoryArea);
730
731 if (!NT_SUCCESS(Status))
732 {
733 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
734 MmUnlockAddressSpace(AddressSpace);
735 ObDereferenceObject(Process);
736 return(Status);
737 }
738
739 InitializeListHead(&MemoryArea->Data.VirtualMemoryData.SegmentListHead);
740
741 Segment->Type = Type;
742 Segment->Protect = Protect;
743 Segment->Length = RegionSize;
744 InsertTailList(&MemoryArea->Data.VirtualMemoryData.SegmentListHead,
745 &Segment->SegmentListEntry);
746
747 DPRINT("*BaseAddress %x\n",*BaseAddress);
748 if ((AllocationType & MEM_COMMIT) &&
749 ((Protect & PAGE_READWRITE) ||
750 (Protect & PAGE_EXECUTE_READWRITE)))
751 {
752 MmReserveSwapPages(RegionSize);
753 }
754
755 *PBaseAddress = BaseAddress;
756 *PRegionSize = RegionSize;
757
758 MmUnlockAddressSpace(AddressSpace);
759 ObDereferenceObject(Process);
760 return(STATUS_SUCCESS);
761 }
762
763
764 NTSTATUS STDCALL NtFlushVirtualMemory(IN HANDLE ProcessHandle,
765 IN PVOID BaseAddress,
766 IN ULONG NumberOfBytesToFlush,
767 OUT PULONG NumberOfBytesFlushed OPTIONAL)
768 /*
769 * FUNCTION: Flushes virtual memory to file
770 * ARGUMENTS:
771 * ProcessHandle = Points to the process that allocated the virtual
772 * memory
773 * BaseAddress = Points to the memory address
774 * NumberOfBytesToFlush = Limits the range to flush,
775 * NumberOfBytesFlushed = Actual number of bytes flushed
776 * RETURNS: Status
777 */
778 {
779 UNIMPLEMENTED;
780 }
781
782
783 NTSTATUS STDCALL NtFreeVirtualMemory(IN HANDLE ProcessHandle,
784 IN PVOID* PBaseAddress,
785 IN PULONG PRegionSize,
786 IN ULONG FreeType)
787 /*
788 * FUNCTION: Frees a range of virtual memory
789 * ARGUMENTS:
790 * ProcessHandle = Points to the process that allocated the virtual
791 * memory
792 * BaseAddress = Points to the memory address, rounded down to a
793 * multiple of the pagesize
794 * RegionSize = Limits the range to free, rounded up to a multiple of
795 * the paging size
796 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
797 * RETURNS: Status
798 */
799 {
800 MEMORY_AREA* MemoryArea;
801 NTSTATUS Status;
802 PEPROCESS Process;
803 PMADDRESS_SPACE AddressSpace;
804 ULONG i;
805 PVOID BaseAddress;
806 ULONG RegionSize;
807
808 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
809 "*RegionSize %x, FreeType %x)\n",ProcessHandle,*BaseAddress,
810 *RegionSize,FreeType);
811
812 BaseAddress = (PVOID)PAGE_ROUND_DOWN((*PBaseAddress));
813 RegionSize = PAGE_ROUND_UP((*PBaseAddress) + (*PRegionSize)) -
814 PAGE_ROUND_DOWN((*PBaseAddress));
815
816 Status = ObReferenceObjectByHandle(ProcessHandle,
817 PROCESS_VM_OPERATION,
818 PsProcessType,
819 UserMode,
820 (PVOID*)(&Process),
821 NULL);
822 if (!NT_SUCCESS(Status))
823 {
824 return(Status);
825 }
826
827 AddressSpace = &Process->AddressSpace;
828
829 MmLockAddressSpace(AddressSpace);
830 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
831 BaseAddress);
832 if (MemoryArea == NULL)
833 {
834 MmUnlockAddressSpace(AddressSpace);
835 ObDereferenceObject(Process);
836 return(STATUS_UNSUCCESSFUL);
837 }
838
839 switch (FreeType)
840 {
841 case MEM_RELEASE:
842 if (MemoryArea->BaseAddress != BaseAddress)
843 {
844 MmUnlockAddressSpace(AddressSpace);
845 ObDereferenceObject(Process);
846 return(STATUS_UNSUCCESSFUL);
847 }
848 #if 0
849 if ((MemoryArea->Type == MEMORY_AREA_COMMIT) &&
850 ((MemoryArea->Attributes & PAGE_READWRITE) ||
851 (MemoryArea->Attributes & PAGE_EXECUTE_READWRITE)))
852 {
853 MmDereserveSwapPages(PAGE_ROUND_UP(MemoryArea->Length));
854 }
855 #endif
856
857 for (i=0; i<=(MemoryArea->Length/PAGESIZE); i++)
858 {
859 LARGE_INTEGER PhysicalAddr;
860
861 PhysicalAddr = MmGetPhysicalAddress(MemoryArea->BaseAddress +
862 (i*PAGESIZE));
863 if (PhysicalAddr.u.LowPart != 0)
864 {
865 MmRemovePageFromWorkingSet(AddressSpace->Process,
866 MemoryArea->BaseAddress +
867 (i*PAGESIZE));
868 MmDereferencePage((PVOID)(ULONG)(PhysicalAddr.u.LowPart));
869 }
870 }
871
872 MmFreeMemoryArea(&Process->AddressSpace,
873 BaseAddress,
874 0,
875 FALSE);
876 MmUnlockAddressSpace(AddressSpace);
877 ObDereferenceObject(Process);
878 return(STATUS_SUCCESS);
879
880 case MEM_DECOMMIT:
881 Status = MmComplexVirtualMemoryOperation(AddressSpace,
882 MemoryArea,
883 BaseAddress,
884 RegionSize,
885 MEM_RESERVE,
886 PAGE_NOACCESS);
887 MmUnlockAddressSpace(AddressSpace);
888 ObDereferenceObject(Process);
889 return(Status);
890 }
891 MmUnlockAddressSpace(AddressSpace);
892 ObDereferenceObject(Process);
893 return(STATUS_NOT_IMPLEMENTED);
894 }
895
896
897 NTSTATUS STDCALL NtLockVirtualMemory(HANDLE ProcessHandle,
898 PVOID BaseAddress,
899 ULONG NumberOfBytesToLock,
900 PULONG NumberOfBytesLocked)
901 {
902 UNIMPLEMENTED;
903 }
904
905
906 VOID MmChangeAreaProtection(PEPROCESS Process,
907 PVOID BaseAddress,
908 ULONG Length,
909 ULONG Protect)
910 {
911 ULONG i;
912
913 for (i=0; i<(Length/PAGESIZE); i++)
914 {
915 if (MmIsPagePresent(Process, BaseAddress + (i*PAGESIZE)))
916 {
917 MmSetPageProtect(Process,
918 BaseAddress + (i*PAGESIZE),
919 Protect);
920 }
921 }
922 }
923
924
925 NTSTATUS STDCALL NtProtectVirtualMemory(IN HANDLE ProcessHandle,
926 IN PVOID BaseAddress,
927 IN ULONG NumberOfBytesToProtect,
928 IN ULONG NewAccessProtection,
929 OUT PULONG OldAccessProtection)
930 {
931 PMEMORY_AREA MemoryArea;
932 PEPROCESS Process;
933 NTSTATUS Status;
934 PMADDRESS_SPACE AddressSpace;
935
936 Status = ObReferenceObjectByHandle(ProcessHandle,
937 PROCESS_VM_OPERATION,
938 PsProcessType,
939 UserMode,
940 (PVOID*)(&Process),
941 NULL);
942 if (Status != STATUS_SUCCESS)
943 {
944 DPRINT("NtProtectVirtualMemory() = %x\n",Status);
945 return(Status);
946 }
947
948 AddressSpace = &Process->AddressSpace;
949
950 MmLockAddressSpace(AddressSpace);
951 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
952 BaseAddress);
953 if (MemoryArea == NULL)
954 {
955 DPRINT("NtProtectVirtualMemory() = %x\n",STATUS_UNSUCCESSFUL);
956 MmUnlockAddressSpace(AddressSpace);
957 ObDereferenceObject(Process);
958 return(STATUS_UNSUCCESSFUL);
959 }
960
961 *OldAccessProtection = MemoryArea->Attributes;
962
963 if (MemoryArea->BaseAddress == BaseAddress &&
964 MemoryArea->Length == NumberOfBytesToProtect)
965 {
966 MemoryArea->Attributes = NewAccessProtection;
967 }
968 else
969 {
970 MemoryArea = MmSplitMemoryArea(Process,
971 &Process->AddressSpace,
972 MemoryArea,
973 BaseAddress,
974 NumberOfBytesToProtect,
975 MemoryArea->Type,
976 NewAccessProtection);
977 }
978 MmChangeAreaProtection(Process,
979 BaseAddress,
980 NumberOfBytesToProtect,
981 NewAccessProtection);
982 MmUnlockAddressSpace(AddressSpace);
983 ObDereferenceObject(Process);
984 return(STATUS_SUCCESS);
985 }
986
987
988 NTSTATUS STDCALL NtQueryVirtualMemory (IN HANDLE ProcessHandle,
989 IN PVOID Address,
990 IN CINT VirtualMemoryInformationClass,
991 OUT PVOID VirtualMemoryInformation,
992 IN ULONG Length,
993 OUT PULONG ResultLength)
994 {
995 NTSTATUS Status;
996 PEPROCESS Process;
997 MEMORY_AREA* MemoryArea;
998
999 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
1000 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
1001 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
1002 VirtualMemoryInformationClass,VirtualMemoryInformation,
1003 Length,ResultLength);
1004
1005 switch(VirtualMemoryInformationClass)
1006 {
1007 case MemoryBasicInformation:
1008 {
1009 PMEMORY_BASIC_INFORMATION Info =
1010 (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
1011 PMADDRESS_SPACE AddressSpace;
1012
1013 if (Length < sizeof(MEMORY_BASIC_INFORMATION))
1014 {
1015 ObDereferenceObject(Process);
1016 return STATUS_INFO_LENGTH_MISMATCH;
1017 }
1018
1019 if (ResultLength)
1020 {
1021 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
1022 }
1023
1024 Status = ObReferenceObjectByHandle(ProcessHandle,
1025 PROCESS_QUERY_INFORMATION,
1026 NULL,
1027 UserMode,
1028 (PVOID*)(&Process),
1029 NULL);
1030
1031 if (!NT_SUCCESS(Status))
1032 {
1033 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
1034 return(Status);
1035 }
1036
1037 AddressSpace = &Process->AddressSpace;
1038 MmLockAddressSpace(AddressSpace);
1039 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
1040 Address);
1041
1042 if (MemoryArea == NULL)
1043 {
1044 Info->State = MEM_FREE;
1045 DPRINT("Virtual memory at %p is free.\n", Address);
1046 MmUnlockAddressSpace(AddressSpace);
1047 ObDereferenceObject(Process);
1048 return (STATUS_SUCCESS);
1049 }
1050
1051 #if 0
1052 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
1053 {
1054 Info->State = MEM_COMMIT;
1055 }
1056 else
1057 {
1058 Info->State = MEM_RESERVE;
1059 }
1060 #endif
1061
1062 Info->BaseAddress = MemoryArea->BaseAddress;
1063 Info->RegionSize = MemoryArea->Length;
1064
1065 DPRINT("BaseAddress %p, RegionSize %x State %x\n",
1066 Info->BaseAddress, Info->RegionSize, Info->State);
1067
1068 MmUnlockAddressSpace(AddressSpace);
1069 ObDereferenceObject(Process);
1070 return STATUS_SUCCESS;
1071 }
1072 break;
1073 }
1074
1075 return STATUS_INVALID_INFO_CLASS;
1076 }
1077
1078
1079 NTSTATUS STDCALL NtReadVirtualMemory(IN HANDLE ProcessHandle,
1080 IN PVOID BaseAddress,
1081 OUT PVOID Buffer,
1082 IN ULONG NumberOfBytesToRead,
1083 OUT PULONG NumberOfBytesRead)
1084 {
1085 NTSTATUS Status;
1086 PMDL Mdl;
1087 PVOID SystemAddress;
1088 PEPROCESS Process;
1089
1090 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1091 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress,
1092 Buffer,NumberOfBytesToRead);
1093
1094 Status = ObReferenceObjectByHandle(ProcessHandle,
1095 PROCESS_VM_WRITE,
1096 NULL,
1097 UserMode,
1098 (PVOID*)(&Process),
1099 NULL);
1100 if (Status != STATUS_SUCCESS)
1101 {
1102 return(Status);
1103 }
1104
1105 Mdl = MmCreateMdl(NULL,
1106 Buffer,
1107 NumberOfBytesToRead);
1108 MmProbeAndLockPages(Mdl,
1109 UserMode,
1110 IoWriteAccess);
1111
1112 KeAttachProcess(Process);
1113
1114 SystemAddress = MmGetSystemAddressForMdl(Mdl);
1115 memcpy(SystemAddress, BaseAddress, NumberOfBytesToRead);
1116
1117 KeDetachProcess();
1118
1119 ObDereferenceObject(Process);
1120
1121 *NumberOfBytesRead = NumberOfBytesToRead;
1122 return(STATUS_SUCCESS);
1123 }
1124
1125
1126 NTSTATUS STDCALL NtUnlockVirtualMemory(HANDLE ProcessHandle,
1127 PVOID BaseAddress,
1128 ULONG NumberOfBytesToUnlock,
1129 PULONG NumberOfBytesUnlocked OPTIONAL)
1130 {
1131 UNIMPLEMENTED;
1132 }
1133
1134
1135 NTSTATUS STDCALL NtWriteVirtualMemory(IN HANDLE ProcessHandle,
1136 IN PVOID BaseAddress,
1137 IN PVOID Buffer,
1138 IN ULONG NumberOfBytesToWrite,
1139 OUT PULONG NumberOfBytesWritten)
1140 {
1141 NTSTATUS Status;
1142 PMDL Mdl;
1143 PVOID SystemAddress;
1144 PEPROCESS Process;
1145
1146 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
1147 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
1148 Buffer,NumberOfBytesToWrite);
1149
1150 Status = ObReferenceObjectByHandle(ProcessHandle,
1151 PROCESS_VM_WRITE,
1152 NULL,
1153 UserMode,
1154 (PVOID*)(&Process),
1155 NULL);
1156 if (Status != STATUS_SUCCESS)
1157 {
1158 return(Status);
1159 }
1160
1161 Mdl = MmCreateMdl(NULL,
1162 Buffer,
1163 NumberOfBytesToWrite);
1164 MmProbeAndLockPages(Mdl,
1165 UserMode,
1166 IoReadAccess);
1167
1168 KeAttachProcess(Process);
1169
1170 DPRINT("Attached to process copying memory\n");
1171
1172 SystemAddress = MmGetSystemAddressForMdl(Mdl);
1173 memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
1174
1175 DPRINT("Done copy\n");
1176
1177 KeDetachProcess();
1178
1179 ObDereferenceObject(Process);
1180
1181 *NumberOfBytesWritten = NumberOfBytesToWrite;
1182
1183 DPRINT("Finished NtWriteVirtualMemory()\n");
1184
1185 return(STATUS_SUCCESS);
1186 }
1187
1188
1189 DWORD
1190 STDCALL
1191 MmSecureVirtualMemory (
1192 DWORD Unknown0,
1193 DWORD Unknown1,
1194 DWORD Unknown2
1195 )
1196 {
1197 UNIMPLEMENTED;
1198 return 0;
1199 }
1200
1201
1202 VOID
1203 STDCALL
1204 MmUnsecureVirtualMemory (
1205 DWORD Unknown0
1206 )
1207 {
1208 UNIMPLEMENTED;
1209 }
1210
1211 /* EOF */