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