1/*
2 * Copyright (c) 2011 Apple Computer, 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#include <mach/mach_types.h>
29#include <kern/thread.h>
30#include <kern/machine.h>
31#include <kern/kalloc.h>
32#include <sys/errno.h>
33
34#include <kperf/sample.h>
35#include <kperf/pet.h>
36#include <kperf/action.h>
37#include <kperf/kperf.h>
38#include <kperf/timetrigger.h>
39
40#include <kern/ipc_tt.h> /* port_name_to_task */
41
42/** misc functions **/
43#include <chud/chud_xnu.h> /* XXX: should bust this out */
44
45/* thread on CPUs before starting the PET thread */
46thread_t *kperf_thread_on_cpus = NULL;
47
48/* interupt sample buffers -- one wired per CPU */
49static struct kperf_sample *intr_samplev = NULL;
50static unsigned intr_samplec = 0;
51
52/* track recursion in the trace code */
53static struct
54{
55	int active;
56	int pad[64 / sizeof(int)];
57} *kpdbg_recursev;
58static unsigned kpdbg_recursec = 0;
59
60/* Curren sampling status */
61static unsigned sampling_status = KPERF_SAMPLING_OFF;
62
63/* Make sure we only init once */
64static unsigned kperf_initted = 0;
65
66extern void (*chudxnu_thread_ast_handler)(thread_t);
67
68struct kperf_sample*
69kperf_intr_sample_buffer(void)
70{
71	unsigned ncpu = chudxnu_cpu_number();
72
73	// XXX: assert?
74	if( ncpu >= intr_samplec )
75		return NULL;
76
77	return &intr_samplev[ncpu];
78}
79
80int
81kperf_kdbg_recurse(int step)
82{
83	unsigned ncpu = chudxnu_cpu_number();
84
85	// XXX: assert?
86	if( ncpu >= kpdbg_recursec )
87		return 1;
88
89	/* recursing in, available */
90	if( (step > 0)
91	    && (kpdbg_recursev[ncpu].active == 0) )
92	{
93		kpdbg_recursev[ncpu].active = 1;
94		return 0;
95	}
96
97	/* recursing in, unavailable */
98	if( (step > 0)
99	    && (kpdbg_recursev[ncpu].active != 0) )
100	{
101		return 1;
102	}
103
104	/* recursing out, unavailable */
105	if( (step < 0)
106	    && (kpdbg_recursev[ncpu].active != 0) )
107	{
108		kpdbg_recursev[ncpu].active = 0;
109		return 0;
110	}
111
112	/* recursing out, available */
113	if( (step < 0)
114	    && (kpdbg_recursev[ncpu].active == 0) )
115		panic( "return from non-recursed kperf kdebug call" );
116
117	panic( "unknown kperf kdebug call" );
118	return 1;
119}
120
121/* setup interrupt sample buffers */
122int
123kperf_init(void)
124{
125	unsigned ncpus = 0;
126	int err;
127
128	if( kperf_initted )
129		return 0;
130
131	/* get number of cpus */
132	ncpus = machine_info.logical_cpu_max;
133
134	kperf_thread_on_cpus = kalloc( ncpus * sizeof(*kperf_thread_on_cpus) );
135	if( kperf_thread_on_cpus == NULL )
136	{
137		err = ENOMEM;
138		goto error;
139	}
140
141	/* clear it */
142	bzero( kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus) );
143
144	/* make the CPU array
145	 * FIXME: cache alignment
146	 */
147	intr_samplev = kalloc( ncpus * sizeof(*intr_samplev));
148	intr_samplec = ncpus;
149
150	if( intr_samplev == NULL )
151	{
152		err = ENOMEM;
153		goto error;
154	}
155
156	/* clear it */
157	bzero( intr_samplev, ncpus * sizeof(*intr_samplev) );
158
159	/* make the recursion array */
160	kpdbg_recursev = kalloc( ncpus * sizeof(*kpdbg_recursev));
161	kpdbg_recursec = ncpus;
162
163	/* clear it */
164	bzero( kpdbg_recursev, ncpus * sizeof(*kpdbg_recursev) );
165
166	/* we're done */
167	kperf_initted = 1;
168
169	return 0;
170error:
171	if( intr_samplev )
172		kfree( intr_samplev, ncpus * sizeof(*intr_samplev) );
173	if( kperf_thread_on_cpus )
174		kfree( kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus) );
175	return err;
176}
177
178/* random misc-ish functions */
179uint32_t
180kperf_get_thread_bits( thread_t thread )
181{
182	return thread->t_chud;
183}
184
185void
186kperf_set_thread_bits( thread_t thread, uint32_t bits )
187{
188	thread->t_chud = bits;
189}
190
191/* mark an AST to fire on a thread */
192void
193kperf_set_thread_ast( thread_t thread )
194{
195	/* FIXME: only call this on current thread from an interrupt
196	 * handler for now...
197	 */
198	if( thread != current_thread() )
199		panic( "unsafe AST set" );
200
201	act_set_kperf(thread);
202}
203
204unsigned
205kperf_sampling_status(void)
206{
207	return sampling_status;
208}
209
210int
211kperf_sampling_enable(void)
212{
213	/* already running! */
214	if( sampling_status == KPERF_SAMPLING_ON )
215		return 0;
216
217	if ( sampling_status != KPERF_SAMPLING_OFF )
218		panic( "kperf: sampling wasn't off" );
219
220	/* make sure interrupt tables and actions are initted */
221	if( !kperf_initted
222	    || (kperf_action_get_count() == 0) )
223		return ECANCELED;
224
225	/* mark as running */
226	sampling_status = KPERF_SAMPLING_ON;
227
228	/* tell timers to enable */
229	kperf_timer_go();
230
231	return 0;
232}
233
234int
235kperf_sampling_disable(void)
236{
237	if( sampling_status != KPERF_SAMPLING_ON )
238		return 0;
239
240	/* mark a shutting down */
241	sampling_status = KPERF_SAMPLING_SHUTDOWN;
242
243	/* tell timers to disable */
244	kperf_timer_stop();
245
246	/* mark as off */
247	sampling_status = KPERF_SAMPLING_OFF;
248
249	return 0;
250}
251
252int
253kperf_port_to_pid(mach_port_name_t portname)
254{
255	task_t task;
256	int pid;
257
258	if( !MACH_PORT_VALID(portname) )
259		return -1;
260
261	task = port_name_to_task(portname);
262
263	if( task == TASK_NULL )
264		return -1;
265
266
267	pid = chudxnu_pid_for_task(task);
268
269	task_deallocate_internal(task);
270
271	return pid;
272}
273