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 }