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