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