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