]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_kpc.c
xnu-3248.60.10.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>
39
40#include <pexpert/pexpert.h>
8a3053a0 41#include <kperf/kperf.h>
39236c6e
A
42
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)
fe8ab488 55#define REQ_SW_INC (14)
3e170ce0 56#define REQ_PMU_VERSION (15)
39236c6e
A
57
58/* Type-munging casts */
59typedef int (*getint_t)(void);
60typedef int (*setint_t)(int);
61
62/* safety */
63static int kpc_initted = 0;
64
65/* locking and buffer for large data requests */
fe8ab488 66#define SYSCTL_BUFFER_SIZE (33 * sizeof(uint64_t))
39236c6e
A
67static lck_grp_attr_t *sysctl_buffer_lckgrp_attr = NULL;
68static lck_grp_t *sysctl_buffer_lckgrp = NULL;
69static lck_mtx_t sysctl_buffer_lock;
70static void *sysctl_buffer = NULL;
71
72typedef int (*setget_func_t)(int);
73
39236c6e
A
74void
75kpc_init(void)
76{
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);
81
82 kpc_arch_init();
fe8ab488 83 kpc_common_init();
39236c6e
A
84 kpc_thread_init();
85
86 kpc_initted = 1;
87}
88
89/* abstract sysctl handlers */
90static int
91sysctl_get_int( struct sysctl_oid *oidp, struct sysctl_req *req,
92 uint32_t value )
93{
94 int error = 0;
95
96 /* copy out the old value */
97 error = sysctl_handle_int(oidp, &value, 0, req);
98
99 return error;
100}
101
fe8ab488
A
102static int
103sysctl_set_int( struct sysctl_req *req, int (*set_func)(int))
104{
105 int error = 0;
106 int value = 0;
107
108 error = SYSCTL_IN( req, &value, sizeof(value) );
109 if( error )
110 return error;
111
112 error = set_func( value );
113
114 return error;
115}
116
39236c6e
A
117static int
118sysctl_getset_int( struct sysctl_oid *oidp, struct sysctl_req *req,
119 int (*get_func)(void), int (*set_func)(int) )
120{
121 int error = 0;
122 uint32_t value = 0;
123
124 /* get the old value and process it */
125 value = get_func();
126
127 /* copy out the old value, get the new value */
128 error = sysctl_handle_int(oidp, &value, 0, req);
129 if (error || !req->newptr)
130 return (error);
131
132 /* if that worked, and we're writing... */
133 error = set_func( value );
134
135 return error;
136}
137
fe8ab488 138
39236c6e
A
139static int
140sysctl_setget_int( struct sysctl_req *req,
141 int (*setget_func)(int) )
142{
143 int error = 0;
144 int value = 0;
145
146 error = SYSCTL_IN( req, &value, sizeof(value) );
147 if( error )
148 return error;
149
150 value = setget_func(value);
151
152 error = SYSCTL_OUT( req, &value, sizeof(value) );
153
154 return error;
155}
156
157static int
158kpc_sysctl_acquire_buffer(void)
159{
160 if( sysctl_buffer == NULL )
fe8ab488
A
161 {
162 sysctl_buffer = kalloc(SYSCTL_BUFFER_SIZE);
163 if( sysctl_buffer )
164 {
165 bzero( sysctl_buffer, SYSCTL_BUFFER_SIZE );
166 }
167 }
39236c6e
A
168
169 if( !sysctl_buffer )
170 {
171 return ENOMEM;
172 }
173
174 return 0;
175}
176
177static int
178sysctl_kpc_get_counters(uint32_t counters,
179 uint32_t *size, void *buf)
180{
181 uint64_t *ctr_buf = (uint64_t*)buf;
182 int curcpu;
183 uint32_t count;
184
185 count = kpc_get_cpu_counters(counters & KPC_ALL_CPUS,
186 counters,
187 &curcpu, &ctr_buf[1]);
188 if (!count)
189 return EINVAL;
190
191 ctr_buf[0] = curcpu;
192
193 *size = (count+1) * sizeof(uint64_t);
194
195 return 0;
196}
197
198static int
199sysctl_kpc_get_shadow_counters(uint32_t counters,
200 uint32_t *size, void *buf)
201{
202 uint64_t *ctr_buf = (uint64_t*)buf;
203 int curcpu;
204 uint32_t count;
205
206 count = kpc_get_shadow_counters(counters & KPC_ALL_CPUS,
207 counters,
208 &curcpu, &ctr_buf[1]);
209
210 if (!count)
211 return EINVAL;
212
213 ctr_buf[0] = curcpu;
214
215 *size = (count+1) * sizeof(uint64_t);
216
217 return 0;
218}
219
220static int
221sysctl_kpc_get_thread_counters(uint32_t tid,
222 uint32_t *size, void *buf)
223{
224 uint32_t count = *size / sizeof(uint64_t);
225 int r;
226
227 if( tid != 0 )
228 return EINVAL;
229
230 r = kpc_get_curthread_counters(&count, buf);
231 if( !r )
232 *size = count * sizeof(uint64_t);
233
234 return r;
235}
236
237static int
238sysctl_kpc_get_config(uint32_t classes, void* buf)
239{
240 return kpc_get_config( classes, buf );
241}
242
243static int
244sysctl_kpc_set_config(uint32_t classes, void* buf)
245{
3e170ce0
A
246 /* userspace cannot reconfigure the power class */
247 if (classes & KPC_CLASS_POWER_MASK)
248 return (EPERM);
39236c6e
A
249 return kpc_set_config( classes, buf);
250}
251
252static int
253sysctl_kpc_get_period(uint32_t classes, void* buf)
254{
255 return kpc_get_period( classes, buf );
256}
257
258static int
259sysctl_kpc_set_period(uint32_t classes, void* buf)
260{
3e170ce0
A
261 /* userspace cannot reconfigure the power class */
262 if (classes & KPC_CLASS_POWER_MASK)
263 return (EPERM);
39236c6e
A
264 return kpc_set_period( classes, buf);
265}
266
267static int
268sysctl_kpc_get_actionid(uint32_t classes, void* buf)
269{
270 return kpc_get_actionid( classes, buf );
271}
272
273static int
274sysctl_kpc_set_actionid(uint32_t classes, void* buf)
275{
276 return kpc_set_actionid( classes, buf);
277}
278
279
280static int
281sysctl_get_bigarray( struct sysctl_req *req,
282 int (*get_fn)(uint32_t, uint32_t*, void*) )
283{
284 int error = 0;
fe8ab488 285 uint32_t bufsize = SYSCTL_BUFFER_SIZE;
39236c6e
A
286 uint32_t arg = 0;
287
288 /* get the argument */
289 error = SYSCTL_IN( req, &arg, sizeof(arg) );
290 if(error)
291 {
292 printf( "kpc: no arg?\n" );
293 return error;
294 }
295
296 /* get the wired buffer */
297 error = kpc_sysctl_acquire_buffer();
298 if (error)
299 return error;
300
301 /* atomically get the array into the wired buffer. We have a double
302 * copy, but this is better than page faulting / interrupting during
303 * a copy.
304 */
305 error = get_fn( arg, &bufsize, sysctl_buffer );
306
307 /* do the copy out */
308 if( !error )
309 error = SYSCTL_OUT( req, sysctl_buffer, bufsize );
310
311 return error;
312}
313
314/* given a config word, how many bytes does it take? */
315static int
316sysctl_config_size( uint32_t config )
317{
318 return kpc_get_config_count(config) * sizeof(kpc_config_t);
319}
320
321static int
322sysctl_counter_size( uint32_t classes )
323{
324 return kpc_get_counter_count(classes) * sizeof(uint64_t);
325}
326
327static int
328sysctl_actionid_size( uint32_t classes )
329{
330 return kpc_get_counter_count(classes) * sizeof(int32_t);
331}
332
333static int
334sysctl_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*) )
338{
339 int error = 0;
fe8ab488 340 uint32_t bufsize = SYSCTL_BUFFER_SIZE;
39236c6e
A
341 uint32_t regsize = 0;
342 uint64_t arg;
343
344 /* get the config word */
345 error = SYSCTL_IN( req, &arg, sizeof(arg) );
346 if(error)
347 {
348 printf( "kpc: no arg?\n" );
349 return error;
350 }
351
352 /* Work out size of registers */
353 regsize = size_fn((uint32_t)arg);
354
355 /* Ignore NULL requests */
356 if(regsize == 0)
357 return EINVAL;
358
359 /* ensure not too big */
360 if( regsize > bufsize )
361 return EINVAL;
362
363 /* get the wired buffer */
364 error = kpc_sysctl_acquire_buffer();
365 if (error)
366 return error;
367
368 // if writing...
369 if(req->newptr)
370 {
371 // copy in the rest in -- sysctl remembers we did one already
372 error = SYSCTL_IN( req, sysctl_buffer,
373 regsize );
374
375 // if SYSCTL_IN fails it means we are only doing a read
376 if(!error) {
377 // set it
378 error = set_fn( (uint32_t)arg, sysctl_buffer );
379 if( error )
380 goto fail;
381 }
382 }
383
384 // if reading
385 if(req->oldptr)
386 {
387 // read it
388 error = get_fn( (uint32_t)arg, sysctl_buffer );
389 if( error )
390 goto fail;
391
392 // copy out the full set
393 error = SYSCTL_OUT( req, sysctl_buffer, regsize );
394 }
395
396fail:
397 return error;
398}
399
400
401
402/*
403 * #define SYSCTL_HANDLER_ARGS (struct sysctl_oid *oidp, \
404 * void *arg1, int arg2, \
405 * struct sysctl_req *req )
406 */
407static int
408kpc_sysctl SYSCTL_HANDLER_ARGS
409{
410 int ret;
411
412 // __unused struct sysctl_oid *unused_oidp = oidp;
413 (void)arg2;
414
415 if( !kpc_initted )
416 panic("kpc_init not called");
417
8a3053a0
A
418 // Most sysctls require an access check, but a few are public.
419 switch( (uintptr_t) arg1 ) {
420 case REQ_CLASSES:
421 case REQ_CONFIG_COUNT:
422 case REQ_COUNTER_COUNT:
423 // These read-only sysctls are public.
424 break;
425
426 default:
427 // Require kperf access to read or write anything else.
428 // This is either root or the blessed pid.
429 ret = kperf_access_check();
430 if (ret) {
431 return ret;
432 }
433 break;
434 }
435
39236c6e
A
436 lck_mtx_lock(&sysctl_buffer_lock);
437
438 /* which request */
439 switch( (uintptr_t) arg1 )
440 {
441 case REQ_CLASSES:
442 ret = sysctl_get_int( oidp, req,
443 kpc_get_classes() );
444 break;
445 case REQ_COUNTING:
446 ret = sysctl_getset_int( oidp, req,
447 (getint_t)kpc_get_running,
448 (setint_t)kpc_set_running );
449 break;
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 );
454 break;
455
456 case REQ_CONFIG_COUNT:
457 ret = sysctl_setget_int( req,
458 (setget_func_t)kpc_get_config_count );
459 break;
460
461 case REQ_COUNTER_COUNT:
462 ret = sysctl_setget_int( req,
463 (setget_func_t)kpc_get_counter_count );
464 break;
465
466
467 case REQ_THREAD_COUNTERS:
468 ret = sysctl_get_bigarray( req, sysctl_kpc_get_thread_counters );
469 break;
470
471 case REQ_COUNTERS:
472 ret = sysctl_get_bigarray( req, sysctl_kpc_get_counters );
473 break;
474
475 case REQ_SHADOW_COUNTERS:
476 ret = sysctl_get_bigarray( req, sysctl_kpc_get_shadow_counters );
477 break;
478
479 case REQ_CONFIG:
480 ret = sysctl_getset_bigarray( req,
481 sysctl_config_size,
482 sysctl_kpc_get_config,
483 sysctl_kpc_set_config );
484 break;
485
486 case REQ_PERIOD:
487 ret = sysctl_getset_bigarray( req,
488 sysctl_counter_size,
489 sysctl_kpc_get_period,
490 sysctl_kpc_set_period );
491 break;
492
493 case REQ_ACTIONID:
494 ret = sysctl_getset_bigarray( req,
495 sysctl_actionid_size,
496 sysctl_kpc_get_actionid,
497 sysctl_kpc_set_actionid );
498 break;
499
fe8ab488
A
500
501 case REQ_SW_INC:
502 ret = sysctl_set_int( req, (setget_func_t)kpc_set_sw_inc );
503 break;
504
3e170ce0
A
505 case REQ_PMU_VERSION:
506 ret = sysctl_get_int(oidp, req, kpc_get_pmu_version());
507 break;
508
39236c6e
A
509 default:
510 ret = ENOENT;
511 break;
512 }
513
514 lck_mtx_unlock(&sysctl_buffer_lock);
515
516 return ret;
517}
518
519
520/*** sysctl definitions ***/
521
522/* root kperf node */
523SYSCTL_NODE(, OID_AUTO, kpc, CTLFLAG_RW|CTLFLAG_LOCKED, 0,
524 "kpc");
525
526/* values */
527SYSCTL_PROC(_kpc, OID_AUTO, classes,
528 CTLTYPE_INT|CTLFLAG_RD|CTLFLAG_ANYBODY,
529 (void*)REQ_CLASSES,
530 sizeof(int), kpc_sysctl, "I", "Available classes");
531
532SYSCTL_PROC(_kpc, OID_AUTO, counting,
533 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
534 (void*)REQ_COUNTING,
535 sizeof(int), kpc_sysctl, "I", "PMCs counting");
536
537SYSCTL_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");
541
3e170ce0
A
542SYSCTL_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");
546
39236c6e
A
547/* faux values */
548SYSCTL_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");
552
553SYSCTL_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");
557
fe8ab488
A
558SYSCTL_PROC(_kpc, OID_AUTO, sw_inc,
559 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
560 (void*)REQ_SW_INC,
561 sizeof(int), kpc_sysctl, "S", "Software increment");
562
39236c6e
A
563/* arrays */
564SYSCTL_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");
569
570SYSCTL_PROC(_kpc, OID_AUTO, counters,
571 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
572 (void*)REQ_COUNTERS,
573 sizeof(uint64_t), kpc_sysctl,
574 "QU", "Current counters");
575
576SYSCTL_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");
581
582SYSCTL_PROC(_kpc, OID_AUTO, config,
583 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
584 (void*)REQ_CONFIG,
585 sizeof(uint64_t), kpc_sysctl,
586 "QU", "Set counter configs");
587
588SYSCTL_PROC(_kpc, OID_AUTO, period,
589 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
590 (void*)REQ_PERIOD,
591 sizeof(uint64_t), kpc_sysctl,
592 "QU", "Set counter periods");
593
594SYSCTL_PROC(_kpc, OID_AUTO, actionid,
595 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
596 (void*)REQ_ACTIONID,
597 sizeof(uint32_t), kpc_sysctl,
598 "QU", "Set counter actionids");
fe8ab488
A
599
600