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 2004 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 <assert.h> 30 #include <ctype.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <macros.h> 36 #include <dirent.h> 37 #include <libgen.h> 38 #include <libdevinfo.h> 39 #define CFGA_PLUGIN_LIB 40 #include <config_admin.h> 41 #include "ap.h" 42 43 int 44 ap_symid(apd_t *a, char *apid, char *symid, size_t bufsize) 45 { 46 int n; 47 int rc; 48 char path[MAXPATHLEN]; 49 char *p; 50 DIR *dirp; 51 struct dirent *dp; 52 53 *symid = '\0'; 54 n = sprintf(path, "/dev/cfg/"); 55 rc = -1; 56 57 if ((dirp = opendir(path)) == NULL) 58 return (rc); 59 60 p = path + n; 61 62 while ((dp = readdir(dirp)) != NULL) { 63 char buf[MAXPATHLEN]; 64 char *cp; 65 size_t len; 66 67 *p = '\0'; 68 (void) strcat(path, dp->d_name); 69 if ((len = readlink(path, buf, sizeof (buf))) == (size_t)-1) 70 continue; 71 buf[len] = '\0'; 72 73 len = strlen("../"); 74 cp = buf; 75 while (strncmp(cp, "../", len) == 0) 76 cp += len; 77 if (cp != buf) 78 cp--; /* Get the '/' */ 79 80 if (strcmp(cp, apid) == 0) { 81 snprintf(symid, bufsize, "%s", dp->d_name); 82 rc = 0; 83 break; 84 } 85 } 86 87 closedir(dirp); 88 return (rc); 89 } 90 91 char * 92 ap_logid(apd_t *a, char *apid) 93 { 94 int n; 95 char *buf; 96 97 if ((buf = calloc(1, MAXPATHLEN)) == NULL) 98 return (NULL); 99 100 /* 101 * Look for a symlink. On any error, fallback to 102 * driver and instance based logical ap_ids. 103 */ 104 if (ap_symid(a, apid, buf, MAXPATHLEN) == 0) 105 n = strlen(buf); 106 else 107 n = snprintf(buf, MAXPATHLEN, "%s%d:%s", 108 a->drv, a->inst, a->minor); 109 /* 110 * Append the dynamic portion, if any. 111 */ 112 if (a->cid != NULL) 113 (void) snprintf(&buf[n], MAXPATHLEN - n, "::%s", a->cid); 114 115 return (buf); 116 } 117 118 int 119 ap_parse(apd_t *a, const char *ap_id) 120 { 121 int i; 122 int rc; 123 int phys; 124 char c; 125 char *s; 126 char *p; 127 char *q; 128 char *base; 129 int len; 130 char *t; 131 132 if (a == NULL) 133 return (-1); 134 135 a->cnum = -1; 136 a->bnum = -1; 137 a->inst = -1; 138 a->apid = ap_id; 139 rc = ERR_NONE; 140 141 if (!str_valid(ap_id)) { 142 rc = ERR_AP_INVAL; 143 goto done; 144 } 145 146 if ((a->path = strdup(ap_id)) == NULL) { 147 rc = ERR_NOMEM; 148 goto done; 149 } 150 151 /* 152 * For a physical ap_id, look only at the base part. 153 * For a logical/symbolic one, use the entire ap_id. 154 */ 155 if (strncmp(a->path, DEVDIR, strlen(DEVDIR)) == 0) { 156 phys = 1; 157 base = strrchr((const char *)a->path, '/') + 1; 158 } else { 159 phys = 0; 160 base = a->path; 161 if ((a->target = strdup(a->path)) == NULL) { 162 rc = ERR_NOMEM; 163 goto done; 164 } 165 } 166 167 if ((s = strchr(base, ':')) == NULL || s[1] == ':') { 168 /* 169 * No ':' found, or got a '::'. If this is a physical 170 * ap_id, it must have a minor separtor ':' which must 171 * appear before the dynamic part (starting with '::'). 172 * For a symbolic ap_id, skip looking for driver/minor 173 * names. 174 */ 175 if (phys) { 176 rc = ERR_AP_INVAL; 177 goto done; 178 } else 179 s = base; 180 } else { 181 /* 182 * Look for driver name/instance only up to the first ':', 183 * i.e. up to the minor node name. 184 */ 185 *s = '\0'; 186 187 if ((p = strchr(base, '@')) != NULL) { 188 /* 189 * Get the driver name/instance. 190 */ 191 *p = '\0'; 192 if ((a->drv = strdup(base)) == NULL) { 193 rc = ERR_NOMEM; 194 goto done; 195 } 196 *p++ = '@'; 197 198 i = strtol(p, &q, 10); 199 if (q > p) 200 a->inst = i; 201 } 202 203 *s++ = ':'; 204 a->minor = s; 205 } 206 207 /* 208 * Need to go to the end of the string before the :: if any 209 * If the string is null then we are done 210 */ 211 t = strstr(s, "::"); 212 if (t != NULL) 213 len = strlen(t); 214 else 215 len = 0; 216 217 s += (strlen(s) - len); 218 219 p = s; 220 221 if (*p == '\0') 222 a->tgt = AP_BOARD; 223 else if (strncmp(p, "::", 2) != 0) { 224 rc = ERR_AP_INVAL; 225 goto done; 226 } else { 227 /* 228 * Save the component id. 229 */ 230 *p++ = '\0'; 231 *p++ = '\0'; 232 a->cid = p; 233 } 234 235 /* 236 * Get the operation target, e.g. slot0, slot0::cpu0. 237 * At this point, a->path points to the /devices path 238 * minus the dynamic part, for a physical ap_id. In 239 * the case of a logical ap_id, the target is already 240 * initialized above. 241 */ 242 if (phys != 0 && (a->target = ap_logid(a, a->path)) == NULL) { 243 rc = ERR_NOMEM; 244 goto done; 245 } 246 247 if (a->tgt == AP_BOARD) 248 goto done; 249 250 while ((*p != '\0') && !isdigit(*p)) 251 p++; 252 253 /* 254 * Get the component unit number, if present. 255 */ 256 i = strtol(p, &s, 10); 257 /* 258 * There must be no characters after the unit number. 259 */ 260 if (*s != '\0') { 261 rc = ERR_CM_INVAL; 262 goto done; 263 } 264 if (s > p) { 265 /* 266 * Disallow leading zeroes, e.g. cpu00, cpu01, cpu001. 267 * If there are 2 or more digits and the first is a zero, 268 * we fail. 269 */ 270 if ((s-p) >= 2 && *p == '0') { 271 rc = ERR_CM_INVAL; 272 goto done; 273 } 274 a->cnum = i; 275 } 276 277 c = *p; 278 *p = '\0'; 279 if ((a->cname = strdup(a->cid)) == NULL) 280 rc = ERR_NOMEM; 281 *p = c; 282 done: 283 switch (rc) { 284 case ERR_NONE: 285 break; 286 case ERR_CM_INVAL: 287 ap_err(a, ERR_CM_INVAL, a->cid); 288 break; 289 default: 290 ap_err(a, rc); 291 break; 292 } 293 294 DBG("path=<%s> ", a->path ? a->path : ""); 295 DBG("drv=<%s> inst=%d minor=<%s> ", 296 a->drv ? a->drv : "", a->inst, a->minor ? a->minor : ""); 297 DBG("target=<%s>\n", a->target ? a->target : ""); 298 DBG("cid=<%s> ", a->cid ? a->cid : ""); 299 DBG("cname=<%s> ", a->cname ? a->cname : ""); 300 DBG("cnum=%d\n", a->cnum); 301 DBG("tgt=%d opts=%x\n", a->tgt, a->opts.flags); 302 303 return (rc == ERR_NONE? 0 : -1); 304 } 305 306 /* 307 * Command table. 308 * 309 * The first set of commands in the table are in sequencing order, 310 * for example, the first group starts with assign and ends with 311 * configure. command sequencer relies on this ordering. 312 */ 313 static char * 314 ap_cmd_names[] = { 315 "assign", 316 "poweron", 317 "test", 318 "connect", 319 "configure", 320 "notify online", 321 "notify add capacity", 322 "suspend check", 323 "request suspend", 324 "request delete capacity", 325 "request offline", 326 "unconfigure", 327 "notify remove", 328 "notify capacity change", 329 "disconnect", 330 "poweroff", 331 "unassign", 332 "notify resume", 333 "status", 334 "getncm", 335 "passthru", 336 "help", 337 "errtest", 338 NULL 339 }; 340 341 char * 342 ap_cmd_name(int i) 343 { 344 return (ap_cmd_names[min(i, CMD_NONE)]); 345 } 346 347 static char * 348 ap_opt_names[] = { 349 "unassign", 350 "skip", 351 "parsable", 352 "nopoweroff", 353 "code", 354 "mid", 355 "err", 356 "platform", 357 "sim", 358 NULL 359 }; 360 361 char * 362 ap_opt_name(int i) 363 { 364 return (ap_opt_names[i]); 365 } 366 367 /* 368 * Command descriptor. 369 * 370 * Each command has a (command) mask specifying the AP target classes 371 * it operates on, e.g. the assign command applies only to boards. 372 * In addition each AP target class has a separate option mask specifying 373 * which command options are valid for that target class. 374 * A global value mask specifies which options require values. 375 */ 376 typedef struct { 377 int cmd; 378 uint_t cmask; 379 uint_t omask[AP_NCLASS]; 380 } ap_cmd_t; 381 382 /* 383 * Command option definitions. 384 */ 385 #define SHFT(i) ((uint_t)1 << (i)) 386 #define NULOPT 0 387 #define ALLOPT 0xffffffff 388 #define CMNOPT (SHFT(OPT_VERBOSE)|SHFT(OPT_PLATFORM)|SHFT(OPT_SIM)) 389 #define CMFOPT (CMNOPT|SHFT(OPT_FORCE)) 390 #define STSOPT (CMNOPT|SHFT(OPT_PARSABLE)) 391 #define BRDDCN (CMNOPT|SHFT(OPT_UNASSIGN)|SHFT(OPT_NOPOWEROFF)) 392 393 #define BRD SHFT(AP_BOARD) 394 #define BIO SHFT(AP_BOARD)|SHFT(AP_IO) 395 #define ALL (BRD|SHFT(AP_CPU)|SHFT(AP_MEM)|SHFT(AP_IO)|SHFT(AP_CMP)) 396 397 static ap_cmd_t 398 ap_cmds[] = { 399 /* 400 * cmd cmd board cpu mem io cmp 401 * cmask omask omask omask omask omask 402 */ 403 {CMD_ASSIGN, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT}, 404 {CMD_UNASSIGN, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT}, 405 {CMD_POWERON, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT}, 406 {CMD_POWEROFF, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT}, 407 {CMD_CONNECT, BRD, 0, CMFOPT, NULOPT, NULOPT, NULOPT, NULOPT}, 408 {CMD_DISCONNECT, BRD, 0, BRDDCN, NULOPT, NULOPT, NULOPT, NULOPT}, 409 {CMD_CONFIGURE, ALL, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 410 {CMD_UNCONFIGURE, ALL, 0, CMFOPT, CMFOPT, CMFOPT, CMFOPT, CMNOPT}, 411 {CMD_RCM_OFFLINE, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 412 {CMD_RCM_ONLINE, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 413 {CMD_RCM_SUSPEND, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 414 {CMD_RCM_RESUME, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 415 {CMD_RCM_CAP_ADD, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 416 {CMD_RCM_CAP_DEL, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 417 {CMD_RCM_CAP_NOTIFY, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 418 {CMD_RCM_REMOVE, BIO, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 419 {CMD_TEST, BRD, 0, CMFOPT, NULOPT, NULOPT, NULOPT, NULOPT}, 420 {CMD_STATUS, ALL, 0, STSOPT, STSOPT, STSOPT, STSOPT, STSOPT}, 421 {CMD_GETNCM, BRD, 0, CMNOPT, NULOPT, NULOPT, NULOPT, NULOPT}, 422 {CMD_PASSTHRU, ALL, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 423 {CMD_HELP, ALL, 0, CMNOPT, CMNOPT, CMNOPT, CMNOPT, CMNOPT}, 424 {CMD_ERRTEST, ALL, 0, ALLOPT, ALLOPT, ALLOPT, ALLOPT, ALLOPT}, 425 {CMD_NONE, 0, 0, 0, 0, 0, 0, 0 } 426 }; 427 428 /* 429 * Global mask for options that require values. 430 */ 431 #define AP_VMASK (\ 432 SHFT(OPT_CODE)|SHFT(OPT_MID)|SHFT(OPT_ERR)| \ 433 SHFT(OPT_PLATFORM)|SHFT(OPT_SKIP)) 434 435 #if SBD_DEBUG 436 void 437 ap_cmds_dump() 438 { 439 int i; 440 ap_cmd_t *acp; 441 442 dbg("vmask=0x%x\n", AP_VMASK); 443 dbg("%23s%5s%5s%9s%9s%9s%9s%9s\n", 444 "cmd", "msk", "none", "brd", "cpu", "mem", "io", "cmp"); 445 446 for (acp = ap_cmds; acp->cmd != CMD_NONE; acp++) { 447 dbg("%23s%5x%5x", ap_cmd_name(acp->cmd), acp->cmask, 448 acp->omask[AP_NONE]); 449 for (i = AP_BOARD; i < AP_NCLASS; i++) { 450 dbg("%9x", acp->omask[i]); 451 } 452 dbg("\n"); 453 } 454 } 455 #endif 456 457 int 458 ap_state_cmd(cfga_cmd_t i, int *cmd) 459 { 460 int c; 461 int rc; 462 463 rc = CFGA_OK; 464 465 switch (i) { 466 case CFGA_CMD_CONNECT: 467 c = CMD_CONNECT; 468 break; 469 case CFGA_CMD_DISCONNECT: 470 c = CMD_DISCONNECT; 471 break; 472 case CFGA_CMD_CONFIGURE: 473 c = CMD_CONFIGURE; 474 break; 475 case CFGA_CMD_UNCONFIGURE: 476 c = CMD_UNCONFIGURE; 477 break; 478 case CFGA_CMD_LOAD: 479 case CFGA_CMD_UNLOAD: 480 rc = CFGA_OPNOTSUPP; 481 c = CMD_NONE; 482 break; 483 default: 484 rc = CFGA_INVAL; 485 c = CMD_NONE; 486 break; 487 } 488 489 *cmd = c; 490 491 return (rc); 492 } 493 494 static int 495 ap_cmd(char *name) 496 { 497 int i; 498 char **p; 499 500 if (name == NULL) 501 return (CMD_NONE); 502 503 for (i = 0, p = ap_cmd_names; *p != NULL; p++, i++) 504 if (strcmp(*p, name) == 0) 505 break; 506 if (*p == NULL) 507 i = CMD_NONE; 508 509 return (i); 510 } 511 512 static int 513 ap_opt_parse(apd_t *a, ap_cmd_t *acp, const char *options) 514 { 515 char *optstr; 516 ap_opts_t *opts; 517 518 /* 519 * Set default values. 520 */ 521 opts = &a->opts; 522 opts->mid = (char *)a->class; 523 opts->err = ERR_CMD_FAIL; 524 525 if (options == NULL) 526 return (0); 527 528 if ((optstr = strdup(options)) == NULL) { 529 ap_err(a, ERR_NOMEM); 530 return (-1); 531 } 532 533 a->options = optstr; 534 535 if (acp->cmd == CMD_PASSTHRU) 536 return (0); 537 538 while (*optstr != '\0') { 539 int i; 540 int opt; 541 int omask; 542 char *p; 543 char *value; 544 char *optname; 545 546 value = NULL; 547 opt = getsubopt(&optstr, ap_opt_names, &value); 548 549 DBG("opt=%d\n", opt); 550 551 if (opt == -1) { 552 ap_err(a, ERR_OPT_INVAL, value); 553 return (-1); 554 } 555 556 optname = ap_opt_names[opt]; 557 omask = acp->omask[a->tgt]; 558 559 i = mask(opt) & omask; 560 561 DBG("tgt=%d opt=%x omask=%x\n", a->tgt, mask(opt), omask); 562 563 if (i == 0) { 564 ap_err(a, ERR_OPT_INVAL, optname); 565 return (-1); 566 } 567 568 /* 569 * Check whether the option requires a value. 570 */ 571 i = mask(opt) & AP_VMASK; 572 if (i != 0 && value == NULL) { 573 ap_err(a, ERR_OPT_NOVAL, optname); 574 return (-1); 575 } else if (i == 0 && value != NULL) { 576 ap_err(a, ERR_OPT_VAL, optname); 577 return (-1); 578 } 579 580 if (value == NULL) 581 assert(opt != OPT_CODE); /* XXX prefix */ 582 583 /* 584 * Set the options's value. 585 */ 586 switch (opt) { 587 case OPT_SIM: 588 case OPT_PARSABLE: 589 case OPT_UNASSIGN: 590 break; 591 case OPT_CODE: 592 i = strtol(value, &p, 10); 593 if (p > value) 594 opts->code = i; 595 break; 596 case OPT_MID: 597 opts->mid = value; 598 break; 599 case OPT_ERR: 600 i = strtol(value, &p, 10); 601 if (p > value) 602 opts->err = i; 603 break; 604 case OPT_NOPOWEROFF: 605 i = ap_cmd("poweroff"); 606 opts->skip |= mask(i); 607 break; 608 case OPT_SKIP: /* for debugging */ 609 /* 610 * The skip value may be a ':' separated 611 * list of steps (commands) to be skipped 612 * during sequencing. 613 */ 614 for (p = strtok(value, ":"); p != NULL; 615 p = strtok(NULL, ":")) { 616 if ((i = ap_cmd(p)) == CMD_NONE) { 617 ap_err(a, ERR_CMD_INVAL, p); 618 return (-1); 619 } 620 opts->skip |= mask(i); 621 } 622 break; 623 case OPT_PLATFORM: 624 opts->platform = value; 625 break; 626 default: 627 ap_err(a, ERR_OPT_INVAL, optname); 628 return (-1); 629 } 630 631 ap_setopt(a, opt); 632 } 633 634 return (0); 635 } 636 637 static ap_cmd_t * 638 ap_cmdp(int cmd) 639 { 640 ap_cmd_t *acp; 641 642 for (acp = ap_cmds; acp->cmd != CMD_NONE; acp++) 643 if (acp->cmd == cmd) 644 break; 645 646 if (acp->cmd == CMD_NONE) 647 return (NULL); 648 649 return (acp); 650 } 651 652 cfga_err_t 653 ap_cmd_parse(apd_t *a, const char *f, const char *options, int *cmd) 654 { 655 int c; 656 int all; 657 int tgt; 658 int target; 659 ap_cmd_t *acp; 660 cfga_err_t rc; 661 662 #ifdef _SBD_DEBUG 663 ap_cmds_dump(); 664 #endif 665 666 rc = CFGA_INVAL; 667 668 if ((c = ap_cmd((char *)f)) == CMD_NONE || 669 (acp = ap_cmdp(c)) == NULL) { 670 ap_err(a, ERR_CMD_INVAL, f); 671 return (rc); 672 } 673 674 /* 675 * Change a->statonly to 1, if the case is CMD_STATUS. We are only 676 * wanting to read the devices and no more 677 */ 678 /* 679 * Get the status for all components if either the list all 680 * option being specified or if we are configuring/unconfiguring 681 * the board. The latter is needed for the RCM interface. 682 */ 683 switch (c) { 684 case CMD_STATUS: 685 all = ap_getopt(a, OPT_LIST_ALL); 686 a->statonly = 1; 687 break; 688 case CMD_CONFIGURE: 689 case CMD_UNCONFIGURE: 690 case CMD_CONNECT: 691 case CMD_DISCONNECT: 692 all = (a->tgt == AP_BOARD); 693 a->statonly = 0; 694 break; 695 default: 696 all = 0; 697 a->statonly = 0; 698 break; 699 } 700 701 if ((rc = apd_init(a, all)) != CFGA_OK) 702 return (rc); 703 704 rc = CFGA_INVAL; 705 706 /* 707 * Get the target here in case it is a component in which 708 * case its type is known after the initialization. 709 */ 710 tgt = a->tgt; 711 target = mask(tgt); 712 713 DBG("cmd=%s(%d) tmask=0x%x cmask=0x%x omask=0x%x\n", 714 ap_cmd_name(c), c, target, 715 acp->cmask, acp->omask[tgt]); 716 717 if ((acp->cmask & target) == 0) 718 ap_err(a, ERR_CMD_NOTSUPP, c); 719 else if (options != NULL && acp->omask[tgt] == 0) 720 ap_err(a, ERR_OPT_INVAL, options); 721 else if (ap_opt_parse(a, acp, options) != -1) { 722 if (c == CMD_STATUS) 723 rc = ap_platopts_check(a, c, c); 724 else 725 rc = CFGA_OK; 726 } 727 728 if (cmd) 729 *cmd = c; 730 731 return (rc); 732 } 733 734 int 735 ap_cnt(apd_t *a) 736 { 737 int cnt; 738 739 if ((a->tgt == AP_BOARD) && ap_getopt(a, OPT_LIST_ALL)) 740 cnt = a->ncm + 1; 741 else 742 cnt = 1; 743 744 return (cnt); 745 } 746