xref: /illumos-gate/usr/src/lib/cfgadm_plugins/sbd/common/ap_sbd.c (revision 22fb2eeb50dd2ffd3aefea05536263203fae0600)
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