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