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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28 #include <sys/modctl.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/stat.h>
32 #include <sys/conf.h>
33 #include <sys/frame.h>
34 #include <sys/dtrace.h>
35 #include <sys/dtrace_impl.h>
36
37 #include <sys/lx_impl.h>
38
39 #define LX_SYSTRACE_SHIFT 16
40 #define LX_SYSTRACE_ISENTRY(x) ((int)(x) >> LX_SYSTRACE_SHIFT)
41 #define LX_SYSTRACE_SYSNUM(x) ((int)(x) & ((1 << LX_SYSTRACE_SHIFT) - 1))
42 #define LX_SYSTRACE_ENTRY(id) ((1 << LX_SYSTRACE_SHIFT) | (id))
43 #define LX_SYSTRACE_RETURN(id) (id)
44
45 #define LX_SYSTRACE_ENTRY_AFRAMES 2
46 #define LX_SYSTRACE_RETURN_AFRAMES 4
47
48 typedef struct lx_systrace_sysent {
49 const char *lss_name;
50 dtrace_id_t lss_entry;
51 dtrace_id_t lss_return;
52 } lx_systrace_sysent_t;
53
54 static dev_info_t *lx_systrace_devi;
55 static dtrace_provider_id_t lx_systrace_id;
56 static kmutex_t lx_systrace_lock;
57 static uint_t lx_systrace_nenabled;
58
59 static int lx_systrace_nsysent;
60 static lx_systrace_sysent_t *lx_systrace_sysent;
61
62 /*ARGSUSED*/
63 static void
64 lx_systrace_entry(ulong_t sysnum, ulong_t arg0, ulong_t arg1, ulong_t arg2,
65 ulong_t arg3, ulong_t arg4, ulong_t arg5)
66 {
67 dtrace_id_t id;
68
69 if (sysnum >= lx_systrace_nsysent)
70 return;
71
72 if ((id = lx_systrace_sysent[sysnum].lss_entry) == DTRACE_IDNONE)
73 return;
74
75 dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
76 }
77
78 /*ARGSUSED*/
79 static void
80 lx_systrace_return(ulong_t sysnum, ulong_t arg0, ulong_t arg1, ulong_t arg2,
81 ulong_t arg3, ulong_t arg4, ulong_t arg5)
82 {
83 dtrace_id_t id;
84
85 if (sysnum >= lx_systrace_nsysent)
86 return;
87
88 if ((id = lx_systrace_sysent[sysnum].lss_return) == DTRACE_IDNONE)
89 return;
90
91 dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
92 }
93
94 /*ARGSUSED*/
95 static void
96 lx_systrace_provide(void *arg, const dtrace_probedesc_t *desc)
97 {
98 int i;
99
100 if (desc != NULL)
101 return;
102
103 for (i = 0; i < lx_systrace_nsysent; i++) {
104 if (dtrace_probe_lookup(lx_systrace_id, NULL,
105 lx_systrace_sysent[i].lss_name, "entry") != 0)
106 continue;
107
108 (void) dtrace_probe_create(lx_systrace_id, NULL,
109 lx_systrace_sysent[i].lss_name, "entry",
110 LX_SYSTRACE_ENTRY_AFRAMES,
111 (void *)((uintptr_t)LX_SYSTRACE_ENTRY(i)));
112
113 (void) dtrace_probe_create(lx_systrace_id, NULL,
114 lx_systrace_sysent[i].lss_name, "return",
115 LX_SYSTRACE_RETURN_AFRAMES,
116 (void *)((uintptr_t)LX_SYSTRACE_RETURN(i)));
117
118 lx_systrace_sysent[i].lss_entry = DTRACE_IDNONE;
119 lx_systrace_sysent[i].lss_return = DTRACE_IDNONE;
120 }
121 }
122
123 /*ARGSUSED*/
124 static int
125 lx_systrace_enable(void *arg, dtrace_id_t id, void *parg)
126 {
127 int sysnum = LX_SYSTRACE_SYSNUM((uintptr_t)parg);
128
129 ASSERT(sysnum < lx_systrace_nsysent);
130
131 mutex_enter(&lx_systrace_lock);
132 if (lx_systrace_nenabled++ == 0)
133 lx_brand_systrace_enable();
134 mutex_exit(&lx_systrace_lock);
135
136 if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
137 lx_systrace_sysent[sysnum].lss_entry = id;
138 } else {
139 lx_systrace_sysent[sysnum].lss_return = id;
140 }
141 return (0);
142 }
143
144 /*ARGSUSED*/
145 static void
146 lx_systrace_disable(void *arg, dtrace_id_t id, void *parg)
147 {
148 int sysnum = LX_SYSTRACE_SYSNUM((uintptr_t)parg);
149
150 ASSERT(sysnum < lx_systrace_nsysent);
151
152 if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
153 lx_systrace_sysent[sysnum].lss_entry = DTRACE_IDNONE;
154 } else {
155 lx_systrace_sysent[sysnum].lss_return = DTRACE_IDNONE;
156 }
157
158 mutex_enter(&lx_systrace_lock);
159 if (--lx_systrace_nenabled == 0)
160 lx_brand_systrace_disable();
161 mutex_exit(&lx_systrace_lock);
162 }
163
164 /*ARGSUSED*/
165 static void
166 lx_systrace_destroy(void *arg, dtrace_id_t id, void *parg)
167 {
168 }
169
170 /*ARGSUSED*/
171 static uint64_t
172 lx_systrace_getarg(void *arg, dtrace_id_t id, void *parg, int argno,
173 int aframes)
174 {
175 struct frame *fp = (struct frame *)dtrace_getfp();
176 uintptr_t *stack;
177 uint64_t val = 0;
178 int i;
179
180 if (argno >= 6)
181 return (0);
182
183 /*
184 * Walk the four frames down the stack to the entry or return callback.
185 * Our callback calls dtrace_probe() which calls dtrace_dif_variable()
186 * which invokes this function to get the extended arguments. We get
187 * the frame pointer in via call to dtrace_getfp() above which makes for
188 * four frames.
189 */
190 for (i = 0; i < 4; i++) {
191 fp = (struct frame *)fp->fr_savfp;
192 }
193
194 stack = (uintptr_t *)&fp[1];
195
196 /*
197 * Skip the first argument to the callback -- the system call number.
198 */
199 argno++;
200
201 #ifdef __amd64
202 /*
203 * On amd64, the first 6 arguments are passed in registers while
204 * subsequent arguments are on the stack.
205 */
206 argno -= 6;
207 #endif
208
209 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
210 val = stack[argno];
211 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
212
213 return (val);
214 }
215
216
217 static const dtrace_pattr_t lx_systrace_attr = {
218 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
219 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
220 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
221 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
222 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
223 };
224
225 static dtrace_pops_t lx_systrace_pops = {
226 lx_systrace_provide,
227 NULL,
228 lx_systrace_enable,
229 lx_systrace_disable,
230 NULL,
231 NULL,
232 NULL,
233 lx_systrace_getarg,
234 NULL,
235 lx_systrace_destroy
236 };
237
238 static int
239 lx_systrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
240 {
241 int i;
242
243 switch (cmd) {
244 case DDI_ATTACH:
245 break;
246 case DDI_RESUME:
247 return (DDI_SUCCESS);
248 default:
249 return (DDI_FAILURE);
250 }
251
252 if (ddi_create_minor_node(devi, "lx_systrace", S_IFCHR,
253 0, DDI_PSEUDO, NULL) == DDI_FAILURE ||
254 dtrace_register("lx-syscall", &lx_systrace_attr,
255 DTRACE_PRIV_KERNEL, 0, &lx_systrace_pops, NULL,
256 &lx_systrace_id) != 0) {
257 ddi_remove_minor_node(devi, NULL);
258 return (DDI_FAILURE);
259 }
260
261 ddi_report_dev(devi);
262 lx_systrace_devi = devi;
263
264 /*
265 * Count up the lx_brand system calls.
266 */
267 for (i = 0; lx_sysent[i].sy_callc != NULL; i++)
268 continue;
269
270 /*
271 * Initialize our corresponding table.
272 */
273 lx_systrace_sysent = kmem_zalloc(i * sizeof (lx_systrace_sysent_t),
274 KM_SLEEP);
275 lx_systrace_nsysent = i;
276
277 for (i = 0; i < lx_systrace_nsysent; i++) {
278 lx_systrace_sysent[i].lss_name = lx_sysent[i].sy_name;
279 lx_systrace_sysent[i].lss_entry = DTRACE_IDNONE;
280 lx_systrace_sysent[i].lss_return = DTRACE_IDNONE;
281 }
282
283 /*
284 * Install probe triggers.
285 */
286 lx_systrace_entry_ptr = lx_systrace_entry;
287 lx_systrace_return_ptr = lx_systrace_return;
288
289 return (DDI_SUCCESS);
290 }
291
292 /*ARGSUSED*/
293 static int
294 lx_systrace_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
295 {
296 switch (cmd) {
297 case DDI_DETACH:
298 break;
299 case DDI_SUSPEND:
300 return (DDI_SUCCESS);
301 default:
302 return (DDI_FAILURE);
303 }
304
305 if (dtrace_unregister(lx_systrace_id) != 0)
306 return (DDI_FAILURE);
307
308 /*
309 * Free table.
310 */
311 kmem_free(lx_systrace_sysent, lx_systrace_nsysent *
312 sizeof (lx_systrace_sysent_t));
313 lx_systrace_sysent = NULL;
314 lx_systrace_nsysent = 0;
315
316 /*
317 * Reset probe triggers.
318 */
319 lx_systrace_entry_ptr = NULL;
320 lx_systrace_return_ptr = NULL;
321
322 return (DDI_SUCCESS);
323 }
324
325 /*ARGSUSED*/
326 static int
327 lx_systrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
328 {
329 return (0);
330 }
331
332 static struct cb_ops lx_systrace_cb_ops = {
333 lx_systrace_open, /* open */
334 nodev, /* close */
335 nulldev, /* strategy */
336 nulldev, /* print */
337 nodev, /* dump */
338 nodev, /* read */
339 nodev, /* write */
340 nodev, /* ioctl */
341 nodev, /* devmap */
342 nodev, /* mmap */
343 nodev, /* segmap */
344 nochpoll, /* poll */
345 ddi_prop_op, /* cb_prop_op */
346 0, /* streamtab */
347 D_NEW | D_MP /* Driver compatibility flag */
348 };
349
350 static struct dev_ops lx_systrace_ops = {
351 DEVO_REV, /* devo_rev */
352 0, /* refcnt */
353 ddi_getinfo_1to1, /* get_dev_info */
354 nulldev, /* identify */
355 nulldev, /* probe */
356 lx_systrace_attach, /* attach */
357 lx_systrace_detach, /* detach */
358 nodev, /* reset */
359 &lx_systrace_cb_ops, /* driver operations */
360 NULL, /* bus operations */
361 nodev, /* dev power */
362 ddi_quiesce_not_needed, /* quiesce */
363 };
364
365 /*
366 * Module linkage information for the kernel.
367 */
368 static struct modldrv modldrv = {
369 &mod_driverops, /* module type (this is a pseudo driver) */
370 "Linux Brand System Call Tracing", /* name of module */
371 &lx_systrace_ops /* driver ops */
372 };
373
374 static struct modlinkage modlinkage = {
375 MODREV_1,
376 (void *)&modldrv,
377 NULL
378 };
379
380 int
381 _init(void)
382 {
383 return (mod_install(&modlinkage));
384 }
385
386 int
387 _info(struct modinfo *modinfop)
388 {
389 return (mod_info(&modlinkage, modinfop));
390 }
391
392 int
393 _fini(void)
394 {
395 return (mod_remove(&modlinkage));
396 }