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
77static FILE *debug_fp;
78static int debugging(void);
79static void dump_ioctl(int, void *);
80static 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
86typedef struct {
87	uint_t   board;
88	uint_t   bank;
89} mema_bank_t;
90
91static 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 */
111struct 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' */
123typedef 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
136static option_set_t process_options(const char *, struct opt_control *,
137	int *, char **);
138
139static struct opt_control add_opts[] = {
140	{OPT_BOOT_ENABLE, 1},
141	{OPT_BOOT_DISABLE, 1},
142	{-1, 0}
143};
144
145static struct opt_control del_opts[] = {
146	{OPT_BOOT_ENABLE, 1},
147	{OPT_BOOT_DISABLE, 1},
148	{OPT_TIMEOUT, 2},
149	{-1, 0}
150};
151
152static 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
162static const char still_testing[] = "bank %s being tested by process %d";
163static const char no_value[] = "sub-option \"%s\" does not take a value";
164static const char missing_value[] = "sub-option \"%s\" needs a value";
165static const char conflict_opt[] = "sub-option \"%s\" conflicts with \"%s\"";
166static const char unk_subopt[] = "sub-option \"%s\" unknown\n"
167	"choose from: %s";
168static const char not_valid[] =
169	"sub-option \"%s\" not valid for this operation\n"
170	"choose from: %s";
171static const char timeout_notnum[] =
172	"timeout value not a positive integer \"%s\"";
173static const char calloc_fail[] = "memory allocation failed (%d*%d bytes)";
174static const char unk_test[] = "test \"%s\" unknown\n"
175	"choose from: %s";
176static const char dup_test[] = "more than one test type specified (\"%s\")";
177static const char dup_num[] = "option specified more than once (\"%s\")";
178static const char no_num[] = "invalid number specified for max_errors(\"%s\")";
179static const char mtest_rw_error[] = "memory test read/write error";
180static const char mtest_lib_error[] = "memory test library error";
181static const char dlist_invalid[] = "invalid disabled-memory-list";
182static const char dlist_write_failed[] = "disabled-memory-list write failed";
183static const char mtest_unknown_error[] = "unknown memory test error";
184static const char ap_invalid[] = "invalid attachment point: %s";
185static const char trans_illegal[] = "illegal transition";
186static const char open_failed[] = "open failed: %s: %s";
187static const char mema_help[] =	"\nAc specific options:\n";
188static const char disable_opts[] = "\t-o disable-at-boot\n";
189static const char enable_opts[] = "\t-o enable-at-boot\n";
190static const char timeout_opts[] = "\t-o timeout=# (seconds)\n";
191static const char test_opts[] =
192	"\t-o {quick, normal, extended},[max_errors=#] -t ap_id [ap_id...]\n";
193static const char private_funcs[] = "\t-x relocate-test ap_id [ap_id...]\n";
194static const char add_is_disabled[] = "memory is disabled at boot";
195static const char add_willbe_disabled[] =
196	"memory will be disabled at boot";
197static const char add_disab_err[] = "cannot get memory disabled status";
198static 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
240static char *
241mema_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 */
309static int
310mema_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
379static void
380mema_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
402static void
403mema_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
415static int
416ap_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
448static cfga_err_t
449ap_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
516static void
517set_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
525static void
526set_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
534static cfga_err_t
535prom_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
578static cfga_err_t
579mema_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
654static cfga_err_t
655mema_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*/
719cfga_err_t
720cfga_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*/
755cfga_err_t
756cfga_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
805static int
806mtest_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
962int
963mtest_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
997int
998mtest_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
1050static char *
1051subopt_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*/
1084cfga_err_t
1085cfga_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
1219static cfga_stat_t
1220rstate_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
1242static cfga_stat_t
1243ostate_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
1262static cfga_cond_t
1263cond_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
1289static void
1290info_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
1361static void
1362mema_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*/
1376cfga_err_t
1377cfga_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*/
1408cfga_err_t
1409cfga_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*/
1427cfga_err_t
1428cfga_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
1443static ac_mem_version_t
1444get_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
1458static char *
1459opt_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
1491static option_set_t
1492process_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
1600static int
1601debugging(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
1623static void
1624dump_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
1695static void
1696dump_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