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 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/dtrace.h> 30 #include <sys/systrace.h> 31 #include <sys/stat.h> 32 #include <sys/systm.h> 33 #include <sys/conf.h> 34 #include <sys/ddi.h> 35 #include <sys/sunddi.h> 36 #include <sys/atomic.h> 37 38 #define SYSTRACE_ARTIFICIAL_FRAMES 1 39 40 #define SYSTRACE_SHIFT 16 41 #define SYSTRACE_ISENTRY(x) ((int)(x) >> SYSTRACE_SHIFT) 42 #define SYSTRACE_SYSNUM(x) ((int)(x) & ((1 << SYSTRACE_SHIFT) - 1)) 43 #define SYSTRACE_ENTRY(id) ((1 << SYSTRACE_SHIFT) | (id)) 44 #define SYSTRACE_RETURN(id) (id) 45 46 #if ((1 << SYSTRACE_SHIFT) <= NSYSCALL) 47 #error 1 << SYSTRACE_SHIFT must exceed number of system calls 48 #endif 49 50 static dev_info_t *systrace_devi; 51 static dtrace_provider_id_t systrace_id; 52 53 static void 54 systrace_init(struct sysent *actual, systrace_sysent_t **interposed) 55 { 56 systrace_sysent_t *sysent = *interposed; 57 int i; 58 59 if (sysent == NULL) { 60 *interposed = sysent = kmem_zalloc(sizeof (systrace_sysent_t) * 61 NSYSCALL, KM_SLEEP); 62 } 63 64 for (i = 0; i < NSYSCALL; i++) { 65 struct sysent *a = &actual[i]; 66 systrace_sysent_t *s = &sysent[i]; 67 68 if (LOADABLE_SYSCALL(a) && !LOADED_SYSCALL(a)) 69 continue; 70 71 if (a->sy_callc == dtrace_systrace_syscall) 72 continue; 73 74 #ifdef _SYSCALL32_IMPL 75 if (a->sy_callc == dtrace_systrace_syscall32) 76 continue; 77 #endif 78 79 s->stsy_underlying = a->sy_callc; 80 } 81 } 82 83 /*ARGSUSED*/ 84 static void 85 systrace_provide(void *arg, const dtrace_probedesc_t *desc) 86 { 87 int i; 88 89 if (desc != NULL) 90 return; 91 92 systrace_init(sysent, &systrace_sysent); 93 #ifdef _SYSCALL32_IMPL 94 systrace_init(sysent32, &systrace_sysent32); 95 #endif 96 97 for (i = 0; i < NSYSCALL; i++) { 98 if (systrace_sysent[i].stsy_underlying == NULL) 99 continue; 100 101 if (dtrace_probe_lookup(systrace_id, NULL, 102 syscallnames[i], "entry") != 0) 103 continue; 104 105 (void) dtrace_probe_create(systrace_id, NULL, syscallnames[i], 106 "entry", SYSTRACE_ARTIFICIAL_FRAMES, 107 (void *)((uintptr_t)SYSTRACE_ENTRY(i))); 108 (void) dtrace_probe_create(systrace_id, NULL, syscallnames[i], 109 "return", SYSTRACE_ARTIFICIAL_FRAMES, 110 (void *)((uintptr_t)SYSTRACE_RETURN(i))); 111 112 systrace_sysent[i].stsy_entry = DTRACE_IDNONE; 113 systrace_sysent[i].stsy_return = DTRACE_IDNONE; 114 #ifdef _SYSCALL32_IMPL 115 systrace_sysent32[i].stsy_entry = DTRACE_IDNONE; 116 systrace_sysent32[i].stsy_return = DTRACE_IDNONE; 117 #endif 118 } 119 } 120 121 /*ARGSUSED*/ 122 static void 123 systrace_destroy(void *arg, dtrace_id_t id, void *parg) 124 { 125 int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg); 126 127 /* 128 * There's nothing to do here but assert that we have actually been 129 * disabled. 130 */ 131 if (SYSTRACE_ISENTRY((uintptr_t)parg)) { 132 ASSERT(systrace_sysent[sysnum].stsy_entry == DTRACE_IDNONE); 133 #ifdef _SYSCALL32_IMPL 134 ASSERT(systrace_sysent32[sysnum].stsy_entry == DTRACE_IDNONE); 135 #endif 136 } else { 137 ASSERT(systrace_sysent[sysnum].stsy_return == DTRACE_IDNONE); 138 #ifdef _SYSCALL32_IMPL 139 ASSERT(systrace_sysent32[sysnum].stsy_return == DTRACE_IDNONE); 140 #endif 141 } 142 } 143 144 /*ARGSUSED*/ 145 static void 146 systrace_enable(void *arg, dtrace_id_t id, void *parg) 147 { 148 int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg); 149 int enabled = (systrace_sysent[sysnum].stsy_entry != DTRACE_IDNONE || 150 systrace_sysent[sysnum].stsy_return != DTRACE_IDNONE); 151 152 if (SYSTRACE_ISENTRY((uintptr_t)parg)) { 153 systrace_sysent[sysnum].stsy_entry = id; 154 #ifdef _SYSCALL32_IMPL 155 systrace_sysent32[sysnum].stsy_entry = id; 156 #endif 157 } else { 158 systrace_sysent[sysnum].stsy_return = id; 159 #ifdef _SYSCALL32_IMPL 160 systrace_sysent32[sysnum].stsy_return = id; 161 #endif 162 } 163 164 if (enabled) { 165 ASSERT(sysent[sysnum].sy_callc == dtrace_systrace_syscall); 166 return; 167 } 168 169 (void) casptr(&sysent[sysnum].sy_callc, 170 (void *)systrace_sysent[sysnum].stsy_underlying, 171 (void *)dtrace_systrace_syscall); 172 #ifdef _SYSCALL32_IMPL 173 (void) casptr(&sysent32[sysnum].sy_callc, 174 (void *)systrace_sysent32[sysnum].stsy_underlying, 175 (void *)dtrace_systrace_syscall32); 176 #endif 177 } 178 179 /*ARGSUSED*/ 180 static void 181 systrace_disable(void *arg, dtrace_id_t id, void *parg) 182 { 183 int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg); 184 int disable = (systrace_sysent[sysnum].stsy_entry == DTRACE_IDNONE || 185 systrace_sysent[sysnum].stsy_return == DTRACE_IDNONE); 186 187 if (disable) { 188 (void) casptr(&sysent[sysnum].sy_callc, 189 (void *)dtrace_systrace_syscall, 190 (void *)systrace_sysent[sysnum].stsy_underlying); 191 192 #ifdef _SYSCALL32_IMPL 193 (void) casptr(&sysent32[sysnum].sy_callc, 194 (void *)dtrace_systrace_syscall32, 195 (void *)systrace_sysent32[sysnum].stsy_underlying); 196 #endif 197 } 198 199 if (SYSTRACE_ISENTRY((uintptr_t)parg)) { 200 systrace_sysent[sysnum].stsy_entry = DTRACE_IDNONE; 201 #ifdef _SYSCALL32_IMPL 202 systrace_sysent32[sysnum].stsy_entry = DTRACE_IDNONE; 203 #endif 204 } else { 205 systrace_sysent[sysnum].stsy_return = DTRACE_IDNONE; 206 #ifdef _SYSCALL32_IMPL 207 systrace_sysent32[sysnum].stsy_return = DTRACE_IDNONE; 208 #endif 209 } 210 } 211 212 static dtrace_pattr_t systrace_attr = { 213 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 214 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN }, 215 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, 216 { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON }, 217 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA }, 218 }; 219 220 static dtrace_pops_t systrace_pops = { 221 systrace_provide, 222 NULL, 223 systrace_enable, 224 systrace_disable, 225 NULL, 226 NULL, 227 NULL, 228 NULL, 229 NULL, 230 systrace_destroy 231 }; 232 233 static int 234 systrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 235 { 236 switch (cmd) { 237 case DDI_ATTACH: 238 break; 239 case DDI_RESUME: 240 return (DDI_SUCCESS); 241 default: 242 return (DDI_FAILURE); 243 } 244 245 systrace_probe = dtrace_probe; 246 membar_enter(); 247 248 if (ddi_create_minor_node(devi, "systrace", S_IFCHR, 0, 249 DDI_PSEUDO, NULL) == DDI_FAILURE || 250 dtrace_register("syscall", &systrace_attr, DTRACE_PRIV_USER, 0, 251 &systrace_pops, NULL, &systrace_id) != 0) { 252 systrace_probe = systrace_stub; 253 ddi_remove_minor_node(devi, NULL); 254 return (DDI_FAILURE); 255 } 256 257 ddi_report_dev(devi); 258 systrace_devi = devi; 259 260 return (DDI_SUCCESS); 261 } 262 263 static int 264 systrace_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 265 { 266 switch (cmd) { 267 case DDI_DETACH: 268 break; 269 case DDI_SUSPEND: 270 return (DDI_SUCCESS); 271 default: 272 return (DDI_FAILURE); 273 } 274 275 if (dtrace_unregister(systrace_id) != 0) 276 return (DDI_FAILURE); 277 278 ddi_remove_minor_node(devi, NULL); 279 systrace_probe = systrace_stub; 280 return (DDI_SUCCESS); 281 } 282 283 /*ARGSUSED*/ 284 static int 285 systrace_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 286 { 287 int error; 288 289 switch (infocmd) { 290 case DDI_INFO_DEVT2DEVINFO: 291 *result = (void *)systrace_devi; 292 error = DDI_SUCCESS; 293 break; 294 case DDI_INFO_DEVT2INSTANCE: 295 *result = (void *)0; 296 error = DDI_SUCCESS; 297 break; 298 default: 299 error = DDI_FAILURE; 300 } 301 return (error); 302 } 303 304 /*ARGSUSED*/ 305 static int 306 systrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 307 { 308 return (0); 309 } 310 311 static struct cb_ops systrace_cb_ops = { 312 systrace_open, /* open */ 313 nodev, /* close */ 314 nulldev, /* strategy */ 315 nulldev, /* print */ 316 nodev, /* dump */ 317 nodev, /* read */ 318 nodev, /* write */ 319 nodev, /* ioctl */ 320 nodev, /* devmap */ 321 nodev, /* mmap */ 322 nodev, /* segmap */ 323 nochpoll, /* poll */ 324 ddi_prop_op, /* cb_prop_op */ 325 0, /* streamtab */ 326 D_NEW | D_MP /* Driver compatibility flag */ 327 }; 328 329 static struct dev_ops systrace_ops = { 330 DEVO_REV, /* devo_rev, */ 331 0, /* refcnt */ 332 systrace_info, /* get_dev_info */ 333 nulldev, /* identify */ 334 nulldev, /* probe */ 335 systrace_attach, /* attach */ 336 systrace_detach, /* detach */ 337 nodev, /* reset */ 338 &systrace_cb_ops, /* driver operations */ 339 NULL, /* bus operations */ 340 nodev /* dev power */ 341 }; 342 343 /* 344 * Module linkage information for the kernel. 345 */ 346 static struct modldrv modldrv = { 347 &mod_driverops, /* module type (this is a pseudo driver) */ 348 "System Call Tracing", /* name of module */ 349 &systrace_ops, /* driver ops */ 350 }; 351 352 static struct modlinkage modlinkage = { 353 MODREV_1, 354 (void *)&modldrv, 355 NULL 356 }; 357 358 int 359 _init(void) 360 { 361 return (mod_install(&modlinkage)); 362 } 363 364 int 365 _info(struct modinfo *modinfop) 366 { 367 return (mod_info(&modlinkage, modinfop)); 368 } 369 370 int 371 _fini(void) 372 { 373 return (mod_remove(&modlinkage)); 374 } 375