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