Sync with trunk (48237)
[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 Status = MmQueryAnonMem(MemoryArea, Address, Info,
79 ResultLength);
80 break;
81
82 case MEMORY_AREA_SECTION_VIEW:
83 Status = MmQuerySectionView(MemoryArea, Address, Info,
84 ResultLength);
85 break;
86
87 default:
88 DPRINT1("unhandled memory area type: 0x%x\n", MemoryArea->Type);
89 Status = STATUS_UNSUCCESSFUL;
90 *ResultLength = 0;
91 }
92 }
93 break;
94 }
95
96 default:
97 {
98 DPRINT1("Unsupported or unimplemented class: %lx\n", VirtualMemoryInformationClass);
99 Status = STATUS_INVALID_INFO_CLASS;
100 *ResultLength = 0;
101 break;
102 }
103 }
104
105 MmUnlockAddressSpace(AddressSpace);
106 ObDereferenceObject(Process);
107
108 return Status;
109 }
110
111 NTSTATUS NTAPI
112 MiProtectVirtualMemory(IN PEPROCESS Process,
113 IN OUT PVOID *BaseAddress,
114 IN OUT PSIZE_T NumberOfBytesToProtect,
115 IN ULONG NewAccessProtection,
116 OUT PULONG OldAccessProtection OPTIONAL)
117 {
118 PMEMORY_AREA MemoryArea;
119 PMMSUPPORT AddressSpace;
120 ULONG OldAccessProtection_;
121 NTSTATUS Status;
122
123 *NumberOfBytesToProtect =
124 PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) -
125 PAGE_ROUND_DOWN(*BaseAddress);
126 *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
127
128 AddressSpace = &Process->Vm;
129
130 MmLockAddressSpace(AddressSpace);
131 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
132 if (MemoryArea == NULL)
133 {
134 MmUnlockAddressSpace(AddressSpace);
135 return STATUS_UNSUCCESSFUL;
136 }
137
138 if (OldAccessProtection == NULL)
139 OldAccessProtection = &OldAccessProtection_;
140
141 if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
142 {
143 Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
144 *NumberOfBytesToProtect, NewAccessProtection,
145 OldAccessProtection);
146 }
147 else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
148 {
149 Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
150 *NumberOfBytesToProtect,
151 NewAccessProtection,
152 OldAccessProtection);
153 }
154 else
155 {
156 /* FIXME: Should we return failure or success in this case? */
157 Status = STATUS_CONFLICTING_ADDRESSES;
158 }
159
160 MmUnlockAddressSpace(AddressSpace);
161
162 return Status;
163 }
164
165 PVOID
166 NTAPI
167 MiMapLockedPagesInUserSpace(IN PMDL Mdl,
168 IN PVOID BaseVa,
169 IN MEMORY_CACHING_TYPE CacheType,
170 IN PVOID BaseAddress)
171 {
172 PVOID Base;
173 PPFN_NUMBER MdlPages;
174 ULONG PageCount;
175 PEPROCESS CurrentProcess;
176 NTSTATUS Status;
177 ULONG Protect;
178 MEMORY_AREA *Result;
179 LARGE_INTEGER BoundaryAddressMultiple;
180
181 /* Calculate the number of pages required. */
182 MdlPages = (PPFN_NUMBER)(Mdl + 1);
183 PageCount = PAGE_ROUND_UP(Mdl->ByteCount + Mdl->ByteOffset) / PAGE_SIZE;
184
185 /* Set default page protection */
186 Protect = PAGE_READWRITE;
187 if (CacheType == MmNonCached) Protect |= PAGE_NOCACHE;
188
189 BoundaryAddressMultiple.QuadPart = 0;
190 Base = BaseAddress;
191
192 CurrentProcess = PsGetCurrentProcess();
193
194 MmLockAddressSpace(&CurrentProcess->Vm);
195 Status = MmCreateMemoryArea(&CurrentProcess->Vm,
196 MEMORY_AREA_MDL_MAPPING,
197 &Base,
198 PageCount * PAGE_SIZE,
199 Protect,
200 &Result,
201 (Base != NULL),
202 0,
203 BoundaryAddressMultiple);
204 MmUnlockAddressSpace(&CurrentProcess->Vm);
205 if (!NT_SUCCESS(Status))
206 {
207 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL)
208 {
209 return NULL;
210 }
211
212 /* Throw exception */
213 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
214 ASSERT(0);
215 }
216
217 /* Set the virtual mappings for the MDL pages. */
218 if (Mdl->MdlFlags & MDL_IO_SPACE)
219 {
220 /* Map the pages */
221 Status = MmCreateVirtualMappingUnsafe(CurrentProcess,
222 Base,
223 Protect,
224 MdlPages,
225 PageCount);
226 }
227 else
228 {
229 /* Map the pages */
230 Status = MmCreateVirtualMapping(CurrentProcess,
231 Base,
232 Protect,
233 MdlPages,
234 PageCount);
235 }
236
237 /* Check if the mapping suceeded */
238 if (!NT_SUCCESS(Status))
239 {
240 /* If it can fail, return NULL */
241 if (Mdl->MdlFlags & MDL_MAPPING_CAN_FAIL) return NULL;
242
243 /* Throw exception */
244 ExRaiseStatus(STATUS_ACCESS_VIOLATION);
245 }
246
247 /* Return the base */
248 Base = (PVOID)((ULONG_PTR)Base + Mdl->ByteOffset);
249 return Base;
250 }
251
252 VOID
253 NTAPI
254 MiUnmapLockedPagesInUserSpace(IN PVOID BaseAddress,
255 IN PMDL Mdl)
256 {
257 PMEMORY_AREA MemoryArea;
258
259 /* Sanity check */
260 ASSERT(Mdl->Process == PsGetCurrentProcess());
261
262 /* Find the memory area */
263 MemoryArea = MmLocateMemoryAreaByAddress(&Mdl->Process->Vm,
264 BaseAddress);
265 ASSERT(MemoryArea);
266
267 /* Free it */
268 MmFreeMemoryArea(&Mdl->Process->Vm,
269 MemoryArea,
270 NULL,
271 NULL);
272 }
273
274 /* SYSTEM CALLS ***************************************************************/
275
276 NTSTATUS NTAPI
277 NtQueryVirtualMemory(IN HANDLE ProcessHandle,
278 IN PVOID Address,
279 IN MEMORY_INFORMATION_CLASS VirtualMemoryInformationClass,
280 OUT PVOID VirtualMemoryInformation,
281 IN SIZE_T Length,
282 OUT PSIZE_T UnsafeResultLength)
283 {
284 NTSTATUS Status;
285 SIZE_T ResultLength = 0;
286 KPROCESSOR_MODE PreviousMode;
287 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
288 UNICODE_STRING ModuleFileName;
289 PMEMORY_SECTION_NAME SectionName = NULL;
290 PEPROCESS Process;
291 union
292 {
293 MEMORY_BASIC_INFORMATION BasicInfo;
294 }
295 VirtualMemoryInfo;
296
297 DPRINT("NtQueryVirtualMemory(ProcessHandle %x, Address %x, "
298 "VirtualMemoryInformationClass %d, VirtualMemoryInformation %x, "
299 "Length %lu ResultLength %x)\n",ProcessHandle,Address,
300 VirtualMemoryInformationClass,VirtualMemoryInformation,
301 Length,ResultLength);
302
303 PreviousMode = ExGetPreviousMode();
304
305 if (PreviousMode != KernelMode)
306 {
307 _SEH2_TRY
308 {
309 ProbeForWrite(VirtualMemoryInformation,
310 Length,
311 sizeof(ULONG_PTR));
312
313 if (UnsafeResultLength) ProbeForWriteSize_t(UnsafeResultLength);
314 }
315 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
316 {
317 /* Return the exception code */
318 _SEH2_YIELD(return _SEH2_GetExceptionCode());
319 }
320 _SEH2_END;
321 }
322
323 if (Address >= MmSystemRangeStart)
324 {
325 DPRINT1("Invalid parameter\n");
326 return STATUS_INVALID_PARAMETER;
327 }
328
329 /* FIXME: Move this inside MiQueryVirtualMemory */
330 if (VirtualMemoryInformationClass == MemorySectionName)
331 {
332 Status = ObReferenceObjectByHandle(ProcessHandle,
333 PROCESS_QUERY_INFORMATION,
334 NULL,
335 PreviousMode,
336 (PVOID*)(&Process),
337 NULL);
338
339 if (!NT_SUCCESS(Status))
340 {
341 DPRINT("NtQueryVirtualMemory() = %x\n",Status);
342 return(Status);
343 }
344
345 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
346 Status = MmGetFileNameForAddress(Address, &ModuleFileName);
347
348 if (NT_SUCCESS(Status))
349 {
350 SectionName = VirtualMemoryInformation;
351 if (PreviousMode != KernelMode)
352 {
353 _SEH2_TRY
354 {
355 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
356 SectionName->SectionFileName.MaximumLength = Length;
357 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
358
359 if (UnsafeResultLength != NULL)
360 {
361 *UnsafeResultLength = ModuleFileName.Length;
362 }
363 }
364 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
365 {
366 Status = _SEH2_GetExceptionCode();
367 }
368 _SEH2_END;
369 }
370 else
371 {
372 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
373 SectionName->SectionFileName.MaximumLength = Length;
374 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
375
376 if (UnsafeResultLength != NULL)
377 {
378 *UnsafeResultLength = ModuleFileName.Length;
379 }
380 }
381 }
382 ObDereferenceObject(Process);
383 return Status;
384 }
385 else
386 {
387 Status = MiQueryVirtualMemory(ProcessHandle,
388 Address,
389 VirtualMemoryInformationClass,
390 &VirtualMemoryInfo,
391 Length,
392 &ResultLength);
393 }
394
395 if (NT_SUCCESS(Status))
396 {
397 if (PreviousMode != KernelMode)
398 {
399 _SEH2_TRY
400 {
401 if (ResultLength > 0)
402 {
403 ProbeForWrite(VirtualMemoryInformation,
404 ResultLength,
405 1);
406 RtlCopyMemory(VirtualMemoryInformation,
407 &VirtualMemoryInfo,
408 ResultLength);
409 }
410 if (UnsafeResultLength != NULL)
411 {
412 *UnsafeResultLength = ResultLength;
413 }
414 }
415 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
416 {
417 Status = _SEH2_GetExceptionCode();
418 }
419 _SEH2_END;
420 }
421 else
422 {
423 if (ResultLength > 0)
424 {
425 RtlCopyMemory(VirtualMemoryInformation,
426 &VirtualMemoryInfo,
427 ResultLength);
428 }
429
430 if (UnsafeResultLength != NULL)
431 {
432 *UnsafeResultLength = ResultLength;
433 }
434 }
435 }
436
437 return(Status);
438 }
439
440 /* EOF */