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