[NTOS:MM] Fix ViewSize parameter passed to MiInsertVadEx() from MiCreatePebOrTeb()
[reactos.git] / ntoskrnl / ex / profile.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ex/profile.c
5 * PURPOSE: Support for Executive Profile Objects
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Thomas Weidenmueller
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 #if defined (ALLOC_PRAGMA)
17 #pragma alloc_text(INIT, ExpInitializeProfileImplementation)
18 #endif
19
20 #define TAG_PROFILE 'forP'
21
22 /* GLOBALS *******************************************************************/
23
24 POBJECT_TYPE ExProfileObjectType = NULL;
25 KMUTEX ExpProfileMutex;
26
27 GENERIC_MAPPING ExpProfileMapping =
28 {
29 STANDARD_RIGHTS_READ | PROFILE_CONTROL,
30 STANDARD_RIGHTS_WRITE | PROFILE_CONTROL,
31 STANDARD_RIGHTS_EXECUTE | PROFILE_CONTROL,
32 PROFILE_ALL_ACCESS
33 };
34
35 /* FUNCTIONS *****************************************************************/
36
37 VOID
38 NTAPI
39 ExpDeleteProfile(PVOID ObjectBody)
40 {
41 PEPROFILE Profile;
42 ULONG State;
43
44 /* Typecast the Object */
45 Profile = ObjectBody;
46
47 /* Check if there if the Profile was started */
48 if (Profile->LockedBufferAddress)
49 {
50 /* Stop the Profile */
51 State = KeStopProfile(Profile->ProfileObject);
52 ASSERT(State != FALSE);
53
54 /* Unmap the Locked Buffer */
55 MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl);
56 MmUnlockPages(Profile->Mdl);
57 IoFreeMdl(Profile->Mdl);
58 ExFreePoolWithTag(Profile->ProfileObject, TAG_PROFILE);
59 }
60
61 /* Check if a Process is associated and reference it */
62 if (Profile->Process) ObDereferenceObject(Profile->Process);
63 }
64
65 BOOLEAN
66 INIT_FUNCTION
67 NTAPI
68 ExpInitializeProfileImplementation(VOID)
69 {
70 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
71 UNICODE_STRING Name;
72 NTSTATUS Status;
73 DPRINT("Creating Profile Object Type\n");
74
75 /* Initialize the Mutex to lock the States */
76 KeInitializeMutex(&ExpProfileMutex, 64);
77
78 /* Create the Event Pair Object Type */
79 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
80 RtlInitUnicodeString(&Name, L"Profile");
81 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
82 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KPROFILE);
83 ObjectTypeInitializer.GenericMapping = ExpProfileMapping;
84 ObjectTypeInitializer.PoolType = NonPagedPool;
85 ObjectTypeInitializer.DeleteProcedure = ExpDeleteProfile;
86 ObjectTypeInitializer.ValidAccessMask = PROFILE_ALL_ACCESS;
87 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
88 Status = ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &ExProfileObjectType);
89 if (!NT_SUCCESS(Status)) return FALSE;
90 return TRUE;
91 }
92
93 NTSTATUS
94 NTAPI
95 NtCreateProfile(OUT PHANDLE ProfileHandle,
96 IN HANDLE Process OPTIONAL,
97 IN PVOID RangeBase,
98 IN SIZE_T RangeSize,
99 IN ULONG BucketSize,
100 IN PVOID Buffer,
101 IN ULONG BufferSize,
102 IN KPROFILE_SOURCE ProfileSource,
103 IN KAFFINITY Affinity)
104 {
105 HANDLE hProfile;
106 PEPROFILE Profile;
107 PEPROCESS pProcess;
108 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
109 OBJECT_ATTRIBUTES ObjectAttributes;
110 NTSTATUS Status;
111 ULONG Log2 = 0;
112 ULONG_PTR Segment = 0;
113 PAGED_CODE();
114
115 /* Easy way out */
116 if(!BufferSize) return STATUS_INVALID_PARAMETER_7;
117
118 /* Check if this is a low-memory profile */
119 if ((!BucketSize) && (RangeBase < (PVOID)(0x10000)))
120 {
121 /* Validate size */
122 if (BufferSize < sizeof(ULONG)) return STATUS_INVALID_PARAMETER_7;
123
124 /* This will become a segmented profile object */
125 Segment = (ULONG_PTR)RangeBase;
126 RangeBase = 0;
127
128 /* Recalculate the bucket size */
129 BucketSize = RangeSize / (BufferSize / sizeof(ULONG));
130
131 /* Convert it to log2 */
132 BucketSize--;
133 while (BucketSize >>= 1) Log2++;
134 BucketSize += Log2 + 1;
135 }
136
137 /* Validate bucket size */
138 if ((BucketSize > 31) || (BucketSize < 2))
139 {
140 DPRINT1("Bucket size invalid\n");
141 return STATUS_INVALID_PARAMETER;
142 }
143
144 /* Make sure that the buckets can map the range */
145 if ((RangeSize >> (BucketSize - 2)) > BufferSize)
146 {
147 DPRINT1("Bucket size too small\n");
148 return STATUS_BUFFER_TOO_SMALL;
149 }
150
151 /* Make sure that the range isn't too gigantic */
152 if (((ULONG_PTR)RangeBase + RangeSize) < RangeSize)
153 {
154 DPRINT1("Range too big\n");
155 return STATUS_BUFFER_OVERFLOW;
156 }
157
158 /* Check if we were called from user-mode */
159 if(PreviousMode != KernelMode)
160 {
161 /* Entry SEH */
162 _SEH2_TRY
163 {
164 /* Make sure that the handle pointer is valid */
165 ProbeForWriteHandle(ProfileHandle);
166
167 /* Check if the buffer is valid */
168 ProbeForWrite(Buffer,
169 BufferSize,
170 sizeof(ULONG));
171 }
172 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
173 {
174 /* Return the exception code */
175 _SEH2_YIELD(return _SEH2_GetExceptionCode());
176 }
177 _SEH2_END;
178 }
179
180 /* Check if a process was specified */
181 if (Process)
182 {
183 /* Reference it */
184 Status = ObReferenceObjectByHandle(Process,
185 PROCESS_QUERY_INFORMATION,
186 PsProcessType,
187 PreviousMode,
188 (PVOID*)&pProcess,
189 NULL);
190 if (!NT_SUCCESS(Status)) return(Status);
191 }
192 else
193 {
194 /* Segmented profile objects cannot be used system-wide */
195 if (Segment) return STATUS_INVALID_PARAMETER;
196
197 /* No process was specified, which means a System-Wide Profile */
198 pProcess = NULL;
199
200 /* For this, we need to check the Privilege */
201 if(!SeSinglePrivilegeCheck(SeSystemProfilePrivilege, PreviousMode))
202 {
203 DPRINT1("NtCreateProfile: Caller requires the SeSystemProfilePrivilege privilege!\n");
204 return STATUS_PRIVILEGE_NOT_HELD;
205 }
206 }
207
208 /* Create the object */
209 InitializeObjectAttributes(&ObjectAttributes,
210 NULL,
211 0,
212 NULL,
213 NULL);
214 Status = ObCreateObject(KernelMode,
215 ExProfileObjectType,
216 &ObjectAttributes,
217 PreviousMode,
218 NULL,
219 sizeof(EPROFILE),
220 0,
221 sizeof(EPROFILE) + sizeof(KPROFILE),
222 (PVOID*)&Profile);
223 if (!NT_SUCCESS(Status))
224 {
225 /* Dereference the process object if it was specified */
226 if (pProcess) ObDereferenceObject(pProcess);
227
228 /* Return Status */
229 return Status;
230 }
231
232 /* Initialize it */
233 Profile->RangeBase = RangeBase;
234 Profile->RangeSize = RangeSize;
235 Profile->Buffer = Buffer;
236 Profile->BufferSize = BufferSize;
237 Profile->BucketSize = BucketSize;
238 Profile->LockedBufferAddress = NULL;
239 Profile->Segment = Segment;
240 Profile->ProfileSource = ProfileSource;
241 Profile->Affinity = Affinity;
242 Profile->Process = pProcess;
243
244 /* Insert into the Object Tree */
245 Status = ObInsertObject ((PVOID)Profile,
246 NULL,
247 PROFILE_CONTROL,
248 0,
249 NULL,
250 &hProfile);
251
252 /* Check for Success */
253 if (!NT_SUCCESS(Status))
254 {
255 /* Dereference Process on failure */
256 if (pProcess) ObDereferenceObject(pProcess);
257 return Status;
258 }
259
260 /* Enter SEH */
261 _SEH2_TRY
262 {
263 /* Copy the created handle back to the caller*/
264 *ProfileHandle = hProfile;
265 }
266 _SEH2_EXCEPT(ExSystemExceptionFilter())
267 {
268 Status = _SEH2_GetExceptionCode();
269 }
270 _SEH2_END;
271
272 /* Return Status */
273 return Status;
274 }
275
276 NTSTATUS
277 NTAPI
278 NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter,
279 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
280 {
281 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
282 LARGE_INTEGER PerfFrequency;
283 NTSTATUS Status = STATUS_SUCCESS;
284
285 /* Check if we were called from user-mode */
286 if (PreviousMode != KernelMode)
287 {
288 /* Entry SEH Block */
289 _SEH2_TRY
290 {
291 /* Make sure the counter and frequency are valid */
292 ProbeForWriteLargeInteger(PerformanceCounter);
293 if (PerformanceFrequency)
294 {
295 ProbeForWriteLargeInteger(PerformanceFrequency);
296 }
297 }
298 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
299 {
300 /* Return the exception code */
301 _SEH2_YIELD(return _SEH2_GetExceptionCode());
302 }
303 _SEH2_END;
304 }
305
306 /* Enter a new SEH Block */
307 _SEH2_TRY
308 {
309 /* Query the Kernel */
310 *PerformanceCounter = KeQueryPerformanceCounter(&PerfFrequency);
311
312 /* Return Frequency if requested */
313 if (PerformanceFrequency) *PerformanceFrequency = PerfFrequency;
314 }
315 _SEH2_EXCEPT(ExSystemExceptionFilter())
316 {
317 /* Get the exception code */
318 Status = _SEH2_GetExceptionCode();
319 }
320 _SEH2_END;
321
322 /* Return status to caller */
323 return Status;
324 }
325
326 NTSTATUS
327 NTAPI
328 NtStartProfile(IN HANDLE ProfileHandle)
329 {
330 PEPROFILE Profile;
331 PKPROFILE ProfileObject;
332 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
333 PVOID TempLockedBufferAddress;
334 NTSTATUS Status;
335 PAGED_CODE();
336
337 /* Get the Object */
338 Status = ObReferenceObjectByHandle(ProfileHandle,
339 PROFILE_CONTROL,
340 ExProfileObjectType,
341 PreviousMode,
342 (PVOID*)&Profile,
343 NULL);
344 if (!NT_SUCCESS(Status)) return(Status);
345
346 /* To avoid a Race, wait on the Mutex */
347 KeWaitForSingleObject(&ExpProfileMutex,
348 Executive,
349 KernelMode,
350 FALSE,
351 NULL);
352
353 /* The Profile can still be enabled though, so handle that */
354 if (Profile->LockedBufferAddress)
355 {
356 /* Release our lock, dereference and return */
357 KeReleaseMutex(&ExpProfileMutex, FALSE);
358 ObDereferenceObject(Profile);
359 return STATUS_PROFILING_NOT_STOPPED;
360 }
361
362 /* Allocate a Kernel Profile Object. */
363 ProfileObject = ExAllocatePoolWithTag(NonPagedPool,
364 sizeof(*ProfileObject),
365 TAG_PROFILE);
366 if (!ProfileObject)
367 {
368 /* Out of memory, fail */
369 KeReleaseMutex(&ExpProfileMutex, FALSE);
370 ObDereferenceObject(Profile);
371 return STATUS_INSUFFICIENT_RESOURCES;
372 }
373
374 /* Allocate the Mdl Structure */
375 Profile->Mdl = IoAllocateMdl(Profile->Buffer, Profile->BufferSize, FALSE, FALSE, NULL);
376
377 /* Protect this in SEH as we might raise an exception */
378 _SEH2_TRY
379 {
380 /* Probe and Lock for Write Access */
381 MmProbeAndLockPages(Profile->Mdl, PreviousMode, IoWriteAccess);
382 }
383 _SEH2_EXCEPT(ExSystemExceptionFilter())
384 {
385 /* Release our lock, free the buffer, dereference and return */
386 KeReleaseMutex(&ExpProfileMutex, FALSE);
387 ObDereferenceObject(Profile);
388 ExFreePoolWithTag(ProfileObject, TAG_PROFILE);
389 _SEH2_YIELD(return _SEH2_GetExceptionCode());
390 }
391 _SEH2_END;
392
393 /* Map the pages */
394 TempLockedBufferAddress = MmMapLockedPages(Profile->Mdl, KernelMode);
395
396 /* Initialize the Kernel Profile Object */
397 Profile->ProfileObject = ProfileObject;
398 KeInitializeProfile(ProfileObject,
399 &Profile->Process->Pcb,
400 Profile->RangeBase,
401 Profile->RangeSize,
402 Profile->BucketSize,
403 Profile->ProfileSource,
404 Profile->Affinity);
405
406 /* Start the Profiling */
407 KeStartProfile(ProfileObject, TempLockedBufferAddress);
408
409 /* Now it's safe to save this */
410 Profile->LockedBufferAddress = TempLockedBufferAddress;
411
412 /* Release mutex, dereference and return */
413 KeReleaseMutex(&ExpProfileMutex, FALSE);
414 ObDereferenceObject(Profile);
415 return STATUS_SUCCESS;
416 }
417
418 NTSTATUS
419 NTAPI
420 NtStopProfile(IN HANDLE ProfileHandle)
421 {
422 PEPROFILE Profile;
423 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
424 NTSTATUS Status;
425 PAGED_CODE();
426
427 /* Get the Object */
428 Status = ObReferenceObjectByHandle(ProfileHandle,
429 PROFILE_CONTROL,
430 ExProfileObjectType,
431 PreviousMode,
432 (PVOID*)&Profile,
433 NULL);
434 if (!NT_SUCCESS(Status)) return(Status);
435
436 /* Get the Mutex */
437 KeWaitForSingleObject(&ExpProfileMutex,
438 Executive,
439 KernelMode,
440 FALSE,
441 NULL);
442
443 /* Make sure the Profile Object is really Started */
444 if (!Profile->LockedBufferAddress)
445 {
446 Status = STATUS_PROFILING_NOT_STARTED;
447 goto Exit;
448 }
449
450 /* Stop the Profile */
451 KeStopProfile(Profile->ProfileObject);
452
453 /* Unlock the Buffer */
454 MmUnmapLockedPages(Profile->LockedBufferAddress, Profile->Mdl);
455 MmUnlockPages(Profile->Mdl);
456 IoFreeMdl(Profile->Mdl);
457 ExFreePoolWithTag(Profile->ProfileObject, TAG_PROFILE);
458
459 /* Clear the Locked Buffer pointer, meaning the Object is Stopped */
460 Profile->LockedBufferAddress = NULL;
461
462 Exit:
463 /* Release Mutex, Dereference and Return */
464 KeReleaseMutex(&ExpProfileMutex, FALSE);
465 ObDereferenceObject(Profile);
466 return Status;
467 }
468
469 NTSTATUS
470 NTAPI
471 NtQueryIntervalProfile(IN KPROFILE_SOURCE ProfileSource,
472 OUT PULONG Interval)
473 {
474 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
475 ULONG ReturnInterval;
476 NTSTATUS Status = STATUS_SUCCESS;
477 PAGED_CODE();
478
479 /* Check if we were called from user-mode */
480 if (PreviousMode != KernelMode)
481 {
482 /* Enter SEH Block */
483 _SEH2_TRY
484 {
485 /* Validate interval */
486 ProbeForWriteUlong(Interval);
487 }
488 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
489 {
490 /* Return the exception code */
491 _SEH2_YIELD(return _SEH2_GetExceptionCode());
492 }
493 _SEH2_END;
494 }
495
496 /* Query the Interval */
497 ReturnInterval = (ULONG)KeQueryIntervalProfile(ProfileSource);
498
499 /* Enter SEH block for return */
500 _SEH2_TRY
501 {
502 /* Return the data */
503 *Interval = ReturnInterval;
504 }
505 _SEH2_EXCEPT(ExSystemExceptionFilter())
506 {
507 /* Get the exception code */
508 Status = _SEH2_GetExceptionCode();
509 }
510 _SEH2_END;
511
512 /* Return Success */
513 return Status;
514 }
515
516 NTSTATUS
517 NTAPI
518 NtSetIntervalProfile(IN ULONG Interval,
519 IN KPROFILE_SOURCE Source)
520 {
521 /* Let the Kernel do the job */
522 KeSetIntervalProfile(Interval, Source);
523
524 /* Nothing can go wrong */
525 return STATUS_SUCCESS;
526 }