xref: /illumos-gate/usr/src/lib/cfgadm_plugins/ac/common/mema.c (revision e2738c5e21a9e5d9a6525e48af4738deda3df455)
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 2005 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 <stddef.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <fcntl.h>
34 #include <signal.h>
35 #include <string.h>
36 #include <locale.h>
37 #include <errno.h>
38 #include <assert.h>
39 #include <sys/dditypes.h>
40 #include <sys/param.h>
41 #include <sys/obpdefs.h>
42 #include <sys/fhc.h>
43 #include <sys/sysctrl.h>
44 #include <sys/ac.h>
45 #include <sys/spitregs.h>
46 #include <config_admin.h>
47 #include "mema_util.h"
48 #include "mema_test.h"
49 #include "mema_prom.h"
50 
51 #ifdef	DEBUG
52 #define	DBG	(void) printf
53 #define	DBG1	(void) printf
54 #define	DBG3	(void) printf
55 #define	DBG4	(void) printf
56 #else
57 #define	DBG(a, b)
58 #define	DBG1(a)
59 #define	DBG3(a, b, c)
60 #define	DBG4(a, b, c, d)
61 #endif
62 
63 #ifndef P_DER_UE
64 /*
65  * <sys/spitregs.h> has these defines inside 'ifdef _KERNEL' at the
66  * time of writing.  Re-define here if that is still the case.
67  */
68 
69 #define	P_DER_UE	0x00000000000000200ULL	/* UE has occurred */
70 #define	P_DER_CE	0x00000000000000100ULL	/* CE has occurred */
71 #define	P_DER_E_SYND	0x000000000000000FFULL	/* SYND<7:0>: ECC syndrome */
72 #endif /* ! P_DER_UE */
73 
74 #define	DEV_DEBUG
75 #ifdef DEV_DEBUG
76 #include <stdio.h>
77 #include <stdlib.h>
78 
79 static FILE *debug_fp;
80 static int debugging(void);
81 static void dump_ioctl(int, void *);
82 static void dump_ioctl_res(int, void *, int, int);
83 #else /* DEV_DEBUG */
84 #define	dump_ioctl(CMD, ARG)
85 #define	dump_ioctl_res(CMD, ARG, RET, ERRNO)
86 #endif /* DEV_DEBUG */
87 
88 typedef struct {
89 	uint_t   board;
90 	uint_t   bank;
91 } mema_bank_t;
92 
93 static char *mema_opts[] = {
94 #define	OPT_BOOT_DISABLE	0
95 	"disable-at-boot",
96 #define	OPT_BOOT_ENABLE		1
97 	"enable-at-boot",
98 #define	OPT_TIMEOUT		2
99 	"timeout",
100 	NULL
101 };
102 
103 #define	OPT_NEEDS_VALUE(O)	((O) == OPT_TIMEOUT)
104 
105 #define	MAX_OPT_LENGTH		(sizeof ("disable-at-boot"))
106 
107 /*
108  * For each function there is an array of opt_control structures giving
109  * the valid options.  The array is terminated by an element with the
110  * subopt field set to -1.  The group field is used to identify
111  * mutually exclusive options, with zero meaning no grouping.
112  */
113 struct opt_control {
114 	int		subopt;
115 	int		group;
116 };
117 
118 /*
119  * Returned set of options.
120  * If the option takes a value, it will be set in 'val'
121  * if the corresponding bit is set in 'bits' is set,
122  * otherwise the pointer in 'val' is undefined.
123  */
124 #define	OPT_VAL_ARRAY_SIZE	32	/* # bits in 'bits' */
125 typedef struct {
126 	unsigned int	bits;
127 	char		*val[OPT_VAL_ARRAY_SIZE];
128 } option_set_t;
129 
130 #define	OPTSET_INIT(S)		((S).bits = 0)
131 #define	_OPT_TO_BIT(O)		(1 << (O))
132 #define	OPTSET_SET_VAL(S, O, V)	((S).bits |= _OPT_TO_BIT(O), \
133 				(S).val[(O)] = (V))
134 #define	OPTSET_TEST(S, O)	(((S).bits & _OPT_TO_BIT(O)) != 0)
135 #define	OPTSET_VAL(S, O)	((S).val[(O)])
136 #define	OPTSET_IS_EMPTY(S)	((S).bits == 0)
137 
138 static option_set_t process_options(const char *, struct opt_control *,
139 	int *, char **);
140 
141 static struct opt_control add_opts[] = {
142 	{OPT_BOOT_ENABLE, 1},
143 	{OPT_BOOT_DISABLE, 1},
144 	{-1, 0}
145 };
146 
147 static struct opt_control del_opts[] = {
148 	{OPT_BOOT_ENABLE, 1},
149 	{OPT_BOOT_DISABLE, 1},
150 	{OPT_TIMEOUT, 2},
151 	{-1, 0}
152 };
153 
154 static struct opt_control stat_opts[] = {
155 	{OPT_BOOT_ENABLE, 1},
156 	{OPT_BOOT_DISABLE, 1},
157 	{-1, 0}
158 };
159 
160 #if !defined(TEXT_DOMAIN)
161 #define	TEXT_DOMAIN	"SYS_TEST"
162 #endif
163 
164 static const char still_testing[] = "bank %s being tested by process %d";
165 static const char no_value[] = "sub-option \"%s\" does not take a value";
166 static const char missing_value[] = "sub-option \"%s\" needs a value";
167 static const char conflict_opt[] = "sub-option \"%s\" conflicts with \"%s\"";
168 static const char unk_subopt[] = "sub-option \"%s\" unknown\n"
169 	"choose from: %s";
170 static const char not_valid[] =
171 	"sub-option \"%s\" not valid for this operation\n"
172 	"choose from: %s";
173 static const char timeout_notnum[] =
174 	"timeout value not a positive integer \"%s\"";
175 static const char calloc_fail[] = "memory allocation failed (%d*%d bytes)";
176 static const char unk_test[] = "test \"%s\" unknown\n"
177 	"choose from: %s";
178 static const char dup_test[] = "more than one test type specified (\"%s\")";
179 static const char dup_num[] = "option specified more than once (\"%s\")";
180 static const char no_num[] = "invalid number specified for max_errors(\"%s\")";
181 static const char mtest_rw_error[] = "memory test read/write error";
182 static const char mtest_lib_error[] = "memory test library error";
183 static const char dlist_invalid[] = "invalid disabled-memory-list";
184 static const char dlist_write_failed[] = "disabled-memory-list write failed";
185 static const char mtest_unknown_error[] = "unknown memory test error";
186 static const char ap_invalid[] = "invalid attachment point: %s";
187 static const char trans_illegal[] = "illegal transition";
188 static const char open_failed[] = "open failed: %s: %s";
189 static const char mema_help[] =	"\nAc specific options:\n";
190 static const char disable_opts[] = "\t-o disable-at-boot\n";
191 static const char enable_opts[] = "\t-o enable-at-boot\n";
192 static const char timeout_opts[] = "\t-o timeout=# (seconds)\n";
193 static const char test_opts[] =
194 	"\t-o {quick, normal, extended},[max_errors=#] -t ap_id [ap_id...]\n";
195 static const char private_funcs[] = "\t-x relocate-test ap_id [ap_id...]\n";
196 static const char add_is_disabled[] = "memory is disabled at boot";
197 static const char add_willbe_disabled[] =
198 	"memory will be disabled at boot";
199 static const char add_disab_err[] = "cannot get memory disabled status";
200 static const char pfunc_unknown[] = "private function \"%s\" unknown";
201 
202 
203 #define	mema_eid(a, b)		(((a) << 8) + (b))
204 #define	mema_str(i)		mema_strs[(i)]
205 
206 #define	AC_BK_BUSY		0
207 #define	AC_BK_ID		1
208 #define	AC_BD_ID		2
209 #define	AC_BD_TYPE		3
210 #define	AC_BD_STATE		4
211 #define	AC_MEM_TEST_ID		5
212 #define	AC_MEM_TEST_PAR		6
213 #define	AC_MEM_PERM		7
214 #define	AC_KPM_CANCELLED	8
215 #define	AC_KPM_REFUSED		9
216 #define	AC_KPM_SPAN		10
217 #define	AC_KPM_DUP		11
218 #define	AC_KPM_FAULT		12
219 #define	AC_KPM_RESOURCE		13
220 #define	AC_KPM_NOTSUP		14
221 #define	AC_KPM_NOHANDLES	15
222 #define	AC_KPM_NONRELOC		16
223 #define	AC_KPM_HANDLE		17
224 #define	AC_KPM_BUSY		18
225 #define	AC_KPM_NOTVIABLE	19
226 #define	AC_KPM_SEQUENCE		20
227 #define	AC_KPM_NOWORK		21
228 #define	AC_KPM_NOTFINISHED	22
229 #define	AC_KPM_NOTRUNNING	23
230 #define	AC_VMEM			24
231 #define	CMD_MEM_STAT		25
232 #define	CMD_MEM_ADD		26
233 #define	CMD_MEM_DEL		27
234 #define	CMD_MEM_TEST_START	28
235 #define	CMD_MEM_TEST_STOP	29
236 #define	AC_UNKNOWN		30
237 #define	AC_INTR			31
238 #define	AC_TIMEOUT		32
239 #define	CMD_MEM_RELOCTEST	33
240 #define	AC_DEINTLV		34
241 
242 static char *
243 mema_strs[] = {
244 	"memory bank busy",
245 	"invalid memory bank",
246 	"invalid board id",
247 	"invalid board type",
248 	"invalid board state",
249 	"invalid memory test id",
250 	"invalid memory test parameter(s)",
251 	"no write permission",
252 	"memory operation cancelled",
253 	"memory operation refused",
254 	"memory already in use (add)",
255 	"memory span duplicate (delete)",
256 	"memory access test failed (add)",
257 	"some resource was not available",
258 	"operation not supported",
259 	"cannot allocate any more handles",
260 	"non-relocatable pages in span",
261 	"bad handle supplied",
262 	"memory in span is being deleted",
263 	"VM viability test failed",
264 	"function called out of sequence",
265 	"no memory to delete",
266 	"delete processing not finished",
267 	"delete processing not running",
268 	"insufficient virtual memory",
269 	"memory stat failed: %s",
270 	"memory add failed: %s",
271 	"memory delete failed: %s",
272 	"memory test start failed: %s",
273 	"memory test stop failed: %s",
274 	"unknown error",
275 	"memory delete killed",
276 	"memory delete timeout",
277 	"memory relocate-test failed: %s",
278 	"memory cannot be de-interleaved"
279 };
280 
281 /*
282  *	AC_MEM_PERM,		EBADF,   AC_ERR_MEM_PERM
283  *	AC_BK_BUSY,		EBUSY,   AC_ERR_MEM_BK
284  *	AC_KPM_CANCELLED,	EINTR,   AC_ERR_KPM_CANCELLED
285  *	AC_KPM_REFUSED,		EINTR,   AC_ERR_KPM_REFUSED
286  *	AC_BK_ID,		EINVAL,  AC_ERR_MEM_BK
287  *	AC_BD_ID,		EINVAL,  AC_ERR_BD
288  *	AC_BD_TYPE,		EINVAL,  AC_ERR_BD_TYPE
289  *	AC_BD_STATE,		EINVAL,  AC_ERR_BD_STATE
290  *	AC_MEM_TEST_ID,		EINVAL,  AC_ERR_MEM_TEST
291  *	AC_MEM_TEST_PAR,	EINVAL,  AC_ERR_MEM_TEST_PAR
292  *	AC_KPM_SPAN,		EINVAL,  AC_ERR_KPM_SPAN
293  *	AC_KPM_DUP,		EINVAL,  AC_ERR_KPM_DUP?
294  *	AC_KPM_FAULT,		EINVAL,  AC_ERR_KPM_FAULT
295  *	AC_KPM_RESOURCE,	EINVAL,  AC_ERR_KPM_RESOURCE
296  *	AC_KPM_NOTSUP,		EINVAL,  AC_ERR_KPM_NOTSUP
297  *	AC_KPM_NOHANDLES,	EINVAL,  AC_ERR_KPM_NOHANDLES
298  *	AC_KPM_NONRELOC,	EINVAL,  AC_ERR_KPM_NONRELOC
299  *	AC_KPM_HANDLE,		EINVAL,  AC_ERR_KPM_HANDLE
300  *	AC_KPM_BUSY,		EINVAL,  AC_ERR_KPM_BUSY
301  *	AC_KPM_NOTVIABLE,	EINVAL,  AC_ERR_KPM_NOTVIABLE
302  *	AC_KPM_SEQUENCE,	EINVAL,  AC_ERR_KPM_SEQUENCE
303  *	AC_KPM_NOWORK,		EINVAL,  AC_ERR_KPM_NOWORK
304  *	AC_KPM_NOTFINISHED,	EINVAL,  AC_ERR_KPM_NOTFINISHED
305  *	AC_KPM_NOTRUNNING,	EINVAL,  AC_ERR_KPM_NOTRUNNING
306  *	AC_VMEM,		ENOMEM,  AC_ERR_VMEM
307  *	AC_INTR,		EINTR,   AC_ERR_INTR
308  *	AC_TIMEOUT,		EINTR,   AC_ERR_TIMEOUT
309  *	AC_DEINTLV,		EINVAL,  AC_ERR_MEM_DEINTLV
310  */
311 static int
312 mema_sid(int err, int acerr)
313 {
314 	if (acerr == AC_ERR_DEFAULT)
315 		return (AC_UNKNOWN);
316 
317 	switch (mema_eid(err, acerr)) {
318 	case mema_eid(EBADF, AC_ERR_MEM_PERM):
319 		return (AC_MEM_PERM);
320 	case mema_eid(EBUSY, AC_ERR_MEM_BK):
321 		return (AC_BK_BUSY);
322 	case mema_eid(EINTR, AC_ERR_KPM_CANCELLED):
323 		return (AC_KPM_CANCELLED);
324 	case mema_eid(EINTR, AC_ERR_KPM_REFUSED):
325 		return (AC_KPM_REFUSED);
326 	case mema_eid(EINVAL, AC_ERR_MEM_BK):
327 		return (AC_BK_ID);
328 	case mema_eid(EINVAL, AC_ERR_BD):
329 		return (AC_BD_ID);
330 	case mema_eid(EINVAL, AC_ERR_BD_TYPE):
331 		return (AC_BD_TYPE);
332 	case mema_eid(EINVAL, AC_ERR_BD_STATE):
333 		return (AC_BD_STATE);
334 	case mema_eid(EINVAL, AC_ERR_MEM_TEST):
335 		return (AC_MEM_TEST_ID);
336 	case mema_eid(EINVAL, AC_ERR_MEM_TEST_PAR):
337 		return (AC_MEM_TEST_PAR);
338 	case mema_eid(EINVAL, AC_ERR_KPM_SPAN):
339 		return (AC_KPM_SPAN);
340 	case mema_eid(EINVAL, AC_ERR_KPM_DUP):
341 		return (AC_KPM_DUP);
342 	case mema_eid(EINVAL, AC_ERR_KPM_FAULT):
343 		return (AC_KPM_FAULT);
344 	case mema_eid(EINVAL, AC_ERR_KPM_RESOURCE):
345 		return (AC_KPM_RESOURCE);
346 	case mema_eid(EINVAL, AC_ERR_KPM_NOTSUP):
347 		return (AC_KPM_NOTSUP);
348 	case mema_eid(EINVAL, AC_ERR_KPM_NOHANDLES):
349 		return (AC_KPM_NOHANDLES);
350 	case mema_eid(EINVAL, AC_ERR_KPM_NONRELOC):
351 		return (AC_KPM_NONRELOC);
352 	case mema_eid(EINVAL, AC_ERR_KPM_HANDLE):
353 		return (AC_KPM_HANDLE);
354 	case mema_eid(EINVAL, AC_ERR_KPM_BUSY):
355 		return (AC_KPM_BUSY);
356 	case mema_eid(EINVAL, AC_ERR_KPM_NOTVIABLE):
357 		return (AC_KPM_NOTVIABLE);
358 	case mema_eid(EINVAL, AC_ERR_KPM_SEQUENCE):
359 		return (AC_KPM_SEQUENCE);
360 	case mema_eid(EINVAL, AC_ERR_KPM_NOWORK):
361 		return (AC_KPM_NOWORK);
362 	case mema_eid(EINVAL, AC_ERR_KPM_NOTFINISHED):
363 		return (AC_KPM_NOTFINISHED);
364 	case mema_eid(EINVAL, AC_ERR_KPM_NOTRUNNING):
365 		return (AC_KPM_NOTRUNNING);
366 	case mema_eid(ENOMEM, AC_ERR_VMEM):
367 		return (AC_VMEM);
368 	case mema_eid(EINTR, AC_ERR_INTR):
369 		return (AC_INTR);
370 	case mema_eid(EINTR, AC_ERR_TIMEOUT):
371 		return (AC_TIMEOUT);
372 	case mema_eid(EINVAL, AC_ERR_MEM_DEINTLV):
373 		return (AC_DEINTLV);
374 	default:
375 		break;
376 	}
377 
378 	return (AC_UNKNOWN);
379 }
380 
381 static void
382 mema_err(ac_cfga_cmd_t *ac, int ret_errno, char **errstring, int cmd)
383 {
384 	char *cname = mema_str(cmd);
385 	char *syserr;
386 	char syserr_num[20];
387 
388 	if (ac) {
389 		syserr = mema_str(mema_sid(ret_errno, ac->errtype));
390 		syserr = dgettext(TEXT_DOMAIN, syserr);
391 	} else {
392 		syserr = strerror(ret_errno);
393 		/* strerror() does its own gettext(). */
394 		if (syserr == NULL) {
395 			(void) sprintf(syserr_num, "errno=%d", errno);
396 			syserr = syserr_num;
397 		}
398 	}
399 
400 	__fmt_errstring(errstring, strlen(syserr),
401 	    dgettext(TEXT_DOMAIN, cname), syserr);
402 }
403 
404 static void
405 mema_cmd_init(ac_cfga_cmd_t *ac, void *cmd, char *outputstr, int force)
406 {
407 	(void) memset((void *)ac, 0, sizeof (*ac));
408 
409 	ac->errtype = AC_ERR_DEFAULT;
410 	ac->private = cmd;
411 	ac->force = force;
412 	ac->outputstr = outputstr;
413 
414 	(void) memset((void *)outputstr, 0, AC_OUTPUT_LEN);
415 }
416 
417 static int
418 ap_bk_idx(const char *ap_id)
419 {
420 	int id;
421 	char *s;
422 	static char *bank = "bank";
423 
424 	DBG("ap_bk_idx(%s)\n", ap_id);
425 
426 	if ((s = strstr(ap_id, bank)) == NULL)
427 		return (-1);
428 	else {
429 		int n;
430 
431 		s += strlen(bank);
432 		n = strlen(s);
433 
434 		DBG3("ap_bk_idx: s=%s, n=%d\n", s, n);
435 
436 		if ((n != 1) || !isdigit(s[0]))
437 			return (-1);
438 	}
439 
440 	id = atoi(s);
441 
442 	if (id < 0 || id > 1)
443 		return (-1);
444 
445 	DBG3("ap_bk_idx(%s)=%d\n", s, id);
446 
447 	return (id);
448 }
449 
450 static cfga_err_t
451 ap_stat(
452 	const char *bank_spec,
453 	int *fdp,
454 	mema_bank_t *bkp,
455 	ac_stat_t *stp,
456 	char **errstring)
457 {
458 	int fd;
459 	int ret, ret_errno;
460 	int bank;
461 	mema_bank_t bk;
462 	ac_stat_t stat;
463 	ac_cfga_cmd_t cmd;
464 	char outputstr[AC_OUTPUT_LEN];
465 
466 	if ((bank = ap_bk_idx(bank_spec)) == -1) {
467 		__fmt_errstring(errstring, strlen(bank_spec),
468 			dgettext(TEXT_DOMAIN, ap_invalid), bank_spec);
469 		return (CFGA_ERROR);
470 	}
471 
472 	bk.bank = bank;
473 
474 	if ((fd = open(bank_spec, ((fdp != NULL) ? O_RDWR : O_RDONLY), 0)) ==
475 	    -1) {
476 		char *syserr;
477 		char syserr_num[20];
478 
479 		syserr = strerror(errno);
480 		if (syserr == NULL) {
481 			(void) sprintf(syserr_num, "errno=%d", errno);
482 			syserr = syserr_num;
483 		}
484 		__fmt_errstring(errstring, strlen(syserr) +
485 		    strlen(bank_spec),
486 		    dgettext(TEXT_DOMAIN, open_failed), bank_spec, syserr);
487 		return (CFGA_ERROR);
488 	}
489 
490 	mema_cmd_init(&cmd, &stat, outputstr, 0);
491 	dump_ioctl(AC_MEM_STAT, NULL);
492 	ret = ioctl(fd, AC_MEM_STAT, &cmd);
493 	ret_errno = errno;
494 	dump_ioctl_res(AC_MEM_STAT, &stat, ret, ret_errno);
495 
496 	if (ret == -1) {
497 		mema_err(&cmd, ret_errno, errstring, CMD_MEM_STAT);
498 		(void) close(fd);
499 		return (CFGA_ERROR);
500 	}
501 
502 	if (fdp)
503 		*fdp = fd;
504 	else
505 		(void) close(fd);
506 
507 	if (stp)
508 		*stp = stat;
509 
510 	if (bkp) {
511 		bkp->bank = bk.bank;
512 		bkp->board = stat.board;
513 	}
514 
515 	return (CFGA_OK);
516 }
517 
518 static void
519 set_disabled_bits(mema_disabled_t *dp, int value)
520 {
521 	if (value == 0)
522 		*dp &= ~PROM_MEMORY_DISABLED;
523 	else
524 		*dp |= PROM_MEMORY_DISABLED;
525 }
526 
527 static void
528 set_present_bits(mema_disabled_t *dp, ac_stat_t *asp)
529 {
530 	if (asp->ostate == SYSC_CFGA_OSTATE_CONFIGURED)
531 		*dp |= PROM_MEMORY_PRESENT;
532 	else
533 		*dp &= ~PROM_MEMORY_DISABLED;
534 }
535 
536 static cfga_err_t
537 prom_do_options(
538 	option_set_t do_option,
539 	int board,
540 	ac_stat_t *asp,
541 	char **errstring)
542 {
543 	cfga_err_t ret;
544 	mema_disabled_t disab;
545 
546 	if (!prom_read_disabled_list(&disab, board))
547 		return (CFGA_ERROR);
548 
549 	set_present_bits(&disab, asp);
550 
551 	ret = CFGA_OK;
552 
553 	if (OPTSET_TEST(do_option, OPT_BOOT_ENABLE)) {
554 		set_disabled_bits(&disab, 0);
555 		if (!prom_viable_disabled_list(&disab)) {
556 			__fmt_errstring(errstring, 0,
557 			    dgettext(TEXT_DOMAIN, dlist_invalid));
558 			ret = CFGA_ERROR;
559 		} else if (!prom_write_disabled_list(&disab, board)) {
560 			__fmt_errstring(errstring, 0,
561 			    dgettext(TEXT_DOMAIN, dlist_write_failed));
562 			ret = CFGA_ERROR;
563 		}
564 	} else if (OPTSET_TEST(do_option, OPT_BOOT_DISABLE)) {
565 		set_disabled_bits(&disab, 1);
566 		if (!prom_viable_disabled_list(&disab)) {
567 			__fmt_errstring(errstring, 0,
568 			    dgettext(TEXT_DOMAIN, dlist_invalid));
569 			ret = CFGA_ERROR;
570 		} else if (!prom_write_disabled_list(&disab, board)) {
571 			__fmt_errstring(errstring, 0,
572 			    dgettext(TEXT_DOMAIN, dlist_write_failed));
573 			ret = CFGA_ERROR;
574 		}
575 	}
576 
577 	return (ret);
578 }
579 
580 static cfga_err_t
581 mema_add(
582 	const char *bank_spec,
583 	const char *options,
584 	char **errstring,
585 	int force)
586 {
587 	mema_bank_t bk;
588 	int fd, ret, ret_errno;
589 	option_set_t do_option;
590 	ac_cfga_cmd_t cmd;
591 	ac_stat_t stat;
592 	char outputstr[AC_OUTPUT_LEN];
593 
594 	ret = 0;
595 	do_option = process_options(options, add_opts, &ret, errstring);
596 	if (ret != 0) {
597 		return (ret);
598 	}
599 
600 	ret = ap_stat(bank_spec, &fd, &bk, &stat, errstring);
601 	if (ret != CFGA_OK)
602 		return (ret);
603 
604 
605 	if (stat.rstate != SYSC_CFGA_RSTATE_CONNECTED ||
606 	    stat.ostate != SYSC_CFGA_OSTATE_UNCONFIGURED) {
607 		__fmt_errstring(errstring, 0,
608 		    dgettext(TEXT_DOMAIN, trans_illegal));
609 		(void) close(fd);
610 		return (CFGA_ERROR);
611 	}
612 
613 	if (!force) {
614 		mema_disabled_t disab;
615 
616 		if (prom_read_disabled_list(&disab, bk.board)) {
617 			if (disab != 0 &&
618 			    !OPTSET_TEST(do_option, OPT_BOOT_ENABLE)) {
619 				__fmt_errstring(errstring, 0,
620 				    dgettext(TEXT_DOMAIN, add_is_disabled));
621 				(void) close(fd);
622 				return (CFGA_ERROR);
623 			}
624 			if (disab == 0 &&
625 			    OPTSET_TEST(do_option, OPT_BOOT_DISABLE)) {
626 				__fmt_errstring(errstring, 0,
627 				    dgettext(TEXT_DOMAIN, add_willbe_disabled));
628 				(void) close(fd);
629 				return (CFGA_ERROR);
630 			}
631 		} else {
632 			__fmt_errstring(errstring, 0,
633 			    dgettext(TEXT_DOMAIN, add_disab_err));
634 			(void) close(fd);
635 			return (CFGA_ERROR);
636 		}
637 	}
638 
639 	mema_cmd_init(&cmd, NULL, outputstr, force);
640 	dump_ioctl(AC_MEM_CONFIGURE, NULL);
641 	ret = ioctl(fd, AC_MEM_CONFIGURE, &cmd);
642 	ret_errno = errno;
643 	dump_ioctl_res(AC_MEM_CONFIGURE, NULL, ret, ret_errno);
644 	(void) close(fd);
645 
646 	if (ret == -1) {
647 		mema_err(&cmd, ret_errno, errstring, CMD_MEM_ADD);
648 		return (CFGA_ERROR);
649 	}
650 
651 	ret = prom_do_options(do_option, bk.board, &stat, errstring);
652 
653 	return (ret);
654 }
655 
656 static cfga_err_t
657 mema_delete(
658 	const char *bank_spec,
659 	const char *options,
660 	char **errstring,
661 	int force)
662 {
663 	mema_bank_t bk;
664 	int fd, ret, ret_errno;
665 	option_set_t do_option;
666 	ac_cfga_cmd_t cmd;
667 	ac_stat_t stat;
668 	char outputstr[AC_OUTPUT_LEN];
669 	int timeout_secs = -1;	/* Init to 'use default'. */
670 
671 	ret = 0;
672 	do_option = process_options(options, del_opts, &ret, errstring);
673 	if (ret != 0) {
674 		return (ret);
675 	}
676 
677 	if (OPTSET_TEST(do_option, OPT_TIMEOUT)) {
678 		char *to_val;
679 		char *ep;
680 
681 		to_val = OPTSET_VAL(do_option, OPT_TIMEOUT);
682 		timeout_secs = (int)strtol(to_val, &ep, 10);
683 		if (*ep != '\0' || ep == to_val || timeout_secs < 0) {
684 			__fmt_errstring(errstring, strlen(to_val),
685 			    dgettext(TEXT_DOMAIN, timeout_notnum), to_val);
686 			return (CFGA_ERROR);
687 		}
688 	}
689 
690 	ret = ap_stat(bank_spec, &fd, &bk, &stat, errstring);
691 	if (ret != CFGA_OK)
692 		return (ret);
693 
694 	if (stat.rstate != SYSC_CFGA_RSTATE_CONNECTED ||
695 	    stat.ostate != SYSC_CFGA_OSTATE_CONFIGURED) {
696 		__fmt_errstring(errstring, 0,
697 		    dgettext(TEXT_DOMAIN, trans_illegal));
698 		(void) close(fd);
699 		return (CFGA_ERROR);
700 	}
701 
702 	mema_cmd_init(&cmd, NULL, outputstr, force);
703 	cmd.arg = timeout_secs;
704 	dump_ioctl(AC_MEM_UNCONFIGURE, NULL);
705 	ret = ioctl(fd, AC_MEM_UNCONFIGURE, &cmd);
706 	ret_errno = errno;
707 	dump_ioctl_res(AC_MEM_UNCONFIGURE, NULL, ret, ret_errno);
708 	(void) close(fd);
709 
710 	if (ret == -1) {
711 		mema_err(&cmd, ret_errno, errstring, CMD_MEM_DEL);
712 		return (CFGA_ERROR);
713 	}
714 
715 	ret = prom_do_options(do_option, bk.board, &stat, errstring);
716 
717 	return (ret);
718 }
719 
720 /*ARGSUSED*/
721 cfga_err_t
722 cfga_change_state(
723 	cfga_cmd_t state_change_cmd,
724 	const char *ap_id,
725 	const char *options,
726 	struct cfga_confirm *confp,
727 	struct cfga_msg *msgp,
728 	char **errstring,
729 	cfga_flags_t flags)
730 {
731 	int force;
732 	cfga_err_t rc;
733 
734 	if (errstring != NULL)
735 		*errstring = NULL;
736 
737 	force = flags & CFGA_FLAG_FORCE;
738 
739 	switch (state_change_cmd) {
740 	case CFGA_CMD_CONFIGURE:
741 		rc =  mema_add(ap_id, options, errstring, force);
742 		break;
743 
744 	case CFGA_CMD_UNCONFIGURE:
745 		rc =  mema_delete(ap_id, options, errstring, force);
746 		break;
747 
748 	default:
749 		rc = CFGA_OPNOTSUPP;
750 		break;
751 	}
752 
753 	return (rc);
754 }
755 
756 /*ARGSUSED*/
757 cfga_err_t
758 cfga_private_func(
759 	const char *function,
760 	const char *ap_id,
761 	const char *options,
762 	struct cfga_confirm *confp,
763 	struct cfga_msg *msgp,
764 	char **errstring,
765 	cfga_flags_t flags)
766 {
767 	mema_bank_t bk;
768 	ac_stat_t stat;
769 	int fd, ret, ret_errno;
770 	ac_cfga_cmd_t cmd;
771 	char outputstr[AC_OUTPUT_LEN];
772 
773 	if (errstring != NULL)
774 		*errstring = NULL;
775 
776 	ret = ap_stat(ap_id, &fd, &bk, &stat, errstring);
777 	if (ret != CFGA_OK)
778 		return (ret);
779 
780 	if (strcmp(function, "relocate-test") == 0) {
781 		struct ac_memx_relocate_stats rstat;
782 
783 		mema_cmd_init(&cmd, NULL, outputstr,
784 		    (flags & CFGA_FLAG_FORCE));
785 		cmd.arg = AC_MEMX_RELOCATE_ALL;
786 		cmd.private = &rstat;
787 		(void) memset((void *)&rstat, 0, sizeof (rstat));
788 		dump_ioctl(AC_MEM_EXERCISE, &cmd);
789 		ret = ioctl(fd, AC_MEM_EXERCISE, &cmd);
790 		ret_errno = errno;
791 		dump_ioctl_res(AC_MEM_EXERCISE, &cmd, ret, ret_errno);
792 		(void) close(fd);
793 
794 		if (ret == -1) {
795 			mema_err(&cmd, ret_errno, errstring, CMD_MEM_RELOCTEST);
796 			return (CFGA_ERROR);
797 		}
798 		return (CFGA_OK);
799 	}
800 
801 	__fmt_errstring(errstring, strlen(function),
802 	    dgettext(TEXT_DOMAIN, pfunc_unknown), function);
803 
804 	return (CFGA_ERROR);
805 }
806 
807 static int
808 mtest_run(
809 	int fd,
810 	int test_fun,
811 	mema_bank_t *abkp,
812 	struct cfga_msg *msgp,
813 	char **errstring,
814 	ulong_t max_errors)
815 {
816 	ac_mem_test_start_t test_start;
817 	ac_mem_test_stop_t test_stop;
818 	struct mtest_handle handle;
819 	int ret, ret_errno;
820 	int res;
821 	ac_cfga_cmd_t cmd;
822 	char outputstr[AC_OUTPUT_LEN];
823 
824 	(void) memset((void *)&test_start, 0, sizeof (test_start));
825 	mema_cmd_init(&cmd, &test_start, outputstr, 0);
826 	dump_ioctl(AC_MEM_TEST_START, &test_start);
827 	ret = ioctl(fd, AC_MEM_TEST_START, &cmd);
828 	ret_errno = errno;
829 	dump_ioctl_res(AC_MEM_TEST_START, &test_start, ret, ret_errno);
830 
831 	if (ret == -1) {
832 		if (ret_errno == ENOTSUP) {
833 			mema_err(&cmd, ret_errno, errstring,
834 				CMD_MEM_TEST_START);
835 			return (CFGA_OPNOTSUPP);
836 		}
837 		if (ret_errno == EBUSY && test_start.tester_pid > 0) {
838 			/*
839 			 * Bank appears to be being tested.  Check that
840 			 * process 'tester_pid' is still running.
841 			 */
842 			if (kill(test_start.tester_pid, 0) != -1 ||
843 			    errno != ESRCH) {
844 				cfga_ap_log_id_t bname;
845 
846 				/* Process still exists. */
847 				(void) sprintf(bname, "board %d bank%d",
848 				    abkp->board, abkp->bank);
849 				__fmt_errstring(errstring, strlen(bname),
850 				    dgettext(TEXT_DOMAIN, still_testing),
851 				    bname, test_start.tester_pid);
852 				return (CFGA_ERROR);
853 			}
854 			/*
855 			 * Do a test stop and re-try the start.
856 			 */
857 			(void) memset((void *)&test_stop, 0,
858 			    sizeof (test_stop));
859 			test_stop.handle = test_start.handle;
860 			test_stop.condition = SYSC_CFGA_COND_UNKNOWN;
861 			mema_cmd_init(&cmd, &test_stop, outputstr, 0);
862 			dump_ioctl(AC_MEM_TEST_STOP, &test_stop);
863 			ret = ioctl(fd, AC_MEM_TEST_STOP, &cmd);
864 			ret_errno = errno;
865 			dump_ioctl_res(AC_MEM_TEST_STOP, &test_stop,
866 			    ret, ret_errno);
867 			/*
868 			 * Ignore test stop error processing and re-try the
869 			 * start.  The error return will be derived from the
870 			 * result of start.
871 			 */
872 			(void) memset((void *)&test_start, 0,
873 			    sizeof (test_start));
874 			mema_cmd_init(&cmd, &test_start, outputstr, 0);
875 			dump_ioctl(AC_MEM_TEST_START, &test_start);
876 			ret = ioctl(fd, AC_MEM_TEST_START, &cmd);
877 			ret_errno = errno;
878 			dump_ioctl_res(AC_MEM_TEST_START, &test_start,
879 			    ret, ret_errno);
880 		}
881 		/* Test return code again to cover the case of a re-try. */
882 		if (ret == -1) {
883 			mema_err(&cmd, ret_errno, errstring,
884 			    CMD_MEM_TEST_START);
885 			return (CFGA_ERROR);
886 		}
887 	}
888 	(void) memset((void *)&handle, 0, sizeof (handle));
889 	handle.fd = fd;
890 	handle.drvhandle = (void *)&test_start;
891 	handle.msgp = msgp;
892 	handle.bank_size = test_start.bank_size;
893 	handle.page_size = test_start.page_size;
894 	handle.line_size = test_start.line_size;
895 	handle.lines_per_page = test_start.page_size / test_start.line_size;
896 	handle.condition = CFGA_COND_UNKNOWN;
897 	handle.max_errors = max_errors;
898 
899 	res = (*mtest_table[test_fun].test_func)(&handle);
900 
901 	mtest_deallocate_buf_all(&handle);
902 
903 	/*
904 	 * Convert memory test code to MEMA_ code.
905 	 */
906 	switch (res) {
907 	case MTEST_DONE:
908 		res = CFGA_OK;
909 		break;
910 	case MTEST_LIB_ERROR:
911 		__fmt_errstring(errstring, 0, dgettext(TEXT_DOMAIN,
912 		    mtest_lib_error));
913 		res = CFGA_ERROR;
914 		break;
915 	case MTEST_DEV_ERROR:
916 		__fmt_errstring(errstring, 0, dgettext(TEXT_DOMAIN,
917 		    mtest_rw_error));
918 		res = CFGA_ERROR;
919 		break;
920 	default:
921 		__fmt_errstring(errstring, 0, dgettext(TEXT_DOMAIN,
922 		    mtest_unknown_error));
923 		res = CFGA_ERROR;
924 		assert(0);
925 		break;
926 	}
927 
928 	(void) memset((void *)&test_stop, 0, sizeof (test_stop));
929 	test_stop.handle = test_start.handle;
930 	switch (handle.condition) {
931 	case CFGA_COND_OK:
932 		test_stop.condition = SYSC_CFGA_COND_OK;
933 		break;
934 	case CFGA_COND_FAILING:
935 		test_stop.condition = SYSC_CFGA_COND_FAILING;
936 		break;
937 	case CFGA_COND_FAILED:
938 		test_stop.condition = SYSC_CFGA_COND_FAILED;
939 		break;
940 	case CFGA_COND_UNKNOWN:
941 		test_stop.condition = SYSC_CFGA_COND_UNKNOWN;
942 		break;
943 	default:
944 		test_stop.condition = SYSC_CFGA_COND_UNKNOWN;
945 		assert(0);
946 		break;
947 	}
948 
949 	mema_cmd_init(&cmd, &test_stop, outputstr, 0);
950 	dump_ioctl(AC_MEM_TEST_STOP, &test_stop);
951 	ret = ioctl(fd, AC_MEM_TEST_STOP, &cmd);
952 	ret_errno = errno;
953 	dump_ioctl_res(AC_MEM_TEST_STOP, &test_stop, ret, ret_errno);
954 	if (ret == -1) {
955 		mema_err(&cmd, ret_errno, errstring,
956 		    CMD_MEM_TEST_STOP);
957 		return (CFGA_ERROR);
958 	}
959 	return (res);
960 }
961 
962 #define	DRVHANDLE(H)	(((ac_mem_test_start_t *)(H)->drvhandle)->handle)
963 
964 int
965 mtest_write(
966 	mtest_handle_t handle,
967 	void *page_buf,
968 	u_longlong_t page_no,
969 	uint_t line_offset,
970 	uint_t line_count)
971 {
972 	ac_mem_test_write_t test_write;
973 	int fd, ret, ret_errno;
974 	ac_cfga_cmd_t cmd;
975 	char outputstr[AC_OUTPUT_LEN];
976 
977 	(void) memset((void *)&test_write, 0, sizeof (test_write));
978 	fd = handle->fd;
979 	test_write.handle = DRVHANDLE(handle);
980 	test_write.page_buf = page_buf;
981 	test_write.address.page_num = page_no;
982 	test_write.address.line_offset = line_offset;
983 	if (line_count == 0)
984 		test_write.address.line_count = handle->lines_per_page;
985 	else
986 		test_write.address.line_count = line_count;
987 
988 	mema_cmd_init(&cmd, &test_write, outputstr, 0);
989 	dump_ioctl(AC_MEM_TEST_WRITE, &test_write);
990 	ret = ioctl(fd, AC_MEM_TEST_WRITE, &cmd);
991 	ret_errno = errno;
992 	dump_ioctl_res(AC_MEM_TEST_WRITE, &test_write, ret, ret_errno);
993 
994 	if (ret == -1)
995 		return (-1);
996 	return (0);
997 }
998 
999 int
1000 mtest_read(
1001 	mtest_handle_t handle,
1002 	void *page_buf,
1003 	u_longlong_t page_no,
1004 	uint_t line_offset,
1005 	uint_t line_count,
1006 	struct mtest_error *errp)
1007 {
1008 	ac_mem_test_read_t test_read;
1009 	sunfire_processor_error_regs_t errbuf;
1010 	int fd, ret, ret_errno;
1011 	ac_cfga_cmd_t cmd;
1012 	char outputstr[AC_OUTPUT_LEN];
1013 
1014 	(void) memset((void *)&test_read, 0, sizeof (test_read));
1015 	(void) memset((void *)&errbuf, 0, sizeof (errbuf));
1016 	fd = handle->fd;
1017 	test_read.handle = DRVHANDLE(handle);
1018 	test_read.page_buf = page_buf;
1019 	test_read.address.page_num = page_no;
1020 	test_read.address.line_offset = line_offset;
1021 	test_read.error_buf =  &errbuf;
1022 	if (line_count == 0)
1023 		test_read.address.line_count = handle->lines_per_page;
1024 	else
1025 		test_read.address.line_count = line_count;
1026 
1027 	mema_cmd_init(&cmd, &test_read, outputstr, 0);
1028 	dump_ioctl(AC_MEM_TEST_READ, &test_read);
1029 	ret = ioctl(fd, AC_MEM_TEST_READ, &cmd);
1030 	ret_errno = errno;
1031 	dump_ioctl_res(AC_MEM_TEST_READ, &test_read, ret, ret_errno);
1032 
1033 	if (ret == -1) {
1034 		if (ret_errno == EIO) {
1035 			/*
1036 			 * Special case indicating CE or UE.
1037 			 */
1038 			if (((errbuf.udbh_error_reg | errbuf.udbl_error_reg) &
1039 			    P_DER_UE) != 0)
1040 				errp->error_type = MTEST_ERR_UE;
1041 			else
1042 				errp->error_type = MTEST_ERR_CE;
1043 		} else {
1044 			return (-1);
1045 		}
1046 	} else {
1047 		errp->error_type = MTEST_ERR_NONE;
1048 	}
1049 	return (0);
1050 }
1051 
1052 static char *
1053 subopt_help_str(char *opts[])
1054 {
1055 	char *str;
1056 	const char *sep;
1057 	int len;
1058 	int i, n;
1059 	static const char help_sep[] = ", ";
1060 	static const char help_nil[] = "???";
1061 
1062 	len = 0;
1063 	n = 0;
1064 	for (i = 0; opts[i] != NULL; i++) {
1065 		n++;
1066 		len += strlen(opts[i]);
1067 	}
1068 	if (n == 0)
1069 		return (strdup(help_nil));
1070 	len += (n - 1) * strlen(help_sep);
1071 	len++;
1072 	str = (char *)malloc(len);
1073 	if (str == NULL)
1074 		return (NULL);
1075 	*str = '\0';
1076 	sep = "";
1077 	for (i = 0; opts[i] != NULL; i++) {
1078 		(void) strcat(str, sep);
1079 		(void) strcat(str, opts[i]);
1080 		sep = help_sep;
1081 	}
1082 	return (str);
1083 }
1084 
1085 /*ARGSUSED*/
1086 cfga_err_t
1087 cfga_test(
1088 	const char *ap_id,
1089 	const char *options,
1090 	struct cfga_msg *msgp,
1091 	char **errstring,
1092 	cfga_flags_t flags)
1093 {
1094 	mema_bank_t bk;
1095 	ac_stat_t stat;
1096 	int test_fun = -1;
1097 	int fd, ret;
1098 	int maxerr_idx;
1099 	long max_errors = -1;
1100 	char *ret_p;
1101 
1102 	if (errstring != NULL)
1103 		*errstring = NULL;
1104 
1105 	/*
1106 	 * Decode test level and max error number.
1107 	 */
1108 	if (options != NULL && *options != '\0') {
1109 		char **opts;
1110 		char *value;
1111 		char *cp, *free_cp;
1112 		int subopt;
1113 
1114 		/* getsubopt() modifies the input string, so copy it. */
1115 		cp = strdup(options);
1116 		if (cp == NULL) {
1117 			return (CFGA_LIB_ERROR);
1118 		}
1119 		free_cp = cp;
1120 		opts = mtest_build_opts(&maxerr_idx);
1121 		if (opts == NULL) {
1122 			free((void *)free_cp);
1123 			return (CFGA_LIB_ERROR);
1124 		}
1125 
1126 		while (*cp != '\0') {
1127 			subopt = getsubopt(&cp, opts, &value);
1128 			if (subopt == -1) {
1129 				char *hlp;
1130 
1131 				hlp = subopt_help_str(opts);
1132 				if (hlp != NULL) {
1133 					__fmt_errstring(errstring,
1134 					strlen(value) + strlen(hlp),
1135 					dgettext(TEXT_DOMAIN, unk_test),
1136 					value, hlp);
1137 					free((void *)hlp);
1138 				} else {
1139 					__fmt_errstring(errstring, 20,
1140 					dgettext(TEXT_DOMAIN, calloc_fail),
1141 						strlen(options) + 1, 1);
1142 				}
1143 				/* Free after printing value. */
1144 				free((void *)free_cp);
1145 				return (CFGA_ERROR);
1146 			}
1147 
1148 			if (test_fun != -1 && subopt != test_fun &&
1149 			    subopt != maxerr_idx) {
1150 				__fmt_errstring(errstring,
1151 				    strlen(opts[subopt]),
1152 				    dgettext(TEXT_DOMAIN, dup_test),
1153 				    opts[subopt]);
1154 				free((void *)free_cp);
1155 				return (CFGA_ERROR);
1156 			}
1157 
1158 			if (subopt < maxerr_idx)
1159 				test_fun = subopt;
1160 			else {
1161 
1162 				if (max_errors != -1 && subopt == maxerr_idx) {
1163 					__fmt_errstring(errstring,
1164 					strlen(opts[subopt]),
1165 					dgettext(TEXT_DOMAIN, dup_num),
1166 					opts[subopt]);
1167 					free((void *)free_cp);
1168 					return (CFGA_ERROR);
1169 				}
1170 
1171 				if (value == NULL) {
1172 					__fmt_errstring(errstring,
1173 					0,
1174 					dgettext(TEXT_DOMAIN, no_num),
1175 					"");
1176 					free((void *)free_cp);
1177 					return (CFGA_ERROR);
1178 				}
1179 
1180 				max_errors = strtol(value, &ret_p, 10);
1181 				if ((ret_p == value) || (*ret_p != '\0') ||
1182 				    (max_errors < 0)) {
1183 					__fmt_errstring(errstring,
1184 					strlen(value),
1185 					dgettext(TEXT_DOMAIN, no_num),
1186 					value);
1187 					free((void *)free_cp);
1188 					return (CFGA_ERROR);
1189 				}
1190 			}
1191 		}
1192 		free((void *)free_cp);
1193 	}
1194 
1195 	if (test_fun == -1)
1196 		test_fun = MTEST_DEFAULT_TEST;
1197 	if (max_errors == -1)
1198 		max_errors = MAX_ERRORS;
1199 
1200 	ret = ap_stat(ap_id, &fd, &bk, &stat, errstring);
1201 	if (ret != CFGA_OK)
1202 		return (ret);
1203 
1204 	if (stat.rstate != SYSC_CFGA_RSTATE_CONNECTED ||
1205 	    stat.ostate != SYSC_CFGA_OSTATE_UNCONFIGURED) {
1206 		__fmt_errstring(errstring, 0,
1207 		    dgettext(TEXT_DOMAIN, trans_illegal));
1208 		(void) close(fd);
1209 		return (CFGA_ERROR);
1210 	}
1211 
1212 	ret = mtest_run(fd, test_fun, &bk,
1213 	    ((flags & CFGA_FLAG_VERBOSE) != 0) ? msgp : NULL, errstring,
1214 	    (ulong_t)max_errors);
1215 
1216 	(void) close(fd);
1217 
1218 	return (ret);
1219 }
1220 
1221 static cfga_stat_t
1222 rstate_cvt(sysc_cfga_rstate_t rs)
1223 {
1224 	cfga_stat_t cs;
1225 
1226 	switch (rs) {
1227 	case SYSC_CFGA_RSTATE_EMPTY:
1228 		cs = CFGA_STAT_EMPTY;
1229 		break;
1230 	case SYSC_CFGA_RSTATE_DISCONNECTED:
1231 		cs = CFGA_STAT_DISCONNECTED;
1232 		break;
1233 	case SYSC_CFGA_RSTATE_CONNECTED:
1234 		cs = CFGA_STAT_CONNECTED;
1235 		break;
1236 	default:
1237 		cs = CFGA_STAT_NONE;
1238 		break;
1239 	}
1240 
1241 	return (cs);
1242 }
1243 
1244 static cfga_stat_t
1245 ostate_cvt(sysc_cfga_ostate_t os)
1246 {
1247 	cfga_stat_t cs;
1248 
1249 	switch (os) {
1250 	case SYSC_CFGA_OSTATE_UNCONFIGURED:
1251 		cs = CFGA_STAT_UNCONFIGURED;
1252 		break;
1253 	case SYSC_CFGA_OSTATE_CONFIGURED:
1254 		cs = CFGA_STAT_CONFIGURED;
1255 		break;
1256 	default:
1257 		cs = CFGA_STAT_NONE;
1258 		break;
1259 	}
1260 
1261 	return (cs);
1262 }
1263 
1264 static cfga_cond_t
1265 cond_cvt(sysc_cfga_cond_t sc)
1266 {
1267 	cfga_cond_t cc;
1268 
1269 	switch (sc) {
1270 	case SYSC_CFGA_COND_OK:
1271 		cc = CFGA_COND_OK;
1272 		break;
1273 	case SYSC_CFGA_COND_FAILING:
1274 		cc = CFGA_COND_FAILING;
1275 		break;
1276 	case SYSC_CFGA_COND_FAILED:
1277 		cc = CFGA_COND_FAILED;
1278 		break;
1279 	case SYSC_CFGA_COND_UNUSABLE:
1280 		cc = CFGA_COND_UNUSABLE;
1281 		break;
1282 	case SYSC_CFGA_COND_UNKNOWN:
1283 	default:
1284 		cc = CFGA_COND_UNKNOWN;
1285 		break;
1286 	}
1287 
1288 	return (cc);
1289 }
1290 
1291 static void
1292 info_set(ac_stat_t *asp, mema_bank_t *bkp, cfga_info_t info)
1293 {
1294 	mema_disabled_t disab;
1295 	uint_t board;
1296 	uint_t n;
1297 	u_longlong_t decode;
1298 	uint_t intlv;
1299 	char *f;
1300 	char *end;
1301 
1302 	end = &info[sizeof (cfga_info_t)];
1303 	*info = NULL;
1304 
1305 	board = bkp->board;
1306 
1307 	/* Print the board number in a way that matches the sysctrl AP. */
1308 	info += snprintf(info, end - info, "slot%d", board);
1309 
1310 	if (asp->real_size == 0) {
1311 		info += snprintf(info, end - info, " empty");
1312 		return;
1313 	}
1314 
1315 	if ((n = asp->real_size) >= 1024) {
1316 		n /= 1024;
1317 		f = "Gb";
1318 	} else
1319 		f = "Mb";
1320 	info += snprintf(info, end - info, " %d%s", n, f);
1321 
1322 	if (asp->rstate == SYSC_CFGA_RSTATE_CONNECTED &&
1323 	    asp->ostate == SYSC_CFGA_OSTATE_CONFIGURED &&
1324 	    asp->use_size != asp->real_size) {
1325 		if ((n = asp->use_size) >= 1024) {
1326 			n /= 1024;
1327 			f = "Gb";
1328 		} else
1329 			f = "Mb";
1330 		info += snprintf(info, end - info, " (%d%s used)", n, f);
1331 	}
1332 
1333 	if (bkp->bank == 0)
1334 		decode = asp->ac_decode0;
1335 	else
1336 		decode = asp->ac_decode1;
1337 
1338 	info += snprintf(info, end - info, " base 0x%llx",
1339 	    GRP_REALBASE(decode));
1340 
1341 	if (bkp->bank == 0)
1342 		intlv = INTLV0(asp->ac_memctl);
1343 	else
1344 		intlv = INTLV1(asp->ac_memctl);
1345 
1346 	if (intlv != 1)
1347 		info += snprintf(info, end - info, " interleaved %u-way",
1348 		    intlv);
1349 
1350 	if (prom_read_disabled_list(&disab, board)) {
1351 		if (disab != 0) {
1352 			info += snprintf(info, end - info, " disabled at boot");
1353 		}
1354 
1355 	}
1356 
1357 	if (asp->rstate == SYSC_CFGA_RSTATE_CONNECTED &&
1358 	    asp->ostate == SYSC_CFGA_OSTATE_CONFIGURED &&
1359 	    asp->nonrelocatable)
1360 		info += snprintf(info, end - info, " permanent");
1361 }
1362 
1363 static void
1364 mema_cvt(ac_stat_t *ac, mema_bank_t *bkp, cfga_stat_data_t *cs)
1365 {
1366 	(void) strcpy(cs->ap_type, "memory");
1367 	cs->ap_r_state = rstate_cvt(ac->rstate);
1368 	cs->ap_o_state = ostate_cvt(ac->ostate);
1369 	cs->ap_cond = cond_cvt(ac->condition);
1370 	cs->ap_busy = (cfga_busy_t)ac->busy;
1371 	cs->ap_status_time = ac->status_time;
1372 	info_set(ac, bkp, cs->ap_info);
1373 	cs->ap_log_id[0] = NULL;
1374 	cs->ap_phys_id[0] = NULL;
1375 }
1376 
1377 /*ARGSUSED*/
1378 cfga_err_t
1379 cfga_stat(
1380 	const char *ap_id,
1381 	struct cfga_stat_data *cs,
1382 	const char *options,
1383 	char **errstring)
1384 {
1385 	int ret;
1386 	mema_bank_t bk;
1387 	ac_stat_t stat;
1388 	option_set_t do_option;
1389 
1390 	if (errstring != NULL)
1391 		*errstring = NULL;
1392 
1393 	ret = 0;
1394 	do_option = process_options(options, stat_opts, &ret, errstring);
1395 	if (ret != 0)
1396 		return (ret);
1397 
1398 	ret = ap_stat(ap_id, NULL, &bk, &stat, errstring);
1399 	if (ret != CFGA_OK)
1400 		return (ret);
1401 
1402 	mema_cvt(&stat, &bk, cs);
1403 
1404 	ret = prom_do_options(do_option, bk.board, &stat, errstring);
1405 
1406 	return (ret);
1407 }
1408 
1409 /*ARGSUSED*/
1410 cfga_err_t
1411 cfga_list(
1412 	const char *ap_id,
1413 	cfga_stat_data_t **ap_list,
1414 	int *nlist,
1415 	const char *options,
1416 	char **errstring)
1417 {
1418 	if (errstring != NULL)
1419 		*errstring = NULL;
1420 
1421 	return (CFGA_NOTSUPP);
1422 }
1423 
1424 /*
1425  * cfga_ap_id_cmp -- use default_ap_id_cmp() in libcfgadm
1426  */
1427 
1428 /*ARGSUSED*/
1429 cfga_err_t
1430 cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
1431 {
1432 
1433 
1434 	(*msgp->message_routine)(msgp->appdata_ptr, mema_help);
1435 	(*msgp->message_routine)(msgp->appdata_ptr, disable_opts);
1436 	(*msgp->message_routine)(msgp->appdata_ptr, enable_opts);
1437 	(*msgp->message_routine)(msgp->appdata_ptr, timeout_opts);
1438 	(*msgp->message_routine)(msgp->appdata_ptr, test_opts);
1439 	(*msgp->message_routine)(msgp->appdata_ptr, private_funcs);
1440 
1441 	return (CFGA_OK);
1442 }
1443 
1444 #if 0
1445 static ac_mem_version_t
1446 get_version(int fd)
1447 {
1448 	ac_mem_version_t ver;
1449 	int ret, ret_errno;
1450 
1451 	ver = 0;
1452 	dump_ioctl(AC_MEM_ADMIN_VER, &ver);
1453 	ret = ioctl(fd, AC_MEM_ADMIN_VER, &ver);
1454 	ret_errno = errno;
1455 	dump_ioctl_res(AC_MEM_ADMIN_VER, &ver, ret, ret_errno);
1456 	return (ver);
1457 }
1458 #endif
1459 
1460 static char *
1461 opt_help_str(struct opt_control *opts)
1462 {
1463 	char *str;
1464 	const char *sep;
1465 	int len;
1466 	int i, n;
1467 	static const char help_sep[] = ", ";
1468 	static const char help_nil[] = "???";
1469 
1470 	len = 0;
1471 	n = 0;
1472 	for (i = 0; opts[i].subopt != -1; i++) {
1473 		n++;
1474 		len += strlen(mema_opts[opts[i].subopt]);
1475 	}
1476 	if (n == 0)
1477 		return (strdup(help_nil));
1478 	len += (n - 1) * strlen(help_sep);
1479 	len++;
1480 	str = (char *)malloc(len);
1481 	if (str == NULL)
1482 		return (NULL);
1483 	*str = '\0';
1484 	sep = "";
1485 	for (i = 0; opts[i].subopt != -1; i++) {
1486 		(void) strcat(str, sep);
1487 		(void) strcat(str, mema_opts[opts[i].subopt]);
1488 		sep = help_sep;
1489 	}
1490 	return (str);
1491 }
1492 
1493 static option_set_t
1494 process_options(
1495 	const char *options,
1496 	struct opt_control *opts,
1497 	int *retp,
1498 	char **errstring)
1499 {
1500 	option_set_t opt_set;
1501 	char *optcopy, *optcopy_alloc;
1502 	char *value;
1503 	int subopt;
1504 	int subopt_err;
1505 	int i;
1506 	int group;
1507 	int need_value;
1508 
1509 	OPTSET_INIT(opt_set);
1510 
1511 	if (options == NULL || *options == '\0') {
1512 		return (opt_set);
1513 	}
1514 
1515 	optcopy = optcopy_alloc = strdup(options);
1516 	if (optcopy_alloc == NULL) {
1517 		__fmt_errstring(errstring, 20,
1518 		    dgettext(TEXT_DOMAIN, calloc_fail), strlen(options) + 1, 1);
1519 		*retp = CFGA_LIB_ERROR;
1520 		return (opt_set);
1521 	}
1522 
1523 	subopt_err = 0;
1524 	while (*optcopy != '\0' && subopt_err == 0) {
1525 		subopt = getsubopt(&optcopy, mema_opts, &value);
1526 		if (subopt == -1) {
1527 			char *hlp;
1528 
1529 			hlp = opt_help_str(opts);
1530 			__fmt_errstring(errstring, strlen(value) + strlen(hlp),
1531 			    dgettext(TEXT_DOMAIN, unk_subopt), value, hlp);
1532 			free((void *)hlp);
1533 			subopt_err = 1;
1534 			break;
1535 		}
1536 		for (i = 0; opts[i].subopt != -1; i++) {
1537 			if (opts[i].subopt == subopt) {
1538 				group = opts[i].group;
1539 				break;
1540 			}
1541 		}
1542 		if (opts[i].subopt == -1) {
1543 			char *hlp;
1544 
1545 			hlp = opt_help_str(opts);
1546 			__fmt_errstring(errstring,
1547 			    MAX_OPT_LENGTH + strlen(hlp),
1548 			    dgettext(TEXT_DOMAIN, not_valid),
1549 			    mema_opts[subopt], hlp);
1550 			free((void *)hlp);
1551 			subopt_err = 1;
1552 			break;
1553 		}
1554 		need_value = OPT_NEEDS_VALUE(subopt);
1555 		if (!need_value && value != NULL) {
1556 			__fmt_errstring(errstring, MAX_OPT_LENGTH,
1557 			    dgettext(TEXT_DOMAIN, no_value),
1558 			    mema_opts[subopt]);
1559 			subopt_err = 1;
1560 			break;
1561 		}
1562 		if (need_value && value == NULL) {
1563 			__fmt_errstring(errstring, MAX_OPT_LENGTH,
1564 			    dgettext(TEXT_DOMAIN, missing_value),
1565 			    mema_opts[subopt]);
1566 			subopt_err = 1;
1567 			break;
1568 		}
1569 		if (OPTSET_TEST(opt_set, subopt)) {
1570 			/* Ignore repeated options. */
1571 			continue;
1572 		}
1573 		if (group != 0 && !OPTSET_IS_EMPTY(opt_set)) {
1574 			for (i = 0; opts[i].subopt != -1; i++) {
1575 				if (i == subopt)
1576 					continue;
1577 				if (opts[i].group == group &&
1578 				    OPTSET_TEST(opt_set, opts[i].subopt))
1579 					break;
1580 			}
1581 			if (opts[i].subopt != -1) {
1582 				__fmt_errstring(errstring, MAX_OPT_LENGTH * 2,
1583 				    dgettext(TEXT_DOMAIN, conflict_opt),
1584 				    mema_opts[subopt],
1585 				    mema_opts[opts[i].subopt]);
1586 				subopt_err = 1;
1587 				break;
1588 			}
1589 		}
1590 		OPTSET_SET_VAL(opt_set, subopt, value);
1591 	}
1592 	free((void *)optcopy_alloc);
1593 	if (subopt_err) {
1594 		*retp = CFGA_ERROR;
1595 	}
1596 
1597 	return (opt_set);
1598 }
1599 
1600 #ifdef DEV_DEBUG
1601 
1602 static int
1603 debugging(void)
1604 {
1605 	char *ep;
1606 	static int inited;
1607 
1608 	if (inited)
1609 		return (debug_fp != NULL);
1610 	inited = 1;
1611 
1612 	if ((ep = getenv("MEMADM_DEBUG")) == NULL) {
1613 		return (0);
1614 	}
1615 	if (*ep == '\0')
1616 		debug_fp = stderr;
1617 	else {
1618 		if ((debug_fp = fopen(ep, "a")) == NULL)
1619 			return (0);
1620 	}
1621 	(void) fprintf(debug_fp, "\nDebug started, pid=%d\n", (int)getpid());
1622 	return (1);
1623 }
1624 
1625 static void
1626 dump_ioctl(
1627 	int cmd,
1628 	void *arg)
1629 {
1630 	if (!debugging())
1631 		return;
1632 
1633 	switch (cmd) {
1634 	case AC_MEM_CONFIGURE:
1635 		(void) fprintf(debug_fp, "IOCTL: AC_MEM_CONFIGURE\n");
1636 		break;
1637 
1638 	case AC_MEM_UNCONFIGURE:
1639 		(void) fprintf(debug_fp, "IOCTL: AC_MEM_UNCONFIGURE\n");
1640 		break;
1641 
1642 	case AC_MEM_TEST_START:
1643 		(void) fprintf(debug_fp, "IOCTL: AC_MEM_TEST_START\n");
1644 		break;
1645 
1646 	case AC_MEM_TEST_STOP: {
1647 		ac_mem_test_stop_t *tstop;
1648 
1649 		tstop = (ac_mem_test_stop_t *)arg;
1650 		(void) fprintf(debug_fp, "IOCTL: AC_MEM_TEST_STOP handle=%#x "
1651 		    "condition=%d\n", tstop->handle, tstop->condition);
1652 	}
1653 		break;
1654 	case AC_MEM_TEST_READ: {
1655 		ac_mem_test_read_t *tread;
1656 
1657 		tread = (ac_mem_test_read_t *)arg;
1658 		(void) fprintf(debug_fp, "IOCTL: AC_MEM_TEST_READ handle=%#x "
1659 		    "buf=%#p page=%#llx off=%#x count=%#x\n",
1660 		    tread->handle, tread->page_buf,
1661 		    tread->address.page_num,
1662 		    tread->address.line_offset, tread->address.line_count);
1663 	}
1664 		break;
1665 	case AC_MEM_TEST_WRITE: {
1666 		ac_mem_test_write_t *twrite;
1667 
1668 		twrite = (ac_mem_test_write_t *)arg;
1669 		(void) fprintf(debug_fp, "IOCTL: AC_MEM_TEST_WRITE handle=%#x "
1670 		    "buf=%#p page=%#llx off=%#x count=%#x\n",
1671 		    twrite->handle, twrite->page_buf,
1672 		    twrite->address.page_num,
1673 		    twrite->address.line_offset, twrite->address.line_count);
1674 	}
1675 		break;
1676 	case AC_MEM_ADMIN_VER:
1677 		(void) fprintf(debug_fp, "IOCTL: AC_MEM_ADMIN_VER:\n");
1678 		break;
1679 	case AC_MEM_STAT:
1680 		(void) fprintf(debug_fp, "IOCTL: AC_MEM_STAT\n");
1681 		break;
1682 	case AC_MEM_EXERCISE: {
1683 		ac_cfga_cmd_t *cmdp;
1684 
1685 		cmdp = arg;
1686 		(void) fprintf(debug_fp, "IOCTL: AC_MEM_EXERCISE arg=%d\n",
1687 		    cmdp->arg);
1688 		break;
1689 	}
1690 	default:
1691 		(void) fprintf(debug_fp, "IOCTL: unknown (%#x)\n", cmd);
1692 		break;
1693 	}
1694 	(void) fflush(debug_fp);
1695 }
1696 
1697 static void
1698 dump_ioctl_res(
1699 	int cmd,
1700 	void *arg,
1701 	int ret,
1702 	int ret_errno)
1703 {
1704 	if (!debugging())
1705 		return;
1706 
1707 	if (ret == -1) {
1708 		(void) fprintf(debug_fp, "IOCTL failed, \"%s\" (errno=%d)\n",
1709 		    strerror(ret_errno), ret_errno);
1710 		(void) fflush(debug_fp);
1711 		return;
1712 	} else {
1713 		(void) fprintf(debug_fp, "IOCTL succeeded, ret=%d\n", ret);
1714 	}
1715 
1716 	switch (cmd) {
1717 	case AC_MEM_CONFIGURE:
1718 	case AC_MEM_UNCONFIGURE:
1719 		break;
1720 	case AC_MEM_TEST_START: {
1721 		ac_mem_test_start_t *tstart;
1722 
1723 		tstart = (ac_mem_test_start_t *)arg;
1724 		(void) fprintf(debug_fp, "    handle=%#x tester_pid=%d "
1725 		    "prev_condition=%d bank_size=%#llx "
1726 		    "page_size=%#x line_size=%#x afar_base=%#llx\n",
1727 		    tstart->handle, (int)tstart->tester_pid,
1728 		    tstart->prev_condition,
1729 		    tstart->bank_size, tstart->page_size,
1730 		    tstart->line_size, tstart->afar_base);
1731 	}
1732 		break;
1733 	case AC_MEM_TEST_STOP:
1734 		break;
1735 	case AC_MEM_TEST_READ: {
1736 		ac_mem_test_read_t *tread;
1737 		sunfire_processor_error_regs_t *err;
1738 
1739 		tread = (ac_mem_test_read_t *)arg;
1740 		err = tread->error_buf;
1741 		if (ret_errno == EIO) {
1742 			(void) fprintf(debug_fp, "module_id=%#llx afsr=%#llx "
1743 			    "afar=%#llx udbh_error_reg=%#llx "
1744 			    "udbl_error_reg=%#llx\n",
1745 			    (longlong_t)err->module_id, (longlong_t)err->afsr,
1746 			    (longlong_t)err->afar,
1747 			    (longlong_t)err->udbh_error_reg,
1748 			    (longlong_t)err->udbl_error_reg);
1749 		} else {
1750 			(void) fprintf(debug_fp, "\n");
1751 		}
1752 	}
1753 		break;
1754 	case AC_MEM_TEST_WRITE:
1755 		break;
1756 	case AC_MEM_ADMIN_VER: {
1757 		ac_mem_version_t *ver;
1758 
1759 		ver = (ac_mem_version_t *)arg;
1760 		(void) fprintf(debug_fp, "    version %d\n", *ver);
1761 	}
1762 		break;
1763 	case AC_MEM_STAT: {
1764 		ac_stat_t *tstat;
1765 
1766 		tstat = (ac_stat_t *)arg;
1767 		(void) fprintf(debug_fp, "    rstate=%u ostate=%u "
1768 		    "condition=%u status_time=%#lx board=%u\n",
1769 		    (uint_t)tstat->rstate, (uint_t)tstat->ostate,
1770 		    (uint_t)tstat->condition, (ulong_t)tstat->status_time,
1771 		    tstat->board);
1772 		(void) fprintf(debug_fp, "    real_size=%u use_size=%u "
1773 		    "busy=%u\n",
1774 		    tstat->real_size, tstat->use_size, tstat->busy);
1775 		(void) fprintf(debug_fp, "    page_size=%#x "
1776 		    "phys_pages=%#llx managed=%#llx nonrelocatable=%#llx\n",
1777 		    tstat->page_size, (longlong_t)tstat->phys_pages,
1778 		    (longlong_t)tstat->managed,
1779 		    (longlong_t)tstat->nonrelocatable);
1780 		(void) fprintf(debug_fp, "    memctl=%#llx "
1781 		    "decode0=%#llx decode1=%#llx\n",
1782 		    (longlong_t)tstat->ac_memctl, (longlong_t)tstat->ac_decode0,
1783 		    (longlong_t)tstat->ac_decode1);
1784 	}
1785 		break;
1786 	case AC_MEM_EXERCISE: {
1787 		ac_cfga_cmd_t *cmdp;
1788 
1789 		cmdp = arg;
1790 		switch (cmdp->arg) {
1791 		case AC_MEMX_RELOCATE_ALL: {
1792 			struct ac_memx_relocate_stats *stp;
1793 
1794 			if ((stp = cmdp->private) != NULL) {
1795 				(void) fprintf(debug_fp, "    base=%u npgs=%u"
1796 				    " nopaget=%u nolock=%u isfree=%u reloc=%u"
1797 				    " noreloc=%u\n",
1798 				    stp->base, stp->npgs, stp->nopaget,
1799 				    stp->nolock, stp->isfree, stp->reloc,
1800 				    stp->noreloc);
1801 			}
1802 			break;
1803 		}
1804 		default:
1805 			break;
1806 		}
1807 		break;
1808 	}
1809 	default:
1810 		break;
1811 	}
1812 	(void) fflush(debug_fp);
1813 }
1814 #endif /* DEV_DEBUG */
1815