Some fixes.
[reactos.git] / reactos / ntoskrnl / mm / virtual.c
1 /* $Id: virtual.c,v 1.28 2000/05/13 13:51:06 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 /* FUNCTIONS ****************************************************************/
29
30 NTSTATUS MmNotPresentFaultVirtualMemory(PMADDRESS_SPACE AddressSpace,
31 MEMORY_AREA* MemoryArea,
32 PVOID Address)
33 {
34 if (MmIsPagePresent(NULL, Address))
35 {
36
37 return(STATUS_SUCCESS);
38 }
39
40 MmSetPage(PsGetCurrentProcess(),
41 Address,
42 MemoryArea->Attributes,
43 (ULONG)MmAllocPage());
44
45 return(STATUS_SUCCESS);
46 }
47
48 NTSTATUS MmReleaseMemoryArea(PEPROCESS Process, PMEMORY_AREA Marea)
49 {
50 PVOID i;
51
52 DPRINT("MmReleaseMemoryArea(Process %x, Marea %x)\n",Process,Marea);
53
54 DPRINT("Releasing %x between %x %x\n",
55 Marea, Marea->BaseAddress, Marea->BaseAddress + Marea->Length);
56
57 if (Marea->Type == MEMORY_AREA_SECTION_VIEW_COMMIT ||
58 Marea->Type == MEMORY_AREA_SECTION_VIEW_RESERVE)
59 {
60 MmUnmapViewOfSection(Process, Marea);
61 }
62
63 for (i = Marea->BaseAddress;
64 i < (Marea->BaseAddress + Marea->Length);
65 i = i+PAGESIZE)
66 {
67 MmDeletePageEntry(Process, i, TRUE);
68 }
69 ExFreePool(Marea);
70
71 return(STATUS_SUCCESS);
72 }
73
74 NTSTATUS MmReleaseMmInfo(PEPROCESS Process)
75 {
76 PLIST_ENTRY CurrentEntry;
77 PMEMORY_AREA Current;
78
79 DPRINT("MmReleaseMmInfo(Process %x)\n",Process);
80
81 MmLockAddressSpace(&Process->Pcb.AddressSpace);
82
83 while (!IsListEmpty(&Process->Pcb.AddressSpace.MAreaListHead))
84 {
85 CurrentEntry = RemoveHeadList(
86 &Process->Pcb.AddressSpace.MAreaListHead);
87 Current = CONTAINING_RECORD(CurrentEntry, MEMORY_AREA, Entry);
88
89 MmReleaseMemoryArea(Process, Current);
90 }
91
92 Mmi386ReleaseMmInfo(Process);
93
94 MmUnlockAddressSpace(&Process->Pcb.AddressSpace);
95
96 DPRINT("Finished MmReleaseMmInfo()\n");
97 return(STATUS_SUCCESS);
98 }
99
100 BOOLEAN STDCALL MmIsNonPagedSystemAddressValid(PVOID VirtualAddress)
101 {
102 UNIMPLEMENTED;
103 }
104
105 BOOLEAN STDCALL MmIsAddressValid(PVOID VirtualAddress)
106 /*
107 * FUNCTION: Checks whether the given address is valid for a read or write
108 * ARGUMENTS:
109 * VirtualAddress = address to check
110 * RETURNS: True if the access would be valid
111 * False if the access would cause a page fault
112 * NOTES: This function checks whether a byte access to the page would
113 * succeed. Is this realistic for RISC processors which don't
114 * allow byte granular access?
115 */
116 {
117 MEMORY_AREA* MemoryArea;
118 PMADDRESS_SPACE AddressSpace;
119
120 AddressSpace = &PsGetCurrentProcess()->Pcb.AddressSpace;
121
122 MmLockAddressSpace(AddressSpace);
123 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
124 VirtualAddress);
125
126 if (MemoryArea == NULL)
127 {
128 MmUnlockAddressSpace(AddressSpace);
129 return(FALSE);
130 }
131 MmUnlockAddressSpace(AddressSpace);
132 return(TRUE);
133 }
134
135
136 NTSTATUS STDCALL NtAllocateVirtualMemory(IN HANDLE ProcessHandle,
137 IN OUT PVOID * BaseAddress,
138 IN ULONG ZeroBits,
139 IN OUT PULONG RegionSize,
140 IN ULONG AllocationType,
141 IN ULONG Protect)
142 /*
143 * FUNCTION: Allocates a block of virtual memory in the process address space
144 * ARGUMENTS:
145 * ProcessHandle = The handle of the process which owns the virtual memory
146 * BaseAddress = A pointer to the virtual memory allocated. If you
147 * supply a non zero value the system will try to
148 * allocate the memory at the address supplied. It round
149 * it down to a multiple of the page size.
150 * ZeroBits = (OPTIONAL) You can specify the number of high order bits
151 * that must be zero, ensuring that the memory will be
152 * allocated at a address below a certain value.
153 * RegionSize = The number of bytes to allocate
154 * AllocationType = Indicates the type of virtual memory you like to
155 * allocated, can be one of the values : MEM_COMMIT,
156 * MEM_RESERVE, MEM_RESET, MEM_TOP_DOWN
157 * Protect = Indicates the protection type of the pages allocated, can be
158 * a combination of PAGE_READONLY, PAGE_READWRITE,
159 * PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_GUARD,
160 * PAGE_NOACCESS
161 * REMARKS:
162 * This function maps to the win32 VirtualAllocEx. Virtual memory is
163 * process based so the protocol starts with a ProcessHandle. I
164 * splitted the functionality of obtaining the actual address and
165 * specifying the start address in two parameters ( BaseAddress and
166 * StartAddress ) The NumberOfBytesAllocated specify the range and the
167 * AllocationType and ProctectionType map to the other two parameters.
168 * RETURNS: Status
169 */
170 {
171 PEPROCESS Process;
172 MEMORY_AREA* MemoryArea;
173 ULONG Type;
174 NTSTATUS Status;
175 PMADDRESS_SPACE AddressSpace;
176
177 DPRINT("NtAllocateVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
178 "ZeroBits %d, *RegionSize %x, AllocationType %x, Protect %x)\n",
179 ProcessHandle,*BaseAddress,ZeroBits,*RegionSize,AllocationType,
180 Protect);
181
182 Status = ObReferenceObjectByHandle(ProcessHandle,
183 PROCESS_VM_OPERATION,
184 NULL,
185 UserMode,
186 (PVOID*)(&Process),
187 NULL);
188 if (Status != STATUS_SUCCESS)
189 {
190 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
191 return(Status);
192 }
193
194 if (AllocationType & MEM_RESERVE)
195 {
196 Type = MEMORY_AREA_RESERVE;
197 }
198 else
199 {
200 Type = MEMORY_AREA_COMMIT;
201 }
202
203 AddressSpace = &Process->Pcb.AddressSpace;
204 MmLockAddressSpace(AddressSpace);
205
206 if ((*BaseAddress) != 0)
207 {
208 MemoryArea = MmOpenMemoryAreaByAddress(&Process->Pcb.AddressSpace,
209 *BaseAddress);
210
211 if (MemoryArea != NULL)
212 {
213 if (MemoryArea->BaseAddress == (*BaseAddress) &&
214 MemoryArea->Length == *RegionSize)
215 {
216 MemoryArea->Type = Type;
217 MemoryArea->Attributes =Protect;
218 DPRINT("*BaseAddress %x\n",*BaseAddress);
219 MmUnlockAddressSpace(AddressSpace);
220 ObDereferenceObject(Process);
221 return(STATUS_SUCCESS);
222 }
223
224 MemoryArea = MmSplitMemoryArea(Process,
225 &Process->Pcb.AddressSpace,
226 MemoryArea,
227 *BaseAddress,
228 *RegionSize,
229 Type,
230 Protect);
231 DPRINT("*BaseAddress %x\n",*BaseAddress);
232 MmUnlockAddressSpace(AddressSpace);
233 ObDereferenceObject(Process);
234 return(STATUS_SUCCESS);
235 }
236 }
237
238 // FIXME RegionSize should be passed as pointer
239 // dwelch: Why?
240
241 Status = MmCreateMemoryArea(Process,
242 &Process->Pcb.AddressSpace,
243 Type,
244 BaseAddress,
245 *RegionSize,
246 Protect,
247 &MemoryArea);
248
249 if (Status != STATUS_SUCCESS)
250 {
251 DPRINT("NtAllocateVirtualMemory() = %x\n",Status);
252 MmUnlockAddressSpace(AddressSpace);
253 ObDereferenceObject(Process);
254 return(Status);
255 }
256
257 DPRINT("*BaseAddress %x\n",*BaseAddress);
258 MmUnlockAddressSpace(AddressSpace);
259 ObDereferenceObject(Process);
260 return(STATUS_SUCCESS);
261 }
262
263
264 NTSTATUS STDCALL NtFlushVirtualMemory(IN HANDLE ProcessHandle,
265 IN PVOID BaseAddress,
266 IN ULONG NumberOfBytesToFlush,
267 OUT PULONG NumberOfBytesFlushed OPTIONAL)
268 /*
269 * FUNCTION: Flushes virtual memory to file
270 * ARGUMENTS:
271 * ProcessHandle = Points to the process that allocated the virtual
272 * memory
273 * BaseAddress = Points to the memory address
274 * NumberOfBytesToFlush = Limits the range to flush,
275 * NumberOfBytesFlushed = Actual number of bytes flushed
276 * RETURNS: Status
277 */
278 {
279 UNIMPLEMENTED;
280 }
281
282
283 NTSTATUS STDCALL NtFreeVirtualMemory(IN HANDLE ProcessHandle,
284 IN PVOID * BaseAddress,
285 IN PULONG RegionSize,
286 IN ULONG FreeType)
287 /*
288 * FUNCTION: Frees a range of virtual memory
289 * ARGUMENTS:
290 * ProcessHandle = Points to the process that allocated the virtual
291 * memory
292 * BaseAddress = Points to the memory address, rounded down to a
293 * multiple of the pagesize
294 * RegionSize = Limits the range to free, rounded up to a multiple of
295 * the paging size
296 * FreeType = Can be one of the values: MEM_DECOMMIT, or MEM_RELEASE
297 * RETURNS: Status
298 */
299 {
300 MEMORY_AREA* MemoryArea;
301 NTSTATUS Status;
302 PEPROCESS Process;
303 PMADDRESS_SPACE AddressSpace;
304
305 DPRINT("NtFreeVirtualMemory(ProcessHandle %x, *BaseAddress %x, "
306 "*RegionSize %x, FreeType %x)\n",ProcessHandle,*BaseAddress,
307 *RegionSize,FreeType);
308
309
310 Status = ObReferenceObjectByHandle(ProcessHandle,
311 PROCESS_VM_OPERATION,
312 PsProcessType,
313 UserMode,
314 (PVOID*)(&Process),
315 NULL);
316 if (Status != STATUS_SUCCESS)
317 {
318 return(Status);
319 }
320
321 AddressSpace = &Process->Pcb.AddressSpace;
322
323 MmLockAddressSpace(AddressSpace);
324 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
325 *BaseAddress);
326 if (MemoryArea == NULL)
327 {
328 MmUnlockAddressSpace(AddressSpace);
329 ObDereferenceObject(Process);
330 return(STATUS_UNSUCCESSFUL);
331 }
332
333 switch (FreeType)
334 {
335 case MEM_RELEASE:
336 if (MemoryArea->BaseAddress != (*BaseAddress))
337 {
338 MmUnlockAddressSpace(AddressSpace);
339 ObDereferenceObject(Process);
340 return(STATUS_UNSUCCESSFUL);
341 }
342 MmFreeMemoryArea(&Process->Pcb.AddressSpace,
343 BaseAddress,
344 0,
345 TRUE);
346 MmUnlockAddressSpace(AddressSpace);
347 ObDereferenceObject(Process);
348 return(STATUS_SUCCESS);
349
350 case MEM_DECOMMIT:
351 MmSplitMemoryArea(Process,
352 &Process->Pcb.AddressSpace,
353 MemoryArea,
354 *BaseAddress,
355 *RegionSize,
356 MEMORY_AREA_RESERVE,
357 MemoryArea->Attributes);
358 MmUnlockAddressSpace(AddressSpace);
359 ObDereferenceObject(Process);
360 return(STATUS_SUCCESS);
361 }
362 MmUnlockAddressSpace(AddressSpace);
363 ObDereferenceObject(Process);
364 return(STATUS_NOT_IMPLEMENTED);
365 }
366
367
368 NTSTATUS STDCALL NtLockVirtualMemory(HANDLE ProcessHandle,
369 PVOID BaseAddress,
370 ULONG NumberOfBytesToLock,
371 PULONG NumberOfBytesLocked)
372 {
373 UNIMPLEMENTED;
374 }
375
376
377 VOID MmChangeAreaProtection(PEPROCESS Process,
378 PVOID BaseAddress,
379 ULONG Length,
380 ULONG Protect)
381 {
382 ULONG i;
383
384 for (i=0; i<(Length/PAGESIZE); i++)
385 {
386 if (MmIsPagePresent(Process, BaseAddress + (i*PAGESIZE)))
387 {
388 MmSetPageProtect(Process,
389 BaseAddress + (i*PAGESIZE),
390 Protect);
391 }
392 }
393 }
394
395
396 NTSTATUS STDCALL NtProtectVirtualMemory(IN HANDLE ProcessHandle,
397 IN PVOID BaseAddress,
398 IN ULONG NumberOfBytesToProtect,
399 IN ULONG NewAccessProtection,
400 OUT PULONG OldAccessProtection)
401 {
402 PMEMORY_AREA MemoryArea;
403 PEPROCESS Process;
404 NTSTATUS Status;
405 PMADDRESS_SPACE AddressSpace;
406
407 Status = ObReferenceObjectByHandle(ProcessHandle,
408 PROCESS_VM_OPERATION,
409 PsProcessType,
410 UserMode,
411 (PVOID*)(&Process),
412 NULL);
413 if (Status != STATUS_SUCCESS)
414 {
415 DPRINT("NtProtectVirtualMemory() = %x\n",Status);
416 return(Status);
417 }
418
419 AddressSpace = &Process->Pcb.AddressSpace;
420
421 MmLockAddressSpace(AddressSpace);
422 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
423 BaseAddress);
424 if (MemoryArea == NULL)
425 {
426 DPRINT("NtProtectVirtualMemory() = %x\n",STATUS_UNSUCCESSFUL);
427 MmUnlockAddressSpace(AddressSpace);
428 ObDereferenceObject(Process);
429 return(STATUS_UNSUCCESSFUL);
430 }
431
432 *OldAccessProtection = MemoryArea->Attributes;
433
434 if (MemoryArea->BaseAddress == BaseAddress &&
435 MemoryArea->Length == NumberOfBytesToProtect)
436 {
437 MemoryArea->Attributes = NewAccessProtection;
438 }
439 else
440 {
441 MemoryArea = MmSplitMemoryArea(Process,
442 &Process->Pcb.AddressSpace,
443 MemoryArea,
444 BaseAddress,
445 NumberOfBytesToProtect,
446 MemoryArea->Type,
447 NewAccessProtection);
448 }
449 MmChangeAreaProtection(Process,
450 BaseAddress,
451 NumberOfBytesToProtect,
452 NewAccessProtection);
453 MmUnlockAddressSpace(AddressSpace);
454 ObDereferenceObject(Process);
455 return(STATUS_SUCCESS);
456 }
457
458
459 NTSTATUS STDCALL NtQueryVirtualMemory (IN HANDLE ProcessHandle,
460 IN PVOID Address,
461 IN CINT VirtualMemoryInformationClass,
462 OUT PVOID VirtualMemoryInformation,
463 IN ULONG Length,
464 OUT PULONG ResultLength)
465 {
466 NTSTATUS Status;
467 PEPROCESS Process;
468 MEMORY_AREA* MemoryArea;
469
470 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
471 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
472 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
473 VirtualMemoryInformationClass,VirtualMemoryInformation,
474 Length,ResultLength);
475
476 switch(VirtualMemoryInformationClass)
477 {
478 case MemoryBasicInformation:
479 {
480 PMEMORY_BASIC_INFORMATION Info =
481 (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
482 PMADDRESS_SPACE AddressSpace;
483
484 if (Length < sizeof(MEMORY_BASIC_INFORMATION))
485 {
486 ObDereferenceObject(Process);
487 return STATUS_INFO_LENGTH_MISMATCH;
488 }
489
490 if (ResultLength)
491 {
492 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
493 }
494
495 Status = ObReferenceObjectByHandle(ProcessHandle,
496 PROCESS_QUERY_INFORMATION,
497 NULL,
498 UserMode,
499 (PVOID*)(&Process),
500 NULL);
501
502 if (!NT_SUCCESS(Status))
503 {
504 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
505 return(Status);
506 }
507
508 AddressSpace = &Process->Pcb.AddressSpace;
509 MmLockAddressSpace(AddressSpace);
510 MemoryArea = MmOpenMemoryAreaByAddress(AddressSpace,
511 Address);
512
513 if (MemoryArea == NULL)
514 {
515 Info->State = MEM_FREE;
516 DPRINT("Virtual memory at %p is free.\n", Address);
517 MmUnlockAddressSpace(AddressSpace);
518 ObDereferenceObject(Process);
519 return (STATUS_SUCCESS);
520 }
521
522 if (MemoryArea->Type == MEMORY_AREA_COMMIT)
523 {
524 Info->State = MEM_COMMIT;
525 }
526 else
527 {
528 Info->State = MEM_RESERVE;
529 }
530
531 Info->BaseAddress = MemoryArea->BaseAddress;
532 Info->RegionSize = MemoryArea->Length;
533
534 DPRINT("BaseAddress %p, RegionSize %x State %x\n",
535 Info->BaseAddress, Info->RegionSize, Info->State);
536
537 MmUnlockAddressSpace(AddressSpace);
538 ObDereferenceObject(Process);
539 return STATUS_SUCCESS;
540 }
541 break;
542 }
543
544 return STATUS_INVALID_INFO_CLASS;
545 }
546
547
548 NTSTATUS STDCALL NtReadVirtualMemory(IN HANDLE ProcessHandle,
549 IN PVOID BaseAddress,
550 OUT PVOID Buffer,
551 IN ULONG NumberOfBytesToRead,
552 OUT PULONG NumberOfBytesRead)
553 {
554 NTSTATUS Status;
555 PMDL Mdl;
556 PVOID SystemAddress;
557 PEPROCESS Process;
558
559 DPRINT("NtReadVirtualMemory(ProcessHandle %x, BaseAddress %x, "
560 "Buffer %x, NumberOfBytesToRead %d)\n",ProcessHandle,BaseAddress,
561 Buffer,NumberOfBytesToRead);
562
563 Status = ObReferenceObjectByHandle(ProcessHandle,
564 PROCESS_VM_WRITE,
565 NULL,
566 UserMode,
567 (PVOID*)(&Process),
568 NULL);
569 if (Status != STATUS_SUCCESS)
570 {
571 return(Status);
572 }
573
574 Mdl = MmCreateMdl(NULL,
575 Buffer,
576 NumberOfBytesToRead);
577 MmProbeAndLockPages(Mdl,
578 UserMode,
579 IoWriteAccess);
580
581 KeAttachProcess(Process);
582
583 SystemAddress = MmGetSystemAddressForMdl(Mdl);
584 memcpy(SystemAddress, BaseAddress, NumberOfBytesToRead);
585
586 KeDetachProcess();
587
588 ObDereferenceObject(Process);
589
590 *NumberOfBytesRead = NumberOfBytesToRead;
591 return(STATUS_SUCCESS);
592 }
593
594
595 NTSTATUS STDCALL NtUnlockVirtualMemory(HANDLE ProcessHandle,
596 PVOID BaseAddress,
597 ULONG NumberOfBytesToUnlock,
598 PULONG NumberOfBytesUnlocked OPTIONAL)
599 {
600 UNIMPLEMENTED;
601 }
602
603
604 NTSTATUS STDCALL NtWriteVirtualMemory(IN HANDLE ProcessHandle,
605 IN PVOID BaseAddress,
606 IN PVOID Buffer,
607 IN ULONG NumberOfBytesToWrite,
608 OUT PULONG NumberOfBytesWritten)
609 {
610 NTSTATUS Status;
611 PMDL Mdl;
612 PVOID SystemAddress;
613 PEPROCESS Process;
614
615 DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
616 "Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
617 Buffer,NumberOfBytesToWrite);
618
619 Status = ObReferenceObjectByHandle(ProcessHandle,
620 PROCESS_VM_WRITE,
621 NULL,
622 UserMode,
623 (PVOID*)(&Process),
624 NULL);
625 if (Status != STATUS_SUCCESS)
626 {
627 return(Status);
628 }
629
630 Mdl = MmCreateMdl(NULL,
631 Buffer,
632 NumberOfBytesToWrite);
633 MmProbeAndLockPages(Mdl,
634 UserMode,
635 IoReadAccess);
636
637 KeAttachProcess(Process);
638
639 DPRINT("Attached to process copying memory\n");
640
641 SystemAddress = MmGetSystemAddressForMdl(Mdl);
642 memcpy(BaseAddress, SystemAddress, NumberOfBytesToWrite);
643
644 DPRINT("Done copy\n");
645
646 KeDetachProcess();
647
648 ObDereferenceObject(Process);
649
650 *NumberOfBytesWritten = NumberOfBytesToWrite;
651
652 DPRINT("Finished NtWriteVirtualMemory()\n");
653
654 return(STATUS_SUCCESS);
655 }
656
657
658 DWORD
659 STDCALL
660 MmSecureVirtualMemory (
661 DWORD Unknown0,
662 DWORD Unknown1,
663 DWORD Unknown2
664 )
665 {
666 UNIMPLEMENTED;
667 return 0;
668 }
669
670
671 VOID
672 STDCALL
673 MmUnsecureVirtualMemory (
674 DWORD Unknown0
675 )
676 {
677 UNIMPLEMENTED;
678 }
679
680 /* EOF */