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 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <ctype.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 #include <libdevinfo.h> 33 #include <errno.h> 34 #include <libintl.h> 35 #define CFGA_PLUGIN_LIB 36 #include <config_admin.h> 37 #include "ap.h" 38 #include <sys/obpdefs.h> 39 #include <sys/processor.h> 40 #include <sys/stat.h> 41 #include <sys/sbd_ioctl.h> 42 #include <sys/int_fmtio.h> 43 44 static cfga_err_t 45 ap_getncm(apd_t *a, sbd_comp_type_t type, int *ncm) 46 { 47 sbd_ioctl_arg_t *ctl; 48 sbd_getncm_cmd_t *cp; 49 50 if (a->fd == -1 || a->ctl == NULL) 51 return (CFGA_LIB_ERROR); 52 53 ctl = (sbd_ioctl_arg_t *)a->ctl; 54 ctl->ic_type = type; 55 ctl->ic_name[0] = '\0'; 56 ctl->ic_unit = 0; 57 ctl->i_len = 0; 58 ctl->i_opts = NULL; 59 60 DBG("ioctl(%d SBD_CMD_GETNCM, 0x%p)\n", a->fd, (void *)ctl); 61 62 if (ioctl(a->fd, SBD_CMD_GETNCM, ctl) == -1) { 63 ap_err(a, ERR_CMD_FAIL, CMD_GETNCM); 64 return (CFGA_ERROR); 65 } 66 67 cp = &ctl->i_cmd.cmd_getncm; 68 69 DBG("ncm(%d)=%d\n", type, cp->g_ncm); 70 71 if (ncm) 72 *ncm = cp->g_ncm; 73 74 return (CFGA_OK); 75 } 76 77 cfga_err_t 78 ap_stat(apd_t *a, int all) 79 { 80 int fd; 81 int ncm; 82 int select; 83 int stsize; 84 int oflag; 85 sbd_stat_cmd_t *sc; 86 sbd_ioctl_arg_t *ctl; 87 cfga_err_t rc; 88 sbd_stat_t *new_stat; 89 90 rc = CFGA_LIB_ERROR; 91 92 DBG("ap_stat(%s)\n", a->path); 93 94 /* Open the file descriptor if not already open */ 95 if (a->fd == -1) { 96 DBG("open(%s)\n", a->path); 97 if (a->statonly != 0) 98 oflag = O_RDONLY; 99 else 100 oflag = O_RDWR; 101 if ((fd = open(a->path, oflag, 0)) == -1) { 102 ap_err(a, ERR_AP_INVAL); 103 return (rc); 104 } 105 a->fd = fd; 106 } else { 107 fd = a->fd; 108 } 109 110 if (a->ctl == NULL && (a->ctl = calloc(1, sizeof (*ctl))) == NULL) { 111 ap_err(a, ERR_CMD_FAIL, CMD_STATUS); 112 return (rc); 113 } 114 115 if (a->tgt == AP_BOARD) { 116 /* 117 * The status target is the board. If we need to 118 * return component data (to support the -a option), 119 * get the number of components on the board. 120 */ 121 select = 0; 122 if (all) { 123 cfga_err_t r; 124 r = ap_getncm(a, SBD_COMP_NONE, &ncm); 125 if (r != CFGA_OK) { 126 return (r); 127 } 128 } else { 129 ncm = 0; 130 } 131 } else { 132 select = 1; 133 ncm = 1; 134 } 135 136 DBG("ncm=%d\n", ncm); 137 138 a->ncm = ncm; 139 140 /* 141 * The status structure contains space for one component; 142 * add the space for the other components if necessary. 143 */ 144 stsize = sizeof (sbd_stat_t); 145 if (ncm > 1) 146 stsize += ((ncm - 1) * sizeof (sbd_dev_stat_t)); 147 148 if ((new_stat = realloc(a->stat, stsize)) == NULL) { 149 ap_err(a, ERR_CMD_FAIL, CMD_STATUS); 150 return (rc); 151 } 152 153 a->stat = new_stat; 154 155 156 ctl = (sbd_ioctl_arg_t *)a->ctl; 157 ctl->i_len = 0; 158 ctl->i_opts = NULL; 159 ctl->ic_type = SBD_COMP_NONE; 160 if (all) 161 ctl->i_flags |= SBD_FLAG_ALLCMP; 162 sc = &ctl->i_cmd.cmd_stat; 163 sc->s_statp = (caddr_t)a->stat; 164 sc->s_nbytes = stsize; 165 166 if (select) { 167 /* 168 * The target is a specific component. Pass its 169 * name and unit number to the driver. Set its 170 * type to UNKNOWN since the plugin does not know 171 * the type of the component specified by the user. 172 */ 173 ctl->ic_type = SBD_COMP_UNKNOWN; 174 ctl->ic_unit = a->cnum; 175 (void) strcpy(ctl->ic_name, a->cname); 176 } 177 178 DBG("ioctl(%d SBD_CMD_STATUS, sc=0x%p sz=%d flags=%d", 179 fd, (void *)sc->s_statp, sc->s_nbytes, ctl->i_flags); 180 if (select) 181 DBG(" cname=<%s> cnum=%d", a->cname, a->cnum); 182 DBG(")\n"); 183 184 if (ioctl(fd, SBD_CMD_STATUS, ctl) == -1) { 185 ap_err(a, ERR_CMD_FAIL, CMD_STATUS); 186 rc = CFGA_ERROR; 187 } else 188 rc = CFGA_OK; 189 190 DBG("ap_stat()=%d\n", rc); 191 192 return (rc); 193 } 194 195 /* 196 * Convert a component to a target type. 197 */ 198 static ap_target_t 199 ap_cm_tgt(sbd_comp_type_t type) 200 { 201 ap_target_t c; 202 203 switch (type) { 204 case SBD_COMP_CPU: 205 c = AP_CPU; 206 break; 207 case SBD_COMP_MEM: 208 c = AP_MEM; 209 break; 210 case SBD_COMP_IO: 211 c = AP_IO; 212 break; 213 case SBD_COMP_CMP: 214 c = AP_CMP; 215 break; 216 default: 217 c = AP_NONE; 218 break; 219 } 220 221 return (c); 222 } 223 224 cfga_err_t 225 apd_init(apd_t *a, int all) 226 { 227 int i; 228 char *cn, *dn; 229 sbd_stat_t *st; 230 sbd_dev_stat_t *dst; 231 cfga_err_t rc; 232 233 /* 234 * Ideally, for board operations (other than status) it is not 235 * necessary to issue the STATUS ioctl. The call however allows a 236 * final sanity check to ensure that the board number returned 237 * by the driver matches the plugin's notion of the board number 238 * as extracted from the ap_id. If this check is not desirable, 239 * we can change the code to issue the status call only when 240 * necessary. Note that for component operations, we need to do 241 * the STATUS in order to figure out the component type and 242 * validate the command/options accordingly. XXX 243 */ 244 if ((rc = ap_stat(a, all)) != CFGA_OK) { 245 ap_err(a, ERR_AP_INVAL); 246 return (rc); 247 } 248 249 st = (sbd_stat_t *)a->stat; 250 251 /* 252 * Set the component count to the returned stat count. 253 */ 254 if (a->ncm > st->s_nstat) { 255 256 DBG("ncm=%d nstat=%d (truncated)\n", a->ncm, st->s_nstat); 257 258 a->ncm = st->s_nstat; 259 } 260 261 if (a->tgt == AP_BOARD) { 262 263 DBG("tgt=%d\n", a->tgt); 264 265 /* 266 * Initialize the RCM module here so that it can record 267 * the initial state of the capacity information. 268 */ 269 rc = ap_rcm_init(a); 270 271 return (rc); 272 } 273 274 a->tgt = AP_NONE; 275 cn = a->cname; 276 277 DBG("cname=<%s> cunit=<%d>\n", a->cname, a->cnum); 278 279 for (dst = st->s_stat, i = 0; i < st->s_nstat; i++, dst++) { 280 281 DBG("ds_name,ds_unit,ds_type=<%s,%d,%d> ", 282 dst->ds_name, dst->ds_unit, dst->ds_type); 283 284 if (dst->ds_unit != a->cnum) 285 continue; 286 287 /* 288 * Consider the names matched if they are either 289 * both absent or the same. It is conceivable that 290 * a NULL component name be considered valid 291 * by the driver. 292 */ 293 dn = dst->ds_name; 294 295 if ((dn == NULL && cn == NULL) || 296 (dn != NULL && cn != NULL && strcmp(dn, cn) == 0)) { 297 a->tgt = ap_cm_tgt(dst->ds_type); 298 a->cmstat = (void *)dst; 299 300 DBG("found "); 301 302 break; 303 } 304 } 305 306 DBG("tgt=%d\n", a->tgt); 307 308 if (a->tgt == AP_NONE) { 309 ap_err(a, ERR_CM_INVAL, a->cid); 310 return (CFGA_INVAL); 311 } 312 313 /* 314 * Initialize the RCM module here so that it can record 315 * the initial state of the capacity information. 316 */ 317 rc = ap_rcm_init(a); 318 319 return (rc); 320 } 321 322 void 323 apd_free(apd_t *a) 324 { 325 if (a == NULL) 326 return; 327 328 ap_rcm_fini(a); 329 330 if (a->fd != -1) 331 (void) close(a->fd); 332 333 s_free(a->options); 334 s_free(a->path); 335 s_free(a->drv); 336 s_free(a->target); 337 s_free(a->cname); 338 s_free(a->ctl); 339 s_free(a->stat); 340 341 free(a); 342 } 343 344 apd_t * 345 apd_alloc(const char *ap_id, cfga_flags_t flags, char **errstring, 346 struct cfga_msg *msgp, struct cfga_confirm *confp) 347 { 348 apd_t *a; 349 350 if ((a = calloc(1, sizeof (*a))) == NULL) 351 return (NULL); 352 353 if (errstring != NULL) 354 *errstring = NULL; 355 356 a->fd = -1; 357 a->errstring = errstring; 358 a->msgp = msgp; 359 a->confp = confp; 360 a->class = "sbd"; 361 362 if (flags & CFGA_FLAG_LIST_ALL) 363 ap_setopt(a, OPT_LIST_ALL); 364 if (flags & CFGA_FLAG_FORCE) 365 ap_setopt(a, OPT_FORCE); 366 if (flags & CFGA_FLAG_VERBOSE) 367 ap_setopt(a, OPT_VERBOSE); 368 369 if (ap_id == NULL || ap_parse(a, ap_id) == 0) 370 return (a); 371 372 apd_free(a); 373 return (NULL); 374 } 375 376 /* 377 * The type field is defined to be parsable by cfgadm(8): It 378 * must not contain white space characters. This function 379 * converts white space to underscore. 380 */ 381 382 static void 383 parsable_strncpy(char *op, const char *ip, size_t n) 384 { 385 char c; 386 387 while (n-- > 0) { 388 c = *ip++; 389 if (isspace(c)) 390 c = '_'; 391 *op++ = c; 392 if (c == '\0') 393 break; 394 } 395 } 396 397 void 398 ap_init(apd_t *a, cfga_list_data_t *ap) 399 { 400 sbd_stat_t *st; 401 402 st = (sbd_stat_t *)a->stat; 403 404 DBG("ap_init bd=%d rs=%d os=%d type=<%s>\n", 405 a->bnum, st->s_rstate, st->s_ostate, st->s_type); 406 407 parsable_strncpy(ap->ap_type, st->s_type, sizeof (ap->ap_type)); 408 ap->ap_r_state = (cfga_stat_t)st->s_rstate; 409 ap->ap_o_state = (cfga_stat_t)st->s_ostate; 410 ap->ap_cond = (cfga_cond_t)st->s_cond; 411 ap->ap_busy = (cfga_busy_t)st->s_busy; 412 ap->ap_status_time = st->s_time; 413 ap_info(a, ap->ap_info, AP_BOARD); 414 } 415 416 typedef struct { 417 int cmd; 418 int ioc; 419 } ap_ioc_t; 420 421 static ap_ioc_t 422 ap_iocs[] = { 423 {CMD_ASSIGN, SBD_CMD_ASSIGN }, 424 {CMD_POWERON, SBD_CMD_POWERON }, 425 {CMD_TEST, SBD_CMD_TEST }, 426 {CMD_CONNECT, SBD_CMD_CONNECT }, 427 {CMD_CONFIGURE, SBD_CMD_CONFIGURE }, 428 {CMD_UNCONFIGURE, SBD_CMD_UNCONFIGURE }, 429 {CMD_DISCONNECT, SBD_CMD_DISCONNECT }, 430 {CMD_POWEROFF, SBD_CMD_POWEROFF }, 431 {CMD_STATUS, SBD_CMD_STATUS }, 432 {CMD_GETNCM, SBD_CMD_GETNCM }, 433 {CMD_UNASSIGN, SBD_CMD_UNASSIGN }, 434 {CMD_PASSTHRU, SBD_CMD_PASSTHRU }, 435 {CMD_NONE, 0 } 436 }; 437 438 static int 439 ap_ioc(int cmd) 440 { 441 ap_ioc_t *acp; 442 443 DBG("ap_ioc(%d)\n", cmd); 444 445 for (acp = ap_iocs; acp->cmd != CMD_NONE; acp++) 446 if (acp->cmd == cmd) 447 break; 448 449 DBG("ap_ioc(%d)=0x%x\n", cmd, acp->ioc); 450 451 return (acp->ioc); 452 } 453 454 cfga_err_t 455 ap_suspend_query(apd_t *a, int cmd, int *check) 456 { 457 int ioc; 458 sbd_dev_stat_t *dst; 459 460 /* 461 * See if the a quiesce operation is required for 462 * this command for any of the components. If the 463 * command does not map to an ioctl, then there is 464 * nothing to do. 465 */ 466 if ((ioc = ap_ioc(cmd)) == 0) 467 return (CFGA_OK); 468 else if (a->tgt == AP_BOARD) { 469 int i; 470 471 dst = ((sbd_stat_t *)a->stat)->s_stat; 472 473 /* 474 * See if any component requires a 475 * OS suspension for this command. 476 */ 477 for (i = 0; i < a->ncm; i++, dst++) 478 if (SBD_CHECK_SUSPEND(ioc, dst->ds_suspend)) 479 (*check)++; 480 } else { 481 dst = (sbd_dev_stat_t *)a->cmstat; 482 if (SBD_CHECK_SUSPEND(ioc, dst->ds_suspend)) 483 (*check)++; 484 } 485 486 return (CFGA_OK); 487 } 488 489 cfga_err_t 490 ap_platopts_check(apd_t *a, int first, int last) 491 { 492 int c; 493 uint_t platopts; 494 sbd_stat_t *stat; 495 ap_opts_t *opts; 496 497 opts = &a->opts; 498 stat = (sbd_stat_t *)a->stat; 499 platopts = stat->s_platopts; 500 501 502 /* 503 * If there are no platform options set then there 504 * is no need to check this operation 505 */ 506 if (opts->platform == NULL) 507 return (CFGA_OK); 508 509 /* 510 * Check if any of the steps in the sequence 511 * allows for a platform option 512 */ 513 for (c = first; c <= last; c++) 514 /* 515 * If the platopt is set it means that the platform does not 516 * support options for this cmd 517 */ 518 if (SBD_CHECK_PLATOPTS(ap_ioc(c), platopts) == 0) { 519 return (CFGA_OK); 520 } 521 522 ap_err(a, ERR_OPT_INVAL, opts->platform); 523 524 return (CFGA_INVAL); 525 } 526 527 cfga_err_t 528 ap_ioctl(apd_t *a, int cmd) 529 { 530 int ioc; 531 sbd_ioctl_arg_t *ctl; 532 533 if (a->ctl == NULL && (a->ctl = calloc(1, sizeof (*ctl))) == NULL) { 534 ap_err(a, ERR_CMD_FAIL, cmd); 535 return (CFGA_LIB_ERROR); 536 } 537 538 ap_msg(a, MSG_ISSUE, cmd, a->target); 539 540 ctl = (sbd_ioctl_arg_t *)a->ctl; 541 ctl->i_flags = 0; 542 ctl->i_len = 0; 543 ctl->i_opts = NULL; 544 545 if (ap_getopt(a, OPT_FORCE)) 546 ctl->i_flags |= SBD_FLAG_FORCE; 547 if (ap_getopt(a, OPT_SUSPEND_OK)) 548 ctl->i_flags |= SBD_FLAG_QUIESCE_OKAY; 549 550 if (a->tgt == AP_BOARD) 551 ctl->ic_type = SBD_COMP_NONE; 552 else { 553 ctl->ic_type = SBD_COMP_UNKNOWN; 554 ctl->ic_unit = a->cnum; 555 (void) strcpy(ctl->ic_name, a->cname); 556 } 557 558 if (!(ioc = ap_ioc(cmd))) { 559 ap_err(a, ERR_CMD_FAIL, cmd); 560 return (CFGA_LIB_ERROR); 561 } 562 563 /* 564 * If this is a passthru command, pass all of its 565 * options; otherwise, pass all options after the 566 * platform keyword. 567 */ 568 if (cmd == CMD_PASSTHRU) 569 ctl->i_opts = a->options; 570 else { 571 /* 572 * Only pass the platform option to the cmds that the platform 573 * has specified as ok 574 */ 575 sbd_stat_t *stat; 576 577 stat = (sbd_stat_t *)a->stat; 578 if (SBD_CHECK_PLATOPTS(ioc, stat->s_platopts) == 0) 579 ctl->i_opts = a->opts.platform; 580 } 581 582 if (ctl->i_opts != NULL) 583 ctl->i_len = strlen(ctl->i_opts) + 1; 584 585 DBG("i_opts=%s\n", ctl->i_opts ? ctl->i_opts : "NULL"); 586 DBG("i_flags=0x%x\n", ctl->i_flags); 587 588 if (ap_getopt(a, OPT_SIM)) { 589 ap_msg(a, MSG_DONE, cmd, a->target); 590 return (CFGA_OK); 591 } 592 593 if (ioctl(a->fd, ioc, ctl) == -1) { 594 ap_err(a, ERR_CMD_FAIL, cmd); 595 return (CFGA_ERROR); 596 } 597 ap_msg(a, MSG_DONE, cmd, a->target); 598 599 return (CFGA_OK); 600 } 601 602 /* 603 * Return the error string corresponding to a given error code. 604 * String table and error code sets are provided by sbd_etab. This data 605 * structure is automatically generated at compile time from the error 606 * code and message text information in sbd_ioctl.h. 607 */ 608 static char * 609 mod_estr(int code) 610 { 611 int i; 612 char *s; 613 extern sbd_etab_t sbd_etab[]; 614 extern int sbd_etab_len; 615 616 s = NULL; 617 618 for (i = 0; i < sbd_etab_len; i++) { 619 sbd_etab_t *eptr = &sbd_etab[i]; 620 621 if ((code >= eptr->t_base) && (code <= eptr->t_bnd)) { 622 int index; 623 char **t_text; 624 625 /* 626 * Found it. Just extract the string 627 */ 628 index = code - eptr->t_base; 629 t_text = eptr->t_text; 630 s = strdup(t_text[index]); 631 break; 632 } 633 } 634 635 if (i == sbd_etab_len) { 636 char buf[32]; 637 638 (void) snprintf(buf, sizeof (buf), "error %d", code); 639 s = strdup(buf); 640 } 641 642 return (s); 643 } 644 645 char * 646 ap_sys_err(apd_t *a, char **rp) 647 { 648 int code; 649 char *p; 650 char *rsc; 651 652 sbd_ioctl_arg_t *ctl = (sbd_ioctl_arg_t *)a->ctl; 653 654 /* 655 * The driver sets the errno to EIO if it returns 656 * more detailed error info via e_code. In all 657 * other cases, use standard error text. 658 */ 659 if (ctl == NULL || errno != EIO) { 660 if ((p = strerror(errno)) != NULL) 661 p = strdup(p); 662 return (p); 663 } 664 665 code = ctl->ie_code; 666 rsc = ctl->ie_rsc; 667 668 if (code) 669 p = mod_estr(code); 670 else if ((p = strerror(errno)) != NULL) 671 p = strdup(p); 672 673 if (*rsc != '\0' && rp != NULL) 674 *rp = strdup(rsc); 675 676 return (p); 677 } 678 679 /* 680 * cfgadm -o err=plugin-err,cmd=name,code=ecode -x errtest ap_id. 681 */ 682 cfga_err_t 683 ap_test_err(apd_t *a, const char *options) 684 { 685 int err; 686 int cmd; 687 ap_opts_t *opts; 688 sbd_ioctl_arg_t ctl; 689 690 opts = &a->opts; 691 err = opts->err; 692 cmd = CMD_DISCONNECT; 693 694 DBG("ap_test_err(%d %d)\n", opts->code, opts->err); 695 696 switch (err) { 697 case ERR_CMD_INVAL: 698 ap_err(a, err, ap_cmd_name(cmd)); 699 break; 700 case ERR_CMD_NOTSUPP: 701 ap_err(a, err, cmd); 702 break; 703 case ERR_CMD_FAIL: 704 errno = EIO; 705 ctl.i_err.e_code = opts->code; 706 *ctl.i_err.e_rsc = '\0'; 707 a->ctl = &ctl; 708 ap_err(a, err, cmd); 709 a->ctl = NULL; 710 break; 711 case ERR_OPT_INVAL: 712 ap_err(a, err, options); 713 break; 714 case ERR_OPT_NOVAL: 715 ap_err(a, err, options); 716 break; 717 case ERR_AP_INVAL: 718 ap_err(a, err); 719 break; 720 case ERR_CM_INVAL: 721 ap_err(a, err, a->cid); 722 break; 723 case ERR_TRANS_INVAL: 724 ap_err(a, ERR_TRANS_INVAL, cmd); 725 break; 726 } 727 728 return (CFGA_LIB_ERROR); 729 } 730 731 static char * 732 ap_help_topics[] = { 733 "\nSbd specific commands/options:\n\n", 734 "\tcfgadm [-o parsable] -l ap_id\n", 735 "\tcfgadm [-o unassign|nopoweroff] -c disconnect ap_id\n", 736 "\tcfgadm -t ap_id\n", 737 "\tcfgadm -x assign ap_id\n", 738 "\tcfgadm -x unassign ap_id\n", 739 "\tcfgadm -x poweron ap_id\n", 740 "\tcfgadm -x poweroff ap_id\n", 741 NULL 742 }; 743 744 /*ARGSUSED*/ 745 cfga_err_t 746 ap_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags) 747 { 748 int len; 749 char **p; 750 char *q; 751 752 if (msgp == NULL || msgp->message_routine == NULL) 753 return (CFGA_OK); 754 755 for (p = ap_help_topics; *p != NULL; p++) { 756 if ((len = strlen(*p)) == 0) 757 continue; 758 if ((q = (char *)calloc(len + 1, 1)) == NULL) 759 continue; 760 (void) strcpy(q, *p); 761 (*msgp->message_routine)(msgp->appdata_ptr, q); 762 free(q); 763 } 764 765 return (CFGA_OK); 766 } 767 768 static char * 769 ap_dev_type(sbd_dev_stat_t *dst) 770 { 771 char *type; 772 773 switch (dst->ds_type) { 774 case SBD_COMP_CPU: 775 type = "cpu"; 776 break; 777 case SBD_COMP_MEM: 778 type = "memory"; 779 break; 780 case SBD_COMP_IO: 781 type = "io"; 782 break; 783 case SBD_COMP_CMP: 784 type = "cpu"; 785 break; 786 default: 787 type = "other"; 788 break; 789 } 790 791 DBG("ap_dev_type(%d)=%s\n", dst->ds_type, type); 792 793 return (type); 794 } 795 796 static sbd_dev_stat_t * 797 ap_cm_stat(apd_t *a, int seq) 798 { 799 sbd_stat_t *st; 800 801 if (seq == CM_DFLT) 802 return (a->cmstat); 803 804 st = (sbd_stat_t *)a->stat; 805 return (st->s_stat + seq); 806 } 807 808 char * 809 ap_cm_devpath(apd_t *a, int seq) 810 { 811 int len; 812 char *path; 813 char *devpath; 814 sbd_io_stat_t *dst; 815 816 817 /* 818 * If no component sequence number is provided 819 * default to the current target component. 820 * Assume an io component so that we can get 821 * the path if the component is indeed of type io. 822 */ 823 if (seq == CM_DFLT) 824 dst = (sbd_io_stat_t *)a->cmstat; 825 else { 826 sbd_stat_t *st; 827 st = (sbd_stat_t *)a->stat; 828 dst = (sbd_io_stat_t *)st->s_stat + seq; 829 } 830 831 if (dst->is_type != SBD_COMP_IO) 832 path = NULL; 833 else 834 path = dst->is_pathname; 835 836 if (str_valid(path)) { 837 len = strlen(DEVDIR) + strlen(path) + 1; 838 839 if ((devpath = calloc(1, len)) == NULL) 840 return (NULL); 841 842 (void) snprintf(devpath, len, "%s%s", DEVDIR, path); 843 } else 844 devpath = NULL; 845 846 DBG("ap_cm_path(%d)=%s\n", seq, devpath ? devpath : ""); 847 848 return (devpath); 849 } 850 851 void 852 ap_cm_id(apd_t *a, int seq, char *id, size_t bufsize) 853 { 854 int unit; 855 char *name; 856 sbd_dev_stat_t *dst; 857 858 dst = ap_cm_stat(a, seq); 859 860 unit = dst->ds_unit; 861 name = dst->ds_name; 862 863 /* 864 * If the component has a unit number, 865 * add it to the id, otherwise just use 866 * the component's name. 867 */ 868 if (unit == -1) 869 (void) snprintf(id, bufsize, "%s", name); 870 else 871 (void) snprintf(id, bufsize, "%s%d", name, unit); 872 873 DBG("ap_cm_id(%d)=%s\n", seq, id); 874 } 875 876 /* 877 * Convert a component to a target type. 878 */ 879 ap_target_t 880 ap_cm_type(apd_t *a, int seq) 881 { 882 ap_target_t c; 883 sbd_dev_stat_t *dst; 884 885 dst = ap_cm_stat(a, seq); 886 887 switch (dst->ds_type) { 888 case SBD_COMP_CPU: 889 c = AP_CPU; 890 break; 891 case SBD_COMP_MEM: 892 c = AP_MEM; 893 break; 894 case SBD_COMP_IO: 895 c = AP_IO; 896 break; 897 case SBD_COMP_CMP: 898 c = AP_CMP; 899 break; 900 default: 901 c = AP_NONE; 902 break; 903 } 904 905 return (c); 906 } 907 908 int 909 ap_cm_ncap(apd_t *a, int seq) 910 { 911 sbd_dev_stat_t *dst; 912 int ncap; 913 914 dst = ap_cm_stat(a, seq); 915 916 switch (dst->ds_type) { 917 case SBD_COMP_CPU: 918 case SBD_COMP_MEM: 919 case SBD_COMP_IO: 920 ncap = 1; 921 break; 922 case SBD_COMP_CMP: 923 ncap = ((sbd_cmp_stat_t *)dst)->ps_ncores; 924 break; 925 default: 926 ncap = 0; 927 break; 928 } 929 930 return (ncap); 931 } 932 933 int 934 ap_cm_capacity(apd_t *a, int seq, void *cap, int *ncap, cfga_stat_t *ostate) 935 { 936 int i; 937 sbd_dev_stat_t *dst; 938 cfga_stat_t os; 939 940 if (cap == NULL) 941 return (0); 942 943 dst = ap_cm_stat(a, seq); 944 os = (cfga_stat_t)dst->ds_ostate; 945 if (os != CFGA_STAT_CONFIGURED && os != CFGA_STAT_UNCONFIGURED) 946 return (0); 947 if (ostate) 948 *ostate = os; 949 950 *ncap = 1; 951 952 switch (dst->ds_type) { 953 case SBD_COMP_CPU: { 954 sbd_cpu_stat_t *cpu = (sbd_cpu_stat_t *)dst; 955 *((processorid_t *)cap) = cpu->cs_cpuid; 956 break; 957 } 958 case SBD_COMP_MEM: { 959 sbd_mem_stat_t *mem = (sbd_mem_stat_t *)dst; 960 *((long *)cap) = mem->ms_totpages; 961 break; 962 } 963 case SBD_COMP_CMP: { 964 sbd_cmp_stat_t *cmp = (sbd_cmp_stat_t *)dst; 965 processorid_t *cpuid; 966 967 cpuid = (processorid_t *)cap; 968 for (i = 0; i < cmp->ps_ncores; i++) { 969 cpuid[i] = cmp->ps_cpuid[i]; 970 } 971 972 *ncap = cmp->ps_ncores; 973 break; 974 } 975 default: 976 return (0); 977 } 978 979 DBG("ap_cm_capacity(%d)=(", seq); 980 for (i = 0; i < *ncap; i++) { 981 DBG("%d ", ((int *)cap)[i]); 982 } 983 DBG("%d)\n", *ostate); 984 985 return (1); 986 } 987 988 void 989 ap_cm_init(apd_t *a, cfga_list_data_t *ap, int seq) 990 { 991 char *type; 992 sbd_stat_t *st; 993 sbd_dev_stat_t *dst; 994 995 st = (sbd_stat_t *)a->stat; 996 dst = st->s_stat + seq; 997 type = ap_dev_type(dst); 998 999 a->cmstat = (void *)dst; 1000 1001 DBG("ap_cm_init bd=%d rs=%d os=%d type=<%s> seq=%d\n", 1002 a->bnum, st->s_rstate, dst->ds_ostate, type, seq); 1003 1004 (void) strncpy(ap->ap_type, type, sizeof (ap->ap_type)); 1005 ap->ap_r_state = (cfga_stat_t)st->s_rstate; 1006 ap->ap_o_state = (cfga_stat_t)dst->ds_ostate; 1007 ap->ap_cond = (cfga_cond_t)dst->ds_cond; 1008 ap->ap_busy = (cfga_busy_t)dst->ds_busy; 1009 ap->ap_status_time = dst->ds_time; 1010 ap_info(a, ap->ap_info, ap_cm_tgt(dst->ds_type)); 1011 } 1012 1013 void 1014 ap_state(apd_t *a, cfga_stat_t *rs, cfga_stat_t *os) 1015 { 1016 sbd_stat_t *st; 1017 sbd_dev_stat_t *dst; 1018 1019 st = (sbd_stat_t *)a->stat; 1020 dst = (sbd_dev_stat_t *)a->cmstat; 1021 1022 if (rs != NULL) { 1023 if (a->tgt == AP_NONE) 1024 *rs = CFGA_STAT_NONE; 1025 else 1026 *rs = (cfga_stat_t)st->s_rstate; 1027 } 1028 1029 if (os != NULL) { 1030 if (a->tgt == AP_NONE) 1031 *os = CFGA_STAT_NONE; 1032 else if (a->tgt == AP_BOARD) 1033 *os = (cfga_stat_t)st->s_ostate; 1034 else 1035 *os = (cfga_stat_t)dst->ds_ostate; 1036 } 1037 } 1038 1039 #define BI_POWERED 0 1040 #define BI_ASSIGNED 1 1041 1042 static const char * 1043 binfo[] = { 1044 "powered-on", 1045 ", assigned" 1046 }; 1047 1048 static const char * 1049 binfo_parsable[] = { 1050 "powered-on", 1051 " assigned" 1052 }; 1053 1054 static void 1055 bd_info(apd_t *a, cfga_info_t info, int parsable) 1056 { 1057 int i; 1058 int nsep; 1059 const char **p; 1060 sbd_stat_t *st; 1061 char *end = &info[sizeof (cfga_info_t)]; 1062 1063 DBG("bd_info(%p)\n", (void *)info); 1064 1065 st = (sbd_stat_t *)a->stat; 1066 1067 if (parsable) { 1068 p = binfo_parsable; 1069 nsep = 1; 1070 } else { 1071 p = binfo; 1072 nsep = 2; 1073 } 1074 1075 i = nsep; 1076 1077 if (st->s_power) { 1078 info += snprintf(info, end - info, p[BI_POWERED]); 1079 i = 0; 1080 } 1081 if (st->s_assigned) 1082 info += snprintf(info, end - info, p[BI_ASSIGNED] + i); 1083 } 1084 1085 #define CI_CPUID 0 1086 #define CI_SPEED 1 1087 #define CI_ECACHE 2 1088 1089 static const char * 1090 cpuinfo[] = { 1091 "cpuid %d", 1092 ", speed %d MHz", 1093 ", ecache %d MBytes" 1094 }; 1095 1096 static const char * 1097 cpuinfo_parsable[] = { 1098 "cpuid=%d", 1099 " speed=%d", 1100 " ecache=%d" 1101 }; 1102 1103 static void 1104 cpu_info(apd_t *a, cfga_info_t info, int parsable) 1105 { 1106 const char **p; 1107 sbd_cpu_stat_t *dst; 1108 char *end = &info[sizeof (cfga_info_t)]; 1109 1110 DBG("cpu_info(%p)\n", (void *)info); 1111 1112 dst = (sbd_cpu_stat_t *)a->cmstat; 1113 1114 if (parsable) 1115 p = cpuinfo_parsable; 1116 else 1117 p = cpuinfo; 1118 1119 info += snprintf(info, end - info, p[CI_CPUID], dst->cs_cpuid); 1120 info += snprintf(info, end - info, p[CI_SPEED], dst->cs_speed); 1121 info += snprintf(info, end - info, p[CI_ECACHE], dst->cs_ecache); 1122 } 1123 1124 #define MI_ADDRESS 0 1125 #define MI_SIZE 1 1126 #define MI_PERMANENT 2 1127 #define MI_UNCONFIGURABLE 3 1128 #define MI_SOURCE 4 1129 #define MI_TARGET 5 1130 #define MI_DELETED 6 1131 #define MI_REMAINING 7 1132 #define MI_INTERLEAVE 8 1133 1134 static const char * 1135 meminfo_nonparsable[] = { 1136 "base address 0x%" PRIx64, 1137 ", %lu KBytes total", 1138 ", %lu KBytes permanent", 1139 ", unconfigurable", 1140 ", memory delete requested on %s", 1141 ", memory delete in progress on %s", 1142 ", %lu KBytes deleted", 1143 ", %lu KBytes remaining", 1144 ", inter board interleave" 1145 }; 1146 1147 static const char * 1148 meminfo_parsable[] = { 1149 "address=0x%" PRIx64, 1150 " size=%lu", 1151 " permanent=%lu", 1152 " unconfigurable", 1153 " source=%s", 1154 " target=%s", 1155 " deleted=%lu", 1156 " remaining=%lu", 1157 " inter-board-interleave" 1158 }; 1159 1160 1161 #define _K1 1024 1162 1163 /* 1164 * This function assumes pagesize > 1024 and that 1165 * pagesize is a multiple of 1024. 1166 */ 1167 static ulong_t 1168 pages_to_kbytes(uint_t pgs) 1169 { 1170 long pagesize; 1171 1172 pagesize = sysconf(_SC_PAGESIZE); 1173 return (pgs * (pagesize / _K1)); 1174 } 1175 1176 static uint64_t 1177 pages_to_bytes(uint_t pgs) 1178 { 1179 long pagesize; 1180 1181 pagesize = sysconf(_SC_PAGESIZE); 1182 return ((uint64_t)pgs * pagesize); 1183 } 1184 1185 static void 1186 mem_info(apd_t *a, cfga_info_t info, int parsable) 1187 { 1188 const char **p; 1189 sbd_mem_stat_t *dst; 1190 int want_progress; 1191 char *end = &info[sizeof (cfga_info_t)]; 1192 1193 DBG("mem_info(%p)\n", (void *)info); 1194 1195 dst = (sbd_mem_stat_t *)a->cmstat; 1196 1197 if (parsable) 1198 p = meminfo_parsable; 1199 else 1200 p = meminfo_nonparsable; 1201 1202 info += snprintf(info, end - info, p[MI_ADDRESS], 1203 pages_to_bytes(dst->ms_basepfn)); 1204 info += snprintf(info, end - info, p[MI_SIZE], 1205 pages_to_kbytes(dst->ms_totpages)); 1206 1207 if (dst->ms_noreloc_pages) 1208 info += snprintf(info, end - info, p[MI_PERMANENT], 1209 pages_to_kbytes(dst->ms_noreloc_pages)); 1210 if (!dst->ms_cage_enabled) 1211 info += snprintf(info, end - info, p[MI_UNCONFIGURABLE]); 1212 if (dst->ms_interleave) 1213 info += snprintf(info, end - info, p[MI_INTERLEAVE]); 1214 1215 /* 1216 * If there is a valid peer physical ap_id specified, 1217 * convert it to a logical id. 1218 */ 1219 want_progress = 0; 1220 if (str_valid(dst->ms_peer_ap_id)) { 1221 char *cm; 1222 char *peer; 1223 char physid[MAXPATHLEN]; 1224 char logid[MAXPATHLEN]; 1225 1226 (void) snprintf(physid, sizeof (physid), "%s%s", 1227 DEVDIR, dst->ms_peer_ap_id); 1228 1229 /* 1230 * Save the component portion of the physid and 1231 * add it back after converting to logical format. 1232 */ 1233 if ((cm = strstr(physid, "::")) != NULL) { 1234 *cm = '\0'; 1235 cm += 2; 1236 } 1237 1238 /* attempt to resolve to symlink */ 1239 if (ap_symid(a, physid, logid, sizeof (logid)) == 0) 1240 peer = logid; 1241 else 1242 peer = physid; 1243 1244 if (dst->ms_peer_is_target) { 1245 info += snprintf(info, end - info, p[MI_TARGET], peer); 1246 if (cm) 1247 info += snprintf(info, end - info, "::%s", cm); 1248 want_progress = 1; 1249 } else { 1250 info += snprintf(info, end - info, p[MI_SOURCE], peer); 1251 if (cm) 1252 info += snprintf(info, end - info, "::%s", cm); 1253 } 1254 } 1255 if (want_progress || 1256 (dst->ms_detpages != 0 && dst->ms_detpages != dst->ms_totpages)) { 1257 info += snprintf(info, end - info, p[MI_DELETED], 1258 pages_to_kbytes(dst->ms_detpages)); 1259 info += snprintf(info, end - info, p[MI_REMAINING], 1260 pages_to_kbytes(dst->ms_totpages - 1261 dst->ms_detpages)); 1262 } 1263 } 1264 1265 #define II_DEVICE 0 1266 #define II_REFERENCED 1 1267 1268 static const char * 1269 ioinfo[] = { 1270 "device %s", 1271 ", referenced" 1272 }; 1273 1274 static const char * 1275 ioinfo_parsable[] = { 1276 "device=%s", 1277 " referenced" 1278 }; 1279 1280 static void 1281 io_info(apd_t *a, cfga_info_t info, int parsable) 1282 { 1283 const char **p; 1284 sbd_io_stat_t *dst; 1285 char *end = &info[sizeof (cfga_info_t)]; 1286 1287 dst = (sbd_io_stat_t *)a->cmstat; 1288 1289 if (parsable) 1290 p = ioinfo_parsable; 1291 else 1292 p = ioinfo; 1293 1294 info += snprintf(info, end - info, p[II_DEVICE], dst->is_pathname); 1295 if (dst->is_referenced) 1296 info += snprintf(info, end - info, p[II_REFERENCED]); 1297 } 1298 1299 #define PI_CPUID 0 1300 #define PI_CPUID_PAIR 1 1301 #define PI_CPUID_CONT 2 1302 #define PI_CPUID_LAST 3 1303 #define PI_SPEED 4 1304 #define PI_ECACHE 5 1305 1306 static const char * 1307 cmpinfo[] = { 1308 "cpuid %d", 1309 " and %d", 1310 ", %d", 1311 ", and %d", 1312 ", speed %d MHz", 1313 ", ecache %d MBytes" 1314 }; 1315 1316 static const char * 1317 cmpinfo_parsable[] = { 1318 "cpuid=%d", 1319 ",%d", 1320 ",%d", 1321 ",%d", 1322 " speed=%d", 1323 " ecache=%d" 1324 }; 1325 1326 static void 1327 cmp_info(apd_t *a, cfga_info_t info, int parsable) 1328 { 1329 int i; 1330 int last; 1331 const char **p; 1332 sbd_cmp_stat_t *dst; 1333 char *end = &info[sizeof (cfga_info_t)]; 1334 1335 DBG("cmp_info(%p)\n", (void *)info); 1336 1337 dst = (sbd_cmp_stat_t *)a->cmstat; 1338 1339 if (parsable) 1340 p = cmpinfo_parsable; 1341 else 1342 p = cmpinfo; 1343 1344 /* Print the first cpuid */ 1345 info += snprintf(info, end - info, p[PI_CPUID], dst->ps_cpuid[0]); 1346 1347 /* 1348 * Print the middle cpuids, if necessary. Stop before 1349 * the last one, since printing the last cpuid is a 1350 * special case for the non parsable form. 1351 */ 1352 for (i = 1; i < (dst->ps_ncores - 1); i++) { 1353 info += snprintf(info, end - info, p[PI_CPUID_CONT], 1354 dst->ps_cpuid[i]); 1355 } 1356 1357 /* Print the last cpuid, if necessary */ 1358 if (dst->ps_ncores > 1) { 1359 last = (dst->ps_ncores == 2) ? PI_CPUID_PAIR : PI_CPUID_LAST; 1360 info += snprintf(info, end - info, 1361 dgettext(TEXT_DOMAIN, p[last]), dst->ps_cpuid[i]); 1362 } 1363 1364 info += snprintf(info, end - info, p[PI_SPEED], dst->ps_speed); 1365 info += snprintf(info, end - info, p[PI_ECACHE], dst->ps_ecache); 1366 } 1367 1368 void 1369 ap_info(apd_t *a, cfga_info_t info, ap_target_t tgt) 1370 { 1371 int parsable = ap_getopt(a, OPT_PARSABLE); 1372 1373 DBG("ap_info(%p, %d)\n", (void *)info, parsable); 1374 1375 switch (tgt) { 1376 case AP_BOARD: 1377 bd_info(a, info, parsable); 1378 break; 1379 case AP_CPU: 1380 cpu_info(a, info, parsable); 1381 break; 1382 case AP_MEM: 1383 mem_info(a, info, parsable); 1384 break; 1385 case AP_IO: 1386 io_info(a, info, parsable); 1387 break; 1388 case AP_CMP: 1389 cmp_info(a, info, parsable); 1390 break; 1391 default: 1392 break; 1393 } 1394 } 1395