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