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