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