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 */
42static topo_modhash_t tmh;
43static uint_t hash_idx;
44
45static uintptr_t curr_pg;
46static uint_t is_root;
47static uint_t verbose;
48static char *pgrp;
49static char *tgt_scheme;
50static 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 */
56typedef 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
63static char *stab_lvls[] = {"Internal", "", "Private", "Obsolete", "External",
64	"Unstable", "Evolving", "Stable", "Standard", "Max"};
65
66/*ARGSUSED*/
67static int
68topo_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*/
153static int
154topo_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*/
247static int
248topo_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*/
325static int
326find_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
344static void
345dump_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*/
368static int
369dump_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*/
414static int
415dump_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*/
453static int
454dump_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*/
510static int
511fmtopo(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
571static int
572ttree_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
593static int
594ttree_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
619static void
620ttree_walk_fini(mdb_walk_state_t *wsp)
621{
622	mdb_free(wsp->walk_data, sizeof (ttree_t));
623}
624
625
626static int
627tmod_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
664static int
665tmod_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
701static void
702tmod_walk_fini(mdb_walk_state_t *wsp)
703{
704	mdb_free(wsp->walk_data, sizeof (topo_mod_t));
705}
706
707
708static int
709tpg_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
730static int
731tpg_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
757static void
758tpg_walk_fini(mdb_walk_state_t *wsp)
759{
760	mdb_free(wsp->walk_data, sizeof (topo_pgroup_t));
761}
762
763
764static int
765tpl_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
786static int
787tpl_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
813static void
814tpl_walk_fini(mdb_walk_state_t *wsp)
815{
816	mdb_free(wsp->walk_data, sizeof (topo_proplist_t));
817}
818
819
820static int
821tnh_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
846static int
847tnh_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
888static void
889tnh_walk_fini(mdb_walk_state_t *wsp)
890{
891	mdb_free(wsp->walk_data, sizeof (tnwalk_state_t));
892}
893
894static int
895tlist_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
915static int
916tlist_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
939static 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
950static 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
966static const mdb_modinfo_t modinfo = {
967	MDB_API_VERSION, dcmds, walkers
968};
969
970const mdb_modinfo_t *
971_mdb_init(void)
972{
973	return (&modinfo);
974}
975