xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/ldi.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 }
415