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 }