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
ap_getncm(apd_t * a,sbd_comp_type_t type,int * ncm)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
ap_stat(apd_t * a,int all)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
ap_cm_tgt(sbd_comp_type_t type)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
apd_init(apd_t * a,int all)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
apd_free(apd_t * a)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 *
apd_alloc(const char * ap_id,cfga_flags_t flags,char ** errstring,struct cfga_msg * msgp,struct cfga_confirm * confp)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(1M): It
378 * must not contain white space characters. This function
379 * converts white space to underscore.
380 */
381
382 static void
parsable_strncpy(char * op,const char * ip,size_t n)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
ap_init(apd_t * a,cfga_list_data_t * ap)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
ap_ioc(int cmd)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
ap_suspend_query(apd_t * a,int cmd,int * check)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
ap_platopts_check(apd_t * a,int first,int last)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
ap_ioctl(apd_t * a,int cmd)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 *
mod_estr(int code)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 *
ap_sys_err(apd_t * a,char ** rp)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
ap_test_err(apd_t * a,const char * options)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
ap_help(struct cfga_msg * msgp,const char * options,cfga_flags_t flags)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 *
ap_dev_type(sbd_dev_stat_t * dst)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 *
ap_cm_stat(apd_t * a,int seq)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 *
ap_cm_devpath(apd_t * a,int seq)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
ap_cm_id(apd_t * a,int seq,char * id,size_t bufsize)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
ap_cm_type(apd_t * a,int seq)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
ap_cm_ncap(apd_t * a,int seq)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
ap_cm_capacity(apd_t * a,int seq,void * cap,int * ncap,cfga_stat_t * ostate)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
ap_cm_init(apd_t * a,cfga_list_data_t * ap,int seq)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
ap_state(apd_t * a,cfga_stat_t * rs,cfga_stat_t * os)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
bd_info(apd_t * a,cfga_info_t info,int parsable)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
cpu_info(apd_t * a,cfga_info_t info,int parsable)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
pages_to_kbytes(uint_t pgs)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
pages_to_bytes(uint_t pgs)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
mem_info(apd_t * a,cfga_info_t info,int parsable)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
io_info(apd_t * a,cfga_info_t info,int parsable)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
cmp_info(apd_t * a,cfga_info_t info,int parsable)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
ap_info(apd_t * a,cfga_info_t info,ap_target_t tgt)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