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