]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_kpc.c
xnu-3789.51.2.tar.gz
[apple/xnu.git] / bsd / kern / kern_kpc.c
CommitLineData
39236c6e
A
1/*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <kern/debug.h>
fe8ab488 30#include <kern/kalloc.h>
39236c6e
A
31#include <sys/param.h>
32#include <sys/mman.h>
33#include <sys/stat.h>
34#include <sys/sysctl.h>
35#include <libkern/libkern.h>
36#include <kern/assert.h>
37
38#include <kern/kpc.h>
39037602 39#include <sys/ktrace.h>
39236c6e
A
40
41#include <pexpert/pexpert.h>
8a3053a0 42#include <kperf/kperf.h>
39236c6e
A
43
44/* Various sysctl requests */
45#define REQ_CLASSES (1)
46#define REQ_COUNTING (2)
47#define REQ_THREAD_COUNTING (3)
48#define REQ_CONFIG_COUNT (4)
49#define REQ_COUNTER_COUNT (5)
50#define REQ_THREAD_COUNTERS (6)
51#define REQ_COUNTERS (7)
52#define REQ_SHADOW_COUNTERS (8)
53#define REQ_CONFIG (9)
54#define REQ_PERIOD (10)
55#define REQ_ACTIONID (11)
fe8ab488 56#define REQ_SW_INC (14)
3e170ce0 57#define REQ_PMU_VERSION (15)
39236c6e
A
58
59/* Type-munging casts */
60typedef int (*getint_t)(void);
61typedef int (*setint_t)(int);
62
63/* safety */
64static int kpc_initted = 0;
65
66/* locking and buffer for large data requests */
fe8ab488 67#define SYSCTL_BUFFER_SIZE (33 * sizeof(uint64_t))
39236c6e
A
68static lck_grp_attr_t *sysctl_buffer_lckgrp_attr = NULL;
69static lck_grp_t *sysctl_buffer_lckgrp = NULL;
70static lck_mtx_t sysctl_buffer_lock;
71static void *sysctl_buffer = NULL;
72
73typedef int (*setget_func_t)(int);
74
39236c6e
A
75void
76kpc_init(void)
77{
78 sysctl_buffer_lckgrp_attr = lck_grp_attr_alloc_init();
79 sysctl_buffer_lckgrp = lck_grp_alloc_init("kpc",
80 sysctl_buffer_lckgrp_attr);
81 lck_mtx_init(&sysctl_buffer_lock, sysctl_buffer_lckgrp, LCK_ATTR_NULL);
82
83 kpc_arch_init();
fe8ab488 84 kpc_common_init();
39236c6e
A
85 kpc_thread_init();
86
87 kpc_initted = 1;
88}
89
90/* abstract sysctl handlers */
91static int
92sysctl_get_int( struct sysctl_oid *oidp, struct sysctl_req *req,
93 uint32_t value )
94{
95 int error = 0;
96
97 /* copy out the old value */
98 error = sysctl_handle_int(oidp, &value, 0, req);
99
100 return error;
101}
102
fe8ab488
A
103static int
104sysctl_set_int( struct sysctl_req *req, int (*set_func)(int))
105{
106 int error = 0;
107 int value = 0;
108
109 error = SYSCTL_IN( req, &value, sizeof(value) );
110 if( error )
111 return error;
112
113 error = set_func( value );
114
115 return error;
116}
117
39236c6e
A
118static int
119sysctl_getset_int( struct sysctl_oid *oidp, struct sysctl_req *req,
120 int (*get_func)(void), int (*set_func)(int) )
121{
122 int error = 0;
123 uint32_t value = 0;
124
125 /* get the old value and process it */
126 value = get_func();
127
128 /* copy out the old value, get the new value */
129 error = sysctl_handle_int(oidp, &value, 0, req);
130 if (error || !req->newptr)
131 return (error);
132
133 /* if that worked, and we're writing... */
134 error = set_func( value );
135
136 return error;
137}
138
fe8ab488 139
39236c6e
A
140static int
141sysctl_setget_int( struct sysctl_req *req,
142 int (*setget_func)(int) )
143{
144 int error = 0;
145 int value = 0;
146
147 error = SYSCTL_IN( req, &value, sizeof(value) );
148 if( error )
149 return error;
150
151 value = setget_func(value);
152
153 error = SYSCTL_OUT( req, &value, sizeof(value) );
154
155 return error;
156}
157
158static int
159kpc_sysctl_acquire_buffer(void)
160{
161 if( sysctl_buffer == NULL )
fe8ab488
A
162 {
163 sysctl_buffer = kalloc(SYSCTL_BUFFER_SIZE);
164 if( sysctl_buffer )
165 {
166 bzero( sysctl_buffer, SYSCTL_BUFFER_SIZE );
167 }
168 }
39236c6e
A
169
170 if( !sysctl_buffer )
171 {
172 return ENOMEM;
173 }
174
175 return 0;
176}
177
178static int
179sysctl_kpc_get_counters(uint32_t counters,
180 uint32_t *size, void *buf)
181{
182 uint64_t *ctr_buf = (uint64_t*)buf;
183 int curcpu;
184 uint32_t count;
185
186 count = kpc_get_cpu_counters(counters & KPC_ALL_CPUS,
187 counters,
188 &curcpu, &ctr_buf[1]);
189 if (!count)
190 return EINVAL;
191
192 ctr_buf[0] = curcpu;
193
194 *size = (count+1) * sizeof(uint64_t);
195
196 return 0;
197}
198
199static int
200sysctl_kpc_get_shadow_counters(uint32_t counters,
201 uint32_t *size, void *buf)
202{
203 uint64_t *ctr_buf = (uint64_t*)buf;
204 int curcpu;
205 uint32_t count;
206
207 count = kpc_get_shadow_counters(counters & KPC_ALL_CPUS,
208 counters,
209 &curcpu, &ctr_buf[1]);
210
211 if (!count)
212 return EINVAL;
213
214 ctr_buf[0] = curcpu;
215
216 *size = (count+1) * sizeof(uint64_t);
217
218 return 0;
219}
220
221static int
222sysctl_kpc_get_thread_counters(uint32_t tid,
223 uint32_t *size, void *buf)
224{
225 uint32_t count = *size / sizeof(uint64_t);
226 int r;
227
228 if( tid != 0 )
229 return EINVAL;
230
231 r = kpc_get_curthread_counters(&count, buf);
232 if( !r )
233 *size = count * sizeof(uint64_t);
234
235 return r;
236}
237
238static int
239sysctl_kpc_get_config(uint32_t classes, void* buf)
240{
241 return kpc_get_config( classes, buf );
242}
243
244static int
245sysctl_kpc_set_config(uint32_t classes, void* buf)
246{
3e170ce0
A
247 /* userspace cannot reconfigure the power class */
248 if (classes & KPC_CLASS_POWER_MASK)
249 return (EPERM);
39236c6e
A
250 return kpc_set_config( classes, buf);
251}
252
253static int
254sysctl_kpc_get_period(uint32_t classes, void* buf)
255{
256 return kpc_get_period( classes, buf );
257}
258
259static int
260sysctl_kpc_set_period(uint32_t classes, void* buf)
261{
3e170ce0
A
262 /* userspace cannot reconfigure the power class */
263 if (classes & KPC_CLASS_POWER_MASK)
264 return (EPERM);
39236c6e
A
265 return kpc_set_period( classes, buf);
266}
267
268static int
269sysctl_kpc_get_actionid(uint32_t classes, void* buf)
270{
271 return kpc_get_actionid( classes, buf );
272}
273
274static int
275sysctl_kpc_set_actionid(uint32_t classes, void* buf)
276{
277 return kpc_set_actionid( classes, buf);
278}
279
280
281static int
282sysctl_get_bigarray( struct sysctl_req *req,
283 int (*get_fn)(uint32_t, uint32_t*, void*) )
284{
285 int error = 0;
fe8ab488 286 uint32_t bufsize = SYSCTL_BUFFER_SIZE;
39236c6e
A
287 uint32_t arg = 0;
288
289 /* get the argument */
290 error = SYSCTL_IN( req, &arg, sizeof(arg) );
291 if(error)
292 {
293 printf( "kpc: no arg?\n" );
294 return error;
295 }
296
297 /* get the wired buffer */
298 error = kpc_sysctl_acquire_buffer();
299 if (error)
300 return error;
301
302 /* atomically get the array into the wired buffer. We have a double
303 * copy, but this is better than page faulting / interrupting during
304 * a copy.
305 */
306 error = get_fn( arg, &bufsize, sysctl_buffer );
307
308 /* do the copy out */
309 if( !error )
310 error = SYSCTL_OUT( req, sysctl_buffer, bufsize );
311
312 return error;
313}
314
315/* given a config word, how many bytes does it take? */
316static int
317sysctl_config_size( uint32_t config )
318{
319 return kpc_get_config_count(config) * sizeof(kpc_config_t);
320}
321
322static int
323sysctl_counter_size( uint32_t classes )
324{
325 return kpc_get_counter_count(classes) * sizeof(uint64_t);
326}
327
328static int
329sysctl_actionid_size( uint32_t classes )
330{
331 return kpc_get_counter_count(classes) * sizeof(int32_t);
332}
333
334static int
335sysctl_getset_bigarray( struct sysctl_req *req,
336 int (*size_fn)(uint32_t arg),
337 int (*get_fn)(uint32_t, void*),
338 int (*set_fn)(uint32_t, void*) )
339{
340 int error = 0;
fe8ab488 341 uint32_t bufsize = SYSCTL_BUFFER_SIZE;
39236c6e
A
342 uint32_t regsize = 0;
343 uint64_t arg;
344
345 /* get the config word */
346 error = SYSCTL_IN( req, &arg, sizeof(arg) );
347 if(error)
348 {
349 printf( "kpc: no arg?\n" );
350 return error;
351 }
352
353 /* Work out size of registers */
354 regsize = size_fn((uint32_t)arg);
355
356 /* Ignore NULL requests */
357 if(regsize == 0)
358 return EINVAL;
359
360 /* ensure not too big */
361 if( regsize > bufsize )
362 return EINVAL;
363
364 /* get the wired buffer */
365 error = kpc_sysctl_acquire_buffer();
366 if (error)
367 return error;
368
369 // if writing...
370 if(req->newptr)
371 {
372 // copy in the rest in -- sysctl remembers we did one already
373 error = SYSCTL_IN( req, sysctl_buffer,
374 regsize );
375
376 // if SYSCTL_IN fails it means we are only doing a read
377 if(!error) {
378 // set it
379 error = set_fn( (uint32_t)arg, sysctl_buffer );
380 if( error )
381 goto fail;
382 }
383 }
384
385 // if reading
386 if(req->oldptr)
387 {
388 // read it
389 error = get_fn( (uint32_t)arg, sysctl_buffer );
390 if( error )
391 goto fail;
392
393 // copy out the full set
394 error = SYSCTL_OUT( req, sysctl_buffer, regsize );
395 }
396
397fail:
398 return error;
399}
400
401
402
403/*
404 * #define SYSCTL_HANDLER_ARGS (struct sysctl_oid *oidp, \
405 * void *arg1, int arg2, \
406 * struct sysctl_req *req )
407 */
408static int
409kpc_sysctl SYSCTL_HANDLER_ARGS
410{
411 int ret;
412
413 // __unused struct sysctl_oid *unused_oidp = oidp;
414 (void)arg2;
415
416 if( !kpc_initted )
417 panic("kpc_init not called");
418
39037602
A
419 lck_mtx_lock(ktrace_lock);
420
8a3053a0
A
421 // Most sysctls require an access check, but a few are public.
422 switch( (uintptr_t) arg1 ) {
423 case REQ_CLASSES:
424 case REQ_CONFIG_COUNT:
425 case REQ_COUNTER_COUNT:
426 // These read-only sysctls are public.
427 break;
428
429 default:
430 // Require kperf access to read or write anything else.
431 // This is either root or the blessed pid.
39037602
A
432 if ((ret = ktrace_read_check())) {
433 lck_mtx_unlock(ktrace_lock);
8a3053a0
A
434 return ret;
435 }
436 break;
437 }
438
39037602
A
439 lck_mtx_unlock(ktrace_lock);
440
39236c6e
A
441 lck_mtx_lock(&sysctl_buffer_lock);
442
443 /* which request */
444 switch( (uintptr_t) arg1 )
445 {
446 case REQ_CLASSES:
447 ret = sysctl_get_int( oidp, req,
448 kpc_get_classes() );
449 break;
450 case REQ_COUNTING:
451 ret = sysctl_getset_int( oidp, req,
452 (getint_t)kpc_get_running,
453 (setint_t)kpc_set_running );
454 break;
455 case REQ_THREAD_COUNTING:
456 ret = sysctl_getset_int( oidp, req,
457 (getint_t)kpc_get_thread_counting,
458 (setint_t)kpc_set_thread_counting );
459 break;
460
461 case REQ_CONFIG_COUNT:
462 ret = sysctl_setget_int( req,
463 (setget_func_t)kpc_get_config_count );
464 break;
465
466 case REQ_COUNTER_COUNT:
467 ret = sysctl_setget_int( req,
468 (setget_func_t)kpc_get_counter_count );
469 break;
470
471
472 case REQ_THREAD_COUNTERS:
473 ret = sysctl_get_bigarray( req, sysctl_kpc_get_thread_counters );
474 break;
475
476 case REQ_COUNTERS:
477 ret = sysctl_get_bigarray( req, sysctl_kpc_get_counters );
478 break;
479
480 case REQ_SHADOW_COUNTERS:
481 ret = sysctl_get_bigarray( req, sysctl_kpc_get_shadow_counters );
482 break;
483
484 case REQ_CONFIG:
485 ret = sysctl_getset_bigarray( req,
486 sysctl_config_size,
487 sysctl_kpc_get_config,
488 sysctl_kpc_set_config );
489 break;
490
491 case REQ_PERIOD:
492 ret = sysctl_getset_bigarray( req,
493 sysctl_counter_size,
494 sysctl_kpc_get_period,
495 sysctl_kpc_set_period );
496 break;
497
498 case REQ_ACTIONID:
499 ret = sysctl_getset_bigarray( req,
500 sysctl_actionid_size,
501 sysctl_kpc_get_actionid,
502 sysctl_kpc_set_actionid );
503 break;
504
fe8ab488
A
505
506 case REQ_SW_INC:
507 ret = sysctl_set_int( req, (setget_func_t)kpc_set_sw_inc );
508 break;
509
3e170ce0
A
510 case REQ_PMU_VERSION:
511 ret = sysctl_get_int(oidp, req, kpc_get_pmu_version());
512 break;
513
39236c6e
A
514 default:
515 ret = ENOENT;
516 break;
517 }
518
519 lck_mtx_unlock(&sysctl_buffer_lock);
520
521 return ret;
522}
523
524
525/*** sysctl definitions ***/
526
527/* root kperf node */
528SYSCTL_NODE(, OID_AUTO, kpc, CTLFLAG_RW|CTLFLAG_LOCKED, 0,
529 "kpc");
530
531/* values */
532SYSCTL_PROC(_kpc, OID_AUTO, classes,
533 CTLTYPE_INT|CTLFLAG_RD|CTLFLAG_ANYBODY,
534 (void*)REQ_CLASSES,
535 sizeof(int), kpc_sysctl, "I", "Available classes");
536
537SYSCTL_PROC(_kpc, OID_AUTO, counting,
538 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
539 (void*)REQ_COUNTING,
540 sizeof(int), kpc_sysctl, "I", "PMCs counting");
541
542SYSCTL_PROC(_kpc, OID_AUTO, thread_counting,
543 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
544 (void*)REQ_THREAD_COUNTING,
545 sizeof(int), kpc_sysctl, "I", "Thread accumulation");
546
3e170ce0
A
547SYSCTL_PROC(_kpc, OID_AUTO, pmu_version,
548 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_ANYBODY,
549 (void *)REQ_PMU_VERSION,
550 sizeof(int), kpc_sysctl, "I", "PMU version for hardware");
551
39236c6e
A
552/* faux values */
553SYSCTL_PROC(_kpc, OID_AUTO, config_count,
554 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
555 (void*)REQ_CONFIG_COUNT,
556 sizeof(int), kpc_sysctl, "S", "Config count");
557
558SYSCTL_PROC(_kpc, OID_AUTO, counter_count,
559 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
560 (void*)REQ_COUNTER_COUNT,
561 sizeof(int), kpc_sysctl, "S", "Counter count");
562
fe8ab488
A
563SYSCTL_PROC(_kpc, OID_AUTO, sw_inc,
564 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
565 (void*)REQ_SW_INC,
566 sizeof(int), kpc_sysctl, "S", "Software increment");
567
39236c6e
A
568/* arrays */
569SYSCTL_PROC(_kpc, OID_AUTO, thread_counters,
570 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
571 (void*)REQ_THREAD_COUNTERS,
572 sizeof(uint64_t), kpc_sysctl,
573 "QU", "Current thread counters");
574
575SYSCTL_PROC(_kpc, OID_AUTO, counters,
576 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
577 (void*)REQ_COUNTERS,
578 sizeof(uint64_t), kpc_sysctl,
579 "QU", "Current counters");
580
581SYSCTL_PROC(_kpc, OID_AUTO, shadow_counters,
582 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
583 (void*)REQ_SHADOW_COUNTERS,
584 sizeof(uint64_t), kpc_sysctl,
585 "QU", "Current shadow counters");
586
587SYSCTL_PROC(_kpc, OID_AUTO, config,
588 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
589 (void*)REQ_CONFIG,
590 sizeof(uint64_t), kpc_sysctl,
591 "QU", "Set counter configs");
592
593SYSCTL_PROC(_kpc, OID_AUTO, period,
594 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
595 (void*)REQ_PERIOD,
596 sizeof(uint64_t), kpc_sysctl,
597 "QU", "Set counter periods");
598
599SYSCTL_PROC(_kpc, OID_AUTO, actionid,
600 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
601 (void*)REQ_ACTIONID,
602 sizeof(uint32_t), kpc_sysctl,
603 "QU", "Set counter actionids");
fe8ab488
A
604
605