xref: /illumos-gate/usr/src/cmd/mdb/intel/modules/generic_cpu/gcpu.c (revision dfe73b3d6f9191b75fe71a92c8854f83c6d16a63)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <mdb/mdb_modapi.h>
28 #include <generic_cpu/gcpu.h>
29 #include <sys/cpu_module_impl.h>
30 #include <sys/cpu_module_ms_impl.h>
31 
32 typedef struct cmi_hdl_impl {
33 	enum cmi_hdl_class cmih_class;		/* Handle nature */
34 	struct cmi_hdl_ops *cmih_ops;		/* Operations vector */
35 	uint_t cmih_chipid;			/* Chipid of cpu resource */
36 	uint_t cmih_coreid;			/* Core within die */
37 	uint_t cmih_strandid;			/* Thread within core */
38 	boolean_t cmih_mstrand;			/* cores are multithreaded */
39 	volatile uint32_t *cmih_refcntp;	/* Reference count pointer */
40 	uint64_t cmih_msrsrc;			/* MSR data source flags */
41 	void *cmih_hdlpriv;			/* cmi_hw.c private data */
42 	void *cmih_spec;			/* cmi_hdl_{set,get}_specific */
43 	void *cmih_cmi;				/* cpu mod control structure */
44 	void *cmih_cmidata;			/* cpu mod private data */
45 	const struct cmi_mc_ops *cmih_mcops;	/* Memory-controller ops */
46 	void *cmih_mcdata;			/* Memory-controller data */
47 	uint64_t cmih_flags;
48 	uint16_t cmih_smbiosid;			/* SMBIOS Type 4 struct ID */
49 	uint_t cmih_smb_chipid;			/* smbios chipid */
50 	nvlist_t *cmih_smb_bboard;		/* smbios bboard */
51 } cmi_hdl_impl_t;
52 
53 typedef struct cmi_hdl_ent {
54 	volatile uint32_t cmae_refcnt;
55 	cmi_hdl_impl_t *cmae_hdlp;
56 } cmi_hdl_ent_t;
57 
58 typedef struct cmi {
59 	struct cmi *cmi_next;
60 	struct cmi *cmi_prev;
61 	const cmi_ops_t *cmi_ops;
62 	struct modctl *cmi_modp;
63 	uint_t cmi_refcnt;
64 } cmi_t;
65 
66 typedef struct cms {
67 	struct cms *cms_next;
68 	struct cms *cms_prev;
69 	const cms_ops_t *cms_ops;
70 	struct modctl *cms_modp;
71 	uint_t cms_refcnt;
72 } cms_t;
73 
74 struct cms_ctl {
75 	cms_t *cs_cms;
76 	void *cs_cmsdata;
77 };
78 
79 #define	CMI_MAX_CHIPID_NBITS		6	/* max chipid of 63 */
80 #define	CMI_MAX_CORES_PER_CHIP_NBITS	4	/* 16 cores per chip max */
81 #define	CMI_MAX_STRANDS_PER_CORE_NBITS	3	/* 8 strands per core max */
82 
83 #define	CMI_MAX_CHIPID			((1 << (CMI_MAX_CHIPID_NBITS)) - 1)
84 #define	CMI_MAX_CORES_PER_CHIP		(1 << CMI_MAX_CORES_PER_CHIP_NBITS)
85 #define	CMI_MAX_STRANDS_PER_CORE	(1 << CMI_MAX_STRANDS_PER_CORE_NBITS)
86 #define	CMI_MAX_STRANDS_PER_CHIP	(CMI_MAX_CORES_PER_CHIP * \
87 					    CMI_MAX_STRANDS_PER_CORE)
88 
89 #define	CMI_HDL_ARR_IDX_CORE(coreid) \
90 	(((coreid) & (CMI_MAX_CORES_PER_CHIP - 1)) << \
91 	CMI_MAX_STRANDS_PER_CORE_NBITS)
92 
93 #define	CMI_HDL_ARR_IDX_STRAND(strandid) \
94 	(((strandid) & (CMI_MAX_STRANDS_PER_CORE - 1)))
95 
96 #define	CMI_HDL_ARR_IDX(coreid, strandid) \
97 	(CMI_HDL_ARR_IDX_CORE(coreid) | CMI_HDL_ARR_IDX_STRAND(strandid))
98 
99 #define	CMI_CHIPID_ARR_SZ		(1 << CMI_MAX_CHIPID_NBITS)
100 
101 struct cmih_walk_state {
102 	int chipid, coreid, strandid;	/* currently visited cpu */
103 	cmi_hdl_ent_t *chip_tab[CMI_CHIPID_ARR_SZ];
104 };
105 
106 /*
107  * Advance the <chipid,coreid,strandid> tuple to the next strand entry
108  * Return true upon sucessful result. Otherwise return false if already reach
109  * the highest strand.
110  */
111 static boolean_t
112 cmih_ent_next(struct cmih_walk_state *wsp)
113 {
114 	uint_t carry = 0;
115 
116 	/* Check for end of the table */
117 	if (wsp->chipid == CMI_MAX_CHIPID &&
118 	    wsp->coreid == (CMI_MAX_CORES_PER_CHIP - 1) &&
119 	    wsp->strandid == (CMI_MAX_STRANDS_PER_CORE - 1))
120 		return (B_FALSE);
121 
122 	/* increment the strand id */
123 	wsp->strandid++;
124 	carry =  wsp->strandid >> CMI_MAX_STRANDS_PER_CORE_NBITS;
125 	wsp->strandid =  wsp->strandid & (CMI_MAX_STRANDS_PER_CORE - 1);
126 	if (carry == 0)
127 		return (B_TRUE);
128 
129 	/* increment the core id */
130 	wsp->coreid++;
131 	carry = wsp->coreid >> CMI_MAX_CORES_PER_CHIP_NBITS;
132 	wsp->coreid = wsp->coreid & (CMI_MAX_CORES_PER_CHIP - 1);
133 	if (carry == 0)
134 		return (B_TRUE);
135 
136 	/* increment the chip id */
137 	wsp->chipid = ++wsp->chipid & (CMI_MAX_CHIPID);
138 
139 	return (B_TRUE);
140 }
141 
142 /*
143  * Lookup for the hdl entry of a given <chip,core,strand>
144  */
145 static cmi_hdl_ent_t *
146 cmih_ent_lookup(struct cmih_walk_state *wsp)
147 {
148 	if (wsp == NULL || wsp->chip_tab[wsp->chipid] == NULL)
149 		return (NULL);	/* chip is not present */
150 
151 	return (wsp->chip_tab[wsp->chipid] +
152 	    CMI_HDL_ARR_IDX(wsp->coreid, wsp->strandid));
153 }
154 
155 /* forward decls */
156 static void
157 cmih_walk_fini(mdb_walk_state_t *wsp);
158 
159 static int
160 cmih_walk_init(mdb_walk_state_t *wsp)
161 {
162 	int i;
163 	ssize_t sz;
164 	struct cmih_walk_state *awsp;
165 	void *pg;
166 	cmi_hdl_ent_t *ent;
167 
168 	if (wsp->walk_addr != NULL) {
169 		mdb_warn("cmihdl is a global walker\n");
170 		return (WALK_ERR);
171 	}
172 
173 	wsp->walk_data = awsp =
174 	    mdb_zalloc(sizeof (struct cmih_walk_state), UM_SLEEP);
175 
176 	/* table of chipid entries */
177 	if ((sz = mdb_readvar(&awsp->chip_tab, "cmi_chip_tab")) == -1) {
178 		mdb_warn("read of cmi_chip_tab failed");
179 		mdb_free(wsp->walk_data, sizeof (struct cmih_walk_state));
180 		wsp->walk_data = NULL;
181 		return (WALK_ERR);
182 	} else if (sz < sizeof (awsp->chip_tab)) {
183 		mdb_warn("Unexpected cmi_chip_tab size (exp=%ld, actual=%ld)",
184 		    sizeof (awsp->chip_tab), sz);
185 		mdb_free(wsp->walk_data, sizeof (struct cmih_walk_state));
186 		wsp->walk_data = NULL;
187 		return (WALK_ERR);
188 	}
189 
190 	/* read the per-chip table that contains all strands of the chip */
191 	sz = CMI_MAX_STRANDS_PER_CHIP * sizeof (cmi_hdl_ent_t);
192 	for (i = 0; i < CMI_CHIPID_ARR_SZ; i++) {
193 		if (awsp->chip_tab[i] == NULL)
194 			continue; /* this chip(i) is not present */
195 		pg = mdb_alloc(sz, UM_SLEEP);
196 		if (mdb_vread(pg, sz, (uintptr_t)awsp->chip_tab[i]) != sz) {
197 			mdb_warn("read of cmi_hdl(%i) array at 0x%p failed",
198 			    i, awsp->chip_tab[i]);
199 			mdb_free(pg, sz);
200 			cmih_walk_fini(wsp);
201 			return (WALK_ERR);
202 		}
203 		awsp->chip_tab[i] = pg;
204 	}
205 
206 	/* Look up the hdl of the first strand <0,0,0> */
207 	wsp->walk_addr = NULL;
208 	if ((ent = cmih_ent_lookup(awsp)) != NULL)
209 		wsp->walk_addr = (uintptr_t)ent->cmae_hdlp;
210 
211 	return (WALK_NEXT);
212 }
213 
214 static int
215 cmih_walk_step(mdb_walk_state_t *wsp)
216 {
217 	struct cmih_walk_state *awsp = wsp->walk_data;
218 	uintptr_t addr = NULL;
219 	cmi_hdl_impl_t hdl;
220 	cmi_hdl_ent_t *ent;
221 	int rv;
222 
223 	if ((ent = cmih_ent_lookup(awsp)) != NULL)
224 		addr = (uintptr_t)ent->cmae_hdlp;
225 	if (wsp->walk_addr == NULL || addr == NULL)
226 		return (cmih_ent_next(awsp) ? WALK_NEXT : WALK_DONE);
227 
228 	if (mdb_vread(&hdl, sizeof (hdl), addr) != sizeof (hdl)) {
229 		mdb_warn("read of handle at 0x%p failed", addr);
230 		return (WALK_DONE);
231 	}
232 
233 	if ((rv = wsp->walk_callback(addr, (void *)&hdl,
234 	    wsp->walk_cbdata)) != WALK_NEXT)
235 		return (rv);
236 
237 	return (cmih_ent_next(awsp) ? WALK_NEXT : WALK_DONE);
238 }
239 
240 static void
241 cmih_walk_fini(mdb_walk_state_t *wsp)
242 {
243 	struct cmih_walk_state *awsp = wsp->walk_data;
244 
245 	if (awsp != NULL) {
246 		int i;
247 		for (i = 0; i < CMI_CHIPID_ARR_SZ; i++) {
248 			/* free the per-chip table */
249 			if (awsp->chip_tab[i] != NULL) {
250 				mdb_free((void *)awsp->chip_tab[i],
251 				    CMI_MAX_STRANDS_PER_CHIP *
252 				    sizeof (cmi_hdl_ent_t));
253 				awsp->chip_tab[i] = NULL;
254 			}
255 		}
256 		mdb_free(wsp->walk_data, sizeof (struct cmih_walk_state));
257 		wsp->walk_data = NULL;
258 	}
259 }
260 
261 struct cmihdl_cb {
262 	int mod_cpuid;
263 	int mod_chipid;
264 	int mod_coreid;
265 	int mod_strandid;
266 	uintptr_t mod_hdladdr;
267 };
268 
269 static int
270 cmihdl_cb(uintptr_t addr, const void *arg, void *data)
271 {
272 	cmi_hdl_impl_t *hdl = (cmi_hdl_impl_t *)arg;
273 	struct cmihdl_cb *cbp = data;
274 	cpu_t *cp;
275 	int rv;
276 
277 	if (cbp->mod_cpuid != -1) {
278 		cp = mdb_alloc(sizeof (cpu_t), UM_SLEEP);
279 		if (mdb_vread(cp, sizeof (cpu_t),
280 		    (uintptr_t)hdl->cmih_hdlpriv) != sizeof (cpu_t)) {
281 			mdb_warn("Read of cpu_t at 0x%p failed",
282 			    hdl->cmih_hdlpriv);
283 			mdb_free(cp, sizeof (cpu_t));
284 			return (WALK_ERR);
285 		}
286 
287 		if (cp->cpu_id == cbp->mod_cpuid) {
288 			cbp->mod_hdladdr = addr;
289 			rv = WALK_DONE;
290 		} else {
291 			rv = WALK_NEXT;
292 		}
293 
294 		mdb_free(cp, sizeof (cpu_t));
295 		return (rv);
296 	} else {
297 		if (hdl->cmih_chipid == cbp->mod_chipid &&
298 		    hdl->cmih_coreid == cbp->mod_coreid &&
299 		    hdl->cmih_strandid == cbp->mod_strandid) {
300 			cbp->mod_hdladdr = addr;
301 			return (WALK_DONE);
302 		} else {
303 			return (WALK_NEXT);
304 		}
305 	}
306 }
307 
308 static int
309 cmihdl_disp(uintptr_t addr, cmi_hdl_impl_t *hdl)
310 {
311 	struct cms_ctl cmsctl;			/* 16 bytes max */
312 	struct modctl cmimodc, cmsmodc;		/* 288 bytes max */
313 	cmi_t cmi;				/* 40 bytes max */
314 	cms_t cms;				/* 40 bytes max */
315 	cpu_t *cp;
316 	char cmimodnm[25], cmsmodnm[25];	/* 50 bytes */
317 	char cpuidstr[4], hwidstr[16];
318 	int native = hdl->cmih_class == CMI_HDL_NATIVE;
319 	uint32_t refcnt;
320 
321 	cmimodnm[0] = cmsmodnm[0] = '-';
322 	cmimodnm[1] = cmsmodnm[1] = '\0';
323 
324 	if (hdl->cmih_cmi != NULL) {
325 		if (mdb_vread(&cmi, sizeof (cmi_t),
326 		    (uintptr_t)hdl->cmih_cmi) != sizeof (cmi)) {
327 			mdb_warn("Read of cmi_t at 0x%p failed",
328 			    hdl->cmih_cmi);
329 			return (0);
330 		}
331 
332 		if (cmi.cmi_modp != NULL) {
333 			if (mdb_vread(&cmimodc, sizeof (struct modctl),
334 			    (uintptr_t)cmi.cmi_modp) != sizeof (cmimodc)) {
335 				mdb_warn("Read of modctl at 0x%p failed",
336 				    cmi.cmi_modp);
337 				return (0);
338 			}
339 
340 			if (mdb_readstr(cmimodnm, sizeof (cmimodnm),
341 			    (uintptr_t)cmimodc.mod_modname) == -1) {
342 				mdb_warn("Read of cmi module name at 0x%p "
343 				    "failed", cmimodc.mod_modname);
344 				return (0);
345 			}
346 		}
347 	}
348 
349 	if (hdl->cmih_spec != NULL) {
350 		if (mdb_vread(&cmsctl, sizeof (struct cms_ctl),
351 		    (uintptr_t)hdl->cmih_spec) != sizeof (cmsctl)) {
352 			mdb_warn("Read of struct cms_ctl at 0x%p failed",
353 			    hdl->cmih_spec);
354 			return (0);
355 		}
356 
357 		if (mdb_vread(&cms, sizeof (cms_t),
358 		    (uintptr_t)cmsctl.cs_cms) != sizeof (cms)) {
359 			mdb_warn("Read of cms_t at 0x%p failed", cmsctl.cs_cms);
360 			return (0);
361 		}
362 
363 		if (cms.cms_modp != NULL) {
364 			if (mdb_vread(&cmsmodc, sizeof (struct modctl),
365 			    (uintptr_t)cms.cms_modp) != sizeof (cmsmodc)) {
366 				mdb_warn("Read of modctl at 0x%p failed",
367 				    cms.cms_modp);
368 				return (0);
369 			}
370 
371 			if (mdb_readstr(cmsmodnm, sizeof (cmsmodnm),
372 			    (uintptr_t)cmsmodc.mod_modname) == -1) {
373 				mdb_warn("Read of cms module name at 0x%p "
374 				    "failed", cmsmodc.mod_modname);
375 				return (0);
376 			}
377 		}
378 	}
379 
380 	if (mdb_vread(&refcnt, sizeof (uint32_t),
381 	    (uintptr_t)hdl->cmih_refcntp) != sizeof (uint32_t)) {
382 		mdb_warn("Read of reference count for hdl 0x%p failed", hdl);
383 		return (0);
384 	}
385 
386 	if (native) {
387 		cp = mdb_alloc(sizeof (cpu_t), UM_SLEEP);
388 
389 		if (mdb_vread(cp, sizeof (cpu_t),
390 		    (uintptr_t)hdl->cmih_hdlpriv) != sizeof (cpu_t)) {
391 			mdb_free(cp, sizeof (cpu_t));
392 			mdb_warn("Read of cpu_t at 0x%p failed",
393 			    hdl->cmih_hdlpriv);
394 			return (0);
395 		}
396 	}
397 
398 	if (native) {
399 		(void) mdb_snprintf(cpuidstr, sizeof (cpuidstr), "%d",
400 		    cp->cpu_id);
401 	} else {
402 		(void) mdb_snprintf(cpuidstr, sizeof (cpuidstr), "-");
403 	}
404 
405 	(void) mdb_snprintf(hwidstr, sizeof (hwidstr), "%d/%d/%d",
406 	    hdl->cmih_chipid, hdl->cmih_coreid, hdl->cmih_strandid);
407 
408 	mdb_printf("%16lx %3d %3s %8s %3s %2s %-13s %-24s\n", addr,
409 	    refcnt, cpuidstr, hwidstr, hdl->cmih_mstrand ? "M" : "S",
410 	    hdl->cmih_mcops ? "Y" : "N", cmimodnm, cmsmodnm);
411 
412 	if (native)
413 		mdb_free(cp, sizeof (cpu_t));
414 
415 	return (1);
416 }
417 
418 #define	HDRFMT "%-16s %3s %3s %8s %3s %2s %-13s %-24s\n"
419 
420 static int
421 cmihdl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
422 {
423 	struct cmihdl_cb cb;
424 	cmi_hdl_impl_t *hdl;
425 
426 	/*
427 	 * If an address is given it must be that of a cmi handle.
428 	 * Otherwise if the user has specified -c <cpuid> or
429 	 * -c <chipid/coreid/strandid> we will lookup a matching handle.
430 	 * Otherwise we'll walk and callback to this dcmd.
431 	 */
432 	if (!(flags & DCMD_ADDRSPEC)) {
433 		char *p, *buf;
434 		int len;
435 
436 		if (argc == 0)
437 			return (mdb_walk_dcmd("cmihdl", "cmihdl", argc,
438 			    argv) == 0 ? DCMD_OK : DCMD_ERR);
439 
440 
441 		if (mdb_getopts(argc, argv,
442 		    'c', MDB_OPT_STR, &p,
443 		    NULL) != argc)
444 			return (DCMD_USAGE);
445 
446 		if ((len = strlen(p)) == 0) {
447 			return (DCMD_USAGE);
448 		} else {
449 			buf = mdb_alloc(len + 1, UM_SLEEP);
450 			strcpy(buf, p);
451 		}
452 
453 		cb.mod_cpuid = cb.mod_chipid = cb.mod_coreid =
454 		    cb.mod_strandid = -1;
455 
456 		if ((p = strchr(buf, '/')) == NULL) {
457 			/* Native cpuid */
458 			cb.mod_cpuid = (int)mdb_strtoull(buf);
459 		} else {
460 			/* Comma-separated triplet chip,core,strand. */
461 			char *q = buf;
462 
463 			*p = '\0';
464 			cb.mod_chipid = (int)mdb_strtoull(q);
465 
466 			if ((q = p + 1) >= buf + len ||
467 			    (p = strchr(q, '/')) == NULL) {
468 				mdb_free(buf, len);
469 				return (DCMD_USAGE);
470 			}
471 
472 			*p = '\0';
473 			cb.mod_coreid = (int)mdb_strtoull(q);
474 
475 			if ((q = p + 1) >= buf + len) {
476 				mdb_free(buf, len);
477 				return (DCMD_USAGE);
478 			}
479 
480 			cb.mod_strandid = (int)mdb_strtoull(q);
481 		}
482 
483 		mdb_free(buf, len);
484 
485 		cb.mod_hdladdr = NULL;
486 		if (mdb_walk("cmihdl", cmihdl_cb, &cb) == -1) {
487 			mdb_warn("cmi_hdl walk failed\n");
488 			return (DCMD_ERR);
489 		}
490 
491 		if (cb.mod_hdladdr == NULL) {
492 			if (cb.mod_cpuid != -1) {
493 				mdb_warn("No handle found for cpuid %d\n",
494 				    cb.mod_cpuid);
495 			} else {
496 
497 				mdb_warn("No handle found for chip %d "
498 				    "core %d strand %d\n", cb.mod_chipid,
499 				    cb.mod_coreid, cb.mod_strandid);
500 			}
501 			return (DCMD_ERR);
502 		}
503 
504 		addr = cb.mod_hdladdr;
505 	}
506 
507 	if (DCMD_HDRSPEC(flags)) {
508 		char ul[] = "----------------------------";
509 		char *p = ul + sizeof (ul) - 1;
510 
511 		mdb_printf(HDRFMT HDRFMT,
512 		    "HANDLE", "REF", "CPU", "CH/CR/ST", "CMT", "MC",
513 		    "MODULE", "MODEL-SPECIFIC",
514 		    p - 16,  p - 3, p - 3, p - 8, p - 3, p - 2, p - 13, p - 24);
515 	}
516 
517 	hdl = mdb_alloc(sizeof (cmi_hdl_impl_t), UM_SLEEP);
518 
519 	if (mdb_vread(hdl, sizeof (cmi_hdl_impl_t), addr) !=
520 	    sizeof (cmi_hdl_impl_t)) {
521 		mdb_free(hdl, sizeof (cmi_hdl_impl_t));
522 		mdb_warn("Read of cmi handle at 0x%p failed", addr);
523 		return (DCMD_ERR);
524 	}
525 
526 	if (!cmihdl_disp(addr, hdl)) {
527 		mdb_free(hdl, sizeof (cmi_hdl_impl_t));
528 		return (DCMD_ERR);
529 	}
530 
531 	mdb_free(hdl, sizeof (cmi_hdl_impl_t));
532 
533 	return (DCMD_OK);
534 }
535 
536 /*ARGSUSED*/
537 static int
538 gcpu_mpt_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
539 {
540 	static const char *const whatstrs[] = {
541 		"ntv-cyc-poll",		/* GCPU_MPT_WHAT_CYC_ERR */
542 		"poll-poked",		/* GCPU_MPT_WHAT_POKE_ERR */
543 		"unfaulting",		/* GCPU_MPT_WHAT_UNFAULTING */
544 		"#MC",			/* GCPU_MPT_WHAT_MC_ERR */
545 		"CMCI-int",		/* GCPU_MPT_WHAT_CMCI_ERR */
546 		"xpv-virq-nrec",	/* GCPU_MPT_WHAT_XPV_VIRQ */
547 		"xpv-virq-lgout",	/* GCPU_MPT_WHAT_XPV_VIRQ_LOGOUT */
548 	};
549 
550 	gcpu_poll_trace_t mpt;
551 	const char *what;
552 
553 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
554 		return (DCMD_USAGE);
555 
556 	if (mdb_vread(&mpt, sizeof (mpt), addr) != sizeof (mpt)) {
557 		mdb_warn("failed to read gcpu_poll_trace_t at 0x%p", addr);
558 		return (DCMD_ERR);
559 	}
560 
561 	if (DCMD_HDRSPEC(flags)) {
562 		mdb_printf("%<u>%?s%</u> %<u>%?s%</u> %<u>%15s%</u> "
563 		    "%<u>%4s%</u>\n", "ADDR", "WHEN", "WHAT", "NERR");
564 	}
565 
566 	if (mpt.mpt_what < sizeof (whatstrs) / sizeof (char *))
567 		what = whatstrs[mpt.mpt_what];
568 	else
569 		what = "???";
570 
571 	mdb_printf("%?p %?p %15s %4u\n", addr, mpt.mpt_when, what,
572 	    mpt.mpt_nerr);
573 
574 	return (DCMD_OK);
575 }
576 
577 typedef struct mptwalk_data {
578 	uintptr_t mw_traceaddr;
579 	gcpu_poll_trace_t *mw_trace;
580 	size_t mw_tracesz;
581 	uint_t mw_tracenent;
582 	uint_t mw_curtrace;
583 } mptwalk_data_t;
584 
585 static int
586 gcpu_mptwalk_init(mdb_walk_state_t *wsp)
587 {
588 	gcpu_poll_trace_t *mpt;
589 	mptwalk_data_t *mw;
590 	GElf_Sym sym;
591 	uint_t nent, i;
592 	hrtime_t latest;
593 
594 	if (wsp->walk_addr == NULL) {
595 		mdb_warn("the address of a poll trace array must be "
596 		    "specified\n");
597 		return (WALK_ERR);
598 	}
599 
600 	if (mdb_lookup_by_name("gcpu_poll_trace_nent", &sym) < 0 ||
601 	    sym.st_size != sizeof (uint_t) || mdb_vread(&nent, sizeof (uint_t),
602 	    sym.st_value) != sizeof (uint_t)) {
603 		mdb_warn("failed to read gcpu_poll_trace_nent from kernel");
604 		return (WALK_ERR);
605 	}
606 
607 	mw = mdb_alloc(sizeof (mptwalk_data_t), UM_SLEEP);
608 	mw->mw_traceaddr = wsp->walk_addr;
609 	mw->mw_tracenent = nent;
610 	mw->mw_tracesz = nent * sizeof (gcpu_poll_trace_t);
611 	mw->mw_trace = mdb_alloc(mw->mw_tracesz, UM_SLEEP);
612 
613 	if (mdb_vread(mw->mw_trace, mw->mw_tracesz, wsp->walk_addr) !=
614 	    mw->mw_tracesz) {
615 		mdb_free(mw->mw_trace, mw->mw_tracesz);
616 		mdb_free(mw, sizeof (mptwalk_data_t));
617 		mdb_warn("failed to read poll trace array from kernel");
618 		return (WALK_ERR);
619 	}
620 
621 	latest = 0;
622 	mw->mw_curtrace = 0;
623 	for (mpt = mw->mw_trace, i = 0; i < mw->mw_tracenent; i++, mpt++) {
624 		if (mpt->mpt_when > latest) {
625 			latest = mpt->mpt_when;
626 			mw->mw_curtrace = i;
627 		}
628 	}
629 
630 	if (latest == 0) {
631 		mdb_free(mw->mw_trace, mw->mw_tracesz);
632 		mdb_free(mw, sizeof (mptwalk_data_t));
633 		return (WALK_DONE); /* trace array is empty */
634 	}
635 
636 	wsp->walk_data = mw;
637 
638 	return (WALK_NEXT);
639 }
640 
641 static int
642 gcpu_mptwalk_step(mdb_walk_state_t *wsp)
643 {
644 	mptwalk_data_t *mw = wsp->walk_data;
645 	gcpu_poll_trace_t *thismpt, *prevmpt;
646 	int prev, rv;
647 
648 	thismpt = &mw->mw_trace[mw->mw_curtrace];
649 
650 	rv = wsp->walk_callback(mw->mw_traceaddr + (mw->mw_curtrace *
651 	    sizeof (gcpu_poll_trace_t)), thismpt, wsp->walk_cbdata);
652 
653 	if (rv != WALK_NEXT)
654 		return (rv);
655 
656 	prev = (mw->mw_curtrace - 1) % mw->mw_tracenent;
657 	prevmpt = &mw->mw_trace[prev];
658 
659 	if (prevmpt->mpt_when == 0 || prevmpt->mpt_when > thismpt->mpt_when)
660 		return (WALK_DONE);
661 
662 	mw->mw_curtrace = prev;
663 
664 	return (WALK_NEXT);
665 }
666 
667 static void
668 gcpu_mptwalk_fini(mdb_walk_state_t *wsp)
669 {
670 	mptwalk_data_t *mw = wsp->walk_data;
671 
672 	mdb_free(mw->mw_trace, mw->mw_tracesz);
673 	mdb_free(mw, sizeof (mptwalk_data_t));
674 }
675 
676 static const mdb_dcmd_t dcmds[] = {
677 	{ "cmihdl", ": -c <cpuid>|<chip,core,strand> ",
678 	    "dump a cmi_handle_t", cmihdl },
679 	{ "gcpu_poll_trace", ":", "dump a poll trace buffer", gcpu_mpt_dump },
680 	{ NULL }
681 };
682 
683 static const mdb_walker_t walkers[] = {
684 	{ "cmihdl", "walks cpu module interface handle list",
685 	    cmih_walk_init, cmih_walk_step, cmih_walk_fini, NULL },
686 	{ "gcpu_poll_trace", "walks poll trace buffers in reverse "
687 	    "chronological order", gcpu_mptwalk_init, gcpu_mptwalk_step,
688 	    gcpu_mptwalk_fini, NULL },
689 	{ NULL }
690 };
691 
692 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
693 
694 const mdb_modinfo_t *
695 _mdb_init(void)
696 {
697 	return (&modinfo);
698 }
699