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