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 + 1) & (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 }