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