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 }