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 2019 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 { 295 mdb_printf("%-?s ", "HANDLE"); 296 297 if (refs) 298 mdb_printf("%4s ", "REFS"); 299 300 mdb_printf("%?s %10s %5s %?s ", "VNODE", "DRV", "MINOR", "EVENTS"); 301 302 if (!ident) { 303 mdb_printf("%?s\n", "IDENT"); 304 } else { 305 ldi_ident_header(0, 0); 306 } 307 } 308 309 static int 310 ldi_handle_print(uintptr_t addr, int ident, int refs) 311 { 312 vnode_t vnode; 313 struct ldi_handle lh; 314 const char *name; 315 316 /* read in the ldi handle */ 317 if (mdb_vread(&lh, sizeof (struct ldi_handle), addr) == -1) { 318 mdb_warn("couldn't read ldi handle at %p", addr); 319 return (DCMD_ERR); 320 } 321 322 /* display the handle address */ 323 mdb_printf("%0?p ", addr); 324 325 /* display the ref count */ 326 if (refs) 327 mdb_printf("%4u ", lh.lh_ref); 328 329 /* display the vnode */ 330 mdb_printf("%0?p ", lh.lh_vp); 331 332 /* read in the vnode associated with the handle */ 333 addr = (uintptr_t)lh.lh_vp; 334 if (mdb_vread(&vnode, sizeof (vnode_t), addr) == -1) { 335 mdb_warn("couldn't read vnode at %p", addr); 336 return (1); 337 } 338 339 /* display the driver name */ 340 if ((name = mdb_major_to_name(getmajor(vnode.v_rdev))) == NULL) { 341 mdb_warn("failed to convert major number to name\n"); 342 return (1); 343 } 344 mdb_printf("%10s ", name); 345 346 /* display the minor number */ 347 mdb_printf("%5d ", getminor(vnode.v_rdev)); 348 349 /* display the event pointer (if any) */ 350 if (lh.lh_events != NULL) { 351 mdb_printf("%0?p ", lh.lh_events); 352 } else { 353 mdb_printf("%?s ", "-"); 354 } 355 356 if (!ident) { 357 /* display the ident address */ 358 mdb_printf("%0?p\n", lh.lh_ident); 359 return (0); 360 } 361 362 /* display the entire ident */ 363 return (ldi_ident_print((uintptr_t)lh.lh_ident, refs)); 364 } 365 366 int 367 ldi_handle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 368 { 369 int ident = 0; 370 int refs = 1; 371 372 if (mdb_getopts(argc, argv, 373 'i', MDB_OPT_SETBITS, TRUE, &ident, NULL) != argc) 374 return (DCMD_USAGE); 375 376 if (ident) 377 refs = 0; 378 379 /* Determine if there is an ldi handle address */ 380 if (!(flags & DCMD_ADDRSPEC)) { 381 if (mdb_walk_dcmd("ldi_handle", "ldi_handle", 382 argc, argv) == -1) { 383 mdb_warn("can't walk ldi handles"); 384 return (DCMD_ERR); 385 } 386 return (DCMD_OK); 387 } 388 389 /* display the header line */ 390 if (DCMD_HDRSPEC(flags)) 391 ldi_handle_header(refs, ident); 392 393 /* display the ldi handle */ 394 if (ldi_handle_print(addr, ident, refs)) 395 return (DCMD_ERR); 396 397 return (DCMD_OK); 398 } 399 400 void 401 ldi_ident_help(void) 402 { 403 mdb_printf("Displays an ldi identifier.\n" 404 "Without the address of an \"ldi_ident_t\", " 405 "print all identifiers.\n" 406 "With an address, print the specified identifier.\n"); 407 } 408 409 void 410 ldi_handle_help(void) 411 { 412 mdb_printf("Displays an ldi handle.\n" 413 "Without the address of an \"ldi_handle_t\", " 414 "print all handles.\n" 415 "With an address, print the specified handle.\n\n" 416 "Switches:\n" 417 " -i print the module identifier information\n"); 418 } 419