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
ldi_handle_walk_init(mdb_walk_state_t * wsp)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
ldi_handle_walk_step(mdb_walk_state_t * wsp)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
ldi_ident_walk_init(mdb_walk_state_t * wsp)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
ldi_ident_walk_step(mdb_walk_state_t * wsp)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
ldi_ident_header(int start,int refs)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
ldi_ident_print(uintptr_t addr,int refs)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
ldi_ident(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
ldi_handle_header(int refs,int ident)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
ldi_handle_print(uintptr_t addr,int ident,int refs)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
ldi_handle(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
ldi_ident_help(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
ldi_handle_help(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 }
415