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