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 2019 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
ldi_handle_walk_init(mdb_walk_state_t * wsp)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
ldi_handle_walk_step(mdb_walk_state_t * wsp)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
ldi_ident_walk_init(mdb_walk_state_t * wsp)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
ldi_ident_walk_step(mdb_walk_state_t * wsp)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
ldi_ident_header(int start,int refs)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
ldi_ident_print(uintptr_t addr,int refs)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
ldi_ident(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
ldi_handle_header(int refs,int ident)293 ldi_handle_header(int refs, int ident)
294 {
295 	mdb_printf("%-?s ", "HANDLE");
296 
297 	if (refs)
298 		mdb_printf("%4s ", "REFS");
299 
300 	mdb_printf("%?s %10s %5s %?s ", "VNODE", "DRV", "MINOR", "EVENTS");
301 
302 	if (!ident) {
303 		mdb_printf("%?s\n", "IDENT");
304 	} else {
305 		ldi_ident_header(0, 0);
306 	}
307 }
308 
309 static int
ldi_handle_print(uintptr_t addr,int ident,int refs)310 ldi_handle_print(uintptr_t addr, int ident, int refs)
311 {
312 	vnode_t			vnode;
313 	struct ldi_handle	lh;
314 	const char		*name;
315 
316 	/* read in the ldi handle */
317 	if (mdb_vread(&lh, sizeof (struct ldi_handle), addr) == -1) {
318 		mdb_warn("couldn't read ldi handle at %p", addr);
319 		return (DCMD_ERR);
320 	}
321 
322 	/* display the handle address */
323 	mdb_printf("%0?p ", addr);
324 
325 	/* display the ref count */
326 	if (refs)
327 		mdb_printf("%4u ", lh.lh_ref);
328 
329 	/* display the vnode */
330 	mdb_printf("%0?p ", lh.lh_vp);
331 
332 	/* read in the vnode associated with the handle */
333 	addr = (uintptr_t)lh.lh_vp;
334 	if (mdb_vread(&vnode, sizeof (vnode_t), addr) == -1) {
335 		mdb_warn("couldn't read vnode at %p", addr);
336 		return (1);
337 	}
338 
339 	/* display the driver name */
340 	if ((name = mdb_major_to_name(getmajor(vnode.v_rdev))) == NULL) {
341 		mdb_warn("failed to convert major number to name\n");
342 		return (1);
343 	}
344 	mdb_printf("%10s ", name);
345 
346 	/* display the minor number */
347 	mdb_printf("%5d ", getminor(vnode.v_rdev));
348 
349 	/* display the event pointer (if any) */
350 	if (lh.lh_events != NULL) {
351 		mdb_printf("%0?p ", lh.lh_events);
352 	} else {
353 		mdb_printf("%?s ", "-");
354 	}
355 
356 	if (!ident) {
357 		/* display the ident address */
358 		mdb_printf("%0?p\n", lh.lh_ident);
359 		return (0);
360 	}
361 
362 	/* display the entire ident  */
363 	return (ldi_ident_print((uintptr_t)lh.lh_ident, refs));
364 }
365 
366 int
ldi_handle(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)367 ldi_handle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
368 {
369 	int			ident = 0;
370 	int			refs = 1;
371 
372 	if (mdb_getopts(argc, argv,
373 	    'i', MDB_OPT_SETBITS, TRUE, &ident, NULL) != argc)
374 		return (DCMD_USAGE);
375 
376 	if (ident)
377 		refs = 0;
378 
379 	/* Determine if there is an ldi handle address */
380 	if (!(flags & DCMD_ADDRSPEC)) {
381 		if (mdb_walk_dcmd("ldi_handle", "ldi_handle",
382 		    argc, argv) == -1) {
383 			mdb_warn("can't walk ldi handles");
384 			return (DCMD_ERR);
385 		}
386 		return (DCMD_OK);
387 	}
388 
389 	/* display the header line */
390 	if (DCMD_HDRSPEC(flags))
391 		ldi_handle_header(refs, ident);
392 
393 	/* display the ldi handle */
394 	if (ldi_handle_print(addr, ident, refs))
395 		return (DCMD_ERR);
396 
397 	return (DCMD_OK);
398 }
399 
400 void
ldi_ident_help(void)401 ldi_ident_help(void)
402 {
403 	mdb_printf("Displays an ldi identifier.\n"
404 	    "Without the address of an \"ldi_ident_t\", "
405 	    "print all identifiers.\n"
406 	    "With an address, print the specified identifier.\n");
407 }
408 
409 void
ldi_handle_help(void)410 ldi_handle_help(void)
411 {
412 	mdb_printf("Displays an ldi handle.\n"
413 	    "Without the address of an \"ldi_handle_t\", "
414 	    "print all handles.\n"
415 	    "With an address, print the specified handle.\n\n"
416 	    "Switches:\n"
417 	    "  -i  print the module identifier information\n");
418 }
419