1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #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 } cmi_hdl_impl_t; 49 50 struct cmi_hdl_arr_ent { 51 volatile uint32_t cmae_refcnt; 52 cmi_hdl_impl_t *cmae_hdlp; 53 }; 54 55 typedef struct cmi { 56 struct cmi *cmi_next; 57 struct cmi *cmi_prev; 58 const cmi_ops_t *cmi_ops; 59 struct modctl *cmi_modp; 60 uint_t cmi_refcnt; 61 } cmi_t; 62 63 typedef struct cms { 64 struct cms *cms_next; 65 struct cms *cms_prev; 66 const cms_ops_t *cms_ops; 67 struct modctl *cms_modp; 68 uint_t cms_refcnt; 69 } cms_t; 70 71 struct cms_ctl { 72 cms_t *cs_cms; 73 void *cs_cmsdata; 74 }; 75 76 #define CMI_MAX_CHIPS 16 77 #define CMI_MAX_CORES_PER_CHIP 8 78 #define CMI_MAX_STRANDS_PER_CORE 2 79 #define CMI_HDL_HASHSZ (CMI_MAX_CHIPS * CMI_MAX_CORES_PER_CHIP * \ 80 CMI_MAX_STRANDS_PER_CORE) 81 82 struct cmih_walk_state { 83 int idx; 84 struct cmi_hdl_arr_ent *arrent; 85 }; 86 87 static int 88 cmih_walk_init(mdb_walk_state_t *wsp) 89 { 90 size_t sz = CMI_HDL_HASHSZ * sizeof (struct cmi_hdl_arr_ent); 91 struct cmih_walk_state *awsp; 92 uintptr_t addr; 93 94 if (wsp->walk_addr != NULL) { 95 mdb_warn("cmihdl is a global walker\n"); 96 return (WALK_ERR); 97 } 98 99 if (mdb_readvar(&addr, "cmi_hdl_arr") == -1) { 100 mdb_warn("read of cmi_hdl_arr failed"); 101 return (WALK_ERR); 102 } else if (addr == NULL) { 103 return (WALK_DONE); 104 } 105 106 wsp->walk_data = awsp = 107 mdb_zalloc(sizeof (struct cmih_walk_state), UM_SLEEP); 108 awsp->arrent = mdb_alloc(sz, UM_SLEEP); 109 110 if (mdb_vread(awsp->arrent, sz, addr) != sz) { 111 mdb_warn("read of cmi_hdl_arr array at 0x%p failed", addr); 112 return (WALK_ERR); 113 } 114 115 wsp->walk_addr = 116 (uintptr_t)((struct cmi_hdl_arr_ent *)wsp->walk_data)->cmae_hdlp; 117 118 return (WALK_NEXT); 119 } 120 121 static int 122 cmih_walk_step(mdb_walk_state_t *wsp) 123 { 124 struct cmih_walk_state *awsp = wsp->walk_data; 125 uintptr_t addr = (uintptr_t)(awsp->arrent)[awsp->idx].cmae_hdlp; 126 cmi_hdl_impl_t hdl; 127 int rv; 128 129 if (wsp->walk_addr == NULL || addr == NULL) 130 return (++awsp->idx < CMI_HDL_HASHSZ ? WALK_NEXT : WALK_DONE); 131 132 if (mdb_vread(&hdl, sizeof (hdl), addr) != sizeof (hdl)) { 133 mdb_warn("read of handle at 0x%p failed", addr); 134 return (WALK_DONE); 135 } 136 137 if ((rv = wsp->walk_callback(addr, (void *)&hdl, 138 wsp->walk_cbdata)) != WALK_NEXT) 139 return (rv); 140 141 return (++awsp->idx < CMI_HDL_HASHSZ ? WALK_NEXT : WALK_DONE); 142 } 143 144 static void 145 cmih_walk_fini(mdb_walk_state_t *wsp) 146 { 147 struct cmih_walk_state *awsp = wsp->walk_data; 148 149 if (awsp != NULL) { 150 if (awsp->arrent != NULL) 151 mdb_free(awsp->arrent, CMI_HDL_HASHSZ * 152 sizeof (struct cmih_walk_state)); 153 mdb_free(wsp->walk_data, sizeof (struct cmih_walk_state)); 154 } 155 } 156 157 struct cmihdl_cb { 158 int mod_cpuid; 159 int mod_chipid; 160 int mod_coreid; 161 int mod_strandid; 162 uintptr_t mod_hdladdr; 163 }; 164 165 static int 166 cmihdl_cb(uintptr_t addr, const void *arg, void *data) 167 { 168 cmi_hdl_impl_t *hdl = (cmi_hdl_impl_t *)arg; 169 struct cmihdl_cb *cbp = data; 170 cpu_t *cp; 171 int rv; 172 173 if (cbp->mod_cpuid != -1) { 174 cp = mdb_alloc(sizeof (cpu_t), UM_SLEEP); 175 if (mdb_vread(cp, sizeof (cpu_t), 176 (uintptr_t)hdl->cmih_hdlpriv) != sizeof (cpu_t)) { 177 mdb_warn("Read of cpu_t at 0x%p failed", 178 hdl->cmih_hdlpriv); 179 mdb_free(cp, sizeof (cpu_t)); 180 return (WALK_ERR); 181 } 182 183 if (cp->cpu_id == cbp->mod_cpuid) { 184 cbp->mod_hdladdr = addr; 185 rv = WALK_DONE; 186 } else { 187 rv = WALK_NEXT; 188 } 189 190 mdb_free(cp, sizeof (cpu_t)); 191 return (rv); 192 } else { 193 if (hdl->cmih_chipid == cbp->mod_chipid && 194 hdl->cmih_coreid == cbp->mod_coreid && 195 hdl->cmih_strandid == cbp->mod_strandid) { 196 cbp->mod_hdladdr = addr; 197 return (WALK_DONE); 198 } else { 199 return (WALK_NEXT); 200 } 201 } 202 } 203 204 static int 205 cmihdl_disp(uintptr_t addr, cmi_hdl_impl_t *hdl) 206 { 207 struct cms_ctl cmsctl; /* 16 bytes max */ 208 struct modctl cmimodc, cmsmodc; /* 288 bytes max */ 209 cmi_t cmi; /* 40 bytes max */ 210 cms_t cms; /* 40 bytes max */ 211 cpu_t *cp; 212 char cmimodnm[25], cmsmodnm[25]; /* 50 bytes */ 213 char cpuidstr[4], hwidstr[16]; 214 int native = hdl->cmih_class == CMI_HDL_NATIVE; 215 uint32_t refcnt; 216 217 cmimodnm[0] = cmsmodnm[0] = '-'; 218 cmimodnm[1] = cmsmodnm[1] = '\0'; 219 220 if (hdl->cmih_cmi != NULL) { 221 if (mdb_vread(&cmi, sizeof (cmi_t), 222 (uintptr_t)hdl->cmih_cmi) != sizeof (cmi)) { 223 mdb_warn("Read of cmi_t at 0x%p failed", 224 hdl->cmih_cmi); 225 return (0); 226 } 227 228 if (cmi.cmi_modp != NULL) { 229 if (mdb_vread(&cmimodc, sizeof (struct modctl), 230 (uintptr_t)cmi.cmi_modp) != sizeof (cmimodc)) { 231 mdb_warn("Read of modctl at 0x%p failed", 232 cmi.cmi_modp); 233 return (0); 234 } 235 236 if (mdb_readstr(cmimodnm, sizeof (cmimodnm), 237 (uintptr_t)cmimodc.mod_modname) == -1) { 238 mdb_warn("Read of cmi module name at 0x%p " 239 "failed", cmimodc.mod_modname); 240 return (0); 241 } 242 } 243 } 244 245 if (hdl->cmih_spec != NULL) { 246 if (mdb_vread(&cmsctl, sizeof (struct cms_ctl), 247 (uintptr_t)hdl->cmih_spec) != sizeof (cmsctl)) { 248 mdb_warn("Read of struct cms_ctl at 0x%p failed", 249 hdl->cmih_spec); 250 return (0); 251 } 252 253 if (mdb_vread(&cms, sizeof (cms_t), 254 (uintptr_t)cmsctl.cs_cms) != sizeof (cms)) { 255 mdb_warn("Read of cms_t at 0x%p failed", cmsctl.cs_cms); 256 return (0); 257 } 258 259 if (cms.cms_modp != NULL) { 260 if (mdb_vread(&cmsmodc, sizeof (struct modctl), 261 (uintptr_t)cms.cms_modp) != sizeof (cmsmodc)) { 262 mdb_warn("Read of modctl at 0x%p failed", 263 cms.cms_modp); 264 return (0); 265 } 266 267 if (mdb_readstr(cmsmodnm, sizeof (cmsmodnm), 268 (uintptr_t)cmsmodc.mod_modname) == -1) { 269 mdb_warn("Read of cms module name at 0x%p " 270 "failed", cmsmodc.mod_modname); 271 return (0); 272 } 273 } 274 } 275 276 if (mdb_vread(&refcnt, sizeof (uint32_t), 277 (uintptr_t)hdl->cmih_refcntp) != sizeof (uint32_t)) { 278 mdb_warn("Read of reference count for hdl 0x%p failed", hdl); 279 return (0); 280 } 281 282 if (native) { 283 cp = mdb_alloc(sizeof (cpu_t), UM_SLEEP); 284 285 if (mdb_vread(cp, sizeof (cpu_t), 286 (uintptr_t)hdl->cmih_hdlpriv) != sizeof (cpu_t)) { 287 mdb_free(cp, sizeof (cpu_t)); 288 mdb_warn("Read of cpu_t at 0x%p failed", 289 hdl->cmih_hdlpriv); 290 return (0); 291 } 292 } 293 294 if (native) { 295 (void) mdb_snprintf(cpuidstr, sizeof (cpuidstr), "%d", 296 cp->cpu_id); 297 } else { 298 (void) mdb_snprintf(cpuidstr, sizeof (cpuidstr), "-"); 299 } 300 301 (void) mdb_snprintf(hwidstr, sizeof (hwidstr), "%d/%d/%d", 302 hdl->cmih_chipid, hdl->cmih_coreid, hdl->cmih_strandid); 303 304 mdb_printf("%16lx %3d %3s %8s %3s %2s %-13s %-24s\n", addr, 305 refcnt, cpuidstr, hwidstr, hdl->cmih_mstrand ? "M" : "S", 306 hdl->cmih_mcops ? "Y" : "N", cmimodnm, cmsmodnm); 307 308 if (native) 309 mdb_free(cp, sizeof (cpu_t)); 310 311 return (1); 312 } 313 314 #define HDRFMT "%-16s %3s %3s %8s %3s %2s %-13s %-24s\n" 315 316 static int 317 cmihdl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 318 { 319 struct cmihdl_cb cb; 320 cmi_hdl_impl_t *hdl; 321 322 /* 323 * If an address is given it must be that of a cmi handle. 324 * Otherwise if the user has specified -c <cpuid> or 325 * -c <chipid/coreid/strandid> we will lookup a matching handle. 326 * Otherwise we'll walk and callback to this dcmd. 327 */ 328 if (!(flags & DCMD_ADDRSPEC)) { 329 char *p, *buf; 330 int len; 331 332 if (argc == 0) 333 return (mdb_walk_dcmd("cmihdl", "cmihdl", argc, 334 argv) == 0 ? DCMD_OK : DCMD_ERR); 335 336 337 if (mdb_getopts(argc, argv, 338 'c', MDB_OPT_STR, &p, 339 NULL) != argc) 340 return (DCMD_USAGE); 341 342 if ((len = strlen(p)) == 0) { 343 return (DCMD_USAGE); 344 } else { 345 buf = mdb_alloc(len + 1, UM_SLEEP); 346 strcpy(buf, p); 347 } 348 349 cb.mod_cpuid = cb.mod_chipid = cb.mod_coreid = 350 cb.mod_strandid = -1; 351 352 if ((p = strchr(buf, '/')) == NULL) { 353 /* Native cpuid */ 354 cb.mod_cpuid = (int)mdb_strtoull(buf); 355 } else { 356 /* Comma-separated triplet chip,core,strand. */ 357 char *q = buf; 358 359 *p = '\0'; 360 cb.mod_chipid = (int)mdb_strtoull(q); 361 362 if ((q = p + 1) >= buf + len || 363 (p = strchr(q, '/')) == NULL) { 364 mdb_free(buf, len); 365 return (DCMD_USAGE); 366 } 367 368 *p = '\0'; 369 cb.mod_coreid = (int)mdb_strtoull(q); 370 371 if ((q = p + 1) >= buf + len) { 372 mdb_free(buf, len); 373 return (DCMD_USAGE); 374 } 375 376 cb.mod_strandid = (int)mdb_strtoull(q); 377 } 378 379 mdb_free(buf, len); 380 381 cb.mod_hdladdr = NULL; 382 if (mdb_walk("cmihdl", cmihdl_cb, &cb) == -1) { 383 mdb_warn("cmi_hdl walk failed\n"); 384 return (DCMD_ERR); 385 } 386 387 if (cb.mod_hdladdr == NULL) { 388 if (cb.mod_cpuid != -1) { 389 mdb_warn("No handle found for cpuid %d\n", 390 cb.mod_cpuid); 391 } else { 392 393 mdb_warn("No handle found for chip %d " 394 "core %d strand %d\n", cb.mod_chipid, 395 cb.mod_coreid, cb.mod_strandid); 396 } 397 return (DCMD_ERR); 398 } 399 400 addr = cb.mod_hdladdr; 401 } 402 403 if (DCMD_HDRSPEC(flags)) { 404 char ul[] = "----------------------------"; 405 char *p = ul + sizeof (ul) - 1; 406 407 mdb_printf(HDRFMT HDRFMT, 408 "HANDLE", "REF", "CPU", "CH/CR/ST", "CMT", "MC", 409 "MODULE", "MODEL-SPECIFIC", 410 p - 16, p - 3, p - 3, p - 8, p - 3, p - 2, p - 13, p - 24); 411 } 412 413 hdl = mdb_alloc(sizeof (cmi_hdl_impl_t), UM_SLEEP); 414 415 if (mdb_vread(hdl, sizeof (cmi_hdl_impl_t), addr) != 416 sizeof (cmi_hdl_impl_t)) { 417 mdb_free(hdl, sizeof (cmi_hdl_impl_t)); 418 mdb_warn("Read of cmi handle at 0x%p failed", addr); 419 return (DCMD_ERR); 420 } 421 422 if (!cmihdl_disp(addr, hdl)) { 423 mdb_free(hdl, sizeof (cmi_hdl_impl_t)); 424 return (DCMD_ERR); 425 } 426 427 mdb_free(hdl, sizeof (cmi_hdl_impl_t)); 428 429 return (DCMD_OK); 430 } 431 432 /*ARGSUSED*/ 433 static int 434 gcpu_mpt_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 435 { 436 static const char *const whatstrs[] = { 437 "ntv-cyc-poll", /* GCPU_MPT_WHAT_CYC_ERR */ 438 "poll-poked", /* GCPU_MPT_WHAT_POKE_ERR */ 439 "unfaulting", /* GCPU_MPT_WHAT_UNFAULTING */ 440 "#MC", /* GCPU_MPT_WHAT_MC_ERR */ 441 "CMCI-int", /* GCPU_MPT_WHAT_CMCI_ERR */ 442 "xpv-virq-nrec", /* GCPU_MPT_WHAT_XPV_VIRQ */ 443 "xpv-virq-lgout", /* GCPU_MPT_WHAT_XPV_VIRQ_LOGOUT */ 444 }; 445 446 gcpu_poll_trace_t mpt; 447 const char *what; 448 449 if (argc != 0 || !(flags & DCMD_ADDRSPEC)) 450 return (DCMD_USAGE); 451 452 if (mdb_vread(&mpt, sizeof (mpt), addr) != sizeof (mpt)) { 453 mdb_warn("failed to read gcpu_poll_trace_t at 0x%p", addr); 454 return (DCMD_ERR); 455 } 456 457 if (DCMD_HDRSPEC(flags)) { 458 mdb_printf("%<u>%?s%</u> %<u>%?s%</u> %<u>%15s%</u> " 459 "%<u>%4s%</u>\n", "ADDR", "WHEN", "WHAT", "NERR"); 460 } 461 462 if (mpt.mpt_what < sizeof (whatstrs) / sizeof (char *)) 463 what = whatstrs[mpt.mpt_what]; 464 else 465 what = "???"; 466 467 mdb_printf("%?p %?p %15s %4u\n", addr, mpt.mpt_when, what, 468 mpt.mpt_nerr); 469 470 return (DCMD_OK); 471 } 472 473 typedef struct mptwalk_data { 474 uintptr_t mw_traceaddr; 475 gcpu_poll_trace_t *mw_trace; 476 size_t mw_tracesz; 477 uint_t mw_tracenent; 478 uint_t mw_curtrace; 479 } mptwalk_data_t; 480 481 static int 482 gcpu_mptwalk_init(mdb_walk_state_t *wsp) 483 { 484 gcpu_poll_trace_t *mpt; 485 mptwalk_data_t *mw; 486 GElf_Sym sym; 487 uint_t nent, i; 488 hrtime_t latest; 489 490 if (wsp->walk_addr == NULL) { 491 mdb_warn("the address of a poll trace array must be " 492 "specified\n"); 493 return (WALK_ERR); 494 } 495 496 if (mdb_lookup_by_name("gcpu_poll_trace_nent", &sym) < 0 || 497 sym.st_size != sizeof (uint_t) || mdb_vread(&nent, sizeof (uint_t), 498 sym.st_value) != sizeof (uint_t)) { 499 mdb_warn("failed to read gcpu_poll_trace_nent from kernel"); 500 return (WALK_ERR); 501 } 502 503 mw = mdb_alloc(sizeof (mptwalk_data_t), UM_SLEEP); 504 mw->mw_traceaddr = wsp->walk_addr; 505 mw->mw_tracenent = nent; 506 mw->mw_tracesz = nent * sizeof (gcpu_poll_trace_t); 507 mw->mw_trace = mdb_alloc(mw->mw_tracesz, UM_SLEEP); 508 509 if (mdb_vread(mw->mw_trace, mw->mw_tracesz, wsp->walk_addr) != 510 mw->mw_tracesz) { 511 mdb_free(mw->mw_trace, mw->mw_tracesz); 512 mdb_free(mw, sizeof (mptwalk_data_t)); 513 mdb_warn("failed to read poll trace array from kernel"); 514 return (WALK_ERR); 515 } 516 517 latest = 0; 518 mw->mw_curtrace = 0; 519 for (mpt = mw->mw_trace, i = 0; i < mw->mw_tracenent; i++, mpt++) { 520 if (mpt->mpt_when > latest) { 521 latest = mpt->mpt_when; 522 mw->mw_curtrace = i; 523 } 524 } 525 526 if (latest == 0) { 527 mdb_free(mw->mw_trace, mw->mw_tracesz); 528 mdb_free(mw, sizeof (mptwalk_data_t)); 529 return (WALK_DONE); /* trace array is empty */ 530 } 531 532 wsp->walk_data = mw; 533 534 return (WALK_NEXT); 535 } 536 537 static int 538 gcpu_mptwalk_step(mdb_walk_state_t *wsp) 539 { 540 mptwalk_data_t *mw = wsp->walk_data; 541 gcpu_poll_trace_t *thismpt, *prevmpt; 542 int prev, rv; 543 544 thismpt = &mw->mw_trace[mw->mw_curtrace]; 545 546 rv = wsp->walk_callback(mw->mw_traceaddr + (mw->mw_curtrace * 547 sizeof (gcpu_poll_trace_t)), thismpt, wsp->walk_cbdata); 548 549 if (rv != WALK_NEXT) 550 return (rv); 551 552 prev = (mw->mw_curtrace - 1) % mw->mw_tracenent; 553 prevmpt = &mw->mw_trace[prev]; 554 555 if (prevmpt->mpt_when == 0 || prevmpt->mpt_when > thismpt->mpt_when) 556 return (WALK_DONE); 557 558 mw->mw_curtrace = prev; 559 560 return (WALK_NEXT); 561 } 562 563 static void 564 gcpu_mptwalk_fini(mdb_walk_state_t *wsp) 565 { 566 mptwalk_data_t *mw = wsp->walk_data; 567 568 mdb_free(mw->mw_trace, mw->mw_tracesz); 569 mdb_free(mw, sizeof (mptwalk_data_t)); 570 } 571 572 static const mdb_dcmd_t dcmds[] = { 573 { "cmihdl", ": -c <cpuid>|<chip,core,strand> ", 574 "dump a cmi_handle_t", cmihdl }, 575 { "gcpu_poll_trace", ":", "dump a poll trace buffer", gcpu_mpt_dump }, 576 { NULL } 577 }; 578 579 static const mdb_walker_t walkers[] = { 580 { "cmihdl", "walks cpu module interface handle list", 581 cmih_walk_init, cmih_walk_step, cmih_walk_fini, NULL }, 582 { "gcpu_poll_trace", "walks poll trace buffers in reverse " 583 "chronological order", gcpu_mptwalk_init, gcpu_mptwalk_step, 584 gcpu_mptwalk_fini, NULL }, 585 { NULL } 586 }; 587 588 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; 589 590 const mdb_modinfo_t * 591 _mdb_init(void) 592 { 593 return (&modinfo); 594 } 595