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 #include <sys/types.h>
  28 #include <sys/sysmacros.h>
  29 #include <sys/dditypes.h>
  30 #include <sys/ddi_impldefs.h>
  31 #include <sys/ddipropdefs.h>
  32 #include <sys/modctl.h>
  33 #include <sys/file.h>
  34 #include <sys/sunldi_impl.h>
  35 
  36 #include <mdb/mdb_modapi.h>
  37 #include <mdb/mdb_ks.h>
  38 
  39 #include "ldi.h"
  40 
  41 /*
  42  * ldi handle walker structure
  43  */
  44 typedef struct lh_walk {
  45         struct ldi_handle       **hash; /* current bucket pointer       */
  46         struct ldi_handle       *lhp;   /* ldi handle pointer           */
  47         size_t                  index;  /* hash table index             */
  48         struct ldi_handle       buf;    /* buffer used for handle reads */
  49 } lh_walk_t;
  50 
  51 /*
  52  * ldi identifier walker structure
  53  */
  54 typedef struct li_walk {
  55         struct ldi_ident        **hash; /* current bucket pointer       */
  56         struct ldi_ident        *lip;   /* ldi handle pointer           */
  57         size_t                  index;  /* hash table index             */
  58         struct ldi_ident        buf;    /* buffer used for ident reads */
  59 } li_walk_t;
  60 
  61 /*
  62  * Options for ldi_handles dcmd
  63  */
  64 #define LH_IDENTINFO    0x1
  65 
  66 /*
  67  * LDI walkers
  68  */
  69 int
  70 ldi_handle_walk_init(mdb_walk_state_t *wsp)
  71 {
  72         lh_walk_t       *lhwp;
  73         GElf_Sym        sym;
  74 
  75         /* get the address of the hash table */
  76         if (mdb_lookup_by_name("ldi_handle_hash", &sym) == -1) {
  77                 mdb_warn("couldn't find ldi_handle_hash");
  78                 return (WALK_ERR);
  79         }
  80 
  81         lhwp = mdb_alloc(sizeof (lh_walk_t), UM_SLEEP|UM_GC);
  82         lhwp->hash = (struct ldi_handle **)(uintptr_t)sym.st_value;
  83         lhwp->index = 0;
  84 
  85         /* get the address of the first element in the first hash bucket */
  86         if ((mdb_vread(&lhwp->lhp, sizeof (struct ldi_handle *),
  87             (uintptr_t)lhwp->hash)) == -1) {
  88                 mdb_warn("couldn't read ldi handle hash at %p", lhwp->hash);
  89                 return (WALK_ERR);
  90         }
  91 
  92         wsp->walk_addr = (uintptr_t)lhwp->lhp;
  93         wsp->walk_data = lhwp;
  94 
  95         return (WALK_NEXT);
  96 }
  97 
  98 int
  99 ldi_handle_walk_step(mdb_walk_state_t *wsp)
 100 {
 101         lh_walk_t       *lhwp = (lh_walk_t *)wsp->walk_data;
 102         int             status;
 103 
 104         /* check if we need to go to the next hash bucket */
 105         while (wsp->walk_addr == NULL) {
 106 
 107                 /* advance to the next bucket */
 108                 if (++(lhwp->index) >= LH_HASH_SZ)
 109                         return (WALK_DONE);
 110 
 111                 /* get handle address from the hash bucket */
 112                 if ((mdb_vread(&lhwp->lhp, sizeof (struct ldi_handle *),
 113                     (uintptr_t)(lhwp->hash + lhwp->index))) == -1) {
 114                         mdb_warn("couldn't read ldi handle hash at %p",
 115                             (uintptr_t)lhwp->hash + lhwp->index);
 116                         return (WALK_ERR);
 117                 }
 118 
 119                 wsp->walk_addr = (uintptr_t)lhwp->lhp;
 120         }
 121 
 122         /* invoke the walker callback for this hash element */
 123         status = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata);
 124         if (status != WALK_NEXT)
 125                 return (status);
 126 
 127         /* get a pointer to the next hash element */
 128         if (mdb_vread(&lhwp->buf, sizeof (struct ldi_handle),
 129             wsp->walk_addr) == -1) {
 130                 mdb_warn("couldn't read ldi handle at %p", wsp->walk_addr);
 131                 return (WALK_ERR);
 132         }
 133         wsp->walk_addr = (uintptr_t)lhwp->buf.lh_next;
 134         return (WALK_NEXT);
 135 }
 136 
 137 int
 138 ldi_ident_walk_init(mdb_walk_state_t *wsp)
 139 {
 140         li_walk_t       *liwp;
 141         GElf_Sym        sym;
 142 
 143         /* get the address of the hash table */
 144         if (mdb_lookup_by_name("ldi_ident_hash", &sym) == -1) {
 145                 mdb_warn("couldn't find ldi_ident_hash");
 146                 return (WALK_ERR);
 147         }
 148 
 149         liwp = mdb_alloc(sizeof (li_walk_t), UM_SLEEP|UM_GC);
 150         liwp->hash = (struct ldi_ident **)(uintptr_t)sym.st_value;
 151         liwp->index = 0;
 152 
 153         /* get the address of the first element in the first hash bucket */
 154         if ((mdb_vread(&liwp->lip, sizeof (struct ldi_ident *),
 155             (uintptr_t)liwp->hash)) == -1) {
 156                 mdb_warn("couldn't read ldi ident hash at %p", liwp->hash);
 157                 return (WALK_ERR);
 158         }
 159 
 160         wsp->walk_addr = (uintptr_t)liwp->lip;
 161         wsp->walk_data = liwp;
 162 
 163         return (WALK_NEXT);
 164 }
 165 
 166 int
 167 ldi_ident_walk_step(mdb_walk_state_t *wsp)
 168 {
 169         li_walk_t       *liwp = (li_walk_t *)wsp->walk_data;
 170         int             status;
 171 
 172         /* check if we need to go to the next hash bucket */
 173         while (wsp->walk_addr == NULL) {
 174 
 175                 /* advance to the next bucket */
 176                 if (++(liwp->index) >= LI_HASH_SZ)
 177                         return (WALK_DONE);
 178 
 179                 /* get handle address from the hash bucket */
 180                 if ((mdb_vread(&liwp->lip, sizeof (struct ldi_ident *),
 181                     (uintptr_t)(liwp->hash + liwp->index))) == -1) {
 182                         mdb_warn("couldn't read ldi ident hash at %p",
 183                             (uintptr_t)liwp->hash + liwp->index);
 184                         return (WALK_ERR);
 185                 }
 186 
 187                 wsp->walk_addr = (uintptr_t)liwp->lip;
 188         }
 189 
 190         /* invoke the walker callback for this hash element */
 191         status = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata);
 192         if (status != WALK_NEXT)
 193                 return (status);
 194 
 195         /* get a pointer to the next hash element */
 196         if (mdb_vread(&liwp->buf, sizeof (struct ldi_ident),
 197             wsp->walk_addr) == -1) {
 198                 mdb_warn("couldn't read ldi ident at %p", wsp->walk_addr);
 199                 return (WALK_ERR);
 200         }
 201         wsp->walk_addr = (uintptr_t)liwp->buf.li_next;
 202         return (WALK_NEXT);
 203 }
 204 
 205 /*
 206  * LDI dcmds
 207  */
 208 static void
 209 ldi_ident_header(int start, int refs)
 210 {
 211         if (start) {
 212                 mdb_printf("%-?s ", "IDENT");
 213         } else {
 214                 mdb_printf("%?s ", "IDENT");
 215         }
 216 
 217         if (refs)
 218                 mdb_printf("%4s ", "REFS");
 219 
 220         mdb_printf("%?s %5s %5s %s\n", "DIP", "MINOR", "MODID", "MODULE NAME");
 221 }
 222 
 223 static int
 224 ldi_ident_print(uintptr_t addr, int refs)
 225 {
 226         struct ldi_ident        li;
 227 
 228         /* read the ldi ident */
 229         if (mdb_vread(&li, sizeof (struct ldi_ident), addr) == -1) {
 230                 mdb_warn("couldn't read ldi ident at %p", addr);
 231                 return (1);
 232         }
 233 
 234         /* display the ident address */
 235         mdb_printf("%0?p ", addr);
 236 
 237         /* display the ref count */
 238         if (refs)
 239                 mdb_printf("%4u ", li.li_ref);
 240 
 241         /* display the dip (if any) */
 242         if (li.li_dip != NULL) {
 243                 mdb_printf("%0?p ", li.li_dip);
 244         } else {
 245                 mdb_printf("%?s ", "-");
 246         }
 247 
 248         /* display the minor node (if any) */
 249         if (li.li_dev != DDI_DEV_T_NONE) {
 250                 mdb_printf("%5u ", getminor(li.li_dev));
 251         } else {
 252                 mdb_printf("%5s ", "-");
 253         }
 254 
 255         /* display the module info */
 256         mdb_printf("%5d %s\n", li.li_modid, li.li_modname);
 257 
 258         return (0);
 259 }
 260 
 261 int
 262 ldi_ident(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 263 {
 264         int     start = 1;
 265         int     refs = 1;
 266 
 267         /* Determine if there is an ldi identifier address */
 268         if (!(flags & DCMD_ADDRSPEC)) {
 269                 if (mdb_walk_dcmd("ldi_ident", "ldi_ident",
 270                     argc, argv) == -1) {
 271                         mdb_warn("can't walk ldi idents");
 272                         return (DCMD_ERR);
 273                 }
 274                 return (DCMD_OK);
 275         }
 276 
 277         /* display the header line */
 278         if (DCMD_HDRSPEC(flags))
 279                 ldi_ident_header(start, refs);
 280 
 281         /* display the ldi ident */
 282         if (ldi_ident_print(addr, refs))
 283                 return (DCMD_ERR);
 284 
 285         return (DCMD_OK);
 286 }
 287 
 288 static void
 289 ldi_handle_header(int refs, int ident) {
 290         mdb_printf("%-?s ", "HANDLE");
 291 
 292         if (refs)
 293                 mdb_printf("%4s ", "REFS");
 294 
 295         mdb_printf("%?s %10s %5s %?s ", "VNODE", "DRV", "MINOR", "EVENTS");
 296 
 297         if (!ident) {
 298                 mdb_printf("%?s\n", "IDENT");
 299         } else {
 300                 ldi_ident_header(0, 0);
 301         }
 302 }
 303 
 304 static int
 305 ldi_handle_print(uintptr_t addr, int ident, int refs)
 306 {
 307         vnode_t                 vnode;
 308         struct ldi_handle       lh;
 309         const char              *name;
 310 
 311         /* read in the ldi handle */
 312         if (mdb_vread(&lh, sizeof (struct ldi_handle), addr) == -1) {
 313                 mdb_warn("couldn't read ldi handle at %p", addr);
 314                 return (DCMD_ERR);
 315         }
 316 
 317         /* display the handle address */
 318         mdb_printf("%0?p ", addr);
 319 
 320         /* display the ref count */
 321         if (refs)
 322                 mdb_printf("%4u ", lh.lh_ref);
 323 
 324         /* display the vnode */
 325         mdb_printf("%0?p ", lh.lh_vp);
 326 
 327         /* read in the vnode associated with the handle */
 328         addr = (uintptr_t)lh.lh_vp;
 329         if (mdb_vread(&vnode, sizeof (vnode_t), addr) == -1) {
 330                 mdb_warn("couldn't read vnode at %p", addr);
 331                 return (1);
 332         }
 333 
 334         /* display the driver name */
 335         if ((name = mdb_major_to_name(getmajor(vnode.v_rdev))) == NULL) {
 336                 mdb_warn("failed to convert major number to name\n");
 337                 return (1);
 338         }
 339         mdb_printf("%10s ", name);
 340 
 341         /* display the minor number */
 342         mdb_printf("%5d ", getminor(vnode.v_rdev));
 343 
 344         /* display the event pointer (if any) */
 345         if (lh.lh_events != NULL) {
 346                 mdb_printf("%0?p ", lh.lh_events);
 347         } else {
 348                 mdb_printf("%?s ", "-");
 349         }
 350 
 351         if (!ident) {
 352                 /* display the ident address */
 353                 mdb_printf("%0?p\n", lh.lh_ident);
 354                 return (0);
 355         }
 356 
 357         /* display the entire ident  */
 358         return (ldi_ident_print((uintptr_t)lh.lh_ident, refs));
 359 }
 360 
 361 int
 362 ldi_handle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 363 {
 364         int                     ident = 0;
 365         int                     refs = 1;
 366 
 367         /* Determine if there is an ldi handle address */
 368         if (!(flags & DCMD_ADDRSPEC)) {
 369                 if (mdb_walk_dcmd("ldi_handle", "ldi_handle",
 370                     argc, argv) == -1) {
 371                         mdb_warn("can't walk ldi handles");
 372                         return (DCMD_ERR);
 373                 } return (DCMD_OK);
 374         }
 375 
 376         if (mdb_getopts(argc, argv,
 377             'i', MDB_OPT_SETBITS, TRUE, &ident) != argc)
 378                 return (DCMD_USAGE);
 379 
 380         if (ident)
 381                 refs = 0;
 382 
 383         /* display the header line */
 384         if (DCMD_HDRSPEC(flags))
 385                 ldi_handle_header(refs, ident);
 386 
 387         /* display the ldi handle */
 388         if (ldi_handle_print(addr, ident, refs))
 389                 return (DCMD_ERR);
 390 
 391         return (DCMD_OK);
 392 }
 393 
 394 void
 395 ldi_ident_help(void)
 396 {
 397         mdb_printf("Displays an ldi identifier.\n"
 398             "Without the address of an \"ldi_ident_t\", "
 399             "print all identifiers.\n"
 400             "With an address, print the specified identifier.\n");
 401 }
 402 
 403 void
 404 ldi_handle_help(void)
 405 {
 406         mdb_printf("Displays an ldi handle.\n"
 407             "Without the address of an \"ldi_handle_t\", "
 408             "print all handles.\n"
 409             "With an address, print the specified handle.\n\n"
 410             "Switches:\n"
 411             "  -i  print the module identifier information\n");
 412 }