xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/ldi.c (revision 09e2ab34f6c69b170fe7478e8b011d6bb505e0d9)
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 == 0) {
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 == 0) {
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 	if (mdb_getopts(argc, argv,
368 	    'i', MDB_OPT_SETBITS, TRUE, &ident) != argc)
369 		return (DCMD_USAGE);
370 
371 	if (ident)
372 		refs = 0;
373 
374 	/* Determine if there is an ldi handle address */
375 	if (!(flags & DCMD_ADDRSPEC)) {
376 		if (mdb_walk_dcmd("ldi_handle", "ldi_handle",
377 		    argc, argv) == -1) {
378 			mdb_warn("can't walk ldi handles");
379 			return (DCMD_ERR);
380 		} return (DCMD_OK);
381 	}
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 }
413