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