xref: /netbsd-src/external/cddl/osnet/dev/systrace/systrace.c (revision 71da105a9638fc66f71b14f65195b3feab64f65c)
1 /*	$NetBSD: systrace.c,v 1.7 2015/03/10 12:17:50 christos Exp $	*/
2 
3 /*
4  * CDDL HEADER START
5  *
6  * The contents of this file are subject to the terms of the
7  * Common Development and Distribution License (the "License").
8  * You may not use this file except in compliance with the License.
9  *
10  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11  * or http://www.opensolaris.org/os/licensing.
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  *
23  * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
24  *
25  * $FreeBSD: src/sys/cddl/dev/systrace/systrace.c,v 1.2.2.1 2009/08/03 08:13:06 kensmith Exp $
26  *
27  */
28 
29 /*
30  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
31  * Use is subject to license terms.
32  */
33 
34 #include <sys/cdefs.h>
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/conf.h>
38 #include <sys/cpuvar.h>
39 #include <sys/fcntl.h>
40 #include <sys/filio.h>
41 #include <sys/kernel.h>
42 #include <sys/kmem.h>
43 #include <sys/kthread.h>
44 #include <sys/limits.h>
45 #include <sys/linker.h>
46 #include <sys/lock.h>
47 #include <sys/malloc.h>
48 #include <sys/module.h>
49 #include <sys/mutex.h>
50 #include <sys/poll.h>
51 #include <sys/proc.h>
52 #include <sys/selinfo.h>
53 #include <sys/syscallargs.h>
54 #include <sys/uio.h>
55 #include <sys/unistd.h>
56 
57 #include <sys/dtrace.h>
58 
59 #include "emultrace.h"
60 
61 #define	CONCAT(x,y)	__CONCAT(x,y)
62 #define	STRING(s)	__STRING(s)
63 
64 #ifndef NATIVE
65 extern const char	* const CONCAT(emulname,_syscallnames)[];
66 extern 	struct sysent 	CONCAT(emulname,_sysent)[];
67 #define	MODNAME		CONCAT(dtrace_syscall_,emulname)
68 #define	MODDEP		"dtrace_syscall,compat_" STRING(emulname)
69 #define	MAXSYSCALL	CONCAT(EMULNAME,_SYS_MAXSYSCALL)
70 #define	SYSCALLNAMES	CONCAT(emulname,_syscallnames)
71 #define	SYSENT		CONCAT(emulname,_sysent)
72 #define	PROVNAME	STRING(emulname) "_syscall"
73 #else
74 extern const char	* const syscallnames[];
75 #define	MODNAME		dtrace_syscall
76 #define	MODDEP		"dtrace"
77 #define	MAXSYSCALL	SYS_MAXSYSCALL
78 #define	SYSCALLNAMES	syscallnames
79 #define	SYSENT		sysent
80 #define	PROVNAME	"syscall"
81 #endif
82 
83 #define	MODCMD		CONCAT(MODNAME,_modcmd)
84 #define EMUL		CONCAT(emul_,emulname)
85 extern struct emul 	EMUL;
86 
87 #define	SYSTRACE_ARTIFICIAL_FRAMES	1
88 
89 #define	SYSTRACE_SHIFT			16
90 #define	SYSTRACE_ISENTRY(x)		((int)(x) >> SYSTRACE_SHIFT)
91 #define	SYSTRACE_SYSNUM(x)		((int)(x) & ((1 << SYSTRACE_SHIFT) - 1))
92 #define	SYSTRACE_ENTRY(id)		((1 << SYSTRACE_SHIFT) | (id))
93 #define	SYSTRACE_RETURN(id)		(id)
94 
95 #if ((1 << SYSTRACE_SHIFT) <= MAXSYSCALL)
96 #error 1 << SYSTRACE_SHIFT must exceed number of system calls
97 #endif
98 
99 static int	systrace_unload(void);
100 static void	systrace_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
101 static void	systrace_provide(void *, const dtrace_probedesc_t *);
102 static void	systrace_destroy(void *, dtrace_id_t, void *);
103 static int	systrace_enable(void *, dtrace_id_t, void *);
104 static void	systrace_disable(void *, dtrace_id_t, void *);
105 static void	systrace_load(void *);
106 
107 static dtrace_pattr_t systrace_attr = {
108 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
109 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
110 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
111 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
112 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
113 };
114 
115 static dtrace_pops_t systrace_pops = {
116 	systrace_provide,
117 	NULL,
118 	systrace_enable,
119 	systrace_disable,
120 	NULL,
121 	NULL,
122 	systrace_getargdesc,
123 	NULL,
124 	NULL,
125 	systrace_destroy
126 };
127 
128 static dtrace_provider_id_t	systrace_id;
129 
130 /*
131  * Probe callback function.
132  *
133  * Note: This function is called for _all_ syscalls, regardless of which sysent
134  *       array the syscall comes from. It could be a standard syscall or a
135  *       compat syscall from something like Linux.
136  */
137 static void
138 systrace_probe(uint32_t id, register_t sysnum, const struct sysent *se,
139     const void *params, const register_t *ret, int error)
140 {
141 	size_t		n_args	= 0;
142 	uintptr_t	uargs[SYS_MAXSYSARGS + 3];
143 
144 	memset(uargs, 0, sizeof(uargs));
145 	if (ret == NULL) {
146 		/* entry syscall, convert params */
147 		systrace_args(sysnum, params, uargs, &n_args);
148 	} else {
149 		/* return syscall, set values and params: */
150 		uargs[0] = ret[0];
151 		uargs[1] = ret[1];
152 		uargs[2] = error;
153 		systrace_args(sysnum, params, uargs + 3, &n_args);
154 	}
155 	/* Process the probe using the converted argments. */
156 	/* XXX: fix for more arguments! */
157 	dtrace_probe(id, uargs[0], uargs[1], uargs[2], uargs[3], uargs[4]);
158 }
159 
160 static void
161 systrace_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
162 {
163 	int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
164 	if (SYSTRACE_ISENTRY((uintptr_t)parg))
165 		systrace_entry_setargdesc(sysnum, desc->dtargd_ndx,
166 		    desc->dtargd_native, sizeof(desc->dtargd_native));
167 	else
168 		systrace_return_setargdesc(sysnum, desc->dtargd_ndx,
169 		    desc->dtargd_native, sizeof(desc->dtargd_native));
170 
171 	if (desc->dtargd_native[0] == '\0')
172 		desc->dtargd_ndx = DTRACE_ARGNONE;
173 }
174 
175 static void
176 systrace_provide(void *arg, const dtrace_probedesc_t *desc)
177 {
178 	int i;
179 
180 	if (desc != NULL)
181 		return;
182 
183 	for (i = 0; i < MAXSYSCALL; i++) {
184 		if (dtrace_probe_lookup(systrace_id, NULL,
185 		    SYSCALLNAMES[i], "entry") != 0)
186 			continue;
187 
188 		(void) dtrace_probe_create(systrace_id, NULL,
189 		    SYSCALLNAMES[i], "entry", SYSTRACE_ARTIFICIAL_FRAMES,
190 		    (void *)(intptr_t)SYSTRACE_ENTRY(i));
191 		(void) dtrace_probe_create(systrace_id, NULL,
192 		    SYSCALLNAMES[i], "return", SYSTRACE_ARTIFICIAL_FRAMES,
193 		    (void *)(intptr_t)SYSTRACE_RETURN(i));
194 	}
195 }
196 
197 static void
198 systrace_destroy(void *arg, dtrace_id_t id, void *parg)
199 {
200 #ifdef DEBUG
201 	int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
202 
203 	/*
204 	 * There's nothing to do here but assert that we have actually been
205 	 * disabled.
206 	 */
207 	if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
208 		ASSERT(sysent[sysnum].sy_entry == 0);
209 	} else {
210 		ASSERT(sysent[sysnum].sy_return == 0);
211 	}
212 #endif
213 }
214 
215 static int
216 systrace_enable(void *arg, dtrace_id_t id, void *parg)
217 {
218 	int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
219 
220 	if (SYSTRACE_ISENTRY((uintptr_t)parg))
221 		SYSENT[sysnum].sy_entry = id;
222 	else
223 		SYSENT[sysnum].sy_return = id;
224 	return 0;
225 }
226 
227 static void
228 systrace_disable(void *arg, dtrace_id_t id, void *parg)
229 {
230 	int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
231 
232 	SYSENT[sysnum].sy_entry = 0;
233 	SYSENT[sysnum].sy_return = 0;
234 }
235 
236 static void
237 systrace_load(void *dummy)
238 {
239 	if (dtrace_register(PROVNAME, &systrace_attr, DTRACE_PRIV_USER,
240 	    NULL, &systrace_pops, NULL, &systrace_id) != 0)
241 		return;
242 
243 	EMUL.e_dtrace_syscall = systrace_probe;
244 }
245 
246 
247 static int
248 systrace_unload()
249 {
250 	int error;
251 
252 	if ((error = dtrace_unregister(systrace_id)) != 0)
253 		return (error);
254 
255 	EMUL.e_dtrace_syscall = NULL;
256 
257 	return error;
258 }
259 
260 static int
261 MODCMD(modcmd_t cmd, void *data)
262 {
263 	switch (cmd) {
264 	case MODULE_CMD_INIT:
265 		systrace_load(NULL);
266 		return 0;
267 
268 	case MODULE_CMD_FINI:
269 		return systrace_unload();
270 
271 	case MODULE_CMD_AUTOUNLOAD:
272 		return EBUSY;
273 
274 	default:
275 		return ENOTTY;
276 	}
277 }
278 
279 MODULE(MODULE_CLASS_MISC, MODNAME, MODDEP)
280