xref: /illumos-gate/usr/src/cmd/mdb/common/modules/libtopo/libtopo.c (revision b0fe7b8fa79924061f3bdf7f240ea116c2c0b704)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/mdb_modapi.h>
29 #include <libelf.h>
30 #include <sys/fm/protocol.h>
31 #include <topo_mod.h>
32 #include <topo_tree.h>
33 #include <topo_module.h>
34 
35 
36 /*
37  * We use this to keep track of which bucket we're in while walking
38  * the modhash and we also cache the length of the hash
39  */
40 static topo_modhash_t tmh;
41 static uint_t hash_idx;
42 
43 static uintptr_t curr_pg;
44 static uint_t is_root;
45 static uint_t verbose;
46 static char *pgrp;
47 static char *tgt_scheme;
48 static char parent[255];
49 
50 /*
51  * This structure is used by the topo_nodehash walker instances to
52  * keep track of where they're at in the node hash
53  */
54 typedef struct tnwalk_state {
55 	uint_t hash_idx;
56 	topo_nodehash_t hash;
57 	topo_nodehash_t *curr_hash;
58 } tnwalk_state_t;
59 
60 
61 static char *stab_lvls[] = {"Internal", "", "Private", "Obsolete", "External",
62 	"Unstable", "Evolving", "Stable", "Standard", "Max"};
63 
64 /*ARGSUSED*/
65 static int
66 topo_handle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
67 {
68 	char uuid[36], root[36], plat[36], isa[36], machine[36], product[36];
69 	topo_hdl_t th;
70 
71 	/*
72 	 * Read in the structure and then read in all of the string fields from
73 	 * the target's addr space
74 	 */
75 	if (mdb_vread(&th, sizeof (th), addr) != sizeof (th)) {
76 		mdb_warn("failed to read topo_hdl_t at %p", addr);
77 		return (DCMD_ERR);
78 	}
79 
80 	if (mdb_readstr(uuid, sizeof (uuid), (uintptr_t)th.th_uuid) < 0) {
81 		(void) mdb_snprintf(uuid, sizeof (uuid), "<%p>", th.th_uuid);
82 	}
83 	if (mdb_readstr(root, sizeof (root), (uintptr_t)th.th_rootdir) < 0) {
84 		(void) mdb_snprintf(root, sizeof (root), "<%p>", th.th_rootdir);
85 	}
86 	if (mdb_readstr(plat, sizeof (plat), (uintptr_t)th.th_platform) < 0) {
87 		(void) mdb_snprintf(plat, sizeof (plat), "<%p>",
88 		    th.th_platform);
89 	}
90 	if (mdb_readstr(isa, sizeof (isa), (uintptr_t)th.th_isa) < 0) {
91 		(void) mdb_snprintf(isa, sizeof (isa), "<%p>", th.th_isa);
92 	}
93 	if (mdb_readstr(machine, sizeof (machine), (uintptr_t)th.th_machine)
94 	    < 0) {
95 
96 		(void) mdb_snprintf(machine, sizeof (machine), "<%p>",
97 		    th.th_machine);
98 	}
99 	if (mdb_readstr(product, sizeof (product), (uintptr_t)th.th_product)
100 	    < 0) {
101 
102 		(void) mdb_snprintf(product, sizeof (product), "<%p>",
103 		    th.th_product);
104 	}
105 
106 	/*
107 	 * Dump it all out in a nice pretty format and keep it to 80 chars wide
108 	 */
109 	if (DCMD_HDRSPEC(flags)) {
110 		mdb_printf("%<u>%-12s %-36s %-30s%</u>\n", "FIELD", "VALUE",
111 		    "DESCR");
112 	}
113 	mdb_printf("%-12s 0x%-34p %-30s\n", "th_lock", th.th_lock,
114 	    "Mutex lock protecting handle");
115 	mdb_printf("%-12s %-36s %-30s\n", "th_uuid", uuid,
116 	    "UUID of the topology snapshot");
117 	mdb_printf("%-12s %-36s %-30s\n", "th_rootdir", root,
118 	    "Root directory of plugin paths");
119 	mdb_printf("%-12s %-36s %-30s\n", "th_platform", plat, "Platform name");
120 	mdb_printf("%-12s %-36s %-30s\n", "th_isa", isa, "ISA name");
121 	mdb_printf("%-12s %-36s %-30s\n", "th_machine", machine,
122 	    "Machine name");
123 	mdb_printf("%-12s %-36s %-30s\n", "th_product", product,
124 	    "Product name");
125 	mdb_printf("%-12s 0x%-34p %-30s\n", "th_di", th.th_di,
126 	    "Handle to the root of the devinfo tree");
127 	mdb_printf("%-12s 0x%-34p %-30s\n", "th_pi", th.th_pi,
128 	    "Handle to the root of the PROM tree");
129 	mdb_printf("%-12s 0x%-34p %-30s\n", "th_modhash", th.th_modhash,
130 	    "Module hash");
131 	mdb_printf("%-12s %-36s %-30s\n", "th_trees", "",
132 	    "Scheme-specific topo tree list");
133 	mdb_printf("  %-12s 0x%-34p %-30s\n", "l_prev", th.th_trees.l_prev,
134 		"");
135 	mdb_printf("  %-12s 0x%-34p %-30s\n", "l_next", th.th_trees.l_next,
136 		"");
137 	mdb_printf("%-12s 0x%-34p %-30s\n", "th_alloc", th.th_alloc,
138 	    "Allocators");
139 	mdb_printf("%-12s %-36d %-30s\n", "tm_ernno", th.th_errno, "errno");
140 	mdb_printf("%-12s %-36d %-30s\n", "tm_debug", th.th_debug,
141 	    "Debug mask");
142 	mdb_printf("%-12s %-36d %-30s\n", "tm_dbout", th.th_dbout,
143 	    "Debug channel");
144 
145 	return (DCMD_OK);
146 }
147 
148 
149 /*ARGSUSED*/
150 static int
151 topo_module(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
152 {
153 	char name[36], path[36], root[36];
154 	topo_mod_t tm;
155 
156 	/*
157 	 * Read in the structure and then read in all of the string fields from
158 	 * the target's addr space
159 	 */
160 	if (mdb_vread(&tm, sizeof (tm), addr) != sizeof (tm)) {
161 		mdb_warn("failed to read topo_mod_t at %p", addr);
162 		return (DCMD_ERR);
163 	}
164 
165 	if (mdb_readstr(name, sizeof (name), (uintptr_t)tm.tm_name) < 0) {
166 		(void) mdb_snprintf(name, sizeof (name), "<%p>", tm.tm_name);
167 	}
168 	if (mdb_readstr(path, sizeof (path), (uintptr_t)tm.tm_path) < 0) {
169 		(void) mdb_snprintf(path, sizeof (path), "<%p>", tm.tm_path);
170 	}
171 	if (mdb_readstr(root, sizeof (root), (uintptr_t)tm.tm_rootdir) < 0) {
172 		(void) mdb_snprintf(root, sizeof (root), "<%p>", tm.tm_rootdir);
173 	}
174 
175 	/*
176 	 * Dump it all out in a nice pretty format and keep it to 80 chars wide
177 	 */
178 	if (DCMD_HDRSPEC(flags)) {
179 		mdb_printf("%<u>%-12s %-36s %-30s%</u>\n",
180 		"FIELD", "VALUE", "DESCR");
181 	}
182 	mdb_printf("%-12s 0x%-34p %-30s\n", "tm_lock", tm.tm_lock,
183 		"Lock for tm_cv/owner/flags/refs");
184 	mdb_printf("%-12s 0x%-34p %-30s\n", "tm_cv", tm.tm_cv,
185 		"Module condition variable");
186 	mdb_printf("%-12s %-36s %-30s\n", "tm_busy", tm.tm_busy,
187 		"Busy indicator");
188 	mdb_printf("%-12s 0x%-34p %-30s\n", "tm_next", tm.tm_next,
189 		"Next module in hash chain");
190 	mdb_printf("%-12s 0x%-34p %-30s\n", "tm_hdl", tm.tm_hdl,
191 		"Topo handle for this module");
192 	mdb_printf("%-12s 0x%-34p %-30s\n", "tm_alloc", tm.tm_alloc,
193 		"Allocators");
194 	mdb_printf("%-12s %-36s %-30s\n", "tm_name", name,
195 		"Basename of module");
196 	mdb_printf("%-12s %-36s %-30s\n", "tm_path", path,
197 		"Full pathname of module");
198 	mdb_printf("%-12s %-36s %-30s\n", "tm_rootdir", root,
199 		"Relative root directory of module");
200 	mdb_printf("%-12s %-36u %-30s\n", "tm_refs", tm.tm_refs,
201 		"Module reference count");
202 	mdb_printf("%-12s %-36u %-30s\n", "tm_flags", tm.tm_flags,
203 		"Module flags");
204 	if (TOPO_MOD_INIT & tm.tm_flags) {
205 		mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_MOD_INIT",
206 			"Module init completed");
207 	}
208 	if (TOPO_MOD_FINI & tm.tm_flags) {
209 		mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_MOD_FINI",
210 			"Module fini completed");
211 	}
212 	if (TOPO_MOD_REG & tm.tm_flags) {
213 		mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_MOD_REG",
214 			"Module registered");
215 	}
216 	if (TOPO_MOD_UNREG & tm.tm_flags) {
217 		mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_MOD_UNREG",
218 			"Module unregistered");
219 	}
220 
221 	mdb_printf("%-12s %-36u %-30s\n", "tm_debug", tm.tm_debug,
222 		"Debug printf mask");
223 	mdb_printf("%-12s 0x%-34p %-30s\n", "tm_data", tm.tm_data,
224 		"Private rtld/builtin data");
225 	mdb_printf("%-12s 0x%-34p %-30s\n", "tm_mops", tm.tm_mops,
226 		"Module class ops vector");
227 	mdb_printf("%-12s 0x%-34p %-30s\n", "tm_info", tm.tm_info,
228 		"Module info registered with handle");
229 	mdb_printf("%-12s %-36d %-30s\n", "tm_ernno", tm.tm_errno,
230 		"Module errno");
231 
232 	return (DCMD_OK);
233 }
234 
235 
236 /*ARGSUSED*/
237 static int
238 topo_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
239 {
240 	char name[36];
241 	tnode_t tn;
242 
243 	if (!addr)
244 		return (DCMD_ERR);
245 
246 	/*
247 	 * Read in the structure and then read in all of the string fields from
248 	 * the target's addr space
249 	 */
250 	if (mdb_vread(&tn, sizeof (tn), addr) != sizeof (tn)) {
251 		mdb_warn("failed to read tnode_t at %p", addr);
252 		return (DCMD_ERR);
253 	}
254 
255 	if (mdb_readstr(name, sizeof (name), (uintptr_t)tn.tn_name) < 0) {
256 		(void) mdb_snprintf(name, sizeof (name), "<%p>", tn.tn_name);
257 	}
258 
259 	/*
260 	 * Dump it all out in a nice pretty format and keep it to 80 chars wide
261 	 */
262 	if (DCMD_HDRSPEC(flags)) {
263 		mdb_printf("%<u>%-12s %-36s %-30s%</u>\n",
264 		"FIELD", "VALUE", "DESCR");
265 	}
266 
267 	mdb_printf("%-12s 0x%-34p %-30s\n", "tn_lock", tn.tn_lock,
268 		"Mutex lock protecting node members");
269 	mdb_printf("%-12s %-36s %-30s\n", "tn_name", name,
270 		"Node name");
271 	mdb_printf("%-12s %-36d %-30s\n", "tn_instance", tn.tn_instance,
272 		"Node instance");
273 	mdb_printf("%-12s %-36d %-30s\n", "tn_state", tn.tn_state,
274 		"Node state");
275 	if (TOPO_NODE_INIT & tn.tn_state) {
276 		mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_NODE_INIT", "");
277 	}
278 	if (TOPO_NODE_ROOT & tn.tn_state) {
279 		mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_NODE_ROOT", "");
280 	}
281 	if (TOPO_NODE_BOUND & tn.tn_state) {
282 		mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_NODE_BOUND", "");
283 	}
284 	if (TOPO_NODE_LINKED & tn.tn_state) {
285 		mdb_printf("%-12s %-36s %-30s\n", "", "TOPO_NODE_LINKED", "");
286 	}
287 	mdb_printf("%-12s %-36d %-30s\n", "tn_fflags", tn.tn_fflags,
288 		"FMRI flags");
289 	mdb_printf("%-12s 0x%-34p %-30s\n", "tn_parent", tn.tn_parent,
290 		"Node parent");
291 	mdb_printf("%-12s 0x%-34p %-30s\n", "tn_phash", tn.tn_phash,
292 		"Parent hash bucket");
293 	mdb_printf("%-12s 0x%-34p %-30s\n", "tn_hdl", tn.tn_hdl,
294 		"Topo handle");
295 	mdb_printf("%-12s 0x%-34p %-30s\n", "tn_enum", tn.tn_enum,
296 		"Enumerator module");
297 	mdb_printf("%-12s %-36s %-30s\n", "tn_children", "",
298 		"Hash table of child nodes");
299 	mdb_printf("  %-12s 0x%-34p %-30s\n", "l_prev", tn.tn_children.l_prev,
300 		"");
301 	mdb_printf("  %-12s 0x%-34p %-30s\n", "l_next", tn.tn_children.l_next,
302 		"");
303 	mdb_printf("%-12s 0x%-34p %-30s\n", "tn_pgroups", &(tn.tn_pgroups),
304 		"Property group list");
305 	mdb_printf("%-12s 0x%-34p %-30s\n", "tn_methods", &(tn.tn_methods),
306 		"Registered method list");
307 	mdb_printf("%-12s 0x%-34p %-30s\n", "tn_priv", tn.tn_priv,
308 		"Private enumerator data");
309 	mdb_printf("%-12s %-36d %-30s\n", "tn_refs", tn.tn_refs,
310 		"Node reference count");
311 
312 	return (DCMD_OK);
313 }
314 
315 /*ARGSUSED*/
316 static int
317 find_tree_root(uintptr_t addr, const void *data, void *arg)
318 {
319 	ttree_t *tree = (ttree_t *)data;
320 	char scheme[36];
321 
322 	if (mdb_readstr(scheme, sizeof (scheme), (uintptr_t)tree->tt_scheme)
323 	    < 0) {
324 		(void) mdb_snprintf(scheme, sizeof (scheme), "<%p>",
325 		    tree->tt_scheme);
326 	}
327 
328 	if (strncmp(tgt_scheme, scheme, 36) == 0) {
329 		*((tnode_t **)arg) = tree->tt_root;
330 		return (WALK_DONE);
331 	}
332 	return (WALK_NEXT);
333 }
334 
335 
336 /*
337  * Dump the given property value. For the actual property values
338  * we dump a pointer to the nvlist which can be decoded using the ::nvlist
339  * dcmd from the libnvpair MDB module
340  */
341 /*ARGSUSED*/
342 static int
343 dump_propval(uintptr_t addr, const void *data, void *arg)
344 {
345 	topo_proplist_t *plistp = (topo_proplist_t *)data;
346 	topo_propval_t pval;
347 	char name[32], *type;
348 
349 	if (mdb_vread(&pval, sizeof (pval), (uintptr_t)plistp->tp_pval)
350 	    != sizeof (pval)) {
351 
352 		mdb_warn("failed to read topo_propval_t at %p",
353 		    plistp->tp_pval);
354 		return (WALK_ERR);
355 	}
356 	if (mdb_readstr(name, sizeof (name), (uintptr_t)pval.tp_name) < 0) {
357 		(void) mdb_snprintf(name, sizeof (name), "<%p>", pval.tp_name);
358 	}
359 	switch (pval.tp_type) {
360 		case TOPO_TYPE_BOOLEAN: type = "boolean"; break;
361 		case TOPO_TYPE_INT32: type = "int32"; break;
362 		case TOPO_TYPE_UINT32: type = "uint32"; break;
363 		case TOPO_TYPE_INT64: type = "int64"; break;
364 		case TOPO_TYPE_UINT64: type = "uint64"; break;
365 		case TOPO_TYPE_STRING: type = "string"; break;
366 		case TOPO_TYPE_FMRI: type = "fmri"; break;
367 		case TOPO_TYPE_INT32_ARRAY: type = "int32[]"; break;
368 		case TOPO_TYPE_UINT32_ARRAY: type = "uint32[]"; break;
369 		case TOPO_TYPE_INT64_ARRAY: type = "int64[]"; break;
370 		case TOPO_TYPE_UINT64_ARRAY: type = "uint64[]"; break;
371 		case TOPO_TYPE_STRING_ARRAY: type = "string[]"; break;
372 		case TOPO_TYPE_FMRI_ARRAY: type = "fmri[]"; break;
373 		default: type = "unknown type";
374 	}
375 	mdb_printf("    %-32s %-16s value: %p\n", name, type, pval.tp_val);
376 	return (WALK_NEXT);
377 }
378 
379 
380 /*
381  * Dumps the contents of the property group.
382  */
383 /*ARGSUSED*/
384 static int
385 dump_pgroup(uintptr_t addr, const void *data, void *arg)
386 {
387 	topo_pgroup_t *pgp = (topo_pgroup_t *)data;
388 	topo_ipgroup_info_t ipg;
389 	char buf[32];
390 
391 	if (mdb_vread(&ipg, sizeof (ipg), (uintptr_t)pgp->tpg_info)
392 	    != sizeof (ipg)) {
393 
394 		mdb_warn("failed to read topo_ipgroup_info_t at %p",
395 		    pgp->tpg_info);
396 		return (WALK_ERR);
397 	}
398 	if (mdb_readstr(buf, sizeof (buf), (uintptr_t)ipg.tpi_name) < 0) {
399 		mdb_warn("failed to read string at %p", ipg.tpi_name);
400 		return (WALK_ERR);
401 	}
402 	/*
403 	 * If this property group is the one we're interested in or if the user
404 	 * specified the "all" property group, we'll dump it
405 	 */
406 	if ((strncmp(pgrp, buf, sizeof (buf)) == 0) ||
407 	    (strncmp(pgrp, "all", sizeof (buf)) == 0)) {
408 
409 		mdb_printf("  group: %-32s version: %d, stability: %s/%s\n",
410 		    buf, ipg.tpi_version, stab_lvls[ipg.tpi_namestab],
411 		    stab_lvls[ipg.tpi_datastab]);
412 
413 		(void) mdb_pwalk("topo_proplist", dump_propval, NULL, curr_pg);
414 	}
415 	return (WALK_NEXT);
416 }
417 
418 
419 /*
420  * Recursive function to dump the specified node and all of it's children
421  */
422 /*ARGSUSED*/
423 static int
424 dump_tnode(uintptr_t addr, const void *data, void *arg)
425 {
426 	tnode_t node;
427 	char pname[255], buf[80], old_pname[255];
428 
429 	if (!addr) {
430 		return (WALK_NEXT);
431 	}
432 
433 	if (mdb_vread(&node, sizeof (node), addr) != sizeof (node)) {
434 		mdb_warn("failed to read tnode_t at %p", addr);
435 		return (WALK_ERR);
436 	}
437 	if (mdb_readstr(buf, sizeof (buf), (uintptr_t)node.tn_name) < 0) {
438 		(void) mdb_snprintf(buf, sizeof (buf), "<%p>",
439 		    node.tn_name);
440 	}
441 
442 	if (is_root) {
443 		mdb_snprintf(pname, sizeof (pname), "%s", parent);
444 		is_root = 0;
445 	} else {
446 		mdb_snprintf(pname, sizeof (pname), "%s/%s=%u",
447 		    parent, buf, node.tn_instance);
448 
449 		if (verbose)
450 			mdb_printf("%s\n  tnode_t: %p\n", pname, addr);
451 		else
452 			mdb_printf("%s\n", pname);
453 	}
454 	mdb_snprintf(old_pname, sizeof (old_pname), "%s", parent);
455 	mdb_snprintf(parent, sizeof (parent), "%s", pname);
456 
457 	if (pgrp)
458 		(void) mdb_pwalk("topo_pgroup", dump_pgroup, NULL, addr);
459 
460 	(void) mdb_pwalk("topo_nodehash", dump_tnode, NULL, addr);
461 	mdb_snprintf(parent, sizeof (parent), "%s", old_pname);
462 
463 	return (WALK_NEXT);
464 }
465 
466 
467 /*
468  * Given a topo_hdl_t *, the topo dcmd dumps the topo tree.  The format of the
469  * output is modeled after fmtopo.  Like fmtopo, by default, we'll dump the
470  * "hc" scheme tree.  The user can optionally specify a different tree via the
471  * "-s <scheme>" option.
472  *
473  * Specifying the "-v" option provides more verbose output.  Currently it
474  * outputs the tnode_t * addr for each node, which is useful if you want to
475  * dump it with the topo_node dcmd.
476  *
477  * The functionality of the "-P" option is similar to fmtopo.
478  */
479 /*ARGSUSED*/
480 static int
481 fmtopo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
482 {
483 	char product[36], *opt_s = NULL, *opt_P = NULL;
484 	topo_hdl_t th;
485 	tnode_t *tree_root;
486 	uint_t opt_v = FALSE;
487 	char *def_scheme = "hc";
488 
489 	if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &opt_v,
490 	    's', MDB_OPT_STR, &opt_s, 'P', MDB_OPT_STR, &opt_P, NULL)
491 	    != argc) {
492 		return (DCMD_USAGE);
493 	}
494 
495 	if (opt_s) {
496 		tgt_scheme = opt_s;
497 	} else {
498 		tgt_scheme = def_scheme;
499 	}
500 
501 	pgrp = opt_P;
502 	verbose = opt_v;
503 	is_root = 1;
504 
505 	/*
506 	 * Read in the topo_handle and some of its string fields from
507 	 * the target's addr space
508 	 */
509 	if (mdb_vread(&th, sizeof (th), addr) != sizeof (th)) {
510 		mdb_warn("failed to read topo_hdl_t at %p", addr);
511 		return (DCMD_ERR);
512 	}
513 
514 	if (mdb_readstr(product, sizeof (product), (uintptr_t)th.th_product)
515 	    < 0) {
516 
517 		(void) mdb_snprintf(product, sizeof (product), "<%p>",
518 		    th.th_product);
519 	}
520 
521 	mdb_snprintf(parent, sizeof (parent),
522 	    "%s://:product-id=%s", tgt_scheme, product);
523 
524 	/*
525 	 * Walk the list of topo trees, looking for the one that is for the
526 	 * scheme we're interested in.
527 	 */
528 	tree_root = NULL;
529 	mdb_pwalk("topo_tree", find_tree_root, &tree_root, addr);
530 
531 	if (! tree_root) {
532 		mdb_warn("failed to find a topo tree for scheme %s\n",
533 		    tgt_scheme);
534 		return (DCMD_ERR);
535 	}
536 
537 	return (dump_tnode((uintptr_t)tree_root, NULL, NULL));
538 }
539 
540 
541 static int
542 ttree_walk_init(mdb_walk_state_t *wsp)
543 {
544 	topo_hdl_t th;
545 
546 	if (wsp->walk_addr == NULL) {
547 		mdb_warn("NULL topo_hdl_t passed in");
548 		return (WALK_ERR);
549 	}
550 
551 	if (mdb_vread(&th, sizeof (th), wsp->walk_addr) != sizeof (th)) {
552 		mdb_warn("failed to read topo_hdl_t at %p", wsp->walk_addr);
553 		return (WALK_ERR);
554 	}
555 
556 	wsp->walk_addr = (uintptr_t)th.th_trees.l_next;
557 	wsp->walk_data = mdb_alloc(sizeof (ttree_t), UM_SLEEP);
558 
559 	return (WALK_NEXT);
560 }
561 
562 
563 static int
564 ttree_walk_step(mdb_walk_state_t *wsp)
565 {
566 	int rv;
567 	ttree_t *tree;
568 
569 	if (wsp->walk_addr == NULL)
570 		return (WALK_DONE);
571 
572 	if (mdb_vread(wsp->walk_data, sizeof (ttree_t), wsp->walk_addr)
573 	    != sizeof (ttree_t)) {
574 
575 		mdb_warn("failed to read ttree_t at %p", wsp->walk_addr);
576 		return (WALK_ERR);
577 	}
578 
579 	rv = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
580 	    wsp->walk_cbdata);
581 
582 	tree = (ttree_t *)wsp->walk_data;
583 	wsp->walk_addr = (uintptr_t)tree->tt_list.l_next;
584 
585 	return (rv);
586 }
587 
588 
589 static void
590 ttree_walk_fini(mdb_walk_state_t *wsp)
591 {
592 	mdb_free(wsp->walk_data, sizeof (ttree_t));
593 }
594 
595 
596 static int
597 tmod_walk_init(mdb_walk_state_t *wsp)
598 {
599 	topo_hdl_t th;
600 
601 	if (wsp->walk_addr == NULL) {
602 		mdb_warn("NULL topo_hdl_t passed in");
603 		return (WALK_ERR);
604 	}
605 
606 	if (mdb_vread(&th, sizeof (th), wsp->walk_addr) != sizeof (th)) {
607 		mdb_warn("failed to read topo_hdl_t at %p", wsp->walk_addr);
608 		return (WALK_ERR);
609 	}
610 
611 	if (mdb_vread(&tmh, sizeof (topo_modhash_t), (uintptr_t)th.th_modhash)
612 	    == -1) {
613 
614 		mdb_warn("failed to read topo_modhash_t at %p", wsp->walk_addr);
615 		return (WALK_DONE);
616 	}
617 
618 	hash_idx = 0;
619 
620 	if (mdb_vread(&(wsp->walk_addr), sizeof (uintptr_t *),
621 	    (uintptr_t)(tmh.mh_hash)) != sizeof (tnode_t *)) {
622 
623 		mdb_warn("failed to read %u bytes at %p", sizeof (tnode_t *),
624 		    tmh.mh_hash);
625 		return (WALK_ERR);
626 	}
627 
628 	wsp->walk_data = mdb_alloc(sizeof (topo_mod_t), UM_SLEEP);
629 
630 	return (WALK_NEXT);
631 }
632 
633 
634 static int
635 tmod_walk_step(mdb_walk_state_t *wsp)
636 {
637 	int rv;
638 	topo_mod_t *tm;
639 
640 	if (wsp->walk_addr == NULL)
641 		return (WALK_DONE);
642 
643 	if (mdb_vread(wsp->walk_data, sizeof (topo_mod_t), wsp->walk_addr)
644 	    == -1) {
645 
646 		mdb_warn("failed to read topo_mod_t at %p", wsp->walk_addr);
647 		return (WALK_DONE);
648 	}
649 
650 	rv = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
651 	    wsp->walk_cbdata);
652 
653 	tm = (topo_mod_t *)wsp->walk_data;
654 
655 	if (tm->tm_next)
656 		wsp->walk_addr = (uintptr_t)tm->tm_next;
657 	else if (++hash_idx < tmh.mh_hashlen)
658 		if (mdb_vread(&(wsp->walk_addr), sizeof (uintptr_t *),
659 		    (uintptr_t)(tmh.mh_hash+hash_idx)) != sizeof (tnode_t *)) {
660 
661 			mdb_warn("failed to read %u bytes at %p",
662 			    sizeof (tnode_t *), tmh.mh_hash+hash_idx);
663 			return (DCMD_ERR);
664 		}
665 	else
666 		wsp->walk_addr = NULL;
667 
668 	return (rv);
669 }
670 
671 static void
672 tmod_walk_fini(mdb_walk_state_t *wsp)
673 {
674 	mdb_free(wsp->walk_data, sizeof (topo_mod_t));
675 }
676 
677 
678 static int
679 tpg_walk_init(mdb_walk_state_t *wsp)
680 {
681 	tnode_t node;
682 
683 	if (wsp->walk_addr == NULL) {
684 		mdb_warn("NULL tnode_t passed in");
685 		return (WALK_ERR);
686 	}
687 
688 	if (mdb_vread(&node, sizeof (node), wsp->walk_addr) != sizeof (node)) {
689 		mdb_warn("failed to read tnode_t at %p", wsp->walk_addr);
690 		return (WALK_ERR);
691 	}
692 
693 	wsp->walk_addr = (uintptr_t)node.tn_pgroups.l_next;
694 	wsp->walk_data = mdb_alloc(sizeof (topo_pgroup_t), UM_SLEEP);
695 
696 	return (WALK_NEXT);
697 }
698 
699 
700 static int
701 tpg_walk_step(mdb_walk_state_t *wsp)
702 {
703 	int rv;
704 	topo_pgroup_t *tpgp;
705 
706 	if (wsp->walk_addr == NULL)
707 		return (WALK_DONE);
708 
709 	if (mdb_vread(wsp->walk_data, sizeof (topo_pgroup_t), wsp->walk_addr)
710 	    == -1) {
711 
712 		mdb_warn("failed to read topo_pgroup_t at %p", wsp->walk_addr);
713 		return (WALK_DONE);
714 	}
715 
716 	curr_pg = wsp->walk_addr;
717 	rv = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
718 	    wsp->walk_cbdata);
719 
720 	tpgp = (topo_pgroup_t *)wsp->walk_data;
721 	wsp->walk_addr = (uintptr_t)tpgp->tpg_list.l_next;
722 
723 	return (rv);
724 }
725 
726 
727 static void
728 tpg_walk_fini(mdb_walk_state_t *wsp)
729 {
730 	mdb_free(wsp->walk_data, sizeof (topo_pgroup_t));
731 }
732 
733 
734 static int
735 tpl_walk_init(mdb_walk_state_t *wsp)
736 {
737 	topo_pgroup_t pg;
738 
739 	if (wsp->walk_addr == NULL) {
740 		mdb_warn("NULL topo_pgroup_t passed in");
741 		return (WALK_ERR);
742 	}
743 
744 	if (mdb_vread(&pg, sizeof (pg), wsp->walk_addr) != sizeof (pg)) {
745 		mdb_warn("failed to read topo_pgroup_t at %p", wsp->walk_addr);
746 		return (WALK_ERR);
747 	}
748 
749 	wsp->walk_addr = (uintptr_t)pg.tpg_pvals.l_next;
750 	wsp->walk_data = mdb_alloc(sizeof (topo_proplist_t), UM_SLEEP);
751 
752 	return (WALK_NEXT);
753 }
754 
755 
756 static int
757 tpl_walk_step(mdb_walk_state_t *wsp)
758 {
759 	int rv;
760 	topo_proplist_t *plp;
761 
762 	if (wsp->walk_addr == NULL)
763 		return (WALK_DONE);
764 
765 	if (mdb_vread(wsp->walk_data, sizeof (topo_proplist_t), wsp->walk_addr)
766 	    == -1) {
767 
768 		mdb_warn("failed to read topo_proplist_t at %p",
769 		    wsp->walk_addr);
770 		return (WALK_DONE);
771 	}
772 	plp = (topo_proplist_t *)wsp->walk_data;
773 
774 	rv = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
775 	    wsp->walk_cbdata);
776 
777 	wsp->walk_addr = (uintptr_t)plp->tp_list.l_next;
778 
779 	return (rv);
780 }
781 
782 
783 static void
784 tpl_walk_fini(mdb_walk_state_t *wsp)
785 {
786 	mdb_free(wsp->walk_data, sizeof (topo_proplist_t));
787 }
788 
789 
790 static int
791 tnh_walk_init(mdb_walk_state_t *wsp)
792 {
793 	tnode_t node;
794 	tnwalk_state_t *state;
795 
796 	if (wsp->walk_addr == NULL) {
797 		mdb_warn("NULL tnode_t passed in");
798 		return (WALK_ERR);
799 	}
800 
801 	if (mdb_vread(&node, sizeof (node), wsp->walk_addr) != sizeof (node)) {
802 		mdb_warn("failed to read tnode_t at %p", wsp->walk_addr);
803 		return (WALK_ERR);
804 	}
805 
806 	state = mdb_zalloc(sizeof (tnwalk_state_t), UM_SLEEP);
807 
808 	state->curr_hash = (topo_nodehash_t *)node.tn_children.l_next;
809 	state->hash_idx = 0;
810 	wsp->walk_data = state;
811 
812 	return (WALK_NEXT);
813 }
814 
815 
816 static int
817 tnh_walk_step(mdb_walk_state_t *wsp)
818 {
819 	tnwalk_state_t *state = wsp->walk_data;
820 	int rv, i = state->hash_idx++;
821 	tnode_t *npp;
822 
823 	if (state->curr_hash == NULL)
824 		return (WALK_DONE);
825 
826 	if (mdb_vread(&(state->hash), sizeof (topo_nodehash_t),
827 	    (uintptr_t)state->curr_hash) != sizeof (topo_nodehash_t)) {
828 
829 		mdb_warn("failed to read topo_nodehash_t at %p",
830 		    (uintptr_t)state->curr_hash);
831 		return (WALK_ERR);
832 	}
833 
834 	if (mdb_vread(&npp, sizeof (tnode_t *),
835 	    (uintptr_t)(state->hash.th_nodearr+i)) != sizeof (tnode_t *)) {
836 
837 		mdb_warn("failed to read %u bytes at %p", sizeof (tnode_t *),
838 		    state->hash.th_nodearr+i);
839 		return (WALK_ERR);
840 	}
841 	wsp->walk_addr = (uintptr_t)npp;
842 
843 	rv = wsp->walk_callback(wsp->walk_addr, state, wsp->walk_cbdata);
844 
845 	if (state->hash_idx >= state->hash.th_arrlen) {
846 		/*
847 		 * move on to the next child hash bucket
848 		 */
849 		state->curr_hash =
850 		    (topo_nodehash_t *)(state->hash.th_list.l_next);
851 		state->hash_idx = 0;
852 	}
853 
854 	return (rv);
855 }
856 
857 
858 static void
859 tnh_walk_fini(mdb_walk_state_t *wsp)
860 {
861 	mdb_free(wsp->walk_data, sizeof (tnwalk_state_t));
862 }
863 
864 
865 static const mdb_dcmd_t dcmds[] = {
866 	{ "topo_handle", "", "print contents of a topo handle", topo_handle,
867 		NULL },
868 	{ "topo_module", "", "print contents of a topo module handle",
869 		topo_module, NULL },
870 	{ "topo_node", "", "print contents of a topo node", topo_node, NULL },
871 	{ "fmtopo", "[-P <pgroup>][-s <scheme>][-v]",
872 	    "print topology of the given handle", fmtopo, NULL },
873 	{ NULL }
874 };
875 
876 static const mdb_walker_t walkers[] = {
877 	{ "topo_tree", "walk the tree list for a given topo handle",
878 		ttree_walk_init, ttree_walk_step, ttree_walk_fini, NULL },
879 	{ "topo_module", "walk the module hash for a given topo handle",
880 		tmod_walk_init, tmod_walk_step, tmod_walk_fini, NULL },
881 	{ "topo_pgroup", "walk the property groups for a given topo node",
882 		tpg_walk_init, tpg_walk_step, tpg_walk_fini, NULL },
883 	{ "topo_proplist", "walk the property list for a given property group",
884 		tpl_walk_init, tpl_walk_step, tpl_walk_fini, NULL },
885 	{ "topo_nodehash", "walk the child nodehash for a given topo node",
886 		tnh_walk_init, tnh_walk_step, tnh_walk_fini, NULL },
887 	{ NULL }
888 };
889 
890 static const mdb_modinfo_t modinfo = {
891 	MDB_API_VERSION, dcmds, walkers
892 };
893 
894 const mdb_modinfo_t *
895 _mdb_init(void)
896 {
897 	return (&modinfo);
898 }
899