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