Sync up to trunk head.
[reactos.git] / ntoskrnl / mm / virtual.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/virtual.c
5 * PURPOSE: Implementing operations on virtual memory.
6 *
7 * PROGRAMMERS: David Welch
8 */
9
10 /* INCLUDE ********************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* PRIVATE FUNCTIONS **********************************************************/
17
18 NTSTATUS FASTCALL
19 MiQueryVirtualMemory(IN HANDLE ProcessHandle,
20 IN PVOID Address,
21 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
22 OUT PVOID VirtualMemoryInformation,
23 IN SIZE_T Length,
24 OUT PSIZE_T ResultLength)
25 {
26 NTSTATUS Status;
27 PEPROCESS Process;
28 MEMORY_AREA* MemoryArea;
29 PMMSUPPORT AddressSpace;
30
31 Status = ObReferenceObjectByHandle(ProcessHandle,
32 PROCESS_QUERY_INFORMATION,
33 NULL,
34 UserMode,
35 (PVOID*)(&Process),
36 NULL);
37
38 if (!NT_SUCCESS(Status))
39 {
40 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
41 return(Status);
42 }
43
44 AddressSpace = &Process->Vm;
45
46 MmLockAddressSpace(AddressSpace);
47 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
48 switch(VirtualMemoryInformationClass)
49 {
50 case MemoryBasicInformation:
51 {
52 PMEMORY_BASIC_INFORMATION Info =
53 (PMEMORY_BASIC_INFORMATION)VirtualMemoryInformation;
54 if (Length != sizeof(MEMORY_BASIC_INFORMATION))
55 {
56 MmUnlockAddressSpace(AddressSpace);
57 ObDereferenceObject(Process);
58 return(STATUS_INFO_LENGTH_MISMATCH);
59 }
60
61 if (MemoryArea == NULL)
62 {
63 Info->Type = 0;
64 Info->State = MEM_FREE;
65 Info->Protect = PAGE_NOACCESS;
66 Info->AllocationProtect = 0;
67 Info->BaseAddress = (PVOID)PAGE_ROUND_DOWN(Address);
68 Info->AllocationBase = NULL;
69 Info->RegionSize = MmFindGapAtAddress(AddressSpace, Info->BaseAddress);
70 Status = STATUS_SUCCESS;
71 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
72 }
73 else
74 {
75 switch(MemoryArea->Type)
76 {
77 case MEMORY_AREA_VIRTUAL_MEMORY:
78 case MEMORY_AREA_PEB_OR_TEB:
79 Status = MmQueryAnonMem(MemoryArea, Address, Info,
80 ResultLength);
81 break;
82
83 case MEMORY_AREA_SECTION_VIEW:
84 Status = MmQuerySectionView(MemoryArea, Address, Info,
85 ResultLength);
86 break;
87
88 case MEMORY_AREA_NO_ACCESS:
89 Info->Type = MEM_PRIVATE;
90 Info->State = MEM_RESERVE;
91 Info->Protect = MemoryArea->Protect;
92 Info->AllocationProtect = MemoryArea->Protect;
93 Info->BaseAddress = MemoryArea->StartingAddress;
94 Info->AllocationBase = MemoryArea->StartingAddress;
95 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
96 (ULONG_PTR)MemoryArea->StartingAddress;
97 Status = STATUS_SUCCESS;
98 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
99 break;
100
101 case MEMORY_AREA_SHARED_DATA:
102 Info->Type = MEM_PRIVATE;
103 Info->State = MEM_COMMIT;
104 Info->Protect = MemoryArea->Protect;
105 Info->AllocationProtect = MemoryArea->Protect;
106 Info->BaseAddress = MemoryArea->StartingAddress;
107 Info->AllocationBase = MemoryArea->StartingAddress;
108 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
109 (ULONG_PTR)MemoryArea->StartingAddress;
110 Status = STATUS_SUCCESS;
111 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
112 break;
113
114 case MEMORY_AREA_SYSTEM:
115 Info->Type = 0;
116 Info->State = MEM_COMMIT;
117 Info->Protect = MemoryArea->Protect;
118 Info->AllocationProtect = MemoryArea->Protect;
119 Info->BaseAddress = MemoryArea->StartingAddress;
120 Info->AllocationBase = MemoryArea->StartingAddress;
121 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
122 (ULONG_PTR)MemoryArea->StartingAddress;
123 Status = STATUS_SUCCESS;
124 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
125 break;
126
127 case MEMORY_AREA_KERNEL_STACK:
128 Info->Type = 0;
129 Info->State = MEM_COMMIT;
130 Info->Protect = MemoryArea->Protect;
131 Info->AllocationProtect = MemoryArea->Protect;
132 Info->BaseAddress = MemoryArea->StartingAddress;
133 Info->AllocationBase = MemoryArea->StartingAddress;
134 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
135 (ULONG_PTR)MemoryArea->StartingAddress;
136 Status = STATUS_SUCCESS;
137 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
138 break;
139
140 case MEMORY_AREA_PAGED_POOL:
141 Info->Type = 0;
142 Info->State = MEM_COMMIT;
143 Info->Protect = MemoryArea->Protect;
144 Info->AllocationProtect = MemoryArea->Protect;
145 Info->BaseAddress = MemoryArea->StartingAddress;
146 Info->AllocationBase = MemoryArea->StartingAddress;
147 Info->RegionSize = (ULONG_PTR)MemoryArea->EndingAddress -
148 (ULONG_PTR)MemoryArea->StartingAddress;
149 Status = STATUS_SUCCESS;
150 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
151 break;
152
153 default:
154 DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type);
155 Status = STATUS_UNSUCCESSFUL;
156 *ResultLength = 0;
157 }
158 }
159 break;
160 }
161
162 default:
163 {
164 DPRINT1("Unsupported or unimplemented class: %lx\n", VirtualMemoryInformationClass);
165 Status = STATUS_INVALID_INFO_CLASS;
166 *ResultLength = 0;
167 break;
168 }
169 }
170
171 MmUnlockAddressSpace(AddressSpace);
172 ObDereferenceObject(Process);
173
174 return Status;
175 }
176
177 NTSTATUS NTAPI
178 MiProtectVirtualMemory(IN PEPROCESS Process,
179 IN OUT PVOID *BaseAddress,
180 IN OUT PSIZE_T NumberOfBytesToProtect,
181 IN ULONG NewAccessProtection,
182 OUT PULONG OldAccessProtection OPTIONAL)
183 {
184 PMEMORY_AREA MemoryArea;
185 PMMSUPPORT AddressSpace;
186 ULONG OldAccessProtection_;
187 NTSTATUS Status;
188
189 *NumberOfBytesToProtect =
190 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
191 PAGE_ROUND_DOWN(*BaseAddress);
192 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
193
194 AddressSpace = &Process->Vm;
195
196 MmLockAddressSpace(AddressSpace);
197 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
198 if (MemoryArea == NULL)
199 {
200 MmUnlockAddressSpace(AddressSpace);
201 return STATUS_UNSUCCESSFUL;
202 }
203
204 if (OldAccessProtection == NULL)
205 OldAccessProtection = &OldAccessProtection_;
206
207 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
208 {
209 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
210 *NumberOfBytesToProtect, NewAccessProtection,
211 OldAccessProtection);
212 }
213 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
214 {
215 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
216 *NumberOfBytesToProtect,
217 NewAccessProtection,
218 OldAccessProtection);
219 }
220 else
221 {
222 /* FIXME: Should we return failure or success in this case? */
223 Status = STATUS_CONFLICTING_ADDRESSES;
224 }
225
226 MmUnlockAddressSpace(AddressSpace);
227
228 return Status;
229 }
230
231 PVOID
232 NTAPI
233 MiMapLockedPagesInUserSpace(IN PMDL Mdl,
234 IN PVOID BaseVa,
235 IN MEMORY_CACHING_TYPE CacheType,
236 IN PVOID BaseAddress)
237 {
238 PVOID Base;
239 PPFN_NUMBER MdlPages;
240 ULONG PageCount;
241 PEPROCESS CurrentProcess;
242 NTSTATUS Status;
243 ULONG Protect;
244 MEMORY_AREA *Result;
245 LARGE_INTEGER BoundaryAddressMultiple;
246
247 /* Calculate the number of pages required. */
248 MdlPages = (PPFN_NUMBER)(Mdl + 1);
249 PageCount = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
250
251 /* Set default page protection */
252 Protect = PAGE_READWRITE;
253 if (CacheType == MmNonCached) Protect |= PAGE_NOCACHE;
254
255 BoundaryAddressMultiple.QuadPart = 0;
256 Base = BaseAddress;
257
258 CurrentProcess = PsGetCurrentProcess();
259
260 MmLockAddressSpace(&CurrentProcess->Vm);
261 Status = MmCreateMemoryArea(&CurrentProcess->Vm,
262 MEMORY_AREA_MDL_MAPPING,
263 &Base,
264 PageCount * PAGE_SIZE,
265 Protect,
266 &Result,
267 (Base != NULL),
268 0,
269 BoundaryAddressMultiple);
270 MmUnlockAddressSpace(&CurrentProcess->Vm);
271 if (!NT_SUCCESS(Status))
272 {
273 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL)
274 {
275 return NULL;
276 }
277
278 /* Throw exception */
279 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
280 ASSERT(0);
281 }
282
283 /* Set the virtual mappings for the MDL pages. */
284 if (Mdl->MdlFlags & MDL_IO_SPACE)
285 {
286 /* Map the pages */
287 Status = MmCreateVirtualMappingUnsafe(CurrentProcess,
288 Base,
289 Protect,
290 MdlPages,
291 PageCount);
292 }
293 else
294 {
295 /* Map the pages */
296 Status = MmCreateVirtualMapping(CurrentProcess,
297 Base,
298 Protect,
299 MdlPages,
300 PageCount);
301 }
302
303 /* Check if the mapping suceeded */
304 if (!NT_SUCCESS(Status))
305 {
306 /* If it can fail, return NULL */
307 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) return NULL;
308
309 /* Throw exception */
310 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
311 }
312
313 /* Return the base */
314 Base = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
315 return Base;
316 }
317
318 VOID
319 NTAPI
320 MiUnmapLockedPagesInUserSpace(IN PVOID BaseAddress,
321 IN PMDL Mdl)
322 {
323 PMEMORY_AREA MemoryArea;
324
325 /* Sanity check */
326 ASSERT(Mdl->Process == PsGetCurrentProcess());
327
328 /* Find the memory area */
329 MemoryArea = MmLocateMemoryAreaByAddress(&Mdl->Process->Vm,
330 BaseAddress);
331 ASSERT(MemoryArea);
332
333 /* Free it */
334 MmFreeMemoryArea(&Mdl->Process->Vm,
335 MemoryArea,
336 NULL,
337 NULL);
338 }
339
340 /* SYSTEM CALLS ***************************************************************/
341
342 NTSTATUS NTAPI
343 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
344 IN PVOID Address,
345 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
346 OUT PVOID VirtualMemoryInformation,
347 IN SIZE_T Length,
348 OUT PSIZE_T UnsafeResultLength)
349 {
350 NTSTATUS Status;
351 SIZE_T ResultLength = 0;
352 KPROCESSOR_MODE PreviousMode;
353 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
354 UNICODE_STRING ModuleFileName;
355 PMEMORY_SECTION_NAME SectionName = NULL;
356 PEPROCESS Process;
357 union
358 {
359 MEMORY_BASIC_INFORMATION BasicInfo;
360 }
361 VirtualMemoryInfo;
362
363 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
364 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
365 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
366 VirtualMemoryInformationClass,VirtualMemoryInformation,
367 Length,ResultLength);
368
369 PreviousMode = ExGetPreviousMode();
370
371 if (PreviousMode != KernelMode)
372 {
373 _SEH2_TRY
374 {
375 ProbeForWrite(VirtualMemoryInformation,
376 Length,
377 sizeof(ULONG_PTR));
378
379 if (UnsafeResultLength) ProbeForWriteSize_t(UnsafeResultLength);
380 }
381 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
382 {
383 /* Return the exception code */
384 _SEH2_YIELD(return _SEH2_GetExceptionCode());
385 }
386 _SEH2_END;
387 }
388
389 if (Address >= MmSystemRangeStart)
390 {
391 DPRINT1("Invalid parameter\n");
392 return STATUS_INVALID_PARAMETER;
393 }
394
395 /* FIXME: Move this inside MiQueryVirtualMemory */
396 if (VirtualMemoryInformationClass == MemorySectionName)
397 {
398 Status = ObReferenceObjectByHandle(ProcessHandle,
399 PROCESS_QUERY_INFORMATION,
400 NULL,
401 PreviousMode,
402 (PVOID*)(&Process),
403 NULL);
404
405 if (!NT_SUCCESS(Status))
406 {
407 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
408 return(Status);
409 }
410
411 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
412 Status = MmGetFileNameForAddress(Address, &ModuleFileName);
413
414 if (NT_SUCCESS(Status))
415 {
416 SectionName = VirtualMemoryInformation;
417 if (PreviousMode != KernelMode)
418 {
419 _SEH2_TRY
420 {
421 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
422 SectionName->SectionFileName.MaximumLength = Length;
423 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
424
425 if (UnsafeResultLength != NULL)
426 {
427 *UnsafeResultLength = ModuleFileName.Length;
428 }
429 }
430 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
431 {
432 Status = _SEH2_GetExceptionCode();
433 }
434 _SEH2_END;
435 }
436 else
437 {
438 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
439 SectionName->SectionFileName.MaximumLength = Length;
440 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
441
442 if (UnsafeResultLength != NULL)
443 {
444 *UnsafeResultLength = ModuleFileName.Length;
445 }
446 }
447 }
448 ObDereferenceObject(Process);
449 return Status;
450 }
451 else
452 {
453 Status = MiQueryVirtualMemory(ProcessHandle,
454 Address,
455 VirtualMemoryInformationClass,
456 &VirtualMemoryInfo,
457 Length,
458 &ResultLength);
459 }
460
461 if (NT_SUCCESS(Status))
462 {
463 if (PreviousMode != KernelMode)
464 {
465 _SEH2_TRY
466 {
467 if (ResultLength > 0)
468 {
469 ProbeForWrite(VirtualMemoryInformation,
470 ResultLength,
471 1);
472 RtlCopyMemory(VirtualMemoryInformation,
473 &VirtualMemoryInfo,
474 ResultLength);
475 }
476 if (UnsafeResultLength != NULL)
477 {
478 *UnsafeResultLength = ResultLength;
479 }
480 }
481 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
482 {
483 Status = _SEH2_GetExceptionCode();
484 }
485 _SEH2_END;
486 }
487 else
488 {
489 if (ResultLength > 0)
490 {
491 RtlCopyMemory(VirtualMemoryInformation,
492 &VirtualMemoryInfo,
493 ResultLength);
494 }
495
496 if (UnsafeResultLength != NULL)
497 {
498 *UnsafeResultLength = ResultLength;
499 }
500 }
501 }
502
503 return(Status);
504 }
505
506 /* EOF */