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