1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright (c) 1994, by Sun Microsytems, Inc.
24 */
25
26/*
27 * Interfaces to control kernel tracing and kernel probes
28 */
29
30#ifndef DEBUG
31#define	NDEBUG	1
32#endif
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <unistd.h>
37#include <string.h>	/* for strerror() */
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <sys/tnf.h>
41#include <fcntl.h>
42#include <errno.h>
43#include <signal.h>
44#include <assert.h>
45
46#include "tnfctl_int.h"
47#include "kernel_int.h"
48
49/* The TNF pseudo-device */
50#define	TNFDRIVER	"/dev/tnfctl"
51
52/* Dummy "test" function  -- just used to flag enabled probes */
53#define	PRBK_DUMMY_TEST	((tnf_probe_test_func_t) 4)
54
55/* Dummy "commit" function -- just used to flag trace enabled */
56#define	PRBK_DUMMY_COMMIT ((tnf_probe_func_t) 8)
57
58/* Dummy "rollback" function -- just used to flag trace disabled */
59#define	PRBK_DUMMY_ROLLBACK ((tnf_probe_func_t) 12)
60
61/* Dummy "end" function */
62#define	PRBK_DUMMY_END ((uintptr_t) 16)
63
64/* Dummy "alloc" function */
65#define	PRBK_DUMMY_ALLOC ((uintptr_t) 20)
66
67/* Minimum and maximum allowed buffer sizes. */
68/* XXX -- maximum should be some function of physmem. */
69#define	KERNEL_MINBUF_SIZE	(128 * 1024)
70#define	KERNEL_MAXBUF_SIZE	(128 * 1024 * 1024)
71
72static tnfctl_errcode_t prbk_get_buf_attrs(tnfctl_handle_t *hdl);
73static tnfctl_errcode_t alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe);
74
75/*
76 * Initialize the kernel interface:  Open the TNF control device,
77 * and determine the current kernel probes state, including the
78 * current pidfilter list.
79 */
80tnfctl_errcode_t
81_tnfctl_prbk_init(tnfctl_handle_t *hdl)
82{
83	tnfctl_errcode_t prexstat;
84	tifiocstate_t kstate;
85	int kfd;
86
87	kfd = open(TNFDRIVER, O_RDWR);
88	if (kfd < 0) {
89		return (tnfctl_status_map(errno));
90	}
91	if (ioctl(kfd, TIFIOCGSTATE, &kstate) < 0)
92		return (tnfctl_status_map(errno));
93
94	hdl->kfd = kfd;
95	hdl->kpidfilter_state = kstate.pidfilter_mode;
96	hdl->trace_state = !kstate.trace_stopped;
97	hdl->trace_min_size = KERNEL_MINBUF_SIZE;
98	prexstat = prbk_get_buf_attrs(hdl);
99	if (prexstat)
100		return (prexstat);
101
102	return (TNFCTL_ERR_NONE);
103}
104
105/*
106 * Close the TNF control device.
107 */
108tnfctl_errcode_t
109_tnfctl_prbk_close(tnfctl_handle_t *hdl)
110{
111	if (hdl == NULL)
112		return (TNFCTL_ERR_NONE);
113
114	if (close(hdl->kfd) == -1) {
115		return (tnfctl_status_map(errno));
116	}
117	return (TNFCTL_ERR_NONE);
118}
119
120/*
121 * Returns function addresses that can be plugged into function pointers
122 * in kernel probes.  These are actually dummy values that get
123 * interpreted by a routine in this file when a probe is flushed.
124 */
125void
126_tnfctl_prbk_get_other_funcs(uintptr_t *allocp, uintptr_t *commitp,
127	uintptr_t *rollbackp, uintptr_t *endp)
128{
129	*allocp = PRBK_DUMMY_ALLOC;
130	*commitp = (uintptr_t) PRBK_DUMMY_COMMIT;
131	*rollbackp = (uintptr_t) PRBK_DUMMY_ROLLBACK;
132	*endp = PRBK_DUMMY_END;
133}
134
135
136/*
137 * Returns test function address
138 */
139void
140_tnfctl_prbk_test_func(uintptr_t *outp)
141{
142	*outp = (uintptr_t) PRBK_DUMMY_TEST;
143}
144
145/*
146 * Allocate a trace buffer.  Check for reasonable size; reject if there's
147 * already a buffer.
148 */
149tnfctl_errcode_t
150_tnfctl_prbk_buffer_alloc(tnfctl_handle_t *hdl, int size)
151{
152	tifiocstate_t bufstat;
153	tnfctl_errcode_t prexstat;
154	int saved_val;
155
156	if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
157		return (tnfctl_status_map(errno));
158	}
159	if (bufstat.buffer_state != TIFIOCBUF_NONE) {
160		return (TNFCTL_ERR_BUFEXISTS);
161	}
162	if (size < KERNEL_MINBUF_SIZE) {
163		return (TNFCTL_ERR_SIZETOOSMALL);
164	} else if (size > KERNEL_MAXBUF_SIZE) {
165		/* REMIND: make this an error ? */
166		size = KERNEL_MAXBUF_SIZE;
167	}
168	if (ioctl(hdl->kfd, TIFIOCALLOCBUF, size) < 0) {
169		saved_val = errno;
170		(void) prbk_get_buf_attrs(hdl);
171		return (tnfctl_status_map(saved_val));
172	}
173
174	prexstat = prbk_get_buf_attrs(hdl);
175	if (prexstat)
176		return (prexstat);
177
178	return (TNFCTL_ERR_NONE);
179}
180
181/*
182 * Deallocate the kernel's trace buffer.
183 */
184tnfctl_errcode_t
185_tnfctl_prbk_buffer_dealloc(tnfctl_handle_t *hdl)
186{
187	tifiocstate_t bufstat;
188	tnfctl_errcode_t prexstat;
189	int saved_val;
190
191	if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
192		return (tnfctl_status_map(errno));
193	}
194	if (bufstat.buffer_state == TIFIOCBUF_NONE) {
195		return (TNFCTL_ERR_NOBUF);
196	}
197
198	if (bufstat.buffer_state == TIFIOCBUF_OK && !bufstat.trace_stopped) {
199		return (TNFCTL_ERR_BADDEALLOC);
200	}
201	if (ioctl(hdl->kfd, TIFIOCDEALLOCBUF) < 0) {
202		saved_val = errno;
203		(void) prbk_get_buf_attrs(hdl);
204		return (tnfctl_status_map(saved_val));
205	}
206
207	prexstat = prbk_get_buf_attrs(hdl);
208	if (prexstat)
209		return (prexstat);
210
211	return (TNFCTL_ERR_NONE);
212}
213
214/*
215 * Turns kernel global tracing on or off.
216 */
217tnfctl_errcode_t
218_tnfctl_prbk_set_tracing(tnfctl_handle_t *hdl, boolean_t onoff)
219{
220	if (hdl->trace_state != onoff &&
221	    ioctl(hdl->kfd, TIFIOCSTRACING, onoff) < 0) {
222		if (errno == ENOMEM && onoff)
223			return (TNFCTL_ERR_NOBUF);
224		else
225			return (tnfctl_status_map(errno));
226	}
227	hdl->trace_state = onoff;
228	return (TNFCTL_ERR_NONE);
229}
230
231/*
232 * Turn process filter mode on or off.  The process filter is maintained
233 * even when process filtering is off, but has no effect:  all processes
234 * are traced.
235 */
236tnfctl_errcode_t
237_tnfctl_prbk_set_pfilter_mode(tnfctl_handle_t *hdl, boolean_t onoff)
238{
239	if (hdl->kpidfilter_state != onoff &&
240	    ioctl(hdl->kfd, TIFIOCSPIDFILTER, onoff) < 0) {
241		return (tnfctl_status_map(errno));
242	}
243	hdl->kpidfilter_state = onoff;
244	return (TNFCTL_ERR_NONE);
245}
246
247/*
248 * Return the process filter list.
249 */
250tnfctl_errcode_t
251_tnfctl_prbk_get_pfilter_list(tnfctl_handle_t *hdl, pid_t **ret_list_p,
252				int *ret_count)
253{
254	tifiocstate_t kstate;
255	int *filterset;
256	int i;
257	pid_t *ret_list;
258
259	if (ioctl(hdl->kfd, TIFIOCGSTATE, &kstate) < 0)
260		return (tnfctl_status_map(errno));
261
262	if (kstate.pidfilter_size == 0) {
263		*ret_count = 0;
264		*ret_list_p = NULL;
265		return (TNFCTL_ERR_NONE);
266	}
267
268	filterset = (int *) malloc((kstate.pidfilter_size + 1) *
269					sizeof (pid_t));
270	if (filterset == NULL)
271		return (TNFCTL_ERR_ALLOCFAIL);
272	if (ioctl(hdl->kfd, TIFIOCPIDFILTERGET, filterset) < 0)
273		return (tnfctl_status_map(errno));
274
275	/* filterset[0] contains size of array */
276	ret_list = malloc(filterset[0] * sizeof (pid_t));
277	if (ret_list == NULL)
278		return (TNFCTL_ERR_ALLOCFAIL);
279
280	for (i = 1; i <= filterset[0]; ++i)
281		ret_list[i - 1] = filterset[i];
282
283	*ret_count = filterset[0];
284	(void) free(filterset);
285	*ret_list_p = ret_list;
286	return (TNFCTL_ERR_NONE);
287}
288
289/*
290 * Add the pid to the process filter list.
291 * check whether it's already in the filter list,
292 * and whether the process exists.
293 */
294tnfctl_errcode_t
295_tnfctl_prbk_pfilter_add(tnfctl_handle_t *hdl, pid_t pid_to_add)
296{
297	if (ioctl(hdl->kfd, TIFIOCSPIDON, pid_to_add) < 0) {
298		return (tnfctl_status_map(errno));
299	}
300	return (TNFCTL_ERR_NONE);
301}
302
303/*
304 * Drop the pid from the process filter list.
305 */
306tnfctl_errcode_t
307_tnfctl_prbk_pfilter_delete(tnfctl_handle_t *hdl, pid_t pid_to_del)
308{
309	if (ioctl(hdl->kfd, TIFIOCSPIDOFF, pid_to_del) < 0) {
310		if (errno == ESRCH) {
311			return (TNFCTL_ERR_NOPROCESS);
312		} else {
313			return (tnfctl_status_map(errno));
314		}
315	}
316	return (TNFCTL_ERR_NONE);
317}
318
319/*
320 * get the buffer attributes - side effect tnfctl handle
321 */
322static tnfctl_errcode_t
323prbk_get_buf_attrs(tnfctl_handle_t *hdl)
324{
325	tifiocstate_t bufstat;
326
327	if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
328		return (tnfctl_status_map(errno));
329	}
330
331	hdl->trace_file_name = NULL;
332	hdl->trace_buf_size = bufstat.buffer_size;
333	if (bufstat.buffer_state == TIFIOCBUF_NONE)
334		hdl->trace_buf_state = TNFCTL_BUF_NONE;
335	else if (bufstat.buffer_state == TIFIOCBUF_BROKEN)
336		hdl->trace_buf_state = TNFCTL_BUF_BROKEN;
337	else
338		hdl->trace_buf_state = TNFCTL_BUF_OK;
339	return (TNFCTL_ERR_NONE);
340}
341
342/*
343 * "Flush" a probe:  i.e., sync up the kernel state with the
344 * (desired) state stored in our data structure.
345 */
346tnfctl_errcode_t
347_tnfctl_prbk_flush(tnfctl_handle_t *hndl, prbctlref_t *p)
348{
349	tnf_probevals_t probebuf;
350
351	probebuf.probenum = p->probe_id;
352	probebuf.enabled = (p->wrkprbctl.test_func != NULL);
353	probebuf.traced = (p->wrkprbctl.commit_func == PRBK_DUMMY_COMMIT);
354	if (ioctl(hndl->kfd, TIFIOCSPROBEVALS, &probebuf) < 0)
355		return (tnfctl_status_map(errno));
356	return (TNFCTL_ERR_NONE);
357}
358
359/*
360 * Refresh our understanding of the existing probes in the kernel.
361 */
362tnfctl_errcode_t
363_tnfctl_refresh_kernel(tnfctl_handle_t *hndl)
364{
365	int maxprobe, i;
366	int pos;
367	tnfctl_errcode_t prexstat;
368	tnf_probevals_t probebuf;
369	objlist_t *obj_p;
370	prbctlref_t *p = NULL;
371
372	prexstat = prbk_get_buf_attrs(hndl);
373	if (prexstat)
374		return (prexstat);
375	/*
376	 * Here is where you'd set obj_p->new to B_FALSE and obj_p->old to
377	 * B_TRUE for all existing objects.  We currently don't need
378	 * it until we get modload/unload working correctly with probes
379	 */
380	if (ioctl(hndl->kfd, TIFIOCGMAXPROBE, &maxprobe) < 0)
381		return (tnfctl_status_map(errno));
382	if (maxprobe == hndl->num_probes) {
383		/* XXX Inadequate in the presence of module unloading */
384		return (TNFCTL_ERR_NONE);
385	}
386
387	prexstat = alloc_probe_space(hndl, maxprobe);
388	if (prexstat)
389		return (prexstat);
390
391	obj_p = hndl->objlist;
392	assert((obj_p != NULL) && (obj_p->probes != NULL));
393
394	for (i = 1; i <= maxprobe; ++i) {
395
396		if (i >= (obj_p->min_probe_num + obj_p->probecnt)) {
397			obj_p = obj_p->next;
398		}
399
400		/* make sure we are in the correct object */
401		assert(obj_p != NULL);
402		assert((i >= obj_p->min_probe_num) &&
403			(i < (obj_p->min_probe_num + obj_p->probecnt)));
404
405		/* get a pointer to correct probe */
406		pos = i - obj_p->min_probe_num;
407		p = &(obj_p->probes[pos]);
408		assert((p != NULL) && (p->probe_id == i) && (p->probe_handle));
409
410		probebuf.probenum = i;
411		if (ioctl(hndl->kfd, TIFIOCGPROBEVALS, &probebuf) < 0) {
412			if (errno == ENOENT) {
413				/*
414				 * This probe has vanished due to a module
415				 * unload.
416				 */
417				p->probe_handle->valid = B_FALSE;
418			} else {
419				return (tnfctl_status_map(errno));
420			}
421		} else {
422			if (p->probe_handle->valid == B_FALSE) {
423				/*
424				 * seeing this probe for the first time
425				 * (alloc_probe_space() initialized this
426				 * "valid" field to B_FALSE)
427				 */
428				/* Update our info about this probe */
429				p->wrkprbctl.test_func = (probebuf.enabled) ?
430					PRBK_DUMMY_TEST : NULL;
431				p->wrkprbctl.commit_func = (probebuf.traced) ?
432					PRBK_DUMMY_COMMIT : PRBK_DUMMY_ROLLBACK;
433				p->probe_handle->valid = B_TRUE;
434				if (probebuf.attrsize < sizeof (probebuf))
435					probebuf.attrsize = sizeof (probebuf);
436				p->attr_string = malloc(probebuf.attrsize);
437				if (p->attr_string == NULL)
438					return (TNFCTL_ERR_ALLOCFAIL);
439				/*
440				 * NOTE: the next statement is a structure
441				 * copy and *not* a pointer assignment
442				 */
443/* LINTED pointer cast may result in improper alignment */
444				*(tnf_probevals_t *) p->attr_string = probebuf;
445				if (ioctl(hndl->kfd, TIFIOCGPROBESTRING,
446						p->attr_string) < 0)
447					return (tnfctl_status_map(errno));
448				if (hndl->create_func) {
449				    p->probe_handle->client_registered_data =
450					hndl->create_func(hndl,
451						p->probe_handle);
452				}
453			}
454		}
455	}
456	hndl->num_probes = maxprobe;
457	return (TNFCTL_ERR_NONE);
458}
459
460/*
461 * check if there are any new probes in the kernel that we aren't aware of.
462 * If so, allocate space for those probes in our data structure.
463 */
464static tnfctl_errcode_t
465alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe)
466{
467	objlist_t **o_pp;
468	objlist_t *obj_p, *nobj_p;
469	int min_probe_num, i;
470	prbctlref_t *probe_p;
471
472	/* we know that: hndl->maxprobe != maxprobe */
473	obj_p = hndl->objlist;
474	if (obj_p == NULL) {
475		/* no objects allocated */
476		o_pp = &(hndl->objlist);
477		min_probe_num = 1;
478	} else {
479		/* find last object */
480		while (obj_p->next != NULL) {
481			/* reset new_probe field on modload/unload */
482			obj_p->new_probe = B_FALSE;
483			obj_p = obj_p->next;
484		}
485		o_pp = &(obj_p->next);
486		min_probe_num = obj_p->min_probe_num + obj_p->probecnt;
487	}
488
489	nobj_p = calloc(1, sizeof (objlist_t));
490	if (nobj_p == NULL)
491		return (TNFCTL_ERR_ALLOCFAIL);
492	/* add to the linked list */
493	*o_pp = nobj_p;
494	/* NULL, B_FALSE, or 0's not explicitly initialized */
495	nobj_p->new_probe = B_TRUE;
496	nobj_p->new = B_TRUE;
497	nobj_p->objfd = -1;
498	nobj_p->min_probe_num = min_probe_num;
499	nobj_p->probecnt = maxprobe - min_probe_num + 1;
500	nobj_p->probes = calloc(nobj_p->probecnt,  sizeof (prbctlref_t));
501	if (nobj_p->probes == NULL) {
502		free(nobj_p);
503		return (TNFCTL_ERR_ALLOCFAIL);
504	}
505
506	probe_p = &(nobj_p->probes[0]);
507	for (i = min_probe_num; i <= maxprobe; i++) {
508		probe_p->obj = nobj_p;
509		probe_p->probe_id = i;
510		probe_p->probe_handle = calloc(1, sizeof (tnfctl_probe_t));
511		if (probe_p->probe_handle == NULL) {
512			if (nobj_p->probes)
513				free(nobj_p->probes);
514			free(nobj_p);
515			return (TNFCTL_ERR_ALLOCFAIL);
516		}
517		probe_p->probe_handle->valid = B_FALSE;
518		probe_p->probe_handle->probe_p = probe_p;
519		/* link in probe handle into chain off tnfctl_handle_t */
520		probe_p->probe_handle->next = hndl->probe_handle_list_head;
521		hndl->probe_handle_list_head = probe_p->probe_handle;
522
523		probe_p++;
524	}
525
526	hndl->num_probes = maxprobe;
527	return (TNFCTL_ERR_NONE);
528}
529