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
ap_symid(apd_t * a,char * apid,char * symid,size_t bufsize)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 *
ap_logid(apd_t * a,char * apid)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
ap_parse(apd_t * a,const char * ap_id)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 *
ap_cmd_name(int i)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 *
ap_opt_name(int i)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
ap_cmds_dump()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
ap_state_cmd(cfga_cmd_t i,int * cmd)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
ap_cmd(char * name)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
ap_opt_parse(apd_t * a,ap_cmd_t * acp,const char * options)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 *
ap_cmdp(int cmd)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
ap_cmd_parse(apd_t * a,const char * f,const char * options,int * cmd)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
ap_cnt(apd_t * a)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