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