2 * Copyright (c) 2012 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <kern/debug.h>
30 #include <kern/kalloc.h>
31 #include <sys/param.h>
34 #include <sys/sysctl.h>
35 #include <libkern/libkern.h>
36 #include <kern/assert.h>
40 #include <pexpert/pexpert.h>
41 #include <kperf/kperf.h>
43 /* Various sysctl requests */
44 #define REQ_CLASSES (1)
45 #define REQ_COUNTING (2)
46 #define REQ_THREAD_COUNTING (3)
47 #define REQ_CONFIG_COUNT (4)
48 #define REQ_COUNTER_COUNT (5)
49 #define REQ_THREAD_COUNTERS (6)
50 #define REQ_COUNTERS (7)
51 #define REQ_SHADOW_COUNTERS (8)
52 #define REQ_CONFIG (9)
53 #define REQ_PERIOD (10)
54 #define REQ_ACTIONID (11)
55 #define REQ_SW_INC (14)
56 #define REQ_PMU_VERSION (15)
58 /* Type-munging casts */
59 typedef int (*getint_t
)(void);
60 typedef int (*setint_t
)(int);
63 static int kpc_initted
= 0;
65 /* locking and buffer for large data requests */
66 #define SYSCTL_BUFFER_SIZE (33 * sizeof(uint64_t))
67 static lck_grp_attr_t
*sysctl_buffer_lckgrp_attr
= NULL
;
68 static lck_grp_t
*sysctl_buffer_lckgrp
= NULL
;
69 static lck_mtx_t sysctl_buffer_lock
;
70 static void *sysctl_buffer
= NULL
;
72 typedef int (*setget_func_t
)(int);
77 sysctl_buffer_lckgrp_attr
= lck_grp_attr_alloc_init();
78 sysctl_buffer_lckgrp
= lck_grp_alloc_init("kpc",
79 sysctl_buffer_lckgrp_attr
);
80 lck_mtx_init(&sysctl_buffer_lock
, sysctl_buffer_lckgrp
, LCK_ATTR_NULL
);
89 /* abstract sysctl handlers */
91 sysctl_get_int( struct sysctl_oid
*oidp
, struct sysctl_req
*req
,
96 /* copy out the old value */
97 error
= sysctl_handle_int(oidp
, &value
, 0, req
);
103 sysctl_set_int( struct sysctl_req
*req
, int (*set_func
)(int))
108 error
= SYSCTL_IN( req
, &value
, sizeof(value
) );
112 error
= set_func( value
);
118 sysctl_getset_int( struct sysctl_oid
*oidp
, struct sysctl_req
*req
,
119 int (*get_func
)(void), int (*set_func
)(int) )
124 /* get the old value and process it */
127 /* copy out the old value, get the new value */
128 error
= sysctl_handle_int(oidp
, &value
, 0, req
);
129 if (error
|| !req
->newptr
)
132 /* if that worked, and we're writing... */
133 error
= set_func( value
);
140 sysctl_setget_int( struct sysctl_req
*req
,
141 int (*setget_func
)(int) )
146 error
= SYSCTL_IN( req
, &value
, sizeof(value
) );
150 value
= setget_func(value
);
152 error
= SYSCTL_OUT( req
, &value
, sizeof(value
) );
158 kpc_sysctl_acquire_buffer(void)
160 if( sysctl_buffer
== NULL
)
162 sysctl_buffer
= kalloc(SYSCTL_BUFFER_SIZE
);
165 bzero( sysctl_buffer
, SYSCTL_BUFFER_SIZE
);
178 sysctl_kpc_get_counters(uint32_t counters
,
179 uint32_t *size
, void *buf
)
181 uint64_t *ctr_buf
= (uint64_t*)buf
;
185 count
= kpc_get_cpu_counters(counters
& KPC_ALL_CPUS
,
187 &curcpu
, &ctr_buf
[1]);
193 *size
= (count
+1) * sizeof(uint64_t);
199 sysctl_kpc_get_shadow_counters(uint32_t counters
,
200 uint32_t *size
, void *buf
)
202 uint64_t *ctr_buf
= (uint64_t*)buf
;
206 count
= kpc_get_shadow_counters(counters
& KPC_ALL_CPUS
,
208 &curcpu
, &ctr_buf
[1]);
215 *size
= (count
+1) * sizeof(uint64_t);
221 sysctl_kpc_get_thread_counters(uint32_t tid
,
222 uint32_t *size
, void *buf
)
224 uint32_t count
= *size
/ sizeof(uint64_t);
230 r
= kpc_get_curthread_counters(&count
, buf
);
232 *size
= count
* sizeof(uint64_t);
238 sysctl_kpc_get_config(uint32_t classes
, void* buf
)
240 return kpc_get_config( classes
, buf
);
244 sysctl_kpc_set_config(uint32_t classes
, void* buf
)
246 /* userspace cannot reconfigure the power class */
247 if (classes
& KPC_CLASS_POWER_MASK
)
249 return kpc_set_config( classes
, buf
);
253 sysctl_kpc_get_period(uint32_t classes
, void* buf
)
255 return kpc_get_period( classes
, buf
);
259 sysctl_kpc_set_period(uint32_t classes
, void* buf
)
261 /* userspace cannot reconfigure the power class */
262 if (classes
& KPC_CLASS_POWER_MASK
)
264 return kpc_set_period( classes
, buf
);
268 sysctl_kpc_get_actionid(uint32_t classes
, void* buf
)
270 return kpc_get_actionid( classes
, buf
);
274 sysctl_kpc_set_actionid(uint32_t classes
, void* buf
)
276 return kpc_set_actionid( classes
, buf
);
281 sysctl_get_bigarray( struct sysctl_req
*req
,
282 int (*get_fn
)(uint32_t, uint32_t*, void*) )
285 uint32_t bufsize
= SYSCTL_BUFFER_SIZE
;
288 /* get the argument */
289 error
= SYSCTL_IN( req
, &arg
, sizeof(arg
) );
292 printf( "kpc: no arg?\n" );
296 /* get the wired buffer */
297 error
= kpc_sysctl_acquire_buffer();
301 /* atomically get the array into the wired buffer. We have a double
302 * copy, but this is better than page faulting / interrupting during
305 error
= get_fn( arg
, &bufsize
, sysctl_buffer
);
307 /* do the copy out */
309 error
= SYSCTL_OUT( req
, sysctl_buffer
, bufsize
);
314 /* given a config word, how many bytes does it take? */
316 sysctl_config_size( uint32_t config
)
318 return kpc_get_config_count(config
) * sizeof(kpc_config_t
);
322 sysctl_counter_size( uint32_t classes
)
324 return kpc_get_counter_count(classes
) * sizeof(uint64_t);
328 sysctl_actionid_size( uint32_t classes
)
330 return kpc_get_counter_count(classes
) * sizeof(int32_t);
334 sysctl_getset_bigarray( struct sysctl_req
*req
,
335 int (*size_fn
)(uint32_t arg
),
336 int (*get_fn
)(uint32_t, void*),
337 int (*set_fn
)(uint32_t, void*) )
340 uint32_t bufsize
= SYSCTL_BUFFER_SIZE
;
341 uint32_t regsize
= 0;
344 /* get the config word */
345 error
= SYSCTL_IN( req
, &arg
, sizeof(arg
) );
348 printf( "kpc: no arg?\n" );
352 /* Work out size of registers */
353 regsize
= size_fn((uint32_t)arg
);
355 /* Ignore NULL requests */
359 /* ensure not too big */
360 if( regsize
> bufsize
)
363 /* get the wired buffer */
364 error
= kpc_sysctl_acquire_buffer();
371 // copy in the rest in -- sysctl remembers we did one already
372 error
= SYSCTL_IN( req
, sysctl_buffer
,
375 // if SYSCTL_IN fails it means we are only doing a read
378 error
= set_fn( (uint32_t)arg
, sysctl_buffer
);
388 error
= get_fn( (uint32_t)arg
, sysctl_buffer
);
392 // copy out the full set
393 error
= SYSCTL_OUT( req
, sysctl_buffer
, regsize
);
403 * #define SYSCTL_HANDLER_ARGS (struct sysctl_oid *oidp, \
404 * void *arg1, int arg2, \
405 * struct sysctl_req *req )
408 kpc_sysctl SYSCTL_HANDLER_ARGS
412 // __unused struct sysctl_oid *unused_oidp = oidp;
416 panic("kpc_init not called");
418 // Most sysctls require an access check, but a few are public.
419 switch( (uintptr_t) arg1
) {
421 case REQ_CONFIG_COUNT
:
422 case REQ_COUNTER_COUNT
:
423 // These read-only sysctls are public.
427 // Require kperf access to read or write anything else.
428 // This is either root or the blessed pid.
429 ret
= kperf_access_check();
436 lck_mtx_lock(&sysctl_buffer_lock
);
439 switch( (uintptr_t) arg1
)
442 ret
= sysctl_get_int( oidp
, req
,
446 ret
= sysctl_getset_int( oidp
, req
,
447 (getint_t
)kpc_get_running
,
448 (setint_t
)kpc_set_running
);
450 case REQ_THREAD_COUNTING
:
451 ret
= sysctl_getset_int( oidp
, req
,
452 (getint_t
)kpc_get_thread_counting
,
453 (setint_t
)kpc_set_thread_counting
);
456 case REQ_CONFIG_COUNT
:
457 ret
= sysctl_setget_int( req
,
458 (setget_func_t
)kpc_get_config_count
);
461 case REQ_COUNTER_COUNT
:
462 ret
= sysctl_setget_int( req
,
463 (setget_func_t
)kpc_get_counter_count
);
467 case REQ_THREAD_COUNTERS
:
468 ret
= sysctl_get_bigarray( req
, sysctl_kpc_get_thread_counters
);
472 ret
= sysctl_get_bigarray( req
, sysctl_kpc_get_counters
);
475 case REQ_SHADOW_COUNTERS
:
476 ret
= sysctl_get_bigarray( req
, sysctl_kpc_get_shadow_counters
);
480 ret
= sysctl_getset_bigarray( req
,
482 sysctl_kpc_get_config
,
483 sysctl_kpc_set_config
);
487 ret
= sysctl_getset_bigarray( req
,
489 sysctl_kpc_get_period
,
490 sysctl_kpc_set_period
);
494 ret
= sysctl_getset_bigarray( req
,
495 sysctl_actionid_size
,
496 sysctl_kpc_get_actionid
,
497 sysctl_kpc_set_actionid
);
502 ret
= sysctl_set_int( req
, (setget_func_t
)kpc_set_sw_inc
);
505 case REQ_PMU_VERSION
:
506 ret
= sysctl_get_int(oidp
, req
, kpc_get_pmu_version());
514 lck_mtx_unlock(&sysctl_buffer_lock
);
520 /*** sysctl definitions ***/
522 /* root kperf node */
523 SYSCTL_NODE(, OID_AUTO
, kpc
, CTLFLAG_RW
|CTLFLAG_LOCKED
, 0,
527 SYSCTL_PROC(_kpc
, OID_AUTO
, classes
,
528 CTLTYPE_INT
|CTLFLAG_RD
|CTLFLAG_ANYBODY
,
530 sizeof(int), kpc_sysctl
, "I", "Available classes");
532 SYSCTL_PROC(_kpc
, OID_AUTO
, counting
,
533 CTLTYPE_INT
|CTLFLAG_RW
|CTLFLAG_ANYBODY
,
535 sizeof(int), kpc_sysctl
, "I", "PMCs counting");
537 SYSCTL_PROC(_kpc
, OID_AUTO
, thread_counting
,
538 CTLTYPE_INT
|CTLFLAG_RW
|CTLFLAG_ANYBODY
,
539 (void*)REQ_THREAD_COUNTING
,
540 sizeof(int), kpc_sysctl
, "I", "Thread accumulation");
542 SYSCTL_PROC(_kpc
, OID_AUTO
, pmu_version
,
543 CTLTYPE_INT
| CTLFLAG_RD
| CTLFLAG_ANYBODY
,
544 (void *)REQ_PMU_VERSION
,
545 sizeof(int), kpc_sysctl
, "I", "PMU version for hardware");
548 SYSCTL_PROC(_kpc
, OID_AUTO
, config_count
,
549 CTLTYPE_INT
|CTLFLAG_RW
|CTLFLAG_ANYBODY
,
550 (void*)REQ_CONFIG_COUNT
,
551 sizeof(int), kpc_sysctl
, "S", "Config count");
553 SYSCTL_PROC(_kpc
, OID_AUTO
, counter_count
,
554 CTLTYPE_INT
|CTLFLAG_RW
|CTLFLAG_ANYBODY
,
555 (void*)REQ_COUNTER_COUNT
,
556 sizeof(int), kpc_sysctl
, "S", "Counter count");
558 SYSCTL_PROC(_kpc
, OID_AUTO
, sw_inc
,
559 CTLTYPE_INT
|CTLFLAG_RW
|CTLFLAG_ANYBODY
,
561 sizeof(int), kpc_sysctl
, "S", "Software increment");
564 SYSCTL_PROC(_kpc
, OID_AUTO
, thread_counters
,
565 CTLFLAG_RD
|CTLFLAG_WR
|CTLFLAG_ANYBODY
,
566 (void*)REQ_THREAD_COUNTERS
,
567 sizeof(uint64_t), kpc_sysctl
,
568 "QU", "Current thread counters");
570 SYSCTL_PROC(_kpc
, OID_AUTO
, counters
,
571 CTLFLAG_RD
|CTLFLAG_WR
|CTLFLAG_ANYBODY
,
573 sizeof(uint64_t), kpc_sysctl
,
574 "QU", "Current counters");
576 SYSCTL_PROC(_kpc
, OID_AUTO
, shadow_counters
,
577 CTLFLAG_RD
|CTLFLAG_WR
|CTLFLAG_ANYBODY
,
578 (void*)REQ_SHADOW_COUNTERS
,
579 sizeof(uint64_t), kpc_sysctl
,
580 "QU", "Current shadow counters");
582 SYSCTL_PROC(_kpc
, OID_AUTO
, config
,
583 CTLFLAG_RD
|CTLFLAG_WR
|CTLFLAG_ANYBODY
,
585 sizeof(uint64_t), kpc_sysctl
,
586 "QU", "Set counter configs");
588 SYSCTL_PROC(_kpc
, OID_AUTO
, period
,
589 CTLFLAG_RD
|CTLFLAG_WR
|CTLFLAG_ANYBODY
,
591 sizeof(uint64_t), kpc_sysctl
,
592 "QU", "Set counter periods");
594 SYSCTL_PROC(_kpc
, OID_AUTO
, actionid
,
595 CTLFLAG_RD
|CTLFLAG_WR
|CTLFLAG_ANYBODY
,
597 sizeof(uint32_t), kpc_sysctl
,
598 "QU", "Set counter actionids");