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