xref: /illumos-gate/usr/src/uts/sun4u/opl/io/drmach.c (revision 3fe80ca4)
125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
2107d06da5SSurya Prakki 
2225cf1a30Sjl /*
2356f33205SJonathan Adams  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2425cf1a30Sjl  * Use is subject to license terms.
2525cf1a30Sjl  */
2625cf1a30Sjl 
27*3fe80ca4SDan Cross /*
28*3fe80ca4SDan Cross  * Copyright 2023 Oxide Computer Company
29*3fe80ca4SDan Cross  */
30*3fe80ca4SDan Cross 
3125cf1a30Sjl #include <sys/debug.h>
3225cf1a30Sjl #include <sys/types.h>
3325cf1a30Sjl #include <sys/varargs.h>
3425cf1a30Sjl #include <sys/errno.h>
3525cf1a30Sjl #include <sys/cred.h>
3625cf1a30Sjl #include <sys/dditypes.h>
3725cf1a30Sjl #include <sys/devops.h>
3825cf1a30Sjl #include <sys/modctl.h>
3925cf1a30Sjl #include <sys/poll.h>
4025cf1a30Sjl #include <sys/conf.h>
4125cf1a30Sjl #include <sys/ddi.h>
4225cf1a30Sjl #include <sys/sunddi.h>
4325cf1a30Sjl #include <sys/sunndi.h>
4425cf1a30Sjl #include <sys/ndi_impldefs.h>
4525cf1a30Sjl #include <sys/stat.h>
4625cf1a30Sjl #include <sys/kmem.h>
4725cf1a30Sjl #include <sys/vmem.h>
4825cf1a30Sjl #include <sys/opl_olympus_regs.h>
4925cf1a30Sjl #include <sys/cpuvar.h>
5025cf1a30Sjl #include <sys/cpupart.h>
5125cf1a30Sjl #include <sys/mem_config.h>
5225cf1a30Sjl #include <sys/ddi_impldefs.h>
5325cf1a30Sjl #include <sys/systm.h>
5425cf1a30Sjl #include <sys/machsystm.h>
5525cf1a30Sjl #include <sys/autoconf.h>
5625cf1a30Sjl #include <sys/cmn_err.h>
5725cf1a30Sjl #include <sys/sysmacros.h>
5825cf1a30Sjl #include <sys/x_call.h>
5925cf1a30Sjl #include <sys/promif.h>
6025cf1a30Sjl #include <sys/prom_plat.h>
6125cf1a30Sjl #include <sys/membar.h>
6225cf1a30Sjl #include <vm/seg_kmem.h>
6325cf1a30Sjl #include <sys/mem_cage.h>
6425cf1a30Sjl #include <sys/stack.h>
6525cf1a30Sjl #include <sys/archsystm.h>
6625cf1a30Sjl #include <vm/hat_sfmmu.h>
6725cf1a30Sjl #include <sys/pte.h>
6825cf1a30Sjl #include <sys/mmu.h>
6925cf1a30Sjl #include <sys/cpu_module.h>
7025cf1a30Sjl #include <sys/obpdefs.h>
7125cf1a30Sjl #include <sys/note.h>
7225cf1a30Sjl #include <sys/ontrap.h>
7325cf1a30Sjl #include <sys/cpu_sgnblk_defs.h>
7425cf1a30Sjl #include <sys/opl.h>
75e98fafb9Sjl #include <sys/cpu_impl.h>
7625cf1a30Sjl 
7725cf1a30Sjl 
7825cf1a30Sjl #include <sys/promimpl.h>
7925cf1a30Sjl #include <sys/prom_plat.h>
8025cf1a30Sjl #include <sys/kobj.h>
8125cf1a30Sjl 
8225cf1a30Sjl #include <sys/sysevent.h>
8325cf1a30Sjl #include <sys/sysevent/dr.h>
8425cf1a30Sjl #include <sys/sysevent/eventdefs.h>
8525cf1a30Sjl 
8625cf1a30Sjl #include <sys/drmach.h>
8725cf1a30Sjl #include <sys/dr_util.h>
8825cf1a30Sjl 
8925cf1a30Sjl #include <sys/fcode.h>
9025cf1a30Sjl #include <sys/opl_cfg.h>
9125cf1a30Sjl 
9225cf1a30Sjl extern void		bcopy32_il(uint64_t, uint64_t);
9325cf1a30Sjl extern void		flush_cache_il(void);
9425cf1a30Sjl extern void		drmach_sleep_il(void);
9525cf1a30Sjl 
9625cf1a30Sjl typedef struct {
9725cf1a30Sjl 	struct drmach_node	*node;
9825cf1a30Sjl 	void			*data;
9925cf1a30Sjl } drmach_node_walk_args_t;
10025cf1a30Sjl 
10125cf1a30Sjl typedef struct drmach_node {
10225cf1a30Sjl 	void		*here;
10325cf1a30Sjl 
10425cf1a30Sjl 	pnode_t		(*get_dnode)(struct drmach_node *node);
10525cf1a30Sjl 	int		(*walk)(struct drmach_node *node, void *data,
10625cf1a30Sjl 				int (*cb)(drmach_node_walk_args_t *args));
10725cf1a30Sjl 	dev_info_t	*(*n_getdip)(struct drmach_node *node);
10825cf1a30Sjl 	int		(*n_getproplen)(struct drmach_node *node, char *name,
10925cf1a30Sjl 				int *len);
11025cf1a30Sjl 	int		(*n_getprop)(struct drmach_node *node, char *name,
11125cf1a30Sjl 				void *buf, int len);
11225cf1a30Sjl 	int		(*get_parent)(struct drmach_node *node,
11325cf1a30Sjl 				struct drmach_node *pnode);
11425cf1a30Sjl } drmach_node_t;
11525cf1a30Sjl 
11625cf1a30Sjl typedef struct {
11725cf1a30Sjl 	int		 min_index;
11825cf1a30Sjl 	int		 max_index;
11925cf1a30Sjl 	int		 arr_sz;
12025cf1a30Sjl 	drmachid_t	*arr;
12125cf1a30Sjl } drmach_array_t;
12225cf1a30Sjl 
12325cf1a30Sjl typedef struct {
12425cf1a30Sjl 	void		*isa;
12525cf1a30Sjl 
12625cf1a30Sjl 	void		(*dispose)(drmachid_t);
12725cf1a30Sjl 	sbd_error_t	*(*release)(drmachid_t);
12825cf1a30Sjl 	sbd_error_t	*(*status)(drmachid_t, drmach_status_t *);
12925cf1a30Sjl 
13025cf1a30Sjl 	char		 name[MAXNAMELEN];
13125cf1a30Sjl } drmach_common_t;
13225cf1a30Sjl 
13325cf1a30Sjl typedef	struct {
13425cf1a30Sjl 	uint32_t	core_present;
13525cf1a30Sjl 	uint32_t	core_hotadded;
13625cf1a30Sjl 	uint32_t	core_started;
13725cf1a30Sjl } drmach_cmp_t;
13825cf1a30Sjl 
13925cf1a30Sjl typedef struct {
14025cf1a30Sjl 	drmach_common_t	 cm;
14125cf1a30Sjl 	int		 bnum;
14225cf1a30Sjl 	int		 assigned;
14325cf1a30Sjl 	int		 powered;
14425cf1a30Sjl 	int		 connected;
14525cf1a30Sjl 	int		 cond;
14625cf1a30Sjl 	drmach_node_t	*tree;
14725cf1a30Sjl 	drmach_array_t	*devices;
14825cf1a30Sjl 	int		boot_board;	/* if board exists on bootup */
14925cf1a30Sjl 	drmach_cmp_t	cores[OPL_MAX_COREID_PER_BOARD];
15025cf1a30Sjl } drmach_board_t;
15125cf1a30Sjl 
15225cf1a30Sjl typedef struct {
15325cf1a30Sjl 	drmach_common_t	 cm;
15425cf1a30Sjl 	drmach_board_t	*bp;
15525cf1a30Sjl 	int		 unum;
15625cf1a30Sjl 	int		portid;
15725cf1a30Sjl 	int		 busy;
15825cf1a30Sjl 	int		 powered;
15925cf1a30Sjl 	const char	*type;
16025cf1a30Sjl 	drmach_node_t	*node;
16125cf1a30Sjl } drmach_device_t;
16225cf1a30Sjl 
16325cf1a30Sjl typedef struct drmach_cpu {
16425cf1a30Sjl 	drmach_device_t  dev;
16525cf1a30Sjl 	processorid_t    cpuid;
16625cf1a30Sjl 	int		sb;
16725cf1a30Sjl 	int		chipid;
16825cf1a30Sjl 	int		coreid;
16925cf1a30Sjl 	int		strandid;
17025cf1a30Sjl 	int		status;
17125cf1a30Sjl #define	OPL_CPU_HOTADDED	1
17225cf1a30Sjl } drmach_cpu_t;
17325cf1a30Sjl 
17425cf1a30Sjl typedef struct drmach_mem {
17525cf1a30Sjl 	drmach_device_t  dev;
17625cf1a30Sjl 	uint64_t	slice_base;
17725cf1a30Sjl 	uint64_t	slice_size;
17825cf1a30Sjl 	uint64_t	base_pa;	/* lowest installed memory base */
17925cf1a30Sjl 	uint64_t	nbytes;		/* size of installed memory */
18025cf1a30Sjl 	struct memlist *memlist;
18125cf1a30Sjl } drmach_mem_t;
18225cf1a30Sjl 
18325cf1a30Sjl typedef struct drmach_io {
18425cf1a30Sjl 	drmach_device_t  dev;
18525cf1a30Sjl 	int	channel;
18625cf1a30Sjl 	int	leaf;
18725cf1a30Sjl } drmach_io_t;
18825cf1a30Sjl 
18925cf1a30Sjl typedef struct drmach_domain_info {
19025cf1a30Sjl 	uint32_t	floating;
19125cf1a30Sjl 	int		allow_dr;
19225cf1a30Sjl } drmach_domain_info_t;
19325cf1a30Sjl 
19425cf1a30Sjl drmach_domain_info_t drmach_domain;
19525cf1a30Sjl 
19625cf1a30Sjl typedef struct {
19725cf1a30Sjl 	int		 flags;
19825cf1a30Sjl 	drmach_device_t	*dp;
19925cf1a30Sjl 	sbd_error_t	*err;
20025cf1a30Sjl 	dev_info_t	*dip;
20125cf1a30Sjl } drmach_config_args_t;
20225cf1a30Sjl 
20325cf1a30Sjl typedef struct {
20425cf1a30Sjl 	drmach_board_t	*obj;
20525cf1a30Sjl 	int		 ndevs;
20625cf1a30Sjl 	void		*a;
20725cf1a30Sjl 	sbd_error_t	*(*found)(void *a, const char *, int, drmachid_t);
20825cf1a30Sjl 	sbd_error_t	*err;
20925cf1a30Sjl } drmach_board_cb_data_t;
21025cf1a30Sjl 
21125cf1a30Sjl static drmach_array_t	*drmach_boards;
21225cf1a30Sjl 
21325cf1a30Sjl static sbd_error_t	*drmach_device_new(drmach_node_t *,
21425cf1a30Sjl 				drmach_board_t *, int, drmachid_t *);
21525cf1a30Sjl static sbd_error_t	*drmach_cpu_new(drmach_device_t *, drmachid_t *);
21625cf1a30Sjl static sbd_error_t	*drmach_mem_new(drmach_device_t *, drmachid_t *);
21725cf1a30Sjl static sbd_error_t	*drmach_io_new(drmach_device_t *, drmachid_t *);
21825cf1a30Sjl 
21925cf1a30Sjl static dev_info_t	*drmach_node_ddi_get_dip(drmach_node_t *np);
22025cf1a30Sjl static int		 drmach_node_ddi_get_prop(drmach_node_t *np,
22125cf1a30Sjl 				char *name, void *buf, int len);
22225cf1a30Sjl static int		 drmach_node_ddi_get_proplen(drmach_node_t *np,
22325cf1a30Sjl 				char *name, int *len);
22425cf1a30Sjl 
225bbe1232eSToomas Soome static int		drmach_get_portid(drmach_node_t *);
22625cf1a30Sjl static	sbd_error_t	*drmach_i_status(drmachid_t, drmach_status_t *);
22725cf1a30Sjl static int		opl_check_dr_status();
22825cf1a30Sjl static void		drmach_io_dispose(drmachid_t);
22925cf1a30Sjl static sbd_error_t	*drmach_io_release(drmachid_t);
23025cf1a30Sjl static sbd_error_t	*drmach_io_status(drmachid_t, drmach_status_t *);
231bbe1232eSToomas Soome static int		drmach_init(void);
232bbe1232eSToomas Soome static void		drmach_fini(void);
23325cf1a30Sjl static void		drmach_swap_pa(drmach_mem_t *, drmach_mem_t *);
23425cf1a30Sjl static drmach_board_t	*drmach_get_board_by_bnum(int);
23525cf1a30Sjl 
2368682d1efSRichard Lowe static sbd_error_t	*drmach_board_release(drmachid_t);
2378682d1efSRichard Lowe static sbd_error_t	*drmach_board_status(drmachid_t, drmach_status_t *);
2388682d1efSRichard Lowe static void		drmach_cpu_dispose(drmachid_t);
2398682d1efSRichard Lowe static sbd_error_t	*drmach_cpu_release(drmachid_t);
2408682d1efSRichard Lowe static sbd_error_t	*drmach_cpu_status(drmachid_t, drmach_status_t *);
2418682d1efSRichard Lowe static void		drmach_mem_dispose(drmachid_t);
2428682d1efSRichard Lowe static sbd_error_t	*drmach_mem_release(drmachid_t);
2438682d1efSRichard Lowe static sbd_error_t	*drmach_mem_status(drmachid_t, drmach_status_t *);
2448682d1efSRichard Lowe 
24525cf1a30Sjl /* options for the second argument in drmach_add_remove_cpu() */
24625cf1a30Sjl #define	HOTADD_CPU	1
24725cf1a30Sjl #define	HOTREMOVE_CPU	2
24825cf1a30Sjl 
24925cf1a30Sjl #define	ON_BOARD_CORE_NUM(x)	(((uint_t)(x) / OPL_MAX_STRANDID_PER_CORE) & \
25025cf1a30Sjl 	(OPL_MAX_COREID_PER_BOARD - 1))
25125cf1a30Sjl 
25225cf1a30Sjl extern struct cpu	*SIGBCPU;
25325cf1a30Sjl 
25425cf1a30Sjl static int		drmach_name2type_idx(char *);
25525cf1a30Sjl static drmach_board_t	*drmach_board_new(int, int);
25625cf1a30Sjl 
25725cf1a30Sjl #ifdef DEBUG
25825cf1a30Sjl 
25925cf1a30Sjl #define	DRMACH_PR		if (drmach_debug) printf
26025cf1a30Sjl int drmach_debug = 1;		 /* set to non-zero to enable debug messages */
26125cf1a30Sjl #else
26225cf1a30Sjl 
26325cf1a30Sjl #define	DRMACH_PR		_NOTE(CONSTANTCONDITION) if (0) printf
26425cf1a30Sjl #endif /* DEBUG */
26525cf1a30Sjl 
26625cf1a30Sjl 
26725cf1a30Sjl #define	DRMACH_OBJ(id)		((drmach_common_t *)id)
26825cf1a30Sjl 
269ddf95635Sbm #define	DRMACH_NULL_ID(id)	((id) == 0)
270ddf95635Sbm 
27125cf1a30Sjl #define	DRMACH_IS_BOARD_ID(id)	\
27225cf1a30Sjl 	((id != 0) &&		\
27325cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
27425cf1a30Sjl 
27525cf1a30Sjl #define	DRMACH_IS_CPU_ID(id)	\
27625cf1a30Sjl 	((id != 0) &&		\
27725cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
27825cf1a30Sjl 
27925cf1a30Sjl #define	DRMACH_IS_MEM_ID(id)	\
28025cf1a30Sjl 	((id != 0) &&		\
28125cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
28225cf1a30Sjl 
28325cf1a30Sjl #define	DRMACH_IS_IO_ID(id)	\
28425cf1a30Sjl 	((id != 0) &&		\
28525cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
28625cf1a30Sjl 
28725cf1a30Sjl #define	DRMACH_IS_DEVICE_ID(id)					\
28825cf1a30Sjl 	((id != 0) &&						\
28925cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
29025cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
29125cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
29225cf1a30Sjl 
29325cf1a30Sjl #define	DRMACH_IS_ID(id)					\
29425cf1a30Sjl 	((id != 0) &&						\
29525cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new ||	\
29625cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
29725cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
29825cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
29925cf1a30Sjl 
30025cf1a30Sjl #define	DRMACH_INTERNAL_ERROR() \
30125cf1a30Sjl 	drerr_new(1, EOPL_INTERNAL, drmach_ie_fmt, __LINE__)
30225cf1a30Sjl 
30325cf1a30Sjl static char		*drmach_ie_fmt = "drmach.c %d";
30425cf1a30Sjl 
30525cf1a30Sjl static struct {
30625cf1a30Sjl 	const char	*name;
30725cf1a30Sjl 	const char	*type;
30825cf1a30Sjl 	sbd_error_t	*(*new)(drmach_device_t *, drmachid_t *);
30925cf1a30Sjl } drmach_name2type[] = {
31025cf1a30Sjl 	{ "cpu",	DRMACH_DEVTYPE_CPU,		drmach_cpu_new },
31125cf1a30Sjl 	{ "pseudo-mc",	DRMACH_DEVTYPE_MEM,		drmach_mem_new },
31225cf1a30Sjl 	{ "pci",	DRMACH_DEVTYPE_PCI,		drmach_io_new  },
31325cf1a30Sjl };
31425cf1a30Sjl 
31525cf1a30Sjl /* utility */
31625cf1a30Sjl #define	MBYTE	(1048576ull)
31725cf1a30Sjl 
31825cf1a30Sjl /*
31925cf1a30Sjl  * drmach autoconfiguration data structures and interfaces
32025cf1a30Sjl  */
32125cf1a30Sjl 
32225cf1a30Sjl extern struct mod_ops mod_miscops;
32325cf1a30Sjl 
32425cf1a30Sjl static struct modlmisc modlmisc = {
32525cf1a30Sjl 	&mod_miscops,
32625cf1a30Sjl 	"OPL DR 1.1"
32725cf1a30Sjl };
32825cf1a30Sjl 
32925cf1a30Sjl static struct modlinkage modlinkage = {
33025cf1a30Sjl 	MODREV_1,
33125cf1a30Sjl 	(void *)&modlmisc,
33225cf1a30Sjl 	NULL
33325cf1a30Sjl };
33425cf1a30Sjl 
33525cf1a30Sjl static krwlock_t drmach_boards_rwlock;
33625cf1a30Sjl 
33725cf1a30Sjl typedef const char	*fn_t;
33825cf1a30Sjl 
33925cf1a30Sjl int
_init(void)34025cf1a30Sjl _init(void)
34125cf1a30Sjl {
34225cf1a30Sjl 	int err;
34325cf1a30Sjl 
34425cf1a30Sjl 	if ((err = drmach_init()) != 0) {
34525cf1a30Sjl 		return (err);
34625cf1a30Sjl 	}
34725cf1a30Sjl 
34825cf1a30Sjl 	if ((err = mod_install(&modlinkage)) != 0) {
34925cf1a30Sjl 		drmach_fini();
35025cf1a30Sjl 	}
35125cf1a30Sjl 
35225cf1a30Sjl 	return (err);
35325cf1a30Sjl }
35425cf1a30Sjl 
35525cf1a30Sjl int
_fini(void)35625cf1a30Sjl _fini(void)
35725cf1a30Sjl {
35825cf1a30Sjl 	int	err;
35925cf1a30Sjl 
36025cf1a30Sjl 	if ((err = mod_remove(&modlinkage)) == 0)
36125cf1a30Sjl 		drmach_fini();
36225cf1a30Sjl 
36325cf1a30Sjl 	return (err);
36425cf1a30Sjl }
36525cf1a30Sjl 
36625cf1a30Sjl int
_info(struct modinfo * modinfop)36725cf1a30Sjl _info(struct modinfo *modinfop)
36825cf1a30Sjl {
36925cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
37025cf1a30Sjl }
37125cf1a30Sjl 
37225cf1a30Sjl struct drmach_mc_lookup {
37325cf1a30Sjl 	int	bnum;
37425cf1a30Sjl 	drmach_board_t	*bp;
37525cf1a30Sjl 	dev_info_t *dip;	/* rv - set if found */
37625cf1a30Sjl };
37725cf1a30Sjl 
37825cf1a30Sjl #define	_ptob64(p) ((uint64_t)(p) << PAGESHIFT)
37925cf1a30Sjl #define	_b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
38025cf1a30Sjl 
38125cf1a30Sjl static int
drmach_setup_mc_info(dev_info_t * dip,drmach_mem_t * mp)38225cf1a30Sjl drmach_setup_mc_info(dev_info_t *dip, drmach_mem_t *mp)
38325cf1a30Sjl {
38425cf1a30Sjl 	uint64_t	memory_ranges[128];
38525cf1a30Sjl 	int len;
38625cf1a30Sjl 	struct memlist	*ml;
38725cf1a30Sjl 	int rv;
38825cf1a30Sjl 	hwd_sb_t *hwd;
38925cf1a30Sjl 	hwd_memory_t *pm;
39025cf1a30Sjl 
39125cf1a30Sjl 	len = sizeof (memory_ranges);
392e98fafb9Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
393e98fafb9Sjl 	    "sb-mem-ranges", (caddr_t)&memory_ranges[0], &len) !=
394e98fafb9Sjl 	    DDI_PROP_SUCCESS) {
39525cf1a30Sjl 		mp->slice_base = 0;
39625cf1a30Sjl 		mp->slice_size = 0;
39725cf1a30Sjl 		return (-1);
39825cf1a30Sjl 	}
39925cf1a30Sjl 	mp->slice_base = memory_ranges[0];
40025cf1a30Sjl 	mp->slice_size = memory_ranges[1];
40125cf1a30Sjl 
40225cf1a30Sjl 	if (!mp->dev.bp->boot_board) {
40325cf1a30Sjl 		int i;
40425cf1a30Sjl 
40525cf1a30Sjl 		rv = opl_read_hwd(mp->dev.bp->bnum, NULL,  NULL, NULL, &hwd);
40625cf1a30Sjl 
40725cf1a30Sjl 		if (rv != 0) {
40825cf1a30Sjl 			return (-1);
40925cf1a30Sjl 		}
41025cf1a30Sjl 
41125cf1a30Sjl 		ml = NULL;
41225cf1a30Sjl 		pm = &hwd->sb_cmu.cmu_memory;
41325cf1a30Sjl 		for (i = 0; i < HWD_MAX_MEM_CHUNKS; i++) {
41425cf1a30Sjl 			if (pm->mem_chunks[i].chnk_size > 0) {
41525cf1a30Sjl 				ml = memlist_add_span(ml,
416e98fafb9Sjl 				    pm->mem_chunks[i].chnk_start_address,
417e98fafb9Sjl 				    pm->mem_chunks[i].chnk_size);
41825cf1a30Sjl 			}
41925cf1a30Sjl 		}
42025cf1a30Sjl 	} else {
42125cf1a30Sjl 		/*
42225cf1a30Sjl 		 * we intersect phys_install to get base_pa.
42325cf1a30Sjl 		 * This only works at bootup time.
42425cf1a30Sjl 		 */
42525cf1a30Sjl 
42625cf1a30Sjl 		memlist_read_lock();
42725cf1a30Sjl 		ml = memlist_dup(phys_install);
42825cf1a30Sjl 		memlist_read_unlock();
42925cf1a30Sjl 
43025cf1a30Sjl 		ml = memlist_del_span(ml, 0ull, mp->slice_base);
43125cf1a30Sjl 		if (ml) {
43225cf1a30Sjl 			uint64_t basepa, endpa;
43325cf1a30Sjl 			endpa = _ptob64(physmax + 1);
43425cf1a30Sjl 
43525cf1a30Sjl 			basepa = mp->slice_base + mp->slice_size;
43625cf1a30Sjl 
43725cf1a30Sjl 			ml = memlist_del_span(ml, basepa, endpa - basepa);
43825cf1a30Sjl 		}
43925cf1a30Sjl 	}
44025cf1a30Sjl 
44125cf1a30Sjl 	if (ml) {
44225cf1a30Sjl 		uint64_t nbytes = 0;
44325cf1a30Sjl 		struct memlist *p;
44456f33205SJonathan Adams 		for (p = ml; p; p = p->ml_next) {
44556f33205SJonathan Adams 			nbytes += p->ml_size;
44625cf1a30Sjl 		}
44725cf1a30Sjl 		if ((mp->nbytes = nbytes) > 0)
44856f33205SJonathan Adams 			mp->base_pa = ml->ml_address;
44925cf1a30Sjl 		else
45025cf1a30Sjl 			mp->base_pa = 0;
45125cf1a30Sjl 		mp->memlist = ml;
45225cf1a30Sjl 	} else {
45325cf1a30Sjl 		mp->base_pa = 0;
45425cf1a30Sjl 		mp->nbytes = 0;
45525cf1a30Sjl 	}
45625cf1a30Sjl 	return (0);
45725cf1a30Sjl }
45825cf1a30Sjl 
45925cf1a30Sjl 
46025cf1a30Sjl struct drmach_hotcpu {
46125cf1a30Sjl 	drmach_board_t *bp;
46225cf1a30Sjl 	int	bnum;
46325cf1a30Sjl 	int	core_id;
464bbe1232eSToomas Soome 	int	rv;
46525cf1a30Sjl 	int	option;
46625cf1a30Sjl };
46725cf1a30Sjl 
46825cf1a30Sjl static int
drmach_cpu_cb(dev_info_t * dip,void * arg)46925cf1a30Sjl drmach_cpu_cb(dev_info_t *dip, void *arg)
47025cf1a30Sjl {
47125cf1a30Sjl 	struct drmach_hotcpu *p = (struct drmach_hotcpu *)arg;
47225cf1a30Sjl 	char name[OBP_MAXDRVNAME];
47325cf1a30Sjl 	int len = OBP_MAXDRVNAME;
47425cf1a30Sjl 	int bnum, core_id, strand_id;
47525cf1a30Sjl 	drmach_board_t *bp;
47625cf1a30Sjl 
47725cf1a30Sjl 	if (dip == ddi_root_node()) {
47825cf1a30Sjl 		return (DDI_WALK_CONTINUE);
47925cf1a30Sjl 	}
48025cf1a30Sjl 
48125cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
48225cf1a30Sjl 	    DDI_PROP_DONTPASS, "name",
48325cf1a30Sjl 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
48425cf1a30Sjl 		return (DDI_WALK_PRUNECHILD);
48525cf1a30Sjl 	}
48625cf1a30Sjl 
48725cf1a30Sjl 	/* only cmp has board number */
48825cf1a30Sjl 	bnum = -1;
48925cf1a30Sjl 	len = sizeof (bnum);
49025cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
49125cf1a30Sjl 	    DDI_PROP_DONTPASS, OBP_BOARDNUM,
49225cf1a30Sjl 	    (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
49325cf1a30Sjl 		bnum = -1;
49425cf1a30Sjl 	}
49525cf1a30Sjl 
49625cf1a30Sjl 	if (strcmp(name, "cmp") == 0) {
49725cf1a30Sjl 		if (bnum != p->bnum)
49825cf1a30Sjl 			return (DDI_WALK_PRUNECHILD);
49925cf1a30Sjl 		return (DDI_WALK_CONTINUE);
50025cf1a30Sjl 	}
50125cf1a30Sjl 	/* we have already pruned all unwanted cores and cpu's above */
50225cf1a30Sjl 	if (strcmp(name, "core") == 0) {
50325cf1a30Sjl 		return (DDI_WALK_CONTINUE);
50425cf1a30Sjl 	}
50525cf1a30Sjl 	if (strcmp(name, "cpu") == 0) {
50625cf1a30Sjl 		processorid_t cpuid;
50725cf1a30Sjl 		len = sizeof (cpuid);
50825cf1a30Sjl 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
50925cf1a30Sjl 		    DDI_PROP_DONTPASS, "cpuid",
51025cf1a30Sjl 		    (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
51125cf1a30Sjl 			p->rv = -1;
51225cf1a30Sjl 			return (DDI_WALK_TERMINATE);
51325cf1a30Sjl 		}
51425cf1a30Sjl 
51525cf1a30Sjl 		core_id = p->core_id;
51625cf1a30Sjl 
51725cf1a30Sjl 		bnum = LSB_ID(cpuid);
51825cf1a30Sjl 
51925cf1a30Sjl 		if (ON_BOARD_CORE_NUM(cpuid) != core_id)
52025cf1a30Sjl 			return (DDI_WALK_CONTINUE);
52125cf1a30Sjl 
52225cf1a30Sjl 		bp = p->bp;
52325cf1a30Sjl 		ASSERT(bnum == bp->bnum);
52425cf1a30Sjl 
52525cf1a30Sjl 		if (p->option == HOTADD_CPU) {
52625cf1a30Sjl 			if (prom_hotaddcpu(cpuid) != 0) {
52725cf1a30Sjl 				p->rv = -1;
52825cf1a30Sjl 				return (DDI_WALK_TERMINATE);
52925cf1a30Sjl 			}
53025cf1a30Sjl 			strand_id = STRAND_ID(cpuid);
53125cf1a30Sjl 			bp->cores[core_id].core_hotadded |= (1 << strand_id);
53225cf1a30Sjl 		} else if (p->option == HOTREMOVE_CPU) {
53325cf1a30Sjl 			if (prom_hotremovecpu(cpuid) != 0) {
53425cf1a30Sjl 				p->rv = -1;
53525cf1a30Sjl 				return (DDI_WALK_TERMINATE);
53625cf1a30Sjl 			}
53725cf1a30Sjl 			strand_id = STRAND_ID(cpuid);
53825cf1a30Sjl 			bp->cores[core_id].core_hotadded &= ~(1 << strand_id);
53925cf1a30Sjl 		}
54025cf1a30Sjl 		return (DDI_WALK_CONTINUE);
54125cf1a30Sjl 	}
54225cf1a30Sjl 
54325cf1a30Sjl 	return (DDI_WALK_PRUNECHILD);
54425cf1a30Sjl }
54525cf1a30Sjl 
54625cf1a30Sjl 
54725cf1a30Sjl static int
drmach_add_remove_cpu(int bnum,int core_id,int option)54825cf1a30Sjl drmach_add_remove_cpu(int bnum, int core_id, int option)
54925cf1a30Sjl {
55025cf1a30Sjl 	struct drmach_hotcpu arg;
55125cf1a30Sjl 	drmach_board_t *bp;
55225cf1a30Sjl 
55325cf1a30Sjl 	bp = drmach_get_board_by_bnum(bnum);
55425cf1a30Sjl 	ASSERT(bp);
55525cf1a30Sjl 
55625cf1a30Sjl 	arg.bp = bp;
55725cf1a30Sjl 	arg.bnum = bnum;
55825cf1a30Sjl 	arg.core_id = core_id;
55925cf1a30Sjl 	arg.rv = 0;
56025cf1a30Sjl 	arg.option = option;
56125cf1a30Sjl 	ddi_walk_devs(ddi_root_node(), drmach_cpu_cb, (void *)&arg);
56225cf1a30Sjl 	return (arg.rv);
56325cf1a30Sjl }
56425cf1a30Sjl 
56525cf1a30Sjl struct drmach_setup_core_arg {
56625cf1a30Sjl 	drmach_board_t *bp;
56725cf1a30Sjl };
56825cf1a30Sjl 
56925cf1a30Sjl static int
drmach_setup_core_cb(dev_info_t * dip,void * arg)57025cf1a30Sjl drmach_setup_core_cb(dev_info_t *dip, void *arg)
57125cf1a30Sjl {
57225cf1a30Sjl 	struct drmach_setup_core_arg *p = (struct drmach_setup_core_arg *)arg;
57325cf1a30Sjl 	char name[OBP_MAXDRVNAME];
57425cf1a30Sjl 	int len = OBP_MAXDRVNAME;
57525cf1a30Sjl 	int bnum;
57625cf1a30Sjl 	int core_id, strand_id;
57725cf1a30Sjl 
57825cf1a30Sjl 	if (dip == ddi_root_node()) {
57925cf1a30Sjl 		return (DDI_WALK_CONTINUE);
58025cf1a30Sjl 	}
58125cf1a30Sjl 
58225cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
58325cf1a30Sjl 	    DDI_PROP_DONTPASS, "name",
58425cf1a30Sjl 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
58525cf1a30Sjl 		return (DDI_WALK_PRUNECHILD);
58625cf1a30Sjl 	}
58725cf1a30Sjl 
58825cf1a30Sjl 	/* only cmp has board number */
58925cf1a30Sjl 	bnum = -1;
59025cf1a30Sjl 	len = sizeof (bnum);
59125cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
59225cf1a30Sjl 	    DDI_PROP_DONTPASS, OBP_BOARDNUM,
59325cf1a30Sjl 	    (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
59425cf1a30Sjl 		bnum = -1;
59525cf1a30Sjl 	}
59625cf1a30Sjl 
59725cf1a30Sjl 	if (strcmp(name, "cmp") == 0) {
59825cf1a30Sjl 		if (bnum != p->bp->bnum)
59925cf1a30Sjl 			return (DDI_WALK_PRUNECHILD);
60025cf1a30Sjl 		return (DDI_WALK_CONTINUE);
60125cf1a30Sjl 	}
60225cf1a30Sjl 	/* we have already pruned all unwanted cores and cpu's above */
60325cf1a30Sjl 	if (strcmp(name, "core") == 0) {
60425cf1a30Sjl 		return (DDI_WALK_CONTINUE);
60525cf1a30Sjl 	}
60625cf1a30Sjl 	if (strcmp(name, "cpu") == 0) {
60725cf1a30Sjl 		processorid_t cpuid;
60825cf1a30Sjl 		len = sizeof (cpuid);
60925cf1a30Sjl 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
61025cf1a30Sjl 		    DDI_PROP_DONTPASS, "cpuid",
61125cf1a30Sjl 		    (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
61225cf1a30Sjl 			return (DDI_WALK_TERMINATE);
61325cf1a30Sjl 		}
61425cf1a30Sjl 		bnum = LSB_ID(cpuid);
61525cf1a30Sjl 		ASSERT(bnum == p->bp->bnum);
61625cf1a30Sjl 		core_id = ON_BOARD_CORE_NUM(cpuid);
61725cf1a30Sjl 		strand_id = STRAND_ID(cpuid);
61825cf1a30Sjl 		p->bp->cores[core_id].core_present |= (1 << strand_id);
61925cf1a30Sjl 		return (DDI_WALK_CONTINUE);
62025cf1a30Sjl 	}
62125cf1a30Sjl 
62225cf1a30Sjl 	return (DDI_WALK_PRUNECHILD);
62325cf1a30Sjl }
62425cf1a30Sjl 
62525cf1a30Sjl 
62625cf1a30Sjl static void
drmach_setup_core_info(drmach_board_t * obj)62725cf1a30Sjl drmach_setup_core_info(drmach_board_t *obj)
62825cf1a30Sjl {
62925cf1a30Sjl 	struct drmach_setup_core_arg arg;
63025cf1a30Sjl 	int i;
63125cf1a30Sjl 
63225cf1a30Sjl 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
63325cf1a30Sjl 		obj->cores[i].core_present = 0;
63425cf1a30Sjl 		obj->cores[i].core_hotadded = 0;
63525cf1a30Sjl 		obj->cores[i].core_started = 0;
63625cf1a30Sjl 	}
63725cf1a30Sjl 	arg.bp = obj;
63825cf1a30Sjl 	ddi_walk_devs(ddi_root_node(), drmach_setup_core_cb, (void *)&arg);
63925cf1a30Sjl 
64025cf1a30Sjl 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
64125cf1a30Sjl 		if (obj->boot_board) {
64225cf1a30Sjl 			obj->cores[i].core_hotadded =
643e98fafb9Sjl 			    obj->cores[i].core_started =
644e98fafb9Sjl 			    obj->cores[i].core_present;
64525cf1a30Sjl 		}
64625cf1a30Sjl 	}
64725cf1a30Sjl }
64825cf1a30Sjl 
64925cf1a30Sjl /*
65025cf1a30Sjl  * drmach_node_* routines serve the purpose of separating the
65125cf1a30Sjl  * rest of the code from the device tree and OBP.  This is necessary
65225cf1a30Sjl  * because of In-Kernel-Probing.  Devices probed after stod, are probed
65325cf1a30Sjl  * by the in-kernel-prober, not OBP.  These devices, therefore, do not
65425cf1a30Sjl  * have dnode ids.
65525cf1a30Sjl  */
65625cf1a30Sjl 
65725cf1a30Sjl typedef struct {
65825cf1a30Sjl 	drmach_node_walk_args_t	*nwargs;
659bbe1232eSToomas Soome 	int			(*cb)(drmach_node_walk_args_t *args);
66025cf1a30Sjl 	int			err;
66125cf1a30Sjl } drmach_node_ddi_walk_args_t;
66225cf1a30Sjl 
66325cf1a30Sjl static int
drmach_node_ddi_walk_cb(dev_info_t * dip,void * arg)66425cf1a30Sjl drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
66525cf1a30Sjl {
66625cf1a30Sjl 	drmach_node_ddi_walk_args_t	*nargs;
66725cf1a30Sjl 
66825cf1a30Sjl 	nargs = (drmach_node_ddi_walk_args_t *)arg;
66925cf1a30Sjl 
67025cf1a30Sjl 	/*
67125cf1a30Sjl 	 * dip doesn't have to be held here as we are called
67225cf1a30Sjl 	 * from ddi_walk_devs() which holds the dip.
67325cf1a30Sjl 	 */
67425cf1a30Sjl 	nargs->nwargs->node->here = (void *)dip;
67525cf1a30Sjl 
67625cf1a30Sjl 	nargs->err = nargs->cb(nargs->nwargs);
67725cf1a30Sjl 
67825cf1a30Sjl 
67925cf1a30Sjl 	/*
68025cf1a30Sjl 	 * Set "here" to NULL so that unheld dip is not accessible
68125cf1a30Sjl 	 * outside ddi_walk_devs()
68225cf1a30Sjl 	 */
68325cf1a30Sjl 	nargs->nwargs->node->here = NULL;
68425cf1a30Sjl 
68525cf1a30Sjl 	if (nargs->err)
68625cf1a30Sjl 		return (DDI_WALK_TERMINATE);
68725cf1a30Sjl 	else
68825cf1a30Sjl 		return (DDI_WALK_CONTINUE);
68925cf1a30Sjl }
69025cf1a30Sjl 
69125cf1a30Sjl static int
drmach_node_ddi_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))69225cf1a30Sjl drmach_node_ddi_walk(drmach_node_t *np, void *data,
693bbe1232eSToomas Soome     int (*cb)(drmach_node_walk_args_t *args))
69425cf1a30Sjl {
69525cf1a30Sjl 	drmach_node_walk_args_t		args;
69625cf1a30Sjl 	drmach_node_ddi_walk_args_t	nargs;
69725cf1a30Sjl 
69825cf1a30Sjl 
69925cf1a30Sjl 	/* initialized args structure for callback */
70025cf1a30Sjl 	args.node = np;
70125cf1a30Sjl 	args.data = data;
70225cf1a30Sjl 
70325cf1a30Sjl 	nargs.nwargs = &args;
70425cf1a30Sjl 	nargs.cb = cb;
70525cf1a30Sjl 	nargs.err = 0;
70625cf1a30Sjl 
70725cf1a30Sjl 	/*
70825cf1a30Sjl 	 * Root node doesn't have to be held in any way.
70925cf1a30Sjl 	 */
710e98fafb9Sjl 	ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
71125cf1a30Sjl 
71225cf1a30Sjl 	return (nargs.err);
71325cf1a30Sjl }
71425cf1a30Sjl 
71525cf1a30Sjl static int
drmach_node_ddi_get_parent(drmach_node_t * np,drmach_node_t * pp)71625cf1a30Sjl drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
71725cf1a30Sjl {
71825cf1a30Sjl 	dev_info_t	*ndip;
71925cf1a30Sjl 	static char	*fn = "drmach_node_ddi_get_parent";
72025cf1a30Sjl 
72125cf1a30Sjl 	ndip = np->n_getdip(np);
72225cf1a30Sjl 	if (ndip == NULL) {
72325cf1a30Sjl 		cmn_err(CE_WARN, "%s: NULL dip", fn);
72425cf1a30Sjl 		return (-1);
72525cf1a30Sjl 	}
72625cf1a30Sjl 
72725cf1a30Sjl 	bcopy(np, pp, sizeof (drmach_node_t));
72825cf1a30Sjl 
72925cf1a30Sjl 	pp->here = (void *)ddi_get_parent(ndip);
73025cf1a30Sjl 	if (pp->here == NULL) {
73125cf1a30Sjl 		cmn_err(CE_WARN, "%s: NULL parent dip", fn);
73225cf1a30Sjl 		return (-1);
73325cf1a30Sjl 	}
73425cf1a30Sjl 
73525cf1a30Sjl 	return (0);
73625cf1a30Sjl }
73725cf1a30Sjl 
73825cf1a30Sjl /*ARGSUSED*/
73925cf1a30Sjl static pnode_t
drmach_node_ddi_get_dnode(drmach_node_t * np)74025cf1a30Sjl drmach_node_ddi_get_dnode(drmach_node_t *np)
74125cf1a30Sjl {
742bbe1232eSToomas Soome 	return (0);
74325cf1a30Sjl }
74425cf1a30Sjl 
74525cf1a30Sjl static drmach_node_t *
drmach_node_new(void)74625cf1a30Sjl drmach_node_new(void)
74725cf1a30Sjl {
74825cf1a30Sjl 	drmach_node_t *np;
74925cf1a30Sjl 
75025cf1a30Sjl 	np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
75125cf1a30Sjl 
75225cf1a30Sjl 	np->get_dnode = drmach_node_ddi_get_dnode;
75325cf1a30Sjl 	np->walk = drmach_node_ddi_walk;
75425cf1a30Sjl 	np->n_getdip = drmach_node_ddi_get_dip;
75525cf1a30Sjl 	np->n_getproplen = drmach_node_ddi_get_proplen;
75625cf1a30Sjl 	np->n_getprop = drmach_node_ddi_get_prop;
75725cf1a30Sjl 	np->get_parent = drmach_node_ddi_get_parent;
75825cf1a30Sjl 
75925cf1a30Sjl 	return (np);
76025cf1a30Sjl }
76125cf1a30Sjl 
76225cf1a30Sjl static void
drmach_node_dispose(drmach_node_t * np)76325cf1a30Sjl drmach_node_dispose(drmach_node_t *np)
76425cf1a30Sjl {
76525cf1a30Sjl 	kmem_free(np, sizeof (*np));
76625cf1a30Sjl }
76725cf1a30Sjl 
76825cf1a30Sjl static dev_info_t *
drmach_node_ddi_get_dip(drmach_node_t * np)76925cf1a30Sjl drmach_node_ddi_get_dip(drmach_node_t *np)
77025cf1a30Sjl {
77125cf1a30Sjl 	return ((dev_info_t *)np->here);
77225cf1a30Sjl }
77325cf1a30Sjl 
77425cf1a30Sjl static int
drmach_node_walk(drmach_node_t * np,void * param,int (* cb)(drmach_node_walk_args_t * args))77525cf1a30Sjl drmach_node_walk(drmach_node_t *np, void *param,
776bbe1232eSToomas Soome     int (*cb)(drmach_node_walk_args_t *args))
77725cf1a30Sjl {
77825cf1a30Sjl 	return (np->walk(np, param, cb));
77925cf1a30Sjl }
78025cf1a30Sjl 
78125cf1a30Sjl static int
drmach_node_ddi_get_prop(drmach_node_t * np,char * name,void * buf,int len)78225cf1a30Sjl drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
78325cf1a30Sjl {
78425cf1a30Sjl 	int		rv = 0;
78525cf1a30Sjl 	dev_info_t	*ndip;
78625cf1a30Sjl 	static char	*fn = "drmach_node_ddi_get_prop";
78725cf1a30Sjl 
78825cf1a30Sjl 
78925cf1a30Sjl 	ndip = np->n_getdip(np);
79025cf1a30Sjl 	if (ndip == NULL) {
79125cf1a30Sjl 		cmn_err(CE_WARN, "%s: NULL dip", fn);
79225cf1a30Sjl 		rv = -1;
79325cf1a30Sjl 	} else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
79425cf1a30Sjl 	    DDI_PROP_DONTPASS, name,
79525cf1a30Sjl 	    (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
79625cf1a30Sjl 		rv = -1;
79725cf1a30Sjl 	}
79825cf1a30Sjl 
79925cf1a30Sjl 	return (rv);
80025cf1a30Sjl }
80125cf1a30Sjl 
80225cf1a30Sjl static int
drmach_node_ddi_get_proplen(drmach_node_t * np,char * name,int * len)80325cf1a30Sjl drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
80425cf1a30Sjl {
80525cf1a30Sjl 	int		rv = 0;
80625cf1a30Sjl 	dev_info_t	*ndip;
80725cf1a30Sjl 
80825cf1a30Sjl 	ndip = np->n_getdip(np);
80925cf1a30Sjl 	if (ndip == NULL) {
81025cf1a30Sjl 		rv = -1;
811e98fafb9Sjl 	} else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS, name,
812e98fafb9Sjl 	    len) != DDI_PROP_SUCCESS) {
81325cf1a30Sjl 		rv = -1;
81425cf1a30Sjl 	}
81525cf1a30Sjl 
81625cf1a30Sjl 	return (rv);
81725cf1a30Sjl }
81825cf1a30Sjl 
81925cf1a30Sjl static drmachid_t
drmach_node_dup(drmach_node_t * np)82025cf1a30Sjl drmach_node_dup(drmach_node_t *np)
82125cf1a30Sjl {
82225cf1a30Sjl 	drmach_node_t *dup;
82325cf1a30Sjl 
82425cf1a30Sjl 	dup = drmach_node_new();
82525cf1a30Sjl 	dup->here = np->here;
82625cf1a30Sjl 	dup->get_dnode = np->get_dnode;
82725cf1a30Sjl 	dup->walk = np->walk;
82825cf1a30Sjl 	dup->n_getdip = np->n_getdip;
82925cf1a30Sjl 	dup->n_getproplen = np->n_getproplen;
83025cf1a30Sjl 	dup->n_getprop = np->n_getprop;
83125cf1a30Sjl 	dup->get_parent = np->get_parent;
83225cf1a30Sjl 
83325cf1a30Sjl 	return (dup);
83425cf1a30Sjl }
83525cf1a30Sjl 
83625cf1a30Sjl /*
83725cf1a30Sjl  * drmach_array provides convenient array construction, access,
83825cf1a30Sjl  * bounds checking and array destruction logic.
83925cf1a30Sjl  */
84025cf1a30Sjl 
84125cf1a30Sjl static drmach_array_t *
drmach_array_new(int min_index,int max_index)84225cf1a30Sjl drmach_array_new(int min_index, int max_index)
84325cf1a30Sjl {
84425cf1a30Sjl 	drmach_array_t *arr;
84525cf1a30Sjl 
84625cf1a30Sjl 	arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
84725cf1a30Sjl 
84825cf1a30Sjl 	arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
84925cf1a30Sjl 	if (arr->arr_sz > 0) {
85025cf1a30Sjl 		arr->min_index = min_index;
85125cf1a30Sjl 		arr->max_index = max_index;
85225cf1a30Sjl 
85325cf1a30Sjl 		arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
85425cf1a30Sjl 		return (arr);
85525cf1a30Sjl 	} else {
85625cf1a30Sjl 		kmem_free(arr, sizeof (*arr));
85725cf1a30Sjl 		return (0);
85825cf1a30Sjl 	}
85925cf1a30Sjl }
86025cf1a30Sjl 
86125cf1a30Sjl static int
drmach_array_set(drmach_array_t * arr,int idx,drmachid_t val)86225cf1a30Sjl drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
86325cf1a30Sjl {
86425cf1a30Sjl 	if (idx < arr->min_index || idx > arr->max_index)
86525cf1a30Sjl 		return (-1);
86625cf1a30Sjl 	else {
86725cf1a30Sjl 		arr->arr[idx - arr->min_index] = val;
86825cf1a30Sjl 		return (0);
86925cf1a30Sjl 	}
87025cf1a30Sjl 	/*NOTREACHED*/
87125cf1a30Sjl }
87225cf1a30Sjl 
87325cf1a30Sjl static int
drmach_array_get(drmach_array_t * arr,int idx,drmachid_t * val)87425cf1a30Sjl drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
87525cf1a30Sjl {
87625cf1a30Sjl 	if (idx < arr->min_index || idx > arr->max_index)
87725cf1a30Sjl 		return (-1);
87825cf1a30Sjl 	else {
87925cf1a30Sjl 		*val = arr->arr[idx - arr->min_index];
88025cf1a30Sjl 		return (0);
88125cf1a30Sjl 	}
88225cf1a30Sjl 	/*NOTREACHED*/
88325cf1a30Sjl }
88425cf1a30Sjl 
88525cf1a30Sjl static int
drmach_array_first(drmach_array_t * arr,int * idx,drmachid_t * val)88625cf1a30Sjl drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
88725cf1a30Sjl {
88825cf1a30Sjl 	int rv;
88925cf1a30Sjl 
89025cf1a30Sjl 	*idx = arr->min_index;
89125cf1a30Sjl 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
89225cf1a30Sjl 		*idx += 1;
89325cf1a30Sjl 
89425cf1a30Sjl 	return (rv);
89525cf1a30Sjl }
89625cf1a30Sjl 
89725cf1a30Sjl static int
drmach_array_next(drmach_array_t * arr,int * idx,drmachid_t * val)89825cf1a30Sjl drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
89925cf1a30Sjl {
90025cf1a30Sjl 	int rv;
90125cf1a30Sjl 
90225cf1a30Sjl 	*idx += 1;
90325cf1a30Sjl 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
90425cf1a30Sjl 		*idx += 1;
90525cf1a30Sjl 
90625cf1a30Sjl 	return (rv);
90725cf1a30Sjl }
90825cf1a30Sjl 
90925cf1a30Sjl static void
drmach_array_dispose(drmach_array_t * arr,void (* disposer)(drmachid_t))91025cf1a30Sjl drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
91125cf1a30Sjl {
91225cf1a30Sjl 	drmachid_t	val;
91325cf1a30Sjl 	int		idx;
91425cf1a30Sjl 	int		rv;
91525cf1a30Sjl 
91625cf1a30Sjl 	rv = drmach_array_first(arr, &idx, &val);
91725cf1a30Sjl 	while (rv == 0) {
91825cf1a30Sjl 		(*disposer)(val);
91925cf1a30Sjl 		rv = drmach_array_next(arr, &idx, &val);
92025cf1a30Sjl 	}
92125cf1a30Sjl 
92225cf1a30Sjl 	kmem_free(arr->arr, arr->arr_sz);
92325cf1a30Sjl 	kmem_free(arr, sizeof (*arr));
92425cf1a30Sjl }
92525cf1a30Sjl 
92625cf1a30Sjl static drmach_board_t *
drmach_get_board_by_bnum(int bnum)92725cf1a30Sjl drmach_get_board_by_bnum(int bnum)
92825cf1a30Sjl {
92925cf1a30Sjl 	drmachid_t id;
93025cf1a30Sjl 
93125cf1a30Sjl 	if (drmach_array_get(drmach_boards, bnum, &id) == 0)
93225cf1a30Sjl 		return ((drmach_board_t *)id);
93325cf1a30Sjl 	else
93425cf1a30Sjl 		return (NULL);
93525cf1a30Sjl }
93625cf1a30Sjl 
93725cf1a30Sjl static pnode_t
drmach_node_get_dnode(drmach_node_t * np)93825cf1a30Sjl drmach_node_get_dnode(drmach_node_t *np)
93925cf1a30Sjl {
94025cf1a30Sjl 	return (np->get_dnode(np));
94125cf1a30Sjl }
94225cf1a30Sjl 
94325cf1a30Sjl /*ARGSUSED*/
94425cf1a30Sjl sbd_error_t *
drmach_configure(drmachid_t id,int flags)94525cf1a30Sjl drmach_configure(drmachid_t id, int flags)
94625cf1a30Sjl {
94725cf1a30Sjl 	drmach_device_t		*dp;
94825cf1a30Sjl 	sbd_error_t		*err = NULL;
94925cf1a30Sjl 	dev_info_t		*rdip;
95025cf1a30Sjl 	dev_info_t		*fdip = NULL;
95125cf1a30Sjl 
95225cf1a30Sjl 	if (DRMACH_IS_CPU_ID(id)) {
95325cf1a30Sjl 		return (NULL);
95425cf1a30Sjl 	}
95525cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
95625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
95725cf1a30Sjl 	dp = id;
95825cf1a30Sjl 	rdip = dp->node->n_getdip(dp->node);
95925cf1a30Sjl 
96025cf1a30Sjl 	ASSERT(rdip);
96125cf1a30Sjl 
96225cf1a30Sjl 	ASSERT(e_ddi_branch_held(rdip));
96325cf1a30Sjl 
96425cf1a30Sjl 	if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
96525cf1a30Sjl 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
96625cf1a30Sjl 		dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
96725cf1a30Sjl 
96825cf1a30Sjl 		(void) ddi_pathname(dip, path);
96925cf1a30Sjl 		err = drerr_new(1,  EOPL_DRVFAIL, path);
97025cf1a30Sjl 
97125cf1a30Sjl 		kmem_free(path, MAXPATHLEN);
97225cf1a30Sjl 
97325cf1a30Sjl 		/* If non-NULL, fdip is returned held and must be released */
97425cf1a30Sjl 		if (fdip != NULL)
97525cf1a30Sjl 			ddi_release_devi(fdip);
97625cf1a30Sjl 	}
97725cf1a30Sjl 
97825cf1a30Sjl 	return (err);
97925cf1a30Sjl }
98025cf1a30Sjl 
98125cf1a30Sjl 
98225cf1a30Sjl static sbd_error_t *
drmach_device_new(drmach_node_t * node,drmach_board_t * bp,int portid,drmachid_t * idp)98325cf1a30Sjl drmach_device_new(drmach_node_t *node,
984bbe1232eSToomas Soome     drmach_board_t *bp, int portid, drmachid_t *idp)
98525cf1a30Sjl {
98625cf1a30Sjl 	int		 i;
98725cf1a30Sjl 	int		 rv;
98825cf1a30Sjl 	drmach_device_t	proto;
98925cf1a30Sjl 	sbd_error_t	*err;
99025cf1a30Sjl 	char		 name[OBP_MAXDRVNAME];
99125cf1a30Sjl 
99225cf1a30Sjl 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
99325cf1a30Sjl 	if (rv) {
99425cf1a30Sjl 		/* every node is expected to have a name */
995e98fafb9Sjl 		err = drerr_new(1, EOPL_GETPROP, "device node %s: property %s",
996e98fafb9Sjl 		    ddi_node_name(node->n_getdip(node)), "name");
99725cf1a30Sjl 		return (err);
99825cf1a30Sjl 	}
99925cf1a30Sjl 
100025cf1a30Sjl 	/*
100125cf1a30Sjl 	 * The node currently being examined is not listed in the name2type[]
100225cf1a30Sjl 	 * array.  In this case, the node is no interest to drmach.  Both
100325cf1a30Sjl 	 * dp and err are initialized here to yield nothing (no device or
100425cf1a30Sjl 	 * error structure) for this case.
100525cf1a30Sjl 	 */
100625cf1a30Sjl 	i = drmach_name2type_idx(name);
100725cf1a30Sjl 
100825cf1a30Sjl 
100925cf1a30Sjl 	if (i < 0) {
101025cf1a30Sjl 		*idp = (drmachid_t)0;
101125cf1a30Sjl 		return (NULL);
101225cf1a30Sjl 	}
101325cf1a30Sjl 
101425cf1a30Sjl 	/* device specific new function will set unum */
101525cf1a30Sjl 
101625cf1a30Sjl 	bzero(&proto, sizeof (proto));
101725cf1a30Sjl 	proto.type = drmach_name2type[i].type;
101825cf1a30Sjl 	proto.bp = bp;
101925cf1a30Sjl 	proto.node = node;
102025cf1a30Sjl 	proto.portid = portid;
102125cf1a30Sjl 
102225cf1a30Sjl 	return (drmach_name2type[i].new(&proto, idp));
102325cf1a30Sjl }
102425cf1a30Sjl 
102525cf1a30Sjl static void
drmach_device_dispose(drmachid_t id)102625cf1a30Sjl drmach_device_dispose(drmachid_t id)
102725cf1a30Sjl {
102825cf1a30Sjl 	drmach_device_t *self = id;
102925cf1a30Sjl 
103025cf1a30Sjl 	self->cm.dispose(id);
103125cf1a30Sjl }
103225cf1a30Sjl 
103325cf1a30Sjl 
103425cf1a30Sjl static drmach_board_t *
drmach_board_new(int bnum,int boot_board)103525cf1a30Sjl drmach_board_new(int bnum, int boot_board)
103625cf1a30Sjl {
103725cf1a30Sjl 	drmach_board_t	*bp;
103825cf1a30Sjl 
103925cf1a30Sjl 	bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
104025cf1a30Sjl 
104125cf1a30Sjl 	bp->cm.isa = (void *)drmach_board_new;
104225cf1a30Sjl 	bp->cm.release = drmach_board_release;
104325cf1a30Sjl 	bp->cm.status = drmach_board_status;
104425cf1a30Sjl 
104525cf1a30Sjl 	(void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
104625cf1a30Sjl 
104725cf1a30Sjl 	bp->bnum = bnum;
104825cf1a30Sjl 	bp->devices = NULL;
104925cf1a30Sjl 	bp->connected = boot_board;
105025cf1a30Sjl 	bp->tree = drmach_node_new();
105125cf1a30Sjl 	bp->assigned = boot_board;
105225cf1a30Sjl 	bp->powered = boot_board;
105325cf1a30Sjl 	bp->boot_board = boot_board;
105425cf1a30Sjl 
105525cf1a30Sjl 	/*
105625cf1a30Sjl 	 * If this is not bootup initialization, we have to wait till
105725cf1a30Sjl 	 * IKP sets up the device nodes in drmach_board_connect().
105825cf1a30Sjl 	 */
105925cf1a30Sjl 	if (boot_board)
106025cf1a30Sjl 		drmach_setup_core_info(bp);
106125cf1a30Sjl 
106207d06da5SSurya Prakki 	(void) drmach_array_set(drmach_boards, bnum, bp);
106325cf1a30Sjl 	return (bp);
106425cf1a30Sjl }
106525cf1a30Sjl 
106625cf1a30Sjl static void
drmach_board_dispose(drmachid_t id)106725cf1a30Sjl drmach_board_dispose(drmachid_t id)
106825cf1a30Sjl {
106925cf1a30Sjl 	drmach_board_t *bp;
107025cf1a30Sjl 
107125cf1a30Sjl 	ASSERT(DRMACH_IS_BOARD_ID(id));
107225cf1a30Sjl 	bp = id;
107325cf1a30Sjl 
107425cf1a30Sjl 	if (bp->tree)
107525cf1a30Sjl 		drmach_node_dispose(bp->tree);
107625cf1a30Sjl 
107725cf1a30Sjl 	if (bp->devices)
107825cf1a30Sjl 		drmach_array_dispose(bp->devices, drmach_device_dispose);
107925cf1a30Sjl 
108025cf1a30Sjl 	kmem_free(bp, sizeof (*bp));
108125cf1a30Sjl }
108225cf1a30Sjl 
108325cf1a30Sjl static sbd_error_t *
drmach_board_status(drmachid_t id,drmach_status_t * stat)108425cf1a30Sjl drmach_board_status(drmachid_t id, drmach_status_t *stat)
108525cf1a30Sjl {
108625cf1a30Sjl 	sbd_error_t	*err = NULL;
108725cf1a30Sjl 	drmach_board_t	*bp;
108825cf1a30Sjl 
108925cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
109025cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
109125cf1a30Sjl 	bp = id;
109225cf1a30Sjl 
109325cf1a30Sjl 	stat->assigned = bp->assigned;
109425cf1a30Sjl 	stat->powered = bp->powered;
109525cf1a30Sjl 	stat->busy = 0;			/* assume not busy */
109625cf1a30Sjl 	stat->configured = 0;		/* assume not configured */
109725cf1a30Sjl 	stat->empty = 0;
109825cf1a30Sjl 	stat->cond = bp->cond = SBD_COND_OK;
109907d06da5SSurya Prakki 	(void) strncpy(stat->type, "System Brd", sizeof (stat->type));
110025cf1a30Sjl 	stat->info[0] = '\0';
110125cf1a30Sjl 
110225cf1a30Sjl 	if (bp->devices) {
110325cf1a30Sjl 		int		 rv;
110425cf1a30Sjl 		int		 d_idx;
110525cf1a30Sjl 		drmachid_t	 d_id;
110625cf1a30Sjl 
110725cf1a30Sjl 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
110825cf1a30Sjl 		while (rv == 0) {
110925cf1a30Sjl 			drmach_status_t	d_stat;
111025cf1a30Sjl 
111125cf1a30Sjl 			err = drmach_i_status(d_id, &d_stat);
111225cf1a30Sjl 			if (err)
111325cf1a30Sjl 				break;
111425cf1a30Sjl 
111525cf1a30Sjl 			stat->busy |= d_stat.busy;
111625cf1a30Sjl 			stat->configured |= d_stat.configured;
111725cf1a30Sjl 
111825cf1a30Sjl 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
111925cf1a30Sjl 		}
112025cf1a30Sjl 	}
112125cf1a30Sjl 
112225cf1a30Sjl 	return (err);
112325cf1a30Sjl }
112425cf1a30Sjl 
112525cf1a30Sjl int
drmach_board_is_floating(drmachid_t id)112625cf1a30Sjl drmach_board_is_floating(drmachid_t id)
112725cf1a30Sjl {
112825cf1a30Sjl 	drmach_board_t *bp;
112925cf1a30Sjl 
113025cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
113125cf1a30Sjl 		return (0);
113225cf1a30Sjl 
113325cf1a30Sjl 	bp = (drmach_board_t *)id;
113425cf1a30Sjl 
113525cf1a30Sjl 	return ((drmach_domain.floating & (1 << bp->bnum)) ? 1 : 0);
113625cf1a30Sjl }
113725cf1a30Sjl 
113825cf1a30Sjl static int
drmach_init(void)113925cf1a30Sjl drmach_init(void)
114025cf1a30Sjl {
114125cf1a30Sjl 	dev_info_t	*rdip;
114225cf1a30Sjl 	int		i, rv, len;
114325cf1a30Sjl 	int		*floating;
114425cf1a30Sjl 
114525cf1a30Sjl 	rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
114625cf1a30Sjl 
114725cf1a30Sjl 	drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
114825cf1a30Sjl 
114925cf1a30Sjl 	rdip = ddi_root_node();
115025cf1a30Sjl 
115125cf1a30Sjl 	if (ddi_getproplen(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1152e98fafb9Sjl 	    "floating-boards", &len) != DDI_PROP_SUCCESS) {
115325cf1a30Sjl 		cmn_err(CE_WARN, "Cannot get floating-boards proplen\n");
115425cf1a30Sjl 	} else {
115525cf1a30Sjl 		floating = (int *)kmem_alloc(len, KM_SLEEP);
1156e98fafb9Sjl 		rv = ddi_prop_op(DDI_DEV_T_ANY, rdip, PROP_LEN_AND_VAL_BUF,
1157e98fafb9Sjl 		    DDI_PROP_DONTPASS, "floating-boards", (caddr_t)floating,
1158e98fafb9Sjl 		    &len);
115925cf1a30Sjl 		if (rv != DDI_PROP_SUCCESS) {
116025cf1a30Sjl 			cmn_err(CE_WARN, "Cannot get floating-boards prop\n");
116125cf1a30Sjl 		} else {
116225cf1a30Sjl 			drmach_domain.floating = 0;
116325cf1a30Sjl 			for (i = 0; i < len / sizeof (int); i++) {
116425cf1a30Sjl 				drmach_domain.floating |= (1 << floating[i]);
116525cf1a30Sjl 			}
116625cf1a30Sjl 		}
116725cf1a30Sjl 		kmem_free(floating, len);
116825cf1a30Sjl 	}
116925cf1a30Sjl 	drmach_domain.allow_dr = opl_check_dr_status();
117025cf1a30Sjl 
117125cf1a30Sjl 	rdip = ddi_get_child(ddi_root_node());
117225cf1a30Sjl 	do {
117325cf1a30Sjl 		int		 bnum;
117425cf1a30Sjl 		drmachid_t	 id;
117525cf1a30Sjl 
117625cf1a30Sjl 		bnum = -1;
1177e98fafb9Sjl 		bnum = ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1178e98fafb9Sjl 		    OBP_BOARDNUM, -1);
117925cf1a30Sjl 		if (bnum == -1)
118025cf1a30Sjl 			continue;
118125cf1a30Sjl 
118225cf1a30Sjl 		if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
1183e98fafb9Sjl 			cmn_err(CE_WARN, "Device node 0x%p has invalid "
118407d06da5SSurya Prakki 			    "property value, %s=%d", (void *)rdip,
118507d06da5SSurya Prakki 			    OBP_BOARDNUM, bnum);
118625cf1a30Sjl 			goto error;
118725cf1a30Sjl 		} else if (id == NULL) {
118825cf1a30Sjl 			(void) drmach_board_new(bnum, 1);
118925cf1a30Sjl 		}
119025cf1a30Sjl 	} while ((rdip = ddi_get_next_sibling(rdip)) != NULL);
119125cf1a30Sjl 
119225cf1a30Sjl 	opl_hold_devtree();
119325cf1a30Sjl 
119425cf1a30Sjl 	/*
119525cf1a30Sjl 	 * Initialize the IKP feature.
119625cf1a30Sjl 	 *
119725cf1a30Sjl 	 * This can be done only after DR has acquired a hold on all the
119825cf1a30Sjl 	 * device nodes that are interesting to IKP.
119925cf1a30Sjl 	 */
120025cf1a30Sjl 	if (opl_init_cfg() != 0) {
120125cf1a30Sjl 		cmn_err(CE_WARN, "DR - IKP initialization failed");
120225cf1a30Sjl 
120325cf1a30Sjl 		opl_release_devtree();
120425cf1a30Sjl 
120525cf1a30Sjl 		goto error;
120625cf1a30Sjl 	}
120725cf1a30Sjl 
120825cf1a30Sjl 	return (0);
120925cf1a30Sjl error:
121025cf1a30Sjl 	drmach_array_dispose(drmach_boards, drmach_board_dispose);
121125cf1a30Sjl 	rw_destroy(&drmach_boards_rwlock);
121225cf1a30Sjl 	return (ENXIO);
121325cf1a30Sjl }
121425cf1a30Sjl 
121525cf1a30Sjl static void
drmach_fini(void)121625cf1a30Sjl drmach_fini(void)
121725cf1a30Sjl {
121825cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
121925cf1a30Sjl 	drmach_array_dispose(drmach_boards, drmach_board_dispose);
122025cf1a30Sjl 	drmach_boards = NULL;
122125cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
122225cf1a30Sjl 
122325cf1a30Sjl 	/*
122425cf1a30Sjl 	 * Walk immediate children of the root devinfo node
122525cf1a30Sjl 	 * releasing holds acquired on branches in drmach_init()
122625cf1a30Sjl 	 */
122725cf1a30Sjl 
122825cf1a30Sjl 	opl_release_devtree();
122925cf1a30Sjl 
123025cf1a30Sjl 	rw_destroy(&drmach_boards_rwlock);
123125cf1a30Sjl }
123225cf1a30Sjl 
123325cf1a30Sjl /*
123425cf1a30Sjl  *	Each system board contains 2 Oberon PCI bridge and
123525cf1a30Sjl  *	1 CMUCH.
123625cf1a30Sjl  *	Each oberon has 2 channels.
123725cf1a30Sjl  *	Each channel has 2 pci-ex leaf.
123825cf1a30Sjl  *	Each CMUCH has 1 pci bus.
123925cf1a30Sjl  *
124025cf1a30Sjl  *
124125cf1a30Sjl  *	Device Path:
124225cf1a30Sjl  *	/pci@<portid>,reg
124325cf1a30Sjl  *
124425cf1a30Sjl  *	where
124525cf1a30Sjl  *	portid[10] = 0
124625cf1a30Sjl  *	portid[9:0] = LLEAF_ID[9:0] of the Oberon Channel
124725cf1a30Sjl  *
124825cf1a30Sjl  *	LLEAF_ID[9:8] = 0
124925cf1a30Sjl  *	LLEAF_ID[8:4] = LSB_ID[4:0]
125025cf1a30Sjl  *	LLEAF_ID[3:1] = IO Channel#[2:0] (0,1,2,3 for Oberon)
125125cf1a30Sjl  *			channel 4 is pcicmu
125225cf1a30Sjl  *	LLEAF_ID[0] = PCI Leaf Number (0 for leaf-A, 1 for leaf-B)
125325cf1a30Sjl  *
125425cf1a30Sjl  *	Properties:
125525cf1a30Sjl  *	name = pci
125625cf1a30Sjl  *	device_type = "pciex"
125725cf1a30Sjl  *	board# = LSBID
125825cf1a30Sjl  *	reg = int32 * 2, Oberon CSR space of the leaf and the UBC space
125925cf1a30Sjl  *	portid = Jupiter Bus Device ID ((LSB_ID << 3)|pciport#)
126025cf1a30Sjl  */
126125cf1a30Sjl 
126225cf1a30Sjl static sbd_error_t *
drmach_io_new(drmach_device_t * proto,drmachid_t * idp)126325cf1a30Sjl drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
126425cf1a30Sjl {
126525cf1a30Sjl 	drmach_io_t	*ip;
126625cf1a30Sjl 
126725cf1a30Sjl 	int		 portid;
126825cf1a30Sjl 
126925cf1a30Sjl 	portid = proto->portid;
127025cf1a30Sjl 	ASSERT(portid != -1);
127125cf1a30Sjl 	proto->unum = portid & (MAX_IO_UNITS_PER_BOARD - 1);
127225cf1a30Sjl 
127325cf1a30Sjl 	ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
127425cf1a30Sjl 	bcopy(proto, &ip->dev, sizeof (ip->dev));
127525cf1a30Sjl 	ip->dev.node = drmach_node_dup(proto->node);
127625cf1a30Sjl 	ip->dev.cm.isa = (void *)drmach_io_new;
127725cf1a30Sjl 	ip->dev.cm.dispose = drmach_io_dispose;
127825cf1a30Sjl 	ip->dev.cm.release = drmach_io_release;
127925cf1a30Sjl 	ip->dev.cm.status = drmach_io_status;
128025cf1a30Sjl 	ip->channel = (portid >> 1) & 0x7;
128125cf1a30Sjl 	ip->leaf = (portid & 0x1);
128225cf1a30Sjl 
128307d06da5SSurya Prakki 	(void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
1284e98fafb9Sjl 	    ip->dev.type, ip->dev.unum);
128525cf1a30Sjl 
128625cf1a30Sjl 	*idp = (drmachid_t)ip;
128725cf1a30Sjl 	return (NULL);
128825cf1a30Sjl }
128925cf1a30Sjl 
129025cf1a30Sjl 
129125cf1a30Sjl static void
drmach_io_dispose(drmachid_t id)129225cf1a30Sjl drmach_io_dispose(drmachid_t id)
129325cf1a30Sjl {
129425cf1a30Sjl 	drmach_io_t *self;
129525cf1a30Sjl 
129625cf1a30Sjl 	ASSERT(DRMACH_IS_IO_ID(id));
129725cf1a30Sjl 
129825cf1a30Sjl 	self = id;
129925cf1a30Sjl 	if (self->dev.node)
130025cf1a30Sjl 		drmach_node_dispose(self->dev.node);
130125cf1a30Sjl 
130225cf1a30Sjl 	kmem_free(self, sizeof (*self));
130325cf1a30Sjl }
130425cf1a30Sjl 
130525cf1a30Sjl /*ARGSUSED*/
130625cf1a30Sjl sbd_error_t *
drmach_pre_op(int cmd,drmachid_t id,drmach_opts_t * opts)130725cf1a30Sjl drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
130825cf1a30Sjl {
130925cf1a30Sjl 	drmach_board_t	*bp = (drmach_board_t *)id;
131025cf1a30Sjl 	sbd_error_t	*err = NULL;
131125cf1a30Sjl 
131225cf1a30Sjl 	/* allow status and ncm operations to always succeed */
131325cf1a30Sjl 	if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) {
131425cf1a30Sjl 		return (NULL);
131525cf1a30Sjl 	}
131625cf1a30Sjl 
131725cf1a30Sjl 	/* check all other commands for the required option string */
131825cf1a30Sjl 
131925cf1a30Sjl 	if ((opts->size > 0) && (opts->copts != NULL)) {
132025cf1a30Sjl 
132125cf1a30Sjl 		DRMACH_PR("platform options: %s\n", opts->copts);
132225cf1a30Sjl 
132325cf1a30Sjl 		if (strstr(opts->copts, "opldr") == NULL) {
132425cf1a30Sjl 			err = drerr_new(1, EOPL_SUPPORT, NULL);
132525cf1a30Sjl 		}
132625cf1a30Sjl 	} else {
132725cf1a30Sjl 		err = drerr_new(1, EOPL_SUPPORT, NULL);
132825cf1a30Sjl 	}
132925cf1a30Sjl 
133025cf1a30Sjl 	if (!err && id && DRMACH_IS_BOARD_ID(id)) {
133125cf1a30Sjl 		switch (cmd) {
133225cf1a30Sjl 			case SBD_CMD_TEST:
133325cf1a30Sjl 			case SBD_CMD_STATUS:
133425cf1a30Sjl 			case SBD_CMD_GETNCM:
133525cf1a30Sjl 				break;
133625cf1a30Sjl 			case SBD_CMD_CONNECT:
133725cf1a30Sjl 				if (bp->connected)
133825cf1a30Sjl 					err = drerr_new(0, ESBD_STATE, NULL);
133925cf1a30Sjl 				else if (!drmach_domain.allow_dr)
1340e98fafb9Sjl 					err = drerr_new(1, EOPL_SUPPORT, NULL);
134125cf1a30Sjl 				break;
134225cf1a30Sjl 			case SBD_CMD_DISCONNECT:
134325cf1a30Sjl 				if (!bp->connected)
134425cf1a30Sjl 					err = drerr_new(0, ESBD_STATE, NULL);
134525cf1a30Sjl 				else if (!drmach_domain.allow_dr)
1346e98fafb9Sjl 					err = drerr_new(1, EOPL_SUPPORT, NULL);
134725cf1a30Sjl 				break;
134825cf1a30Sjl 			default:
134925cf1a30Sjl 				if (!drmach_domain.allow_dr)
1350e98fafb9Sjl 					err = drerr_new(1, EOPL_SUPPORT, NULL);
135125cf1a30Sjl 				break;
135225cf1a30Sjl 
135325cf1a30Sjl 		}
135425cf1a30Sjl 	}
135525cf1a30Sjl 
135625cf1a30Sjl 	return (err);
135725cf1a30Sjl }
135825cf1a30Sjl 
135925cf1a30Sjl /*ARGSUSED*/
136025cf1a30Sjl sbd_error_t *
drmach_post_op(int cmd,drmachid_t id,drmach_opts_t * opts)136125cf1a30Sjl drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
136225cf1a30Sjl {
136325cf1a30Sjl 	return (NULL);
136425cf1a30Sjl }
136525cf1a30Sjl 
136625cf1a30Sjl sbd_error_t *
drmach_board_assign(int bnum,drmachid_t * id)136725cf1a30Sjl drmach_board_assign(int bnum, drmachid_t *id)
136825cf1a30Sjl {
136925cf1a30Sjl 	sbd_error_t	*err = NULL;
137025cf1a30Sjl 
137125cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
137225cf1a30Sjl 
137325cf1a30Sjl 	if (drmach_array_get(drmach_boards, bnum, id) == -1) {
137425cf1a30Sjl 		err = drerr_new(1, EOPL_BNUM, "%d", bnum);
137525cf1a30Sjl 	} else {
137625cf1a30Sjl 		drmach_board_t	*bp;
137725cf1a30Sjl 
137825cf1a30Sjl 		if (*id)
137925cf1a30Sjl 			rw_downgrade(&drmach_boards_rwlock);
138025cf1a30Sjl 
138125cf1a30Sjl 		bp = *id;
138225cf1a30Sjl 		if (!(*id))
138325cf1a30Sjl 			bp = *id  =
1384e98fafb9Sjl 			    (drmachid_t)drmach_board_new(bnum, 0);
138525cf1a30Sjl 		bp->assigned = 1;
138625cf1a30Sjl 	}
138725cf1a30Sjl 
138825cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
138925cf1a30Sjl 
139025cf1a30Sjl 	return (err);
139125cf1a30Sjl }
139225cf1a30Sjl 
139325cf1a30Sjl /*ARGSUSED*/
139425cf1a30Sjl sbd_error_t *
drmach_board_connect(drmachid_t id,drmach_opts_t * opts)139525cf1a30Sjl drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
139625cf1a30Sjl {
1397e98fafb9Sjl 	extern int	cpu_alljupiter;
139825cf1a30Sjl 	drmach_board_t	*obj = (drmach_board_t *)id;
1399e98fafb9Sjl 	unsigned	cpu_impl;
140025cf1a30Sjl 
140125cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
140225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
140325cf1a30Sjl 
1404e98fafb9Sjl 	if (opl_probe_sb(obj->bnum, &cpu_impl) != 0)
14058eafe49bSbm 		return (drerr_new(1, EOPL_PROBE, NULL));
140625cf1a30Sjl 
1407e98fafb9Sjl 	if (cpu_alljupiter) {
14088a040953Swh 		if (cpu_impl & (1 << OLYMPUS_C_IMPL)) {
14098a040953Swh 			(void) opl_unprobe_sb(obj->bnum);
1410e98fafb9Sjl 			return (drerr_new(1, EOPL_MIXED_CPU, NULL));
14118a040953Swh 		}
1412e98fafb9Sjl 	}
1413e98fafb9Sjl 
141425cf1a30Sjl 	(void) prom_attach_notice(obj->bnum);
141525cf1a30Sjl 
141625cf1a30Sjl 	drmach_setup_core_info(obj);
141725cf1a30Sjl 
141825cf1a30Sjl 	obj->connected = 1;
141925cf1a30Sjl 
142025cf1a30Sjl 	return (NULL);
142125cf1a30Sjl }
142225cf1a30Sjl 
142325cf1a30Sjl static int drmach_cache_flush_flag[NCPU];
142425cf1a30Sjl 
142525cf1a30Sjl /*ARGSUSED*/
142625cf1a30Sjl static void
drmach_flush_cache(uint64_t id,uint64_t dummy)142725cf1a30Sjl drmach_flush_cache(uint64_t id, uint64_t dummy)
142825cf1a30Sjl {
142925cf1a30Sjl 	extern void cpu_flush_ecache(void);
143025cf1a30Sjl 
143125cf1a30Sjl 	cpu_flush_ecache();
143225cf1a30Sjl 	drmach_cache_flush_flag[id] = 0;
143325cf1a30Sjl }
143425cf1a30Sjl 
143525cf1a30Sjl static void
drmach_flush_all()143625cf1a30Sjl drmach_flush_all()
143725cf1a30Sjl {
143825cf1a30Sjl 	cpuset_t	xc_cpuset;
143925cf1a30Sjl 	int		i;
144025cf1a30Sjl 
144125cf1a30Sjl 	xc_cpuset = cpu_ready_set;
144225cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
144325cf1a30Sjl 		if (CPU_IN_SET(xc_cpuset, i)) {
144425cf1a30Sjl 			drmach_cache_flush_flag[i] = 1;
144525cf1a30Sjl 			xc_one(i, drmach_flush_cache, i, 0);
144625cf1a30Sjl 			while (drmach_cache_flush_flag[i]) {
144725cf1a30Sjl 				DELAY(1000);
144825cf1a30Sjl 			}
144925cf1a30Sjl 		}
145025cf1a30Sjl 	}
145125cf1a30Sjl }
145225cf1a30Sjl 
145325cf1a30Sjl static int
drmach_disconnect_cpus(drmach_board_t * bp)145425cf1a30Sjl drmach_disconnect_cpus(drmach_board_t *bp)
145525cf1a30Sjl {
145625cf1a30Sjl 	int i, bnum;
145725cf1a30Sjl 
145825cf1a30Sjl 	bnum = bp->bnum;
145925cf1a30Sjl 
146025cf1a30Sjl 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
1461e98fafb9Sjl 		if (bp->cores[i].core_present) {
1462e98fafb9Sjl 			if (bp->cores[i].core_started)
1463e98fafb9Sjl 				return (-1);
1464e98fafb9Sjl 			if (bp->cores[i].core_hotadded) {
1465e98fafb9Sjl 				if (drmach_add_remove_cpu(bnum, i,
1466e98fafb9Sjl 				    HOTREMOVE_CPU)) {
1467e98fafb9Sjl 					cmn_err(CE_WARN, "Failed to remove "
1468e98fafb9Sjl 					    "CMP %d on board %d\n", i, bnum);
1469e98fafb9Sjl 					return (-1);
1470e98fafb9Sjl 				}
1471e98fafb9Sjl 			}
147225cf1a30Sjl 		}
147325cf1a30Sjl 	}
147425cf1a30Sjl 	return (0);
147525cf1a30Sjl }
147625cf1a30Sjl 
147725cf1a30Sjl /*ARGSUSED*/
147825cf1a30Sjl sbd_error_t *
drmach_board_disconnect(drmachid_t id,drmach_opts_t * opts)147925cf1a30Sjl drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
148025cf1a30Sjl {
148125cf1a30Sjl 	drmach_board_t *obj;
148225cf1a30Sjl 	int rv = 0;
148325cf1a30Sjl 	sbd_error_t		*err = NULL;
148425cf1a30Sjl 
1485ddf95635Sbm 	if (DRMACH_NULL_ID(id))
1486ddf95635Sbm 		return (NULL);
148725cf1a30Sjl 
148825cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
148925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
149025cf1a30Sjl 
149125cf1a30Sjl 	obj = (drmach_board_t *)id;
149225cf1a30Sjl 
149325cf1a30Sjl 	if (drmach_disconnect_cpus(obj)) {
14948eafe49bSbm 		err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
149525cf1a30Sjl 		return (err);
149625cf1a30Sjl 	}
149725cf1a30Sjl 
149825cf1a30Sjl 	rv = opl_unprobe_sb(obj->bnum);
149925cf1a30Sjl 
150025cf1a30Sjl 	if (rv == 0) {
150107d06da5SSurya Prakki 		(void) prom_detach_notice(obj->bnum);
150225cf1a30Sjl 		obj->connected = 0;
150325cf1a30Sjl 
150425cf1a30Sjl 	} else
15058eafe49bSbm 		err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
150625cf1a30Sjl 
150725cf1a30Sjl 	return (err);
150825cf1a30Sjl }
150925cf1a30Sjl 
151025cf1a30Sjl static int
drmach_get_portid(drmach_node_t * np)151125cf1a30Sjl drmach_get_portid(drmach_node_t *np)
151225cf1a30Sjl {
151325cf1a30Sjl 	int		portid;
151425cf1a30Sjl 	char		type[OBP_MAXPROPNAME];
151525cf1a30Sjl 
151625cf1a30Sjl 	if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
151725cf1a30Sjl 		return (portid);
151825cf1a30Sjl 
151925cf1a30Sjl 	/*
152025cf1a30Sjl 	 * Get the device_type property to see if we should
152125cf1a30Sjl 	 * continue processing this node.
152225cf1a30Sjl 	 */
152325cf1a30Sjl 	if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
152425cf1a30Sjl 		return (-1);
152525cf1a30Sjl 
152625cf1a30Sjl 	if (strcmp(type, OPL_CPU_NODE) == 0) {
152725cf1a30Sjl 		/*
152825cf1a30Sjl 		 * We return cpuid because it has no portid
152925cf1a30Sjl 		 */
153025cf1a30Sjl 		if (np->n_getprop(np, "cpuid", &portid, sizeof (portid)) == 0)
153125cf1a30Sjl 			return (portid);
153225cf1a30Sjl 	}
153325cf1a30Sjl 
153425cf1a30Sjl 	return (-1);
153525cf1a30Sjl }
153625cf1a30Sjl 
153725cf1a30Sjl /*
153825cf1a30Sjl  * This is a helper function to determine if a given
153925cf1a30Sjl  * node should be considered for a dr operation according
154025cf1a30Sjl  * to predefined dr type nodes and the node's name.
154125cf1a30Sjl  * Formal Parameter : The name of a device node.
154225cf1a30Sjl  * Return Value: -1, name does not map to a valid dr type.
154325cf1a30Sjl  *		 A value greater or equal to 0, name is a valid dr type.
154425cf1a30Sjl  */
154525cf1a30Sjl static int
drmach_name2type_idx(char * name)154625cf1a30Sjl drmach_name2type_idx(char *name)
154725cf1a30Sjl {
1548bbe1232eSToomas Soome 	int	index, ntypes;
154925cf1a30Sjl 
155025cf1a30Sjl 	if (name == NULL)
155125cf1a30Sjl 		return (-1);
155225cf1a30Sjl 
155325cf1a30Sjl 	/*
155425cf1a30Sjl 	 * Determine how many possible types are currently supported
155525cf1a30Sjl 	 * for dr.
155625cf1a30Sjl 	 */
155725cf1a30Sjl 	ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
155825cf1a30Sjl 
155925cf1a30Sjl 	/* Determine if the node's name correspond to a predefined type. */
156025cf1a30Sjl 	for (index = 0; index < ntypes; index++) {
156125cf1a30Sjl 		if (strcmp(drmach_name2type[index].name, name) == 0)
156225cf1a30Sjl 			/* The node is an allowed type for dr. */
156325cf1a30Sjl 			return (index);
156425cf1a30Sjl 	}
156525cf1a30Sjl 
156625cf1a30Sjl 	/*
156725cf1a30Sjl 	 * If the name of the node does not map to any of the
156825cf1a30Sjl 	 * types in the array drmach_name2type then the node is not of
156925cf1a30Sjl 	 * interest to dr.
157025cf1a30Sjl 	 */
157125cf1a30Sjl 	return (-1);
157225cf1a30Sjl }
157325cf1a30Sjl 
157425cf1a30Sjl /*
157525cf1a30Sjl  * there is some complication on OPL:
157625cf1a30Sjl  * - pseudo-mc nodes do not have portid property
157725cf1a30Sjl  * - portid[9:5] of cmp node is LSB #, portid[7:3] of pci is LSB#
157825cf1a30Sjl  * - cmp has board#
157925cf1a30Sjl  * - core and cpu nodes do not have portid and board# properties
158025cf1a30Sjl  * starcat uses portid to derive the board# but that does not work
158125cf1a30Sjl  * for us.  starfire reads board# property to filter the devices.
158225cf1a30Sjl  * That does not work either.  So for these specific device,
158325cf1a30Sjl  * we use specific hard coded methods to get the board# -
158425cf1a30Sjl  * cpu: LSB# = CPUID[9:5]
158525cf1a30Sjl  */
158625cf1a30Sjl 
158725cf1a30Sjl static int
drmach_board_find_devices_cb(drmach_node_walk_args_t * args)158825cf1a30Sjl drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
158925cf1a30Sjl {
159025cf1a30Sjl 	drmach_node_t			*node = args->node;
159125cf1a30Sjl 	drmach_board_cb_data_t		*data = args->data;
159225cf1a30Sjl 	drmach_board_t			*obj = data->obj;
159325cf1a30Sjl 
159425cf1a30Sjl 	int		rv, portid;
159525cf1a30Sjl 	int		bnum;
159625cf1a30Sjl 	drmachid_t	id;
159725cf1a30Sjl 	drmach_device_t	*device;
159825cf1a30Sjl 	char name[OBP_MAXDRVNAME];
159925cf1a30Sjl 
160025cf1a30Sjl 	portid = drmach_get_portid(node);
160125cf1a30Sjl 	/*
160225cf1a30Sjl 	 * core, cpu and pseudo-mc do not have portid
160325cf1a30Sjl 	 * we use cpuid as the portid of the cpu node
160425cf1a30Sjl 	 * for pseudo-mc, we do not use portid info.
160525cf1a30Sjl 	 */
160625cf1a30Sjl 
160725cf1a30Sjl 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
160825cf1a30Sjl 	if (rv)
160925cf1a30Sjl 		return (0);
161025cf1a30Sjl 
161125cf1a30Sjl 
161225cf1a30Sjl 	rv = node->n_getprop(node, OBP_BOARDNUM, &bnum, sizeof (bnum));
161325cf1a30Sjl 
161425cf1a30Sjl 	if (rv) {
161525cf1a30Sjl 		/*
161625cf1a30Sjl 		 * cpu does not have board# property.  We use
161725cf1a30Sjl 		 * CPUID[9:5]
161825cf1a30Sjl 		 */
161925cf1a30Sjl 		if (strcmp("cpu", name) == 0) {
162025cf1a30Sjl 			bnum = (portid >> 5) & 0x1f;
162125cf1a30Sjl 		} else
162225cf1a30Sjl 			return (0);
162325cf1a30Sjl 	}
162425cf1a30Sjl 
162525cf1a30Sjl 
162625cf1a30Sjl 	if (bnum != obj->bnum)
162725cf1a30Sjl 		return (0);
162825cf1a30Sjl 
162925cf1a30Sjl 	if (drmach_name2type_idx(name) < 0) {
163025cf1a30Sjl 		return (0);
163125cf1a30Sjl 	}
163225cf1a30Sjl 
163325cf1a30Sjl 	/*
163425cf1a30Sjl 	 * Create a device data structure from this node data.
163525cf1a30Sjl 	 * The call may yield nothing if the node is not of interest
163625cf1a30Sjl 	 * to drmach.
163725cf1a30Sjl 	 */
163825cf1a30Sjl 	data->err = drmach_device_new(node, obj, portid, &id);
163925cf1a30Sjl 	if (data->err)
164025cf1a30Sjl 		return (-1);
164125cf1a30Sjl 	else if (!id) {
164225cf1a30Sjl 		/*
164325cf1a30Sjl 		 * drmach_device_new examined the node we passed in
164425cf1a30Sjl 		 * and determined that it was one not of interest to
164525cf1a30Sjl 		 * drmach.  So, it is skipped.
164625cf1a30Sjl 		 */
164725cf1a30Sjl 		return (0);
164825cf1a30Sjl 	}
164925cf1a30Sjl 
165025cf1a30Sjl 	rv = drmach_array_set(obj->devices, data->ndevs++, id);
165125cf1a30Sjl 	if (rv) {
165225cf1a30Sjl 		data->err = DRMACH_INTERNAL_ERROR();
165325cf1a30Sjl 		return (-1);
165425cf1a30Sjl 	}
165525cf1a30Sjl 	device = id;
165625cf1a30Sjl 
165725cf1a30Sjl 	data->err = (*data->found)(data->a, device->type, device->unum, id);
165825cf1a30Sjl 	return (data->err == NULL ? 0 : -1);
165925cf1a30Sjl }
166025cf1a30Sjl 
166125cf1a30Sjl sbd_error_t *
drmach_board_find_devices(drmachid_t id,void * a,sbd_error_t * (* found)(void * a,const char *,int,drmachid_t))166225cf1a30Sjl drmach_board_find_devices(drmachid_t id, void *a,
1663bbe1232eSToomas Soome     sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
166425cf1a30Sjl {
166525cf1a30Sjl 	drmach_board_t		*bp = (drmach_board_t *)id;
166625cf1a30Sjl 	sbd_error_t		*err;
166725cf1a30Sjl 	int			 max_devices;
166825cf1a30Sjl 	int			 rv;
166925cf1a30Sjl 	drmach_board_cb_data_t	data;
167025cf1a30Sjl 
167125cf1a30Sjl 
167225cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
167325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
167425cf1a30Sjl 
167525cf1a30Sjl 	max_devices  = MAX_CPU_UNITS_PER_BOARD;
167625cf1a30Sjl 	max_devices += MAX_MEM_UNITS_PER_BOARD;
167725cf1a30Sjl 	max_devices += MAX_IO_UNITS_PER_BOARD;
167825cf1a30Sjl 
167925cf1a30Sjl 	bp->devices = drmach_array_new(0, max_devices);
168025cf1a30Sjl 
168125cf1a30Sjl 	if (bp->tree == NULL)
168225cf1a30Sjl 		bp->tree = drmach_node_new();
168325cf1a30Sjl 
168425cf1a30Sjl 	data.obj = bp;
168525cf1a30Sjl 	data.ndevs = 0;
168625cf1a30Sjl 	data.found = found;
168725cf1a30Sjl 	data.a = a;
168825cf1a30Sjl 	data.err = NULL;
168925cf1a30Sjl 
169025cf1a30Sjl 	rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
169125cf1a30Sjl 	if (rv == 0)
169225cf1a30Sjl 		err = NULL;
169325cf1a30Sjl 	else {
169425cf1a30Sjl 		drmach_array_dispose(bp->devices, drmach_device_dispose);
169525cf1a30Sjl 		bp->devices = NULL;
169625cf1a30Sjl 
169725cf1a30Sjl 		if (data.err)
169825cf1a30Sjl 			err = data.err;
169925cf1a30Sjl 		else
170025cf1a30Sjl 			err = DRMACH_INTERNAL_ERROR();
170125cf1a30Sjl 	}
170225cf1a30Sjl 
170325cf1a30Sjl 	return (err);
170425cf1a30Sjl }
170525cf1a30Sjl 
170625cf1a30Sjl int
drmach_board_lookup(int bnum,drmachid_t * id)170725cf1a30Sjl drmach_board_lookup(int bnum, drmachid_t *id)
170825cf1a30Sjl {
170925cf1a30Sjl 	int	rv = 0;
171025cf1a30Sjl 
171125cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_READER);
171225cf1a30Sjl 	if (drmach_array_get(drmach_boards, bnum, id)) {
171325cf1a30Sjl 		*id = 0;
171425cf1a30Sjl 		rv = -1;
171525cf1a30Sjl 	}
171625cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
171725cf1a30Sjl 	return (rv);
171825cf1a30Sjl }
171925cf1a30Sjl 
172025cf1a30Sjl sbd_error_t *
drmach_board_name(int bnum,char * buf,int buflen)172125cf1a30Sjl drmach_board_name(int bnum, char *buf, int buflen)
172225cf1a30Sjl {
172307d06da5SSurya Prakki 	(void) snprintf(buf, buflen, "SB%d", bnum);
172425cf1a30Sjl 	return (NULL);
172525cf1a30Sjl }
172625cf1a30Sjl 
172725cf1a30Sjl sbd_error_t *
drmach_board_poweroff(drmachid_t id)172825cf1a30Sjl drmach_board_poweroff(drmachid_t id)
172925cf1a30Sjl {
173025cf1a30Sjl 	drmach_board_t	*bp;
173125cf1a30Sjl 	sbd_error_t	*err;
173225cf1a30Sjl 	drmach_status_t	 stat;
173325cf1a30Sjl 
1734ddf95635Sbm 	if (DRMACH_NULL_ID(id))
1735ddf95635Sbm 		return (NULL);
1736ddf95635Sbm 
173725cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
173825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
173925cf1a30Sjl 	bp = id;
174025cf1a30Sjl 
174125cf1a30Sjl 	err = drmach_board_status(id, &stat);
174225cf1a30Sjl 
174325cf1a30Sjl 	if (!err) {
174425cf1a30Sjl 		if (stat.configured || stat.busy)
174525cf1a30Sjl 			err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
174625cf1a30Sjl 		else {
174725cf1a30Sjl 			bp->powered = 0;
174825cf1a30Sjl 		}
174925cf1a30Sjl 	}
175025cf1a30Sjl 	return (err);
175125cf1a30Sjl }
175225cf1a30Sjl 
175325cf1a30Sjl sbd_error_t *
drmach_board_poweron(drmachid_t id)175425cf1a30Sjl drmach_board_poweron(drmachid_t id)
175525cf1a30Sjl {
175625cf1a30Sjl 	drmach_board_t	*bp;
175725cf1a30Sjl 
175825cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
175925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
176025cf1a30Sjl 	bp = id;
176125cf1a30Sjl 
176225cf1a30Sjl 	bp->powered = 1;
176325cf1a30Sjl 
176425cf1a30Sjl 	return (NULL);
176525cf1a30Sjl }
176625cf1a30Sjl 
176725cf1a30Sjl static sbd_error_t *
drmach_board_release(drmachid_t id)176825cf1a30Sjl drmach_board_release(drmachid_t id)
176925cf1a30Sjl {
177025cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
177125cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
177225cf1a30Sjl 	return (NULL);
177325cf1a30Sjl }
177425cf1a30Sjl 
177525cf1a30Sjl /*ARGSUSED*/
177625cf1a30Sjl sbd_error_t *
drmach_board_test(drmachid_t id,drmach_opts_t * opts,int force)177725cf1a30Sjl drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
177825cf1a30Sjl {
177925cf1a30Sjl 	return (NULL);
178025cf1a30Sjl }
178125cf1a30Sjl 
178225cf1a30Sjl sbd_error_t *
drmach_board_unassign(drmachid_t id)178325cf1a30Sjl drmach_board_unassign(drmachid_t id)
178425cf1a30Sjl {
178525cf1a30Sjl 	drmach_board_t	*bp;
178625cf1a30Sjl 	sbd_error_t	*err;
178725cf1a30Sjl 	drmach_status_t	 stat;
178825cf1a30Sjl 
1789ddf95635Sbm 	if (DRMACH_NULL_ID(id))
1790ddf95635Sbm 		return (NULL);
179125cf1a30Sjl 
179225cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id)) {
179325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
179425cf1a30Sjl 	}
179525cf1a30Sjl 	bp = id;
179625cf1a30Sjl 
179725cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
179825cf1a30Sjl 
179925cf1a30Sjl 	err = drmach_board_status(id, &stat);
180025cf1a30Sjl 	if (err) {
180125cf1a30Sjl 		rw_exit(&drmach_boards_rwlock);
180225cf1a30Sjl 		return (err);
180325cf1a30Sjl 	}
180425cf1a30Sjl 	if (stat.configured || stat.busy) {
180525cf1a30Sjl 		err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
180625cf1a30Sjl 	} else {
180725cf1a30Sjl 		if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
180825cf1a30Sjl 			err = DRMACH_INTERNAL_ERROR();
180925cf1a30Sjl 		else
181025cf1a30Sjl 			drmach_board_dispose(bp);
181125cf1a30Sjl 	}
181225cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
181325cf1a30Sjl 	return (err);
181425cf1a30Sjl }
181525cf1a30Sjl 
181625cf1a30Sjl /*
181725cf1a30Sjl  * We have to do more on OPL - e.g. set up sram tte, read cpuid, strand id,
181825cf1a30Sjl  * implementation #, etc
181925cf1a30Sjl  */
182025cf1a30Sjl 
182125cf1a30Sjl static sbd_error_t *
drmach_cpu_new(drmach_device_t * proto,drmachid_t * idp)182225cf1a30Sjl drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
182325cf1a30Sjl {
182425cf1a30Sjl 	int		 portid;
182525cf1a30Sjl 	drmach_cpu_t	*cp = NULL;
182625cf1a30Sjl 
182725cf1a30Sjl 	/* portid is CPUID of the node */
182825cf1a30Sjl 	portid = proto->portid;
182925cf1a30Sjl 	ASSERT(portid != -1);
183025cf1a30Sjl 
183125cf1a30Sjl 	/* unum = (CMP/CHIP ID) + (ON_BOARD_CORE_NUM * MAX_CMPID_PER_BOARD) */
183225cf1a30Sjl 	proto->unum = ((portid/OPL_MAX_CPUID_PER_CMP) &
1833e98fafb9Sjl 	    (OPL_MAX_CMPID_PER_BOARD - 1)) +
1834e98fafb9Sjl 	    ((portid & (OPL_MAX_CPUID_PER_CMP - 1)) *
1835e98fafb9Sjl 	    (OPL_MAX_CMPID_PER_BOARD));
183625cf1a30Sjl 
183725cf1a30Sjl 	cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
183825cf1a30Sjl 	bcopy(proto, &cp->dev, sizeof (cp->dev));
183925cf1a30Sjl 	cp->dev.node = drmach_node_dup(proto->node);
184025cf1a30Sjl 	cp->dev.cm.isa = (void *)drmach_cpu_new;
184125cf1a30Sjl 	cp->dev.cm.dispose = drmach_cpu_dispose;
184225cf1a30Sjl 	cp->dev.cm.release = drmach_cpu_release;
184325cf1a30Sjl 	cp->dev.cm.status = drmach_cpu_status;
184425cf1a30Sjl 
184507d06da5SSurya Prakki 	(void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
1846e98fafb9Sjl 	    cp->dev.type, cp->dev.unum);
184725cf1a30Sjl 
184825cf1a30Sjl /*
184925cf1a30Sjl  *	CPU ID representation
185025cf1a30Sjl  *	CPUID[9:5] = SB#
185125cf1a30Sjl  *	CPUID[4:3] = Chip#
185225cf1a30Sjl  *	CPUID[2:1] = Core# (Only 2 core for OPL)
185325cf1a30Sjl  *	CPUID[0:0] = Strand#
185425cf1a30Sjl  */
185525cf1a30Sjl 
185625cf1a30Sjl /*
185725cf1a30Sjl  *	reg property of the strand contains strand ID
185825cf1a30Sjl  *	reg property of the parent node contains core ID
185925cf1a30Sjl  *	We should use them.
186025cf1a30Sjl  */
186125cf1a30Sjl 	cp->cpuid = portid;
186225cf1a30Sjl 	cp->sb = (portid >> 5) & 0x1f;
186325cf1a30Sjl 	cp->chipid = (portid >> 3) & 0x3;
186425cf1a30Sjl 	cp->coreid = (portid >> 1) & 0x3;
186525cf1a30Sjl 	cp->strandid = portid & 0x1;
186625cf1a30Sjl 
186725cf1a30Sjl 	*idp = (drmachid_t)cp;
186825cf1a30Sjl 	return (NULL);
186925cf1a30Sjl }
187025cf1a30Sjl 
187125cf1a30Sjl 
187225cf1a30Sjl static void
drmach_cpu_dispose(drmachid_t id)187325cf1a30Sjl drmach_cpu_dispose(drmachid_t id)
187425cf1a30Sjl {
187525cf1a30Sjl 	drmach_cpu_t	*self;
187625cf1a30Sjl 
187725cf1a30Sjl 	ASSERT(DRMACH_IS_CPU_ID(id));
187825cf1a30Sjl 
187925cf1a30Sjl 	self = id;
188025cf1a30Sjl 	if (self->dev.node)
188125cf1a30Sjl 		drmach_node_dispose(self->dev.node);
188225cf1a30Sjl 
188325cf1a30Sjl 	kmem_free(self, sizeof (*self));
188425cf1a30Sjl }
188525cf1a30Sjl 
188625cf1a30Sjl static int
drmach_cpu_start(struct cpu * cp)188725cf1a30Sjl drmach_cpu_start(struct cpu *cp)
188825cf1a30Sjl {
188925cf1a30Sjl 	int		cpuid = cp->cpu_id;
189025cf1a30Sjl 	extern int	restart_other_cpu(int);
189125cf1a30Sjl 
189225cf1a30Sjl 	ASSERT(MUTEX_HELD(&cpu_lock));
189325cf1a30Sjl 	ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
189425cf1a30Sjl 
189525cf1a30Sjl 	cp->cpu_flags &= ~CPU_POWEROFF;
189625cf1a30Sjl 
189725cf1a30Sjl 	/*
189825cf1a30Sjl 	 * NOTE: restart_other_cpu pauses cpus during the
189925cf1a30Sjl 	 *	 slave cpu start.  This helps to quiesce the
190025cf1a30Sjl 	 *	 bus traffic a bit which makes the tick sync
190125cf1a30Sjl 	 *	 routine in the prom more robust.
190225cf1a30Sjl 	 */
190325cf1a30Sjl 	DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
190425cf1a30Sjl 
190507d06da5SSurya Prakki 	(void) restart_other_cpu(cpuid);
190625cf1a30Sjl 
190725cf1a30Sjl 	return (0);
190825cf1a30Sjl }
190925cf1a30Sjl 
191025cf1a30Sjl static sbd_error_t *
drmach_cpu_release(drmachid_t id)191125cf1a30Sjl drmach_cpu_release(drmachid_t id)
191225cf1a30Sjl {
191325cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
191425cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
191525cf1a30Sjl 
191625cf1a30Sjl 	return (NULL);
191725cf1a30Sjl }
191825cf1a30Sjl 
191925cf1a30Sjl static sbd_error_t *
drmach_cpu_status(drmachid_t id,drmach_status_t * stat)192025cf1a30Sjl drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
192125cf1a30Sjl {
192225cf1a30Sjl 	drmach_cpu_t *cp;
192325cf1a30Sjl 	drmach_device_t *dp;
192425cf1a30Sjl 
192525cf1a30Sjl 	ASSERT(DRMACH_IS_CPU_ID(id));
192625cf1a30Sjl 	cp = (drmach_cpu_t *)id;
192725cf1a30Sjl 	dp = &cp->dev;
192825cf1a30Sjl 
192925cf1a30Sjl 	stat->assigned = dp->bp->assigned;
193025cf1a30Sjl 	stat->powered = dp->bp->powered;
193125cf1a30Sjl 	mutex_enter(&cpu_lock);
193225cf1a30Sjl 	stat->configured = (cpu_get(cp->cpuid) != NULL);
193325cf1a30Sjl 	mutex_exit(&cpu_lock);
193425cf1a30Sjl 	stat->busy = dp->busy;
193507d06da5SSurya Prakki 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
193625cf1a30Sjl 	stat->info[0] = '\0';
193725cf1a30Sjl 
193825cf1a30Sjl 	return (NULL);
193925cf1a30Sjl }
194025cf1a30Sjl 
194125cf1a30Sjl sbd_error_t *
drmach_cpu_disconnect(drmachid_t id)194225cf1a30Sjl drmach_cpu_disconnect(drmachid_t id)
194325cf1a30Sjl {
194425cf1a30Sjl 
194525cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
194625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
194725cf1a30Sjl 
194825cf1a30Sjl 	return (NULL);
194925cf1a30Sjl }
195025cf1a30Sjl 
195125cf1a30Sjl sbd_error_t *
drmach_cpu_get_id(drmachid_t id,processorid_t * cpuid)195225cf1a30Sjl drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
195325cf1a30Sjl {
195425cf1a30Sjl 	drmach_cpu_t *cpu;
195525cf1a30Sjl 
195625cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
195725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
195825cf1a30Sjl 	cpu = (drmach_cpu_t *)id;
195925cf1a30Sjl 
196025cf1a30Sjl 	/* get from cpu directly on OPL */
196125cf1a30Sjl 	*cpuid = cpu->cpuid;
196225cf1a30Sjl 	return (NULL);
196325cf1a30Sjl }
196425cf1a30Sjl 
196525cf1a30Sjl sbd_error_t *
drmach_cpu_get_impl(drmachid_t id,int * ip)196625cf1a30Sjl drmach_cpu_get_impl(drmachid_t id, int *ip)
196725cf1a30Sjl {
196825cf1a30Sjl 	drmach_device_t *cpu;
196925cf1a30Sjl 	drmach_node_t	*np;
197025cf1a30Sjl 	drmach_node_t	pp;
197125cf1a30Sjl 	int		impl;
197225cf1a30Sjl 	char		type[OBP_MAXPROPNAME];
197325cf1a30Sjl 
197425cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
197525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
197625cf1a30Sjl 
197725cf1a30Sjl 	cpu = id;
197825cf1a30Sjl 	np = cpu->node;
197925cf1a30Sjl 
198025cf1a30Sjl 	if (np->get_parent(np, &pp) != 0) {
198125cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
198225cf1a30Sjl 	}
198325cf1a30Sjl 
198425cf1a30Sjl 	/* the parent should be core */
198525cf1a30Sjl 
198625cf1a30Sjl 	if (pp.n_getprop(&pp, "device_type", &type, sizeof (type)) != 0) {
198725cf1a30Sjl 		return (drerr_new(0, EOPL_GETPROP, NULL));
198825cf1a30Sjl 	}
198925cf1a30Sjl 
199025cf1a30Sjl 	if (strcmp(type, OPL_CORE_NODE) == 0) {
1991e98fafb9Sjl 		if (pp.n_getprop(&pp, "implementation#", &impl,
1992e98fafb9Sjl 		    sizeof (impl)) != 0) {
199325cf1a30Sjl 			return (drerr_new(0, EOPL_GETPROP, NULL));
199425cf1a30Sjl 		}
199525cf1a30Sjl 	} else {
199625cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
199725cf1a30Sjl 	}
199825cf1a30Sjl 
199925cf1a30Sjl 	*ip = impl;
200025cf1a30Sjl 
200125cf1a30Sjl 	return (NULL);
200225cf1a30Sjl }
200325cf1a30Sjl 
200425cf1a30Sjl sbd_error_t *
drmach_get_dip(drmachid_t id,dev_info_t ** dip)200525cf1a30Sjl drmach_get_dip(drmachid_t id, dev_info_t **dip)
200625cf1a30Sjl {
200725cf1a30Sjl 	drmach_device_t	*dp;
200825cf1a30Sjl 
200925cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
201025cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
201125cf1a30Sjl 	dp = id;
201225cf1a30Sjl 
201325cf1a30Sjl 	*dip = dp->node->n_getdip(dp->node);
201425cf1a30Sjl 	return (NULL);
201525cf1a30Sjl }
201625cf1a30Sjl 
201725cf1a30Sjl sbd_error_t *
drmach_io_is_attached(drmachid_t id,int * yes)201825cf1a30Sjl drmach_io_is_attached(drmachid_t id, int *yes)
201925cf1a30Sjl {
202025cf1a30Sjl 	drmach_device_t *dp;
202125cf1a30Sjl 	dev_info_t	*dip;
202225cf1a30Sjl 	int		state;
202325cf1a30Sjl 
202425cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
202525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
202625cf1a30Sjl 	dp = id;
202725cf1a30Sjl 
202825cf1a30Sjl 	dip = dp->node->n_getdip(dp->node);
202925cf1a30Sjl 	if (dip == NULL) {
203025cf1a30Sjl 		*yes = 0;
203125cf1a30Sjl 		return (NULL);
203225cf1a30Sjl 	}
203325cf1a30Sjl 
203425cf1a30Sjl 	state = ddi_get_devstate(dip);
203525cf1a30Sjl 	*yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) ||
203625cf1a30Sjl 	    (state == DDI_DEVSTATE_UP));
203725cf1a30Sjl 
203825cf1a30Sjl 	return (NULL);
203925cf1a30Sjl }
204025cf1a30Sjl 
204125cf1a30Sjl struct drmach_io_cb {
204225cf1a30Sjl 	char	*name;	/* name of the node */
204325cf1a30Sjl 	int	(*func)(dev_info_t *);
204425cf1a30Sjl 	int	rv;
204568ac2337Sjl 	dev_info_t *dip;
204625cf1a30Sjl };
204725cf1a30Sjl 
204825cf1a30Sjl #define	DRMACH_IO_POST_ATTACH	0
204925cf1a30Sjl #define	DRMACH_IO_PRE_RELEASE	1
205025cf1a30Sjl 
205125cf1a30Sjl static int
drmach_io_cb_check(dev_info_t * dip,void * arg)205225cf1a30Sjl drmach_io_cb_check(dev_info_t *dip, void *arg)
205325cf1a30Sjl {
205425cf1a30Sjl 	struct drmach_io_cb *p = (struct drmach_io_cb *)arg;
205525cf1a30Sjl 	char name[OBP_MAXDRVNAME];
205625cf1a30Sjl 	int len = OBP_MAXDRVNAME;
205725cf1a30Sjl 
2058e98fafb9Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "name",
205925cf1a30Sjl 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
206025cf1a30Sjl 		return (DDI_WALK_PRUNECHILD);
206125cf1a30Sjl 	}
206225cf1a30Sjl 
206325cf1a30Sjl 	if (strcmp(name, p->name) == 0) {
206468ac2337Sjl 		ndi_hold_devi(dip);
206568ac2337Sjl 		p->dip = dip;
206625cf1a30Sjl 		return (DDI_WALK_TERMINATE);
206725cf1a30Sjl 	}
206825cf1a30Sjl 
206925cf1a30Sjl 	return (DDI_WALK_CONTINUE);
207025cf1a30Sjl }
207125cf1a30Sjl 
207225cf1a30Sjl 
207325cf1a30Sjl static int
drmach_console_ops(drmachid_t * id,int state)207425cf1a30Sjl drmach_console_ops(drmachid_t *id, int state)
207525cf1a30Sjl {
207625cf1a30Sjl 	drmach_io_t *obj = (drmach_io_t *)id;
207725cf1a30Sjl 	struct drmach_io_cb arg;
207825cf1a30Sjl 	int (*msudetp)(dev_info_t *);
207925cf1a30Sjl 	int (*msuattp)(dev_info_t *);
208025cf1a30Sjl 	dev_info_t *dip, *pdip;
208125cf1a30Sjl 
208225cf1a30Sjl 	/* 4 is pcicmu channel */
208325cf1a30Sjl 	if (obj->channel != 4)
208425cf1a30Sjl 		return (0);
208525cf1a30Sjl 
208625cf1a30Sjl 	arg.name = "serial";
208725cf1a30Sjl 	arg.func = NULL;
208825cf1a30Sjl 	if (state == DRMACH_IO_PRE_RELEASE) {
208925cf1a30Sjl 		msudetp = (int (*)(dev_info_t *))
209025cf1a30Sjl 		    modgetsymvalue("oplmsu_dr_detach", 0);
209125cf1a30Sjl 		if (msudetp != NULL)
209225cf1a30Sjl 			arg.func = msudetp;
209325cf1a30Sjl 	} else if (state == DRMACH_IO_POST_ATTACH) {
209425cf1a30Sjl 		msuattp = (int (*)(dev_info_t *))
209525cf1a30Sjl 		    modgetsymvalue("oplmsu_dr_attach", 0);
209625cf1a30Sjl 		if (msuattp != NULL)
209725cf1a30Sjl 			arg.func = msuattp;
209868ac2337Sjl 	} else {
209925cf1a30Sjl 		return (0);
210068ac2337Sjl 	}
210125cf1a30Sjl 
210225cf1a30Sjl 	if (arg.func == NULL) {
210325cf1a30Sjl 		return (0);
210425cf1a30Sjl 	}
210525cf1a30Sjl 
210625cf1a30Sjl 	arg.rv = 0;
210768ac2337Sjl 	arg.dip = NULL;
210825cf1a30Sjl 
210925cf1a30Sjl 	dip = obj->dev.node->n_getdip(obj->dev.node);
2110*3fe80ca4SDan Cross 	pdip = ddi_get_parent(dip);
2111*3fe80ca4SDan Cross 	if (pdip == NULL) {
211225cf1a30Sjl 		/* this cannot happen unless something bad happens */
211325cf1a30Sjl 		return (-1);
211425cf1a30Sjl 	}
2115*3fe80ca4SDan Cross 	ndi_hold_devi(pdip);
2116*3fe80ca4SDan Cross 	ndi_devi_enter(pdip);
211725cf1a30Sjl 
211825cf1a30Sjl 	ddi_walk_devs(dip, drmach_io_cb_check, (void *)&arg);
211925cf1a30Sjl 
2120*3fe80ca4SDan Cross 	ndi_devi_exit(pdip);
212168ac2337Sjl 	ndi_rele_devi(pdip);
212268ac2337Sjl 
212368ac2337Sjl 	if (arg.dip) {
212468ac2337Sjl 		arg.rv = (*arg.func)(arg.dip);
212568ac2337Sjl 		ndi_rele_devi(arg.dip);
212668ac2337Sjl 	} else {
212768ac2337Sjl 		arg.rv = -1;
212825cf1a30Sjl 	}
212925cf1a30Sjl 
213025cf1a30Sjl 	return (arg.rv);
213125cf1a30Sjl }
213225cf1a30Sjl 
213325cf1a30Sjl sbd_error_t *
drmach_io_pre_release(drmachid_t id)213425cf1a30Sjl drmach_io_pre_release(drmachid_t id)
213525cf1a30Sjl {
213625cf1a30Sjl 	int rv;
213725cf1a30Sjl 
213825cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
213925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
214025cf1a30Sjl 
214125cf1a30Sjl 	rv = drmach_console_ops(id, DRMACH_IO_PRE_RELEASE);
214225cf1a30Sjl 
214325cf1a30Sjl 	if (rv != 0)
214425cf1a30Sjl 		cmn_err(CE_WARN, "IO callback failed in pre-release\n");
214525cf1a30Sjl 
214625cf1a30Sjl 	return (NULL);
214725cf1a30Sjl }
214825cf1a30Sjl 
214925cf1a30Sjl static sbd_error_t *
drmach_io_release(drmachid_t id)215025cf1a30Sjl drmach_io_release(drmachid_t id)
215125cf1a30Sjl {
215225cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
215325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
215425cf1a30Sjl 	return (NULL);
215525cf1a30Sjl }
215625cf1a30Sjl 
215725cf1a30Sjl sbd_error_t *
drmach_io_unrelease(drmachid_t id)215825cf1a30Sjl drmach_io_unrelease(drmachid_t id)
215925cf1a30Sjl {
216025cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
216125cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
216225cf1a30Sjl 	return (NULL);
216325cf1a30Sjl }
216425cf1a30Sjl 
216525cf1a30Sjl /*ARGSUSED*/
216625cf1a30Sjl sbd_error_t *
drmach_io_post_release(drmachid_t id)216725cf1a30Sjl drmach_io_post_release(drmachid_t id)
216825cf1a30Sjl {
216925cf1a30Sjl 	return (NULL);
217025cf1a30Sjl }
217125cf1a30Sjl 
217225cf1a30Sjl /*ARGSUSED*/
217325cf1a30Sjl sbd_error_t *
drmach_io_post_attach(drmachid_t id)217425cf1a30Sjl drmach_io_post_attach(drmachid_t id)
217525cf1a30Sjl {
217625cf1a30Sjl 	int rv;
217725cf1a30Sjl 
217825cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
217925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
218025cf1a30Sjl 
218125cf1a30Sjl 	rv = drmach_console_ops(id, DRMACH_IO_POST_ATTACH);
218225cf1a30Sjl 
218325cf1a30Sjl 	if (rv != 0)
218425cf1a30Sjl 		cmn_err(CE_WARN, "IO callback failed in post-attach\n");
218525cf1a30Sjl 
218625cf1a30Sjl 	return (0);
218725cf1a30Sjl }
218825cf1a30Sjl 
218925cf1a30Sjl static sbd_error_t *
drmach_io_status(drmachid_t id,drmach_status_t * stat)219025cf1a30Sjl drmach_io_status(drmachid_t id, drmach_status_t *stat)
219125cf1a30Sjl {
219225cf1a30Sjl 	drmach_device_t *dp;
219325cf1a30Sjl 	sbd_error_t	*err;
219425cf1a30Sjl 	int		 configured;
219525cf1a30Sjl 
219625cf1a30Sjl 	ASSERT(DRMACH_IS_IO_ID(id));
219725cf1a30Sjl 	dp = id;
219825cf1a30Sjl 
219925cf1a30Sjl 	err = drmach_io_is_attached(id, &configured);
220025cf1a30Sjl 	if (err)
220125cf1a30Sjl 		return (err);
220225cf1a30Sjl 
220325cf1a30Sjl 	stat->assigned = dp->bp->assigned;
220425cf1a30Sjl 	stat->powered = dp->bp->powered;
220525cf1a30Sjl 	stat->configured = (configured != 0);
220625cf1a30Sjl 	stat->busy = dp->busy;
220707d06da5SSurya Prakki 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
220825cf1a30Sjl 	stat->info[0] = '\0';
220925cf1a30Sjl 
221025cf1a30Sjl 	return (NULL);
221125cf1a30Sjl }
221225cf1a30Sjl 
221325cf1a30Sjl static sbd_error_t *
drmach_mem_new(drmach_device_t * proto,drmachid_t * idp)221425cf1a30Sjl drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
221525cf1a30Sjl {
221625cf1a30Sjl 	dev_info_t *dip;
221768ac2337Sjl 	int rv;
221825cf1a30Sjl 
221925cf1a30Sjl 	drmach_mem_t	*mp;
222025cf1a30Sjl 
222168ac2337Sjl 	rv = 0;
222268ac2337Sjl 
222368ac2337Sjl 	if ((proto->node->n_getproplen(proto->node, "mc-addr", &rv) < 0) ||
2224e98fafb9Sjl 	    (rv <= 0)) {
222568ac2337Sjl 		*idp = (drmachid_t)0;
222668ac2337Sjl 		return (NULL);
222768ac2337Sjl 	}
222868ac2337Sjl 
222925cf1a30Sjl 	mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
223025cf1a30Sjl 	proto->unum = 0;
223125cf1a30Sjl 
223225cf1a30Sjl 	bcopy(proto, &mp->dev, sizeof (mp->dev));
223325cf1a30Sjl 	mp->dev.node = drmach_node_dup(proto->node);
223425cf1a30Sjl 	mp->dev.cm.isa = (void *)drmach_mem_new;
223525cf1a30Sjl 	mp->dev.cm.dispose = drmach_mem_dispose;
223625cf1a30Sjl 	mp->dev.cm.release = drmach_mem_release;
223725cf1a30Sjl 	mp->dev.cm.status = drmach_mem_status;
223825cf1a30Sjl 
223907d06da5SSurya Prakki 	(void) snprintf(mp->dev.cm.name, sizeof (mp->dev.cm.name), "%s",
224007d06da5SSurya Prakki 	    mp->dev.type);
224125cf1a30Sjl 
224225cf1a30Sjl 	dip = mp->dev.node->n_getdip(mp->dev.node);
224325cf1a30Sjl 	if (drmach_setup_mc_info(dip, mp) != 0) {
22448eafe49bSbm 		return (drerr_new(1, EOPL_MC_SETUP, NULL));
224525cf1a30Sjl 	}
224625cf1a30Sjl 
224768ac2337Sjl 	/* make sure we do not create memoryless nodes */
224868ac2337Sjl 	if (mp->nbytes == 0) {
224968ac2337Sjl 		*idp = (drmachid_t)NULL;
225068ac2337Sjl 		kmem_free(mp, sizeof (drmach_mem_t));
225168ac2337Sjl 	} else
225268ac2337Sjl 		*idp = (drmachid_t)mp;
225368ac2337Sjl 
225425cf1a30Sjl 	return (NULL);
225525cf1a30Sjl }
225625cf1a30Sjl 
225725cf1a30Sjl static void
drmach_mem_dispose(drmachid_t id)225825cf1a30Sjl drmach_mem_dispose(drmachid_t id)
225925cf1a30Sjl {
226025cf1a30Sjl 	drmach_mem_t *mp;
226125cf1a30Sjl 
226225cf1a30Sjl 	ASSERT(DRMACH_IS_MEM_ID(id));
226325cf1a30Sjl 
226425cf1a30Sjl 
226525cf1a30Sjl 	mp = id;
226625cf1a30Sjl 
226725cf1a30Sjl 	if (mp->dev.node)
226825cf1a30Sjl 		drmach_node_dispose(mp->dev.node);
226925cf1a30Sjl 
227025cf1a30Sjl 	if (mp->memlist) {
227125cf1a30Sjl 		memlist_delete(mp->memlist);
227225cf1a30Sjl 		mp->memlist = NULL;
227325cf1a30Sjl 	}
227468ac2337Sjl 
227568ac2337Sjl 	kmem_free(mp, sizeof (*mp));
227625cf1a30Sjl }
227725cf1a30Sjl 
227825cf1a30Sjl sbd_error_t *
drmach_mem_add_span(drmachid_t id,uint64_t basepa,uint64_t size)227925cf1a30Sjl drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
228025cf1a30Sjl {
228125cf1a30Sjl 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
228225cf1a30Sjl 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
228325cf1a30Sjl 	int		rv;
228425cf1a30Sjl 
228525cf1a30Sjl 	ASSERT(size != 0);
228625cf1a30Sjl 
228725cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
228825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
228925cf1a30Sjl 
229085f58038Sdp 	rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
229125cf1a30Sjl 	if (rv == ENOMEM) {
229207d06da5SSurya Prakki 		cmn_err(CE_WARN, "%lu megabytes not available to kernel cage",
229307d06da5SSurya Prakki 		    (ulong_t)(size == 0 ? 0 : size / MBYTE));
229425cf1a30Sjl 	} else if (rv != 0) {
229525cf1a30Sjl 		/* catch this in debug kernels */
229625cf1a30Sjl 		ASSERT(0);
229725cf1a30Sjl 
2298e98fafb9Sjl 		cmn_err(CE_WARN, "unexpected kcage_range_add return value %d",
2299e98fafb9Sjl 		    rv);
230025cf1a30Sjl 	}
230125cf1a30Sjl 
230225cf1a30Sjl 	if (rv) {
230325cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
230425cf1a30Sjl 	}
230525cf1a30Sjl 	else
230625cf1a30Sjl 		return (NULL);
230725cf1a30Sjl }
230825cf1a30Sjl 
230925cf1a30Sjl sbd_error_t *
drmach_mem_del_span(drmachid_t id,uint64_t basepa,uint64_t size)231025cf1a30Sjl drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
231125cf1a30Sjl {
231225cf1a30Sjl 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
231325cf1a30Sjl 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
231425cf1a30Sjl 	int		rv;
231525cf1a30Sjl 
231625cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
231725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
231825cf1a30Sjl 
231925cf1a30Sjl 	if (size > 0) {
232025cf1a30Sjl 		rv = kcage_range_delete_post_mem_del(basepfn, npages);
232125cf1a30Sjl 		if (rv != 0) {
232225cf1a30Sjl 			cmn_err(CE_WARN,
232325cf1a30Sjl 			    "unexpected kcage_range_delete_post_mem_del"
232425cf1a30Sjl 			    " return value %d", rv);
232525cf1a30Sjl 			return (DRMACH_INTERNAL_ERROR());
232625cf1a30Sjl 		}
232725cf1a30Sjl 	}
232825cf1a30Sjl 
232925cf1a30Sjl 	return (NULL);
233025cf1a30Sjl }
233125cf1a30Sjl 
233225cf1a30Sjl sbd_error_t *
drmach_mem_disable(drmachid_t id)233325cf1a30Sjl drmach_mem_disable(drmachid_t id)
233425cf1a30Sjl {
233525cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
233625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
233725cf1a30Sjl 	else {
233825cf1a30Sjl 		drmach_flush_all();
233925cf1a30Sjl 		return (NULL);
234025cf1a30Sjl 	}
234125cf1a30Sjl }
234225cf1a30Sjl 
234325cf1a30Sjl sbd_error_t *
drmach_mem_enable(drmachid_t id)234425cf1a30Sjl drmach_mem_enable(drmachid_t id)
234525cf1a30Sjl {
234625cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
234725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
234825cf1a30Sjl 	else
234925cf1a30Sjl 		return (NULL);
235025cf1a30Sjl }
235125cf1a30Sjl 
235225cf1a30Sjl sbd_error_t *
drmach_mem_get_info(drmachid_t id,drmach_mem_info_t * mem)235325cf1a30Sjl drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem)
235425cf1a30Sjl {
235525cf1a30Sjl 	drmach_mem_t *mp;
235625cf1a30Sjl 
235725cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
235825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
235925cf1a30Sjl 
236025cf1a30Sjl 	mp = (drmach_mem_t *)id;
236125cf1a30Sjl 
236225cf1a30Sjl 	/*
236325cf1a30Sjl 	 * This is only used by dr to round up/down the memory
236425cf1a30Sjl 	 * for copying. Our unit of memory isolation is 64 MB.
236525cf1a30Sjl 	 */
236625cf1a30Sjl 
236725cf1a30Sjl 	mem->mi_alignment_mask = (64 * 1024 * 1024 - 1);
236825cf1a30Sjl 	mem->mi_basepa = mp->base_pa;
236925cf1a30Sjl 	mem->mi_size = mp->nbytes;
237025cf1a30Sjl 	mem->mi_slice_size = mp->slice_size;
237125cf1a30Sjl 
237225cf1a30Sjl 	return (NULL);
237325cf1a30Sjl }
237425cf1a30Sjl 
237525cf1a30Sjl sbd_error_t *
drmach_mem_get_base_physaddr(drmachid_t id,uint64_t * pa)237625cf1a30Sjl drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa)
237725cf1a30Sjl {
237825cf1a30Sjl 	drmach_mem_t *mp;
237925cf1a30Sjl 
238025cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
238125cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
238225cf1a30Sjl 
238325cf1a30Sjl 	mp = (drmach_mem_t *)id;
238425cf1a30Sjl 
238525cf1a30Sjl 	*pa = mp->base_pa;
238625cf1a30Sjl 	return (NULL);
238725cf1a30Sjl }
238825cf1a30Sjl 
238925cf1a30Sjl sbd_error_t *
drmach_mem_get_memlist(drmachid_t id,struct memlist ** ml)239025cf1a30Sjl drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
239125cf1a30Sjl {
239225cf1a30Sjl 	drmach_mem_t	*mem;
2393b307f191Sbm #ifdef	DEBUG
239425cf1a30Sjl 	int		rv;
2395b307f191Sbm #endif
239625cf1a30Sjl 	struct memlist	*mlist;
239725cf1a30Sjl 
239825cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
239925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
240025cf1a30Sjl 
240125cf1a30Sjl 	mem = (drmach_mem_t *)id;
240225cf1a30Sjl 	mlist = memlist_dup(mem->memlist);
240325cf1a30Sjl 
240425cf1a30Sjl #ifdef DEBUG
240525cf1a30Sjl 	/*
240625cf1a30Sjl 	 * Make sure the incoming memlist doesn't already
240725cf1a30Sjl 	 * intersect with what's present in the system (phys_install).
240825cf1a30Sjl 	 */
240925cf1a30Sjl 	memlist_read_lock();
241025cf1a30Sjl 	rv = memlist_intersect(phys_install, mlist);
241125cf1a30Sjl 	memlist_read_unlock();
241225cf1a30Sjl 	if (rv) {
2413e98fafb9Sjl 		DRMACH_PR("Derived memlist intersects with phys_install\n");
241425cf1a30Sjl 		memlist_dump(mlist);
241525cf1a30Sjl 
241625cf1a30Sjl 		DRMACH_PR("phys_install memlist:\n");
241725cf1a30Sjl 		memlist_dump(phys_install);
241825cf1a30Sjl 
241925cf1a30Sjl 		memlist_delete(mlist);
242025cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
242125cf1a30Sjl 	}
242225cf1a30Sjl 
242325cf1a30Sjl 	DRMACH_PR("Derived memlist:");
242425cf1a30Sjl 	memlist_dump(mlist);
242525cf1a30Sjl #endif
242625cf1a30Sjl 	*ml = mlist;
242725cf1a30Sjl 
242825cf1a30Sjl 	return (NULL);
242925cf1a30Sjl }
243025cf1a30Sjl 
243125cf1a30Sjl sbd_error_t *
drmach_mem_get_slice_size(drmachid_t id,uint64_t * bytes)243225cf1a30Sjl drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
243325cf1a30Sjl {
243425cf1a30Sjl 	drmach_mem_t	*mem;
243525cf1a30Sjl 
243625cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
243725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
243825cf1a30Sjl 
243925cf1a30Sjl 	mem = (drmach_mem_t *)id;
244025cf1a30Sjl 
244125cf1a30Sjl 	*bytes = mem->slice_size;
244225cf1a30Sjl 
244325cf1a30Sjl 	return (NULL);
244425cf1a30Sjl }
244525cf1a30Sjl 
244625cf1a30Sjl 
244725cf1a30Sjl /* ARGSUSED */
244825cf1a30Sjl processorid_t
drmach_mem_cpu_affinity(drmachid_t id)244925cf1a30Sjl drmach_mem_cpu_affinity(drmachid_t id)
245025cf1a30Sjl {
245125cf1a30Sjl 	return (CPU_CURRENT);
245225cf1a30Sjl }
245325cf1a30Sjl 
245425cf1a30Sjl static sbd_error_t *
drmach_mem_release(drmachid_t id)245525cf1a30Sjl drmach_mem_release(drmachid_t id)
245625cf1a30Sjl {
245725cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
245825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
245925cf1a30Sjl 	return (NULL);
246025cf1a30Sjl }
246125cf1a30Sjl 
246225cf1a30Sjl static sbd_error_t *
drmach_mem_status(drmachid_t id,drmach_status_t * stat)246325cf1a30Sjl drmach_mem_status(drmachid_t id, drmach_status_t *stat)
246425cf1a30Sjl {
246525cf1a30Sjl 	drmach_mem_t *dp;
246625cf1a30Sjl 	uint64_t	 pa, slice_size;
246725cf1a30Sjl 	struct memlist	*ml;
246825cf1a30Sjl 
246925cf1a30Sjl 	ASSERT(DRMACH_IS_MEM_ID(id));
247025cf1a30Sjl 	dp = id;
247125cf1a30Sjl 
247225cf1a30Sjl 	/* get starting physical address of target memory */
247325cf1a30Sjl 	pa = dp->base_pa;
247425cf1a30Sjl 
247525cf1a30Sjl 	/* round down to slice boundary */
247625cf1a30Sjl 	slice_size = dp->slice_size;
247725cf1a30Sjl 	pa &= ~(slice_size - 1);
247825cf1a30Sjl 
247925cf1a30Sjl 	/* stop at first span that is in slice */
248025cf1a30Sjl 	memlist_read_lock();
248156f33205SJonathan Adams 	for (ml = phys_install; ml; ml = ml->ml_next)
248256f33205SJonathan Adams 		if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
248325cf1a30Sjl 			break;
248425cf1a30Sjl 	memlist_read_unlock();
248525cf1a30Sjl 
248625cf1a30Sjl 	stat->assigned = dp->dev.bp->assigned;
248725cf1a30Sjl 	stat->powered = dp->dev.bp->powered;
248825cf1a30Sjl 	stat->configured = (ml != NULL);
248925cf1a30Sjl 	stat->busy = dp->dev.busy;
249007d06da5SSurya Prakki 	(void) strncpy(stat->type, dp->dev.type, sizeof (stat->type));
249125cf1a30Sjl 	stat->info[0] = '\0';
249225cf1a30Sjl 
249325cf1a30Sjl 	return (NULL);
249425cf1a30Sjl }
249525cf1a30Sjl 
249625cf1a30Sjl 
249725cf1a30Sjl sbd_error_t *
drmach_board_deprobe(drmachid_t id)249825cf1a30Sjl drmach_board_deprobe(drmachid_t id)
249925cf1a30Sjl {
250025cf1a30Sjl 	drmach_board_t	*bp;
250125cf1a30Sjl 
250225cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
250325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
250425cf1a30Sjl 
250525cf1a30Sjl 	bp = id;
250625cf1a30Sjl 
250768ac2337Sjl 	cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum);
250825cf1a30Sjl 
250925cf1a30Sjl 	if (bp->tree) {
251025cf1a30Sjl 		drmach_node_dispose(bp->tree);
251125cf1a30Sjl 		bp->tree = NULL;
251225cf1a30Sjl 	}
251325cf1a30Sjl 	if (bp->devices) {
251425cf1a30Sjl 		drmach_array_dispose(bp->devices, drmach_device_dispose);
251525cf1a30Sjl 		bp->devices = NULL;
251625cf1a30Sjl 	}
251725cf1a30Sjl 
251825cf1a30Sjl 	bp->boot_board = 0;
251925cf1a30Sjl 
252025cf1a30Sjl 	return (NULL);
252125cf1a30Sjl }
252225cf1a30Sjl 
252325cf1a30Sjl /*ARGSUSED*/
252425cf1a30Sjl static sbd_error_t *
drmach_pt_ikprobe(drmachid_t id,drmach_opts_t * opts)252525cf1a30Sjl drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
252625cf1a30Sjl {
252725cf1a30Sjl 	drmach_board_t		*bp = (drmach_board_t *)id;
252825cf1a30Sjl 	sbd_error_t		*err = NULL;
252925cf1a30Sjl 	int	rv;
2530e98fafb9Sjl 	unsigned cpu_impl;
253125cf1a30Sjl 
253225cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
253325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
253425cf1a30Sjl 
253525cf1a30Sjl 	DRMACH_PR("calling opl_probe_board for bnum=%d\n", bp->bnum);
2536e98fafb9Sjl 	rv = opl_probe_sb(bp->bnum, &cpu_impl);
253725cf1a30Sjl 	if (rv != 0) {
25388eafe49bSbm 		err = drerr_new(1, EOPL_PROBE, bp->cm.name);
253925cf1a30Sjl 		return (err);
254025cf1a30Sjl 	}
254125cf1a30Sjl 	return (err);
254225cf1a30Sjl }
254325cf1a30Sjl 
254425cf1a30Sjl /*ARGSUSED*/
254525cf1a30Sjl static sbd_error_t *
drmach_pt_ikdeprobe(drmachid_t id,drmach_opts_t * opts)254625cf1a30Sjl drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
254725cf1a30Sjl {
254825cf1a30Sjl 	drmach_board_t	*bp;
254925cf1a30Sjl 	sbd_error_t	*err = NULL;
255025cf1a30Sjl 	int	rv;
255125cf1a30Sjl 
255225cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
255325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
255425cf1a30Sjl 	bp = (drmach_board_t *)id;
255525cf1a30Sjl 
255625cf1a30Sjl 	cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
255725cf1a30Sjl 
255825cf1a30Sjl 	rv = opl_unprobe_sb(bp->bnum);
255925cf1a30Sjl 	if (rv != 0) {
25608eafe49bSbm 		err = drerr_new(1, EOPL_DEPROBE, bp->cm.name);
256125cf1a30Sjl 	}
256225cf1a30Sjl 
256325cf1a30Sjl 	return (err);
256425cf1a30Sjl }
256525cf1a30Sjl 
256625cf1a30Sjl 
256725cf1a30Sjl /*ARGSUSED*/
256825cf1a30Sjl sbd_error_t *
drmach_pt_readmem(drmachid_t id,drmach_opts_t * opts)256925cf1a30Sjl drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
257025cf1a30Sjl {
257125cf1a30Sjl 	struct memlist	*ml;
257225cf1a30Sjl 	uint64_t	src_pa;
257325cf1a30Sjl 	uint64_t	dst_pa;
257425cf1a30Sjl 	uint64_t	dst;
257525cf1a30Sjl 
257625cf1a30Sjl 	dst_pa = va_to_pa(&dst);
257725cf1a30Sjl 
257825cf1a30Sjl 	memlist_read_lock();
257956f33205SJonathan Adams 	for (ml = phys_install; ml; ml = ml->ml_next) {
258025cf1a30Sjl 		uint64_t	nbytes;
258125cf1a30Sjl 
258256f33205SJonathan Adams 		src_pa = ml->ml_address;
258356f33205SJonathan Adams 		nbytes = ml->ml_size;
258425cf1a30Sjl 
258525cf1a30Sjl 		while (nbytes != 0ull) {
258625cf1a30Sjl 
258725cf1a30Sjl 			/* copy 32 bytes at arc_pa to dst_pa */
258825cf1a30Sjl 			bcopy32_il(src_pa, dst_pa);
258925cf1a30Sjl 
259025cf1a30Sjl 			/* increment by 32 bytes */
259125cf1a30Sjl 			src_pa += (4 * sizeof (uint64_t));
259225cf1a30Sjl 
259325cf1a30Sjl 			/* decrement by 32 bytes */
259425cf1a30Sjl 			nbytes -= (4 * sizeof (uint64_t));
259525cf1a30Sjl 		}
259625cf1a30Sjl 	}
259725cf1a30Sjl 	memlist_read_unlock();
259825cf1a30Sjl 
259925cf1a30Sjl 	return (NULL);
260025cf1a30Sjl }
260125cf1a30Sjl 
260225cf1a30Sjl static struct {
260325cf1a30Sjl 	const char	*name;
260425cf1a30Sjl 	sbd_error_t	*(*handler)(drmachid_t id, drmach_opts_t *opts);
260525cf1a30Sjl } drmach_pt_arr[] = {
260625cf1a30Sjl 	{ "readmem",		drmach_pt_readmem		},
260725cf1a30Sjl 	{ "ikprobe",	drmach_pt_ikprobe	},
260825cf1a30Sjl 	{ "ikdeprobe",	drmach_pt_ikdeprobe	},
260925cf1a30Sjl 
261025cf1a30Sjl 	/* the following line must always be last */
261125cf1a30Sjl 	{ NULL,			NULL				}
261225cf1a30Sjl };
261325cf1a30Sjl 
261425cf1a30Sjl /*ARGSUSED*/
261525cf1a30Sjl sbd_error_t *
drmach_passthru(drmachid_t id,drmach_opts_t * opts)261625cf1a30Sjl drmach_passthru(drmachid_t id, drmach_opts_t *opts)
261725cf1a30Sjl {
261825cf1a30Sjl 	int		i;
261925cf1a30Sjl 	sbd_error_t	*err;
262025cf1a30Sjl 
262125cf1a30Sjl 	i = 0;
262225cf1a30Sjl 	while (drmach_pt_arr[i].name != NULL) {
262325cf1a30Sjl 		int len = strlen(drmach_pt_arr[i].name);
262425cf1a30Sjl 
262525cf1a30Sjl 		if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
262625cf1a30Sjl 			break;
262725cf1a30Sjl 
262825cf1a30Sjl 		i += 1;
262925cf1a30Sjl 	}
263025cf1a30Sjl 
263125cf1a30Sjl 	if (drmach_pt_arr[i].name == NULL)
263225cf1a30Sjl 		err = drerr_new(0, EOPL_UNKPTCMD, opts->copts);
263325cf1a30Sjl 	else
263425cf1a30Sjl 		err = (*drmach_pt_arr[i].handler)(id, opts);
263525cf1a30Sjl 
263625cf1a30Sjl 	return (err);
263725cf1a30Sjl }
263825cf1a30Sjl 
263925cf1a30Sjl sbd_error_t *
drmach_release(drmachid_t id)264025cf1a30Sjl drmach_release(drmachid_t id)
264125cf1a30Sjl {
264225cf1a30Sjl 	drmach_common_t *cp;
264325cf1a30Sjl 
264425cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
264525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
264625cf1a30Sjl 	cp = id;
264725cf1a30Sjl 
264825cf1a30Sjl 	return (cp->release(id));
264925cf1a30Sjl }
265025cf1a30Sjl 
265125cf1a30Sjl sbd_error_t *
drmach_status(drmachid_t id,drmach_status_t * stat)265225cf1a30Sjl drmach_status(drmachid_t id, drmach_status_t *stat)
265325cf1a30Sjl {
265425cf1a30Sjl 	drmach_common_t *cp;
265525cf1a30Sjl 	sbd_error_t	*err;
265625cf1a30Sjl 
265725cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_READER);
265825cf1a30Sjl 
265925cf1a30Sjl 	if (!DRMACH_IS_ID(id)) {
266025cf1a30Sjl 		rw_exit(&drmach_boards_rwlock);
266125cf1a30Sjl 		return (drerr_new(0, EOPL_NOTID, NULL));
266225cf1a30Sjl 	}
266325cf1a30Sjl 	cp = (drmach_common_t *)id;
266425cf1a30Sjl 	err = cp->status(id, stat);
266525cf1a30Sjl 
266625cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
266725cf1a30Sjl 
266825cf1a30Sjl 	return (err);
266925cf1a30Sjl }
267025cf1a30Sjl 
267125cf1a30Sjl static sbd_error_t *
drmach_i_status(drmachid_t id,drmach_status_t * stat)267225cf1a30Sjl drmach_i_status(drmachid_t id, drmach_status_t *stat)
267325cf1a30Sjl {
267425cf1a30Sjl 	drmach_common_t *cp;
267525cf1a30Sjl 
267625cf1a30Sjl 	if (!DRMACH_IS_ID(id))
267725cf1a30Sjl 		return (drerr_new(0, EOPL_NOTID, NULL));
267825cf1a30Sjl 	cp = id;
267925cf1a30Sjl 
268025cf1a30Sjl 	return (cp->status(id, stat));
268125cf1a30Sjl }
268225cf1a30Sjl 
268325cf1a30Sjl /*ARGSUSED*/
268425cf1a30Sjl sbd_error_t *
drmach_unconfigure(drmachid_t id,int flags)268525cf1a30Sjl drmach_unconfigure(drmachid_t id, int flags)
268625cf1a30Sjl {
268725cf1a30Sjl 	drmach_device_t *dp;
268825cf1a30Sjl 	dev_info_t	*rdip, *fdip = NULL;
268925cf1a30Sjl 	char name[OBP_MAXDRVNAME];
269025cf1a30Sjl 	int rv;
269125cf1a30Sjl 
269225cf1a30Sjl 	if (DRMACH_IS_CPU_ID(id))
269325cf1a30Sjl 		return (NULL);
269425cf1a30Sjl 
269525cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
269625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
269725cf1a30Sjl 
269825cf1a30Sjl 	dp = id;
269925cf1a30Sjl 
270025cf1a30Sjl 	rdip = dp->node->n_getdip(dp->node);
270125cf1a30Sjl 
270225cf1a30Sjl 	ASSERT(rdip);
270325cf1a30Sjl 
270425cf1a30Sjl 	rv = dp->node->n_getprop(dp->node, "name", name, OBP_MAXDRVNAME);
270525cf1a30Sjl 
270625cf1a30Sjl 	if (rv)
270725cf1a30Sjl 		return (NULL);
270825cf1a30Sjl 
270925cf1a30Sjl 	/*
271025cf1a30Sjl 	 * Note: FORCE flag is no longer necessary under devfs
271125cf1a30Sjl 	 */
271225cf1a30Sjl 
271325cf1a30Sjl 	ASSERT(e_ddi_branch_held(rdip));
271425cf1a30Sjl 	if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) {
271525cf1a30Sjl 		sbd_error_t	*err;
271625cf1a30Sjl 		char		*path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
271725cf1a30Sjl 
271825cf1a30Sjl 		/*
271925cf1a30Sjl 		 * If non-NULL, fdip is returned held and must be released.
272025cf1a30Sjl 		 */
272125cf1a30Sjl 		if (fdip != NULL) {
272225cf1a30Sjl 			(void) ddi_pathname(fdip, path);
272325cf1a30Sjl 			ndi_rele_devi(fdip);
272425cf1a30Sjl 		} else {
272525cf1a30Sjl 			(void) ddi_pathname(rdip, path);
272625cf1a30Sjl 		}
272725cf1a30Sjl 
272825cf1a30Sjl 		err = drerr_new(1, EOPL_DRVFAIL, path);
272925cf1a30Sjl 
273025cf1a30Sjl 		kmem_free(path, MAXPATHLEN);
273125cf1a30Sjl 
273225cf1a30Sjl 		return (err);
273325cf1a30Sjl 	}
273425cf1a30Sjl 
273525cf1a30Sjl 	return (NULL);
273625cf1a30Sjl }
273725cf1a30Sjl 
273825cf1a30Sjl 
273925cf1a30Sjl int
drmach_cpu_poweron(struct cpu * cp)274025cf1a30Sjl drmach_cpu_poweron(struct cpu *cp)
274125cf1a30Sjl {
274225cf1a30Sjl 	int bnum, cpuid, onb_core_num, strand_id;
274325cf1a30Sjl 	drmach_board_t *bp;
274425cf1a30Sjl 
274525cf1a30Sjl 	DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
274625cf1a30Sjl 
274725cf1a30Sjl 	cpuid = cp->cpu_id;
274825cf1a30Sjl 	bnum = LSB_ID(cpuid);
274925cf1a30Sjl 	onb_core_num = ON_BOARD_CORE_NUM(cpuid);
275025cf1a30Sjl 	strand_id = STRAND_ID(cpuid);
275125cf1a30Sjl 	bp = drmach_get_board_by_bnum(bnum);
275225cf1a30Sjl 
275325cf1a30Sjl 	ASSERT(bp);
275425cf1a30Sjl 	if (bp->cores[onb_core_num].core_hotadded == 0) {
275525cf1a30Sjl 		if (drmach_add_remove_cpu(bnum, onb_core_num,
2756e98fafb9Sjl 		    HOTADD_CPU) != 0) {
275725cf1a30Sjl 			cmn_err(CE_WARN, "Failed to add CMP %d on board %d\n",
2758e98fafb9Sjl 			    onb_core_num, bnum);
275925cf1a30Sjl 			return (EIO);
276025cf1a30Sjl 		}
276125cf1a30Sjl 	}
276225cf1a30Sjl 
276325cf1a30Sjl 	ASSERT(MUTEX_HELD(&cpu_lock));
276425cf1a30Sjl 
276525cf1a30Sjl 	if (drmach_cpu_start(cp) != 0) {
276625cf1a30Sjl 		if (bp->cores[onb_core_num].core_started == 0) {
276725cf1a30Sjl 			/*
276825cf1a30Sjl 			 * we must undo the hotadd or no one will do that
276925cf1a30Sjl 			 * If this fails, we will do this again in
277025cf1a30Sjl 			 * drmach_board_disconnect.
277125cf1a30Sjl 			 */
277225cf1a30Sjl 			if (drmach_add_remove_cpu(bnum, onb_core_num,
2773e98fafb9Sjl 			    HOTREMOVE_CPU) != 0) {
277425cf1a30Sjl 				cmn_err(CE_WARN, "Failed to remove CMP %d "
2775e98fafb9Sjl 				    "on board %d\n", onb_core_num, bnum);
277625cf1a30Sjl 			}
277725cf1a30Sjl 		}
277825cf1a30Sjl 		return (EBUSY);
277925cf1a30Sjl 	} else {
278025cf1a30Sjl 		bp->cores[onb_core_num].core_started |= (1 << strand_id);
278125cf1a30Sjl 		return (0);
278225cf1a30Sjl 	}
278325cf1a30Sjl }
278425cf1a30Sjl 
278525cf1a30Sjl int
drmach_cpu_poweroff(struct cpu * cp)278625cf1a30Sjl drmach_cpu_poweroff(struct cpu *cp)
278725cf1a30Sjl {
2788bbe1232eSToomas Soome 	int		rv = 0;
278925cf1a30Sjl 	processorid_t	cpuid = cp->cpu_id;
279025cf1a30Sjl 
279125cf1a30Sjl 	DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
279225cf1a30Sjl 
279325cf1a30Sjl 	ASSERT(MUTEX_HELD(&cpu_lock));
279425cf1a30Sjl 
279525cf1a30Sjl 	/*
279625cf1a30Sjl 	 * Capture all CPUs (except for detaching proc) to prevent
279725cf1a30Sjl 	 * crosscalls to the detaching proc until it has cleared its
279825cf1a30Sjl 	 * bit in cpu_ready_set.
279925cf1a30Sjl 	 *
280025cf1a30Sjl 	 * The CPU's remain paused and the prom_mutex is known to be free.
280125cf1a30Sjl 	 * This prevents the x-trap victim from blocking when doing prom
280225cf1a30Sjl 	 * IEEE-1275 calls at a high PIL level.
280325cf1a30Sjl 	 */
280425cf1a30Sjl 
280525cf1a30Sjl 	promsafe_pause_cpus();
280625cf1a30Sjl 
280725cf1a30Sjl 	/*
280825cf1a30Sjl 	 * Quiesce interrupts on the target CPU. We do this by setting
280925cf1a30Sjl 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
281025cf1a30Sjl 	 * prevent it from receiving cross calls and cross traps.
281125cf1a30Sjl 	 * This prevents the processor from receiving any new soft interrupts.
281225cf1a30Sjl 	 */
281325cf1a30Sjl 	mp_cpu_quiesce(cp);
281425cf1a30Sjl 
281525cf1a30Sjl 	rv = prom_stopcpu_bycpuid(cpuid);
281625cf1a30Sjl 	if (rv == 0)
281725cf1a30Sjl 		cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
281825cf1a30Sjl 
281925cf1a30Sjl 	start_cpus();
282025cf1a30Sjl 
282125cf1a30Sjl 	if (rv == 0) {
282225cf1a30Sjl 		int bnum, onb_core_num, strand_id;
282325cf1a30Sjl 		drmach_board_t *bp;
282425cf1a30Sjl 
282525cf1a30Sjl 		CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
282625cf1a30Sjl 
282725cf1a30Sjl 		bnum = LSB_ID(cpuid);
282825cf1a30Sjl 		onb_core_num = ON_BOARD_CORE_NUM(cpuid);
282925cf1a30Sjl 		strand_id = STRAND_ID(cpuid);
283025cf1a30Sjl 		bp = drmach_get_board_by_bnum(bnum);
283125cf1a30Sjl 		ASSERT(bp);
283225cf1a30Sjl 
283325cf1a30Sjl 		bp->cores[onb_core_num].core_started &= ~(1 << strand_id);
283425cf1a30Sjl 		if (bp->cores[onb_core_num].core_started == 0) {
283525cf1a30Sjl 			if (drmach_add_remove_cpu(bnum, onb_core_num,
2836e98fafb9Sjl 			    HOTREMOVE_CPU) != 0) {
2837e98fafb9Sjl 				cmn_err(CE_WARN, "Failed to remove CMP %d LSB "
2838e98fafb9Sjl 				    "%d\n", onb_core_num, bnum);
283925cf1a30Sjl 				return (EIO);
284025cf1a30Sjl 			}
284125cf1a30Sjl 		}
284225cf1a30Sjl 	}
284325cf1a30Sjl 
284425cf1a30Sjl 	return (rv);
284525cf1a30Sjl }
284625cf1a30Sjl 
284725cf1a30Sjl /*ARGSUSED*/
284825cf1a30Sjl int
drmach_verify_sr(dev_info_t * dip,int sflag)284925cf1a30Sjl drmach_verify_sr(dev_info_t *dip, int sflag)
285025cf1a30Sjl {
285125cf1a30Sjl 	return (0);
285225cf1a30Sjl }
285325cf1a30Sjl 
285425cf1a30Sjl void
drmach_suspend_last(void)285525cf1a30Sjl drmach_suspend_last(void)
285625cf1a30Sjl {
285725cf1a30Sjl }
285825cf1a30Sjl 
285925cf1a30Sjl void
drmach_resume_first(void)286025cf1a30Sjl drmach_resume_first(void)
286125cf1a30Sjl {
286225cf1a30Sjl }
286325cf1a30Sjl 
286425cf1a30Sjl /*
286525cf1a30Sjl  * Log a DR sysevent.
286625cf1a30Sjl  * Return value: 0 success, non-zero failure.
286725cf1a30Sjl  */
286825cf1a30Sjl int
drmach_log_sysevent(int board,char * hint,int flag,int verbose)286925cf1a30Sjl drmach_log_sysevent(int board, char *hint, int flag, int verbose)
287025cf1a30Sjl {
287125cf1a30Sjl 	sysevent_t			*ev;
287225cf1a30Sjl 	sysevent_id_t			eid;
287325cf1a30Sjl 	int				rv, km_flag;
287425cf1a30Sjl 	sysevent_value_t		evnt_val;
287525cf1a30Sjl 	sysevent_attr_list_t		*evnt_attr_list = NULL;
287625cf1a30Sjl 	char				attach_pnt[MAXNAMELEN];
287725cf1a30Sjl 
287825cf1a30Sjl 	km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
287925cf1a30Sjl 	attach_pnt[0] = '\0';
288025cf1a30Sjl 	if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
288125cf1a30Sjl 		rv = -1;
288225cf1a30Sjl 		goto logexit;
288325cf1a30Sjl 	}
2884b307f191Sbm 	if (verbose) {
288525cf1a30Sjl 		DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
2886e98fafb9Sjl 		    attach_pnt, hint, flag, verbose);
2887b307f191Sbm 	}
288825cf1a30Sjl 
288925cf1a30Sjl 	if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
2890e98fafb9Sjl 	    SUNW_KERN_PUB"dr", km_flag)) == NULL) {
289125cf1a30Sjl 		rv = -2;
289225cf1a30Sjl 		goto logexit;
289325cf1a30Sjl 	}
289425cf1a30Sjl 	evnt_val.value_type = SE_DATA_TYPE_STRING;
289525cf1a30Sjl 	evnt_val.value.sv_string = attach_pnt;
2896e98fafb9Sjl 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
2897e98fafb9Sjl 	    km_flag)) != 0)
289825cf1a30Sjl 		goto logexit;
289925cf1a30Sjl 
290025cf1a30Sjl 	evnt_val.value_type = SE_DATA_TYPE_STRING;
290125cf1a30Sjl 	evnt_val.value.sv_string = hint;
2902e98fafb9Sjl 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
2903e98fafb9Sjl 	    km_flag)) != 0) {
290425cf1a30Sjl 		sysevent_free_attr(evnt_attr_list);
290525cf1a30Sjl 		goto logexit;
290625cf1a30Sjl 	}
290725cf1a30Sjl 
290825cf1a30Sjl 	(void) sysevent_attach_attributes(ev, evnt_attr_list);
290925cf1a30Sjl 
291025cf1a30Sjl 	/*
291125cf1a30Sjl 	 * Log the event but do not sleep waiting for its
291225cf1a30Sjl 	 * delivery. This provides insulation from syseventd.
291325cf1a30Sjl 	 */
291425cf1a30Sjl 	rv = log_sysevent(ev, SE_NOSLEEP, &eid);
291525cf1a30Sjl 
291625cf1a30Sjl logexit:
291725cf1a30Sjl 	if (ev)
291825cf1a30Sjl 		sysevent_free(ev);
291925cf1a30Sjl 	if ((rv != 0) && verbose)
2920e98fafb9Sjl 		cmn_err(CE_WARN, "drmach_log_sysevent failed (rv %d) for %s "
2921e98fafb9Sjl 		    " %s\n", rv, attach_pnt, hint);
292225cf1a30Sjl 
292325cf1a30Sjl 	return (rv);
292425cf1a30Sjl }
292525cf1a30Sjl 
292625cf1a30Sjl #define	OPL_DR_STATUS_PROP "dr-status"
292725cf1a30Sjl 
292825cf1a30Sjl static int
opl_check_dr_status()292925cf1a30Sjl opl_check_dr_status()
293025cf1a30Sjl {
293125cf1a30Sjl 	pnode_t	node;
293225cf1a30Sjl 	int	rtn, len;
293325cf1a30Sjl 	char	*str;
293425cf1a30Sjl 
293525cf1a30Sjl 	node = prom_rootnode();
293625cf1a30Sjl 	if (node == OBP_BADNODE) {
293725cf1a30Sjl 		return (1);
293825cf1a30Sjl 	}
293925cf1a30Sjl 
294025cf1a30Sjl 	len = prom_getproplen(node, OPL_DR_STATUS_PROP);
294125cf1a30Sjl 	if (len == -1) {
294225cf1a30Sjl 		/*
294325cf1a30Sjl 		 * dr-status doesn't exist when DR is activated and
294425cf1a30Sjl 		 * any warning messages aren't needed.
294525cf1a30Sjl 		 */
294625cf1a30Sjl 		return (1);
294725cf1a30Sjl 	}
294825cf1a30Sjl 
294925cf1a30Sjl 	str = (char *)kmem_zalloc(len+1, KM_SLEEP);
295025cf1a30Sjl 	rtn = prom_getprop(node, OPL_DR_STATUS_PROP, str);
295125cf1a30Sjl 	kmem_free(str, len + 1);
295225cf1a30Sjl 	if (rtn == -1) {
295325cf1a30Sjl 		return (1);
295425cf1a30Sjl 	} else {
295525cf1a30Sjl 		return (0);
295625cf1a30Sjl 	}
295725cf1a30Sjl }
295825cf1a30Sjl 
295925cf1a30Sjl /* we are allocating memlist from TLB locked pages to avoid tlbmisses */
296025cf1a30Sjl 
296125cf1a30Sjl static struct memlist *
drmach_memlist_add_span(drmach_copy_rename_program_t * p,struct memlist * mlist,uint64_t base,uint64_t len)296225cf1a30Sjl drmach_memlist_add_span(drmach_copy_rename_program_t *p,
2963bbe1232eSToomas Soome     struct memlist *mlist, uint64_t base, uint64_t len)
296425cf1a30Sjl {
296525cf1a30Sjl 	struct memlist	*ml, *tl, *nl;
296625cf1a30Sjl 
296725cf1a30Sjl 	if (len == 0ull)
296825cf1a30Sjl 		return (NULL);
296925cf1a30Sjl 
297025cf1a30Sjl 	if (mlist == NULL) {
297125cf1a30Sjl 		mlist = p->free_mlist;
297225cf1a30Sjl 		if (mlist == NULL)
297325cf1a30Sjl 			return (NULL);
297456f33205SJonathan Adams 		p->free_mlist = mlist->ml_next;
297556f33205SJonathan Adams 		mlist->ml_address = base;
297656f33205SJonathan Adams 		mlist->ml_size = len;
297756f33205SJonathan Adams 		mlist->ml_next = mlist->ml_prev = NULL;
297825cf1a30Sjl 
297925cf1a30Sjl 		return (mlist);
298025cf1a30Sjl 	}
298125cf1a30Sjl 
298256f33205SJonathan Adams 	for (tl = ml = mlist; ml; tl = ml, ml = ml->ml_next) {
298356f33205SJonathan Adams 		if (base < ml->ml_address) {
298456f33205SJonathan Adams 			if ((base + len) < ml->ml_address) {
298525cf1a30Sjl 				nl = p->free_mlist;
298625cf1a30Sjl 				if (nl == NULL)
298725cf1a30Sjl 					return (NULL);
298856f33205SJonathan Adams 				p->free_mlist = nl->ml_next;
298956f33205SJonathan Adams 				nl->ml_address = base;
299056f33205SJonathan Adams 				nl->ml_size = len;
299156f33205SJonathan Adams 				nl->ml_next = ml;
299256f33205SJonathan Adams 				if ((nl->ml_prev = ml->ml_prev) != NULL)
299356f33205SJonathan Adams 					nl->ml_prev->ml_next = nl;
299456f33205SJonathan Adams 				ml->ml_prev = nl;
299525cf1a30Sjl 				if (mlist == ml)
299625cf1a30Sjl 					mlist = nl;
299725cf1a30Sjl 			} else {
299856f33205SJonathan Adams 				ml->ml_size = MAX((base + len),
299956f33205SJonathan Adams 				    (ml->ml_address + ml->ml_size)) - base;
300056f33205SJonathan Adams 				ml->ml_address = base;
300125cf1a30Sjl 			}
300225cf1a30Sjl 			break;
300325cf1a30Sjl 
300456f33205SJonathan Adams 		} else if (base <= (ml->ml_address + ml->ml_size)) {
300556f33205SJonathan Adams 			ml->ml_size =
300656f33205SJonathan Adams 			    MAX((base + len), (ml->ml_address + ml->ml_size)) -
300756f33205SJonathan Adams 			    MIN(ml->ml_address, base);
300856f33205SJonathan Adams 			ml->ml_address = MIN(ml->ml_address, base);
300925cf1a30Sjl 			break;
301025cf1a30Sjl 		}
301125cf1a30Sjl 	}
301225cf1a30Sjl 	if (ml == NULL) {
301325cf1a30Sjl 		nl = p->free_mlist;
301425cf1a30Sjl 		if (nl == NULL)
301525cf1a30Sjl 			return (NULL);
301656f33205SJonathan Adams 		p->free_mlist = nl->ml_next;
301756f33205SJonathan Adams 		nl->ml_address = base;
301856f33205SJonathan Adams 		nl->ml_size = len;
301956f33205SJonathan Adams 		nl->ml_next = NULL;
302056f33205SJonathan Adams 		nl->ml_prev = tl;
302156f33205SJonathan Adams 		tl->ml_next = nl;
302225cf1a30Sjl 	}
302325cf1a30Sjl 
302425cf1a30Sjl 	return (mlist);
302525cf1a30Sjl }
302625cf1a30Sjl 
302725cf1a30Sjl /*
302825cf1a30Sjl  * The routine performs the necessary memory COPY and MC adr SWITCH.
302925cf1a30Sjl  * Both operations MUST be at the same "level" so that the stack is
303025cf1a30Sjl  * maintained correctly between the copy and switch.  The switch
303125cf1a30Sjl  * portion implements a caching mechanism to guarantee the code text
303225cf1a30Sjl  * is cached prior to execution.  This is to guard against possible
303325cf1a30Sjl  * memory access while the MC adr's are being modified.
303425cf1a30Sjl  *
303525cf1a30Sjl  * IMPORTANT: The _drmach_copy_rename_end() function must immediately
303625cf1a30Sjl  * follow drmach_copy_rename_prog__relocatable() so that the correct
303725cf1a30Sjl  * "length" of the drmach_copy_rename_prog__relocatable can be
303825cf1a30Sjl  * calculated.  This routine MUST be a LEAF function, i.e. it can
303925cf1a30Sjl  * make NO function calls, primarily for two reasons:
304025cf1a30Sjl  *
304125cf1a30Sjl  *	1. We must keep the stack consistent across the "switch".
304225cf1a30Sjl  *	2. Function calls are compiled to relative offsets, and
304325cf1a30Sjl  *	   we execute this function we'll be executing it from
304425cf1a30Sjl  *	   a copied version in a different area of memory, thus
304525cf1a30Sjl  *	   the relative offsets will be bogus.
304625cf1a30Sjl  *
304725cf1a30Sjl  * Moreover, it must have the "__relocatable" suffix to inform DTrace
304825cf1a30Sjl  * providers (and anything else, for that matter) that this
304925cf1a30Sjl  * function's text is manually relocated elsewhere before it is
305025cf1a30Sjl  * executed.  That is, it cannot be safely instrumented with any
305125cf1a30Sjl  * methodology that is PC-relative.
305225cf1a30Sjl  */
305325cf1a30Sjl 
305425cf1a30Sjl /*
305525cf1a30Sjl  * We multiply this to system_clock_frequency so we
305625cf1a30Sjl  * are setting a delay of fmem_timeout second for
3057b307f191Sbm  * the rename command.
3058b307f191Sbm  *
3059b307f191Sbm  * FMEM command itself should complete within 15 sec.
3060b307f191Sbm  * We add 2 more sec to be conservative.
3061b307f191Sbm  *
3062b307f191Sbm  * Note that there is also a SCF BUSY bit checking
3063b307f191Sbm  * in drmach_asm.s right before FMEM command is
3064b307f191Sbm  * issued.  XSCF sets the SCF BUSY bit when the
3065b307f191Sbm  * other domain on the same PSB reboots and it
3066b307f191Sbm  * will not be able to service the FMEM command
3067b307f191Sbm  * within 15 sec.   After setting the SCF BUSY
3068b307f191Sbm  * bit, XSCF will wait a while before servicing
3069b307f191Sbm  * other reboot command so there is no race
3070b307f191Sbm  * condition.
307125cf1a30Sjl  */
3072b307f191Sbm 
307325cf1a30Sjl static int	fmem_timeout = 17;
3074b307f191Sbm 
3075b307f191Sbm /*
3076b307f191Sbm  *	The empirical data on some OPL system shows that
3077b307f191Sbm  *	we can copy 250 MB per second.  We set it to
3078bbe1232eSToomas Soome  *	80 MB to be conservative.  In normal case,
3079b307f191Sbm  *	this timeout does not affect anything.
3080b307f191Sbm  */
3081b307f191Sbm 
3082b307f191Sbm static int	min_copy_size_per_sec = 80 * 1024 * 1024;
3083b307f191Sbm 
3084b307f191Sbm /*
3085b307f191Sbm  *	This is the timeout value for the xcall synchronization
3086b307f191Sbm  *	to get all the CPU ready to do the parallel copying.
3087b307f191Sbm  *	Even on a fully loaded system, 10 sec. should be long
3088b307f191Sbm  *	enough.
3089b307f191Sbm  */
3090b307f191Sbm 
3091b307f191Sbm static int	cpu_xcall_delay = 10;
309225cf1a30Sjl int drmach_disable_mcopy = 0;
309325cf1a30Sjl 
309468ac2337Sjl /*
309568ac2337Sjl  * The following delay loop executes sleep instruction to yield the
309668ac2337Sjl  * CPU to other strands.  If this is not done, some strand will tie
309768ac2337Sjl  * up the CPU in busy loops while the other strand cannot do useful
309868ac2337Sjl  * work.  The copy procedure will take a much longer time without this.
309968ac2337Sjl  */
310025cf1a30Sjl #define	DR_DELAY_IL(ms, freq)					\
310125cf1a30Sjl 	{							\
310225cf1a30Sjl 		uint64_t start;					\
310325cf1a30Sjl 		uint64_t nstick;				\
310425cf1a30Sjl 		volatile uint64_t now;				\
310525cf1a30Sjl 		nstick = ((uint64_t)ms * freq)/1000;		\
310625cf1a30Sjl 		start = drmach_get_stick_il();			\
310725cf1a30Sjl 		now = start;					\
310825cf1a30Sjl 		while ((now - start) <= nstick) {		\
310925cf1a30Sjl 			drmach_sleep_il();			\
311025cf1a30Sjl 			now = drmach_get_stick_il();		\
311125cf1a30Sjl 		}						\
311225cf1a30Sjl 	}
311325cf1a30Sjl 
31146534c6f0Swh /* Each loop is 2ms, timeout at 1000ms */
31156534c6f0Swh static int drmach_copy_rename_timeout = 500;
31166534c6f0Swh 
311725cf1a30Sjl static int
drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t * prog,int cpuid)311825cf1a30Sjl drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog,
3119bbe1232eSToomas Soome     int cpuid)
312025cf1a30Sjl {
312125cf1a30Sjl 	struct memlist		*ml;
312225cf1a30Sjl 	register int		rtn;
312325cf1a30Sjl 	int			i;
312425cf1a30Sjl 	register uint64_t	curr, limit;
312525cf1a30Sjl 	extern uint64_t		drmach_get_stick_il();
312625cf1a30Sjl 	extern void		membar_sync_il();
312725cf1a30Sjl 	extern void		flush_instr_mem_il(void*);
312868ac2337Sjl 	extern void		flush_windows_il(void);
312925cf1a30Sjl 	uint64_t		copy_start;
313025cf1a30Sjl 
313168ac2337Sjl 	/*
313268ac2337Sjl 	 * flush_windows is moved here to make sure all
313368ac2337Sjl 	 * registers used in the callers are flushed to
313468ac2337Sjl 	 * memory before the copy.
313568ac2337Sjl 	 *
313668ac2337Sjl 	 * If flush_windows() is called too early in the
313768ac2337Sjl 	 * calling function, the compiler might put some
313868ac2337Sjl 	 * data in the local registers after flush_windows().
313968ac2337Sjl 	 * After FMA, if there is any fill trap, the registers
314068ac2337Sjl 	 * will contain stale data.
314168ac2337Sjl 	 */
314268ac2337Sjl 
314368ac2337Sjl 	flush_windows_il();
314468ac2337Sjl 
314525cf1a30Sjl 	prog->critical->stat[cpuid] = FMEM_LOOP_COPY_READY;
314625cf1a30Sjl 	membar_sync_il();
314725cf1a30Sjl 
314825cf1a30Sjl 	if (prog->data->cpuid == cpuid) {
314925cf1a30Sjl 		limit = drmach_get_stick_il();
3150b307f191Sbm 		limit += cpu_xcall_delay * system_clock_freq;
315125cf1a30Sjl 		for (i = 0; i < NCPU; i++) {
315225cf1a30Sjl 			if (CPU_IN_SET(prog->data->cpu_slave_set, i)) {
3153e98fafb9Sjl 				/* wait for all CPU's to be ready */
3154e98fafb9Sjl 				for (;;) {
3155e98fafb9Sjl 					if (prog->critical->stat[i] ==
3156e98fafb9Sjl 					    FMEM_LOOP_COPY_READY) {
3157e98fafb9Sjl 						break;
3158e98fafb9Sjl 					}
3159e98fafb9Sjl 					DR_DELAY_IL(1, prog->data->stick_freq);
3160e98fafb9Sjl 				}
3161e98fafb9Sjl 				curr = drmach_get_stick_il();
3162e98fafb9Sjl 				if (curr > limit) {
3163e98fafb9Sjl 					prog->data->fmem_status.error =
3164e98fafb9Sjl 					    EOPL_FMEM_XC_TIMEOUT;
3165e98fafb9Sjl 					return (EOPL_FMEM_XC_TIMEOUT);
316625cf1a30Sjl 				}
316725cf1a30Sjl 			}
316825cf1a30Sjl 		}
316925cf1a30Sjl 		prog->data->fmem_status.stat = FMEM_LOOP_COPY_READY;
317025cf1a30Sjl 		membar_sync_il();
317125cf1a30Sjl 		copy_start = drmach_get_stick_il();
317225cf1a30Sjl 	} else {
317325cf1a30Sjl 		for (;;) {
317425cf1a30Sjl 			if (prog->data->fmem_status.stat ==
3175e98fafb9Sjl 			    FMEM_LOOP_COPY_READY) {
317625cf1a30Sjl 				break;
317725cf1a30Sjl 			}
317825cf1a30Sjl 			if (prog->data->fmem_status.error) {
3179e98fafb9Sjl 				prog->data->error[cpuid] = EOPL_FMEM_TERMINATE;
3180b307f191Sbm 				return (EOPL_FMEM_TERMINATE);
318125cf1a30Sjl 			}
318268ac2337Sjl 			DR_DELAY_IL(1, prog->data->stick_freq);
318325cf1a30Sjl 		}
318425cf1a30Sjl 	}
318525cf1a30Sjl 
318625cf1a30Sjl 	/*
318725cf1a30Sjl 	 * DO COPY.
318825cf1a30Sjl 	 */
318925cf1a30Sjl 	if (CPU_IN_SET(prog->data->cpu_copy_set, cpuid)) {
319056f33205SJonathan Adams 		for (ml = prog->data->cpu_ml[cpuid]; ml; ml = ml->ml_next) {
3191e98fafb9Sjl 			uint64_t	s_pa, t_pa;
3192e98fafb9Sjl 			uint64_t	nbytes;
3193e98fafb9Sjl 
319456f33205SJonathan Adams 			s_pa = prog->data->s_copybasepa + ml->ml_address;
319556f33205SJonathan Adams 			t_pa = prog->data->t_copybasepa + ml->ml_address;
319656f33205SJonathan Adams 			nbytes = ml->ml_size;
3197e98fafb9Sjl 
3198e98fafb9Sjl 			while (nbytes != 0ull) {
3199e98fafb9Sjl 				/*
3200e98fafb9Sjl 				 * If the master has detected error, we just
3201e98fafb9Sjl 				 * bail out
3202e98fafb9Sjl 				 */
3203e98fafb9Sjl 				if (prog->data->fmem_status.error !=
3204e98fafb9Sjl 				    ESBD_NOERROR) {
3205e98fafb9Sjl 					prog->data->error[cpuid] =
3206e98fafb9Sjl 					    EOPL_FMEM_TERMINATE;
3207e98fafb9Sjl 					return (EOPL_FMEM_TERMINATE);
3208e98fafb9Sjl 				}
3209e98fafb9Sjl 				/*
3210e98fafb9Sjl 				 * This copy does NOT use an ASI
3211e98fafb9Sjl 				 * that avoids the Ecache, therefore
3212e98fafb9Sjl 				 * the dst_pa addresses may remain
3213e98fafb9Sjl 				 * in our Ecache after the dst_pa
3214e98fafb9Sjl 				 * has been removed from the system.
3215e98fafb9Sjl 				 * A subsequent write-back to memory
3216e98fafb9Sjl 				 * will cause an ARB-stop because the
3217e98fafb9Sjl 				 * physical address no longer exists
3218e98fafb9Sjl 				 * in the system. Therefore we must
3219e98fafb9Sjl 				 * flush out local Ecache after we
3220e98fafb9Sjl 				 * finish the copy.
3221e98fafb9Sjl 				 */
3222e98fafb9Sjl 
3223e98fafb9Sjl 				/* copy 32 bytes at src_pa to dst_pa */
3224e98fafb9Sjl 				bcopy32_il(s_pa, t_pa);
3225e98fafb9Sjl 
3226e98fafb9Sjl 				/*
3227e98fafb9Sjl 				 * increment the counter to signal that we are
3228e98fafb9Sjl 				 * alive
3229e98fafb9Sjl 				 */
3230e98fafb9Sjl 				prog->stat->nbytes[cpuid] += 32;
3231e98fafb9Sjl 
3232e98fafb9Sjl 				/* increment by 32 bytes */
3233e98fafb9Sjl 				s_pa += (4 * sizeof (uint64_t));
3234e98fafb9Sjl 				t_pa += (4 * sizeof (uint64_t));
3235e98fafb9Sjl 
3236e98fafb9Sjl 				/* decrement by 32 bytes */
3237e98fafb9Sjl 				nbytes -= (4 * sizeof (uint64_t));
323825cf1a30Sjl 			}
323925cf1a30Sjl 		}
3240e98fafb9Sjl 		prog->critical->stat[cpuid] = FMEM_LOOP_COPY_DONE;
3241e98fafb9Sjl 		membar_sync_il();
324225cf1a30Sjl 	}
324325cf1a30Sjl 
324425cf1a30Sjl 	/*
324525cf1a30Sjl 	 * Since bcopy32_il() does NOT use an ASI to bypass
324625cf1a30Sjl 	 * the Ecache, we need to flush our Ecache after
324725cf1a30Sjl 	 * the copy is complete.
324825cf1a30Sjl 	 */
324925cf1a30Sjl 	flush_cache_il();
325025cf1a30Sjl 
325125cf1a30Sjl 	/*
325225cf1a30Sjl 	 * drmach_fmem_exec_script()
325325cf1a30Sjl 	 */
325425cf1a30Sjl 	if (prog->data->cpuid == cpuid) {
325525cf1a30Sjl 		uint64_t	last, now;
325625cf1a30Sjl 
325725cf1a30Sjl 		limit = copy_start + prog->data->copy_delay;
325825cf1a30Sjl 		for (i = 0; i < NCPU; i++) {
3259e98fafb9Sjl 			if (!CPU_IN_SET(prog->data->cpu_slave_set, i))
3260e98fafb9Sjl 				continue;
3261e98fafb9Sjl 
3262e98fafb9Sjl 			for (;;) {
3263e98fafb9Sjl 				/*
3264e98fafb9Sjl 				 * we get FMEM_LOOP_FMEM_READY in
3265e98fafb9Sjl 				 * normal case
3266e98fafb9Sjl 				 */
326725cf1a30Sjl 				if (prog->critical->stat[i] ==
3268e98fafb9Sjl 				    FMEM_LOOP_FMEM_READY) {
326925cf1a30Sjl 					break;
327025cf1a30Sjl 				}
327125cf1a30Sjl 				/* got error traps */
3272b307f191Sbm 				if (prog->data->error[i] ==
3273e98fafb9Sjl 				    EOPL_FMEM_COPY_ERROR) {
327425cf1a30Sjl 					prog->data->fmem_status.error =
3275e98fafb9Sjl 					    EOPL_FMEM_COPY_ERROR;
3276b307f191Sbm 					return (EOPL_FMEM_COPY_ERROR);
327725cf1a30Sjl 				}
3278e98fafb9Sjl 				/*
3279e98fafb9Sjl 				 * if we have not reached limit, wait
3280e98fafb9Sjl 				 * more
3281e98fafb9Sjl 				 */
328225cf1a30Sjl 				curr = drmach_get_stick_il();
328325cf1a30Sjl 				if (curr <= limit)
328425cf1a30Sjl 					continue;
328525cf1a30Sjl 
328625cf1a30Sjl 				prog->data->slowest_cpuid = i;
3287e98fafb9Sjl 				prog->data->copy_wait_time = curr - copy_start;
328825cf1a30Sjl 
328925cf1a30Sjl 				/* now check if slave is alive */
329025cf1a30Sjl 				last = prog->stat->nbytes[i];
329125cf1a30Sjl 
329225cf1a30Sjl 				DR_DELAY_IL(1, prog->data->stick_freq);
329325cf1a30Sjl 
329425cf1a30Sjl 				now = prog->stat->nbytes[i];
329525cf1a30Sjl 				if (now <= last) {
3296e98fafb9Sjl 					/*
3297e98fafb9Sjl 					 * no progress, perhaps just
3298e98fafb9Sjl 					 * finished
3299e98fafb9Sjl 					 */
330025cf1a30Sjl 					DR_DELAY_IL(1, prog->data->stick_freq);
330125cf1a30Sjl 					if (prog->critical->stat[i] ==
3302e98fafb9Sjl 					    FMEM_LOOP_FMEM_READY)
330325cf1a30Sjl 						break;
330425cf1a30Sjl 					/* copy error */
3305b307f191Sbm 					if (prog->data->error[i] ==
3306e98fafb9Sjl 					    EOPL_FMEM_COPY_ERROR) {
3307e98fafb9Sjl 						prog->data-> fmem_status.error =
3308e98fafb9Sjl 						    EOPL_FMEM_COPY_ERROR;
3309b307f191Sbm 						return (EOPL_FMEM_COPY_ERROR);
331025cf1a30Sjl 					}
33116534c6f0Swh 
33126534c6f0Swh 					prog->data->copy_rename_count++;
33136534c6f0Swh 					if (prog->data->copy_rename_count
33146534c6f0Swh 					    < drmach_copy_rename_timeout) {
33156534c6f0Swh 						continue;
33166534c6f0Swh 					} else {
33176534c6f0Swh 						prog->data->fmem_status.error =
33186534c6f0Swh 						    EOPL_FMEM_COPY_TIMEOUT;
33196534c6f0Swh 						return (EOPL_FMEM_COPY_TIMEOUT);
33206534c6f0Swh 					}
332125cf1a30Sjl 				}
332225cf1a30Sjl 			}
332325cf1a30Sjl 		}
3324b307f191Sbm 
332525cf1a30Sjl 		prog->critical->stat[cpuid] = FMEM_LOOP_FMEM_READY;
332625cf1a30Sjl 		prog->data->fmem_status.stat  = FMEM_LOOP_FMEM_READY;
332725cf1a30Sjl 
332825cf1a30Sjl 		membar_sync_il();
332925cf1a30Sjl 		flush_instr_mem_il((void*) (prog->critical));
333025cf1a30Sjl 		/*
333125cf1a30Sjl 		 * drmach_fmem_exec_script()
333225cf1a30Sjl 		 */
333325cf1a30Sjl 		rtn = prog->critical->fmem((void *)prog->critical, PAGESIZE);
333425cf1a30Sjl 		return (rtn);
333525cf1a30Sjl 	} else {
333625cf1a30Sjl 		flush_instr_mem_il((void*) (prog->critical));
333725cf1a30Sjl 		/*
333825cf1a30Sjl 		 * drmach_fmem_loop_script()
333925cf1a30Sjl 		 */
3340e98fafb9Sjl 		rtn = prog->critical->loop((void *)(prog->critical), PAGESIZE,
3341e98fafb9Sjl 		    (void *)&(prog->critical->stat[cpuid]));
334225cf1a30Sjl 		prog->data->error[cpuid] = rtn;
334325cf1a30Sjl 		/* slave thread does not care the rv */
334425cf1a30Sjl 		return (0);
334525cf1a30Sjl 	}
334625cf1a30Sjl }
334725cf1a30Sjl 
334825cf1a30Sjl static void
drmach_copy_rename_end(void)334925cf1a30Sjl drmach_copy_rename_end(void)
335025cf1a30Sjl {
335125cf1a30Sjl 	/*
335225cf1a30Sjl 	 * IMPORTANT:	This function's location MUST be located immediately
335325cf1a30Sjl 	 *		following drmach_copy_rename_prog__relocatable to
335425cf1a30Sjl 	 *		accurately estimate its size.  Note that this assumes
335525cf1a30Sjl 	 *		the compiler keeps these functions in the order in
335625cf1a30Sjl 	 *		which they appear :-o
335725cf1a30Sjl 	 */
335825cf1a30Sjl }
335925cf1a30Sjl 
336025cf1a30Sjl 
33616534c6f0Swh static int
drmach_setup_memlist(drmach_copy_rename_program_t * p)336225cf1a30Sjl drmach_setup_memlist(drmach_copy_rename_program_t *p)
336325cf1a30Sjl {
336425cf1a30Sjl 	struct memlist *ml;
336525cf1a30Sjl 	caddr_t buf;
33666534c6f0Swh 	int nbytes, s, n_elements;
336725cf1a30Sjl 
336825cf1a30Sjl 	nbytes = PAGESIZE;
33696534c6f0Swh 	n_elements = 0;
337025cf1a30Sjl 	s = roundup(sizeof (struct memlist), sizeof (void *));
337125cf1a30Sjl 	p->free_mlist = NULL;
337225cf1a30Sjl 	buf = p->memlist_buffer;
337325cf1a30Sjl 	while (nbytes >= sizeof (struct memlist)) {
337425cf1a30Sjl 		ml = (struct memlist *)buf;
337556f33205SJonathan Adams 		ml->ml_next = p->free_mlist;
337625cf1a30Sjl 		p->free_mlist = ml;
337725cf1a30Sjl 		buf += s;
33786534c6f0Swh 		n_elements++;
337925cf1a30Sjl 		nbytes -= s;
338025cf1a30Sjl 	}
33816534c6f0Swh 	return (n_elements);
338225cf1a30Sjl }
338325cf1a30Sjl 
338468ac2337Sjl static void
drmach_lock_critical(caddr_t va,caddr_t new_va)338568ac2337Sjl drmach_lock_critical(caddr_t va, caddr_t new_va)
338668ac2337Sjl {
338768ac2337Sjl 	tte_t tte;
338868ac2337Sjl 	int i;
338968ac2337Sjl 
339068ac2337Sjl 	kpreempt_disable();
339168ac2337Sjl 
339268ac2337Sjl 	for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
339368ac2337Sjl 		vtag_flushpage(new_va, (uint64_t)ksfmmup);
3394e98fafb9Sjl 		sfmmu_memtte(&tte, va_to_pfn(va), PROC_DATA|HAT_NOSYNC, TTE8K);
339568ac2337Sjl 		tte.tte_intlo |= TTE_LCK_INT;
339668ac2337Sjl 		sfmmu_dtlb_ld_kva(new_va, &tte);
339768ac2337Sjl 		sfmmu_itlb_ld_kva(new_va, &tte);
339868ac2337Sjl 		va += PAGESIZE;
339968ac2337Sjl 		new_va += PAGESIZE;
340068ac2337Sjl 	}
340168ac2337Sjl }
340268ac2337Sjl 
340368ac2337Sjl static void
drmach_unlock_critical(caddr_t va)340468ac2337Sjl drmach_unlock_critical(caddr_t va)
340568ac2337Sjl {
340668ac2337Sjl 	int i;
340768ac2337Sjl 
340868ac2337Sjl 	for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
340968ac2337Sjl 		vtag_flushpage(va, (uint64_t)ksfmmup);
341068ac2337Sjl 		va += PAGESIZE;
341168ac2337Sjl 	}
341268ac2337Sjl 
341368ac2337Sjl 	kpreempt_enable();
341468ac2337Sjl }
341568ac2337Sjl 
341625cf1a30Sjl sbd_error_t *
drmach_copy_rename_init(drmachid_t t_id,drmachid_t s_id,struct memlist * c_ml,drmachid_t * pgm_id)341725cf1a30Sjl drmach_copy_rename_init(drmachid_t t_id, drmachid_t s_id,
3418bbe1232eSToomas Soome     struct memlist *c_ml, drmachid_t *pgm_id)
341925cf1a30Sjl {
342025cf1a30Sjl 	drmach_mem_t	*s_mem;
342125cf1a30Sjl 	drmach_mem_t	*t_mem;
342225cf1a30Sjl 	struct memlist	*x_ml;
342325cf1a30Sjl 	uint64_t	s_copybasepa, t_copybasepa;
342425cf1a30Sjl 	uint_t		len;
342525cf1a30Sjl 	caddr_t		bp, wp;
34266534c6f0Swh 	int		s_bd, t_bd, cpuid, active_cpus, i;
34276534c6f0Swh 	int		max_elms, mlist_size, rv;
34286534c6f0Swh 	uint64_t	c_addr;
34296534c6f0Swh 	size_t		c_size, copy_sz, sz;
34306534c6f0Swh 	extern void	drmach_fmem_loop_script();
34316534c6f0Swh 	extern void	drmach_fmem_loop_script_rtn();
34326534c6f0Swh 	extern int	drmach_fmem_exec_script();
34336534c6f0Swh 	extern void	drmach_fmem_exec_script_end();
343425cf1a30Sjl 	sbd_error_t	*err;
343568ac2337Sjl 	drmach_copy_rename_program_t *prog = NULL;
343668ac2337Sjl 	drmach_copy_rename_program_t *prog_kmem = NULL;
343725cf1a30Sjl 	void		(*mc_suspend)(void);
343825cf1a30Sjl 	void		(*mc_resume)(void);
343925cf1a30Sjl 	int		(*scf_fmem_start)(int, int);
344025cf1a30Sjl 	int		(*scf_fmem_end)(void);
344125cf1a30Sjl 	int		(*scf_fmem_cancel)(void);
344268ac2337Sjl 	uint64_t	(*scf_get_base_addr)(void);
344325cf1a30Sjl 
344425cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(s_id))
344525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
344625cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(t_id))
344725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
344825cf1a30Sjl 
344925cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
345025cf1a30Sjl 		int lsb_id, onb_core_num, strand_id;
345125cf1a30Sjl 		drmach_board_t *bp;
345225cf1a30Sjl 
345325cf1a30Sjl 		/*
345425cf1a30Sjl 		 * this kind of CPU will spin in cache
345525cf1a30Sjl 		 */
345625cf1a30Sjl 		if (CPU_IN_SET(cpu_ready_set, i))
345725cf1a30Sjl 			continue;
345825cf1a30Sjl 
345925cf1a30Sjl 		/*
346025cf1a30Sjl 		 * Now check for any inactive CPU's that
346125cf1a30Sjl 		 * have been hotadded.  This can only occur in
346225cf1a30Sjl 		 * error condition in drmach_cpu_poweron().
346325cf1a30Sjl 		 */
346425cf1a30Sjl 		lsb_id = LSB_ID(i);
346525cf1a30Sjl 		onb_core_num = ON_BOARD_CORE_NUM(i);
346625cf1a30Sjl 		strand_id = STRAND_ID(i);
346725cf1a30Sjl 		bp = drmach_get_board_by_bnum(lsb_id);
346825cf1a30Sjl 		if (bp == NULL)
346925cf1a30Sjl 			continue;
347025cf1a30Sjl 		if (bp->cores[onb_core_num].core_hotadded &
347125cf1a30Sjl 		    (1 << strand_id)) {
3472e98fafb9Sjl 			if (!(bp->cores[onb_core_num].core_started &
3473e98fafb9Sjl 			    (1 << strand_id))) {
3474e98fafb9Sjl 				return (drerr_new(1, EOPL_CPU_STATE, NULL));
3475e98fafb9Sjl 			}
347625cf1a30Sjl 		}
347725cf1a30Sjl 	}
347825cf1a30Sjl 
347925cf1a30Sjl 	mc_suspend = (void (*)(void))
348025cf1a30Sjl 	    modgetsymvalue("opl_mc_suspend", 0);
348125cf1a30Sjl 	mc_resume = (void (*)(void))
348225cf1a30Sjl 	    modgetsymvalue("opl_mc_resume", 0);
348325cf1a30Sjl 
348425cf1a30Sjl 	if (mc_suspend == NULL || mc_resume == NULL) {
34858eafe49bSbm 		return (drerr_new(1, EOPL_MC_OPL, NULL));
348625cf1a30Sjl 	}
348725cf1a30Sjl 
348825cf1a30Sjl 	scf_fmem_start = (int (*)(int, int))
348925cf1a30Sjl 	    modgetsymvalue("scf_fmem_start", 0);
349025cf1a30Sjl 	if (scf_fmem_start == NULL) {
34918eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
349225cf1a30Sjl 	}
349325cf1a30Sjl 	scf_fmem_end = (int (*)(void))
349425cf1a30Sjl 	    modgetsymvalue("scf_fmem_end", 0);
349525cf1a30Sjl 	if (scf_fmem_end == NULL) {
34968eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
349725cf1a30Sjl 	}
349825cf1a30Sjl 	scf_fmem_cancel = (int (*)(void))
349925cf1a30Sjl 	    modgetsymvalue("scf_fmem_cancel", 0);
350025cf1a30Sjl 	if (scf_fmem_cancel == NULL) {
35018eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
350268ac2337Sjl 	}
350368ac2337Sjl 	scf_get_base_addr = (uint64_t (*)(void))
350468ac2337Sjl 	    modgetsymvalue("scf_get_base_addr", 0);
350568ac2337Sjl 	if (scf_get_base_addr == NULL) {
35068eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
350725cf1a30Sjl 	}
350825cf1a30Sjl 	s_mem = s_id;
350925cf1a30Sjl 	t_mem = t_id;
351025cf1a30Sjl 
351125cf1a30Sjl 	s_bd = s_mem->dev.bp->bnum;
351225cf1a30Sjl 	t_bd = t_mem->dev.bp->bnum;
351325cf1a30Sjl 
351425cf1a30Sjl 	/* calculate source and target base pa */
351525cf1a30Sjl 
351625cf1a30Sjl 	s_copybasepa = s_mem->slice_base;
351725cf1a30Sjl 	t_copybasepa = t_mem->slice_base;
351825cf1a30Sjl 
351925cf1a30Sjl 	/* adjust copy memlist addresses to be relative to copy base pa */
352025cf1a30Sjl 	x_ml = c_ml;
35216534c6f0Swh 	mlist_size = 0;
352225cf1a30Sjl 	while (x_ml != NULL) {
352356f33205SJonathan Adams 		x_ml->ml_address -= s_copybasepa;
352456f33205SJonathan Adams 		x_ml = x_ml->ml_next;
35256534c6f0Swh 		mlist_size++;
352625cf1a30Sjl 	}
352725cf1a30Sjl 
352825cf1a30Sjl 	/*
352925cf1a30Sjl 	 * bp will be page aligned, since we're calling
353025cf1a30Sjl 	 * kmem_zalloc() with an exact multiple of PAGESIZE.
353125cf1a30Sjl 	 */
353225cf1a30Sjl 
353368ac2337Sjl 	prog_kmem = (drmach_copy_rename_program_t *)kmem_zalloc(
3534e98fafb9Sjl 	    DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, KM_SLEEP);
353568ac2337Sjl 
353668ac2337Sjl 	prog_kmem->prog = prog_kmem;
353768ac2337Sjl 
353868ac2337Sjl 	/*
353968ac2337Sjl 	 * To avoid MTLB hit, we allocate a new VM space and remap
354068ac2337Sjl 	 * the kmem_alloc buffer to that address.  This solves
354168ac2337Sjl 	 * 2 problems we found:
354268ac2337Sjl 	 * - the kmem_alloc buffer can be just a chunk inside
354368ac2337Sjl 	 *   a much larger, e.g. 4MB buffer and MTLB will occur
354468ac2337Sjl 	 *   if there are both a 4MB and a 8K TLB mapping to
354568ac2337Sjl 	 *   the same VA range.
354668ac2337Sjl 	 * - the kmem mapping got dropped into the TLB by other
354768ac2337Sjl 	 *   strands, unintentionally.
354868ac2337Sjl 	 * Note that the pointers like data, critical, memlist_buffer,
354968ac2337Sjl 	 * and stat inside the copy rename structure are mapped to this
355068ac2337Sjl 	 * alternate VM space so we must make sure we lock the TLB mapping
355168ac2337Sjl 	 * whenever we access data pointed to by these pointers.
355268ac2337Sjl 	 */
355368ac2337Sjl 
355468ac2337Sjl 	prog = prog_kmem->locked_prog = vmem_alloc(heap_arena,
3555e98fafb9Sjl 	    DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, VM_SLEEP);
355668ac2337Sjl 	wp = bp = (caddr_t)prog;
355768ac2337Sjl 
355868ac2337Sjl 	/* Now remap prog_kmem to prog */
355968ac2337Sjl 	drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
356068ac2337Sjl 
356168ac2337Sjl 	/* All pointers in prog are based on the alternate mapping */
356225cf1a30Sjl 	prog->data = (drmach_copy_rename_data_t *)roundup(((uint64_t)prog +
3563e98fafb9Sjl 	    sizeof (drmach_copy_rename_program_t)), sizeof (void *));
356425cf1a30Sjl 
356525cf1a30Sjl 	ASSERT(((uint64_t)prog->data + sizeof (drmach_copy_rename_data_t))
3566e98fafb9Sjl 	    <= ((uint64_t)prog + PAGESIZE));
356725cf1a30Sjl 
356825cf1a30Sjl 	prog->critical = (drmach_copy_rename_critical_t *)
3569e98fafb9Sjl 	    (wp + DRMACH_FMEM_CRITICAL_PAGE * PAGESIZE);
357025cf1a30Sjl 
3571e98fafb9Sjl 	prog->memlist_buffer = (caddr_t)(wp + DRMACH_FMEM_MLIST_PAGE *
3572e98fafb9Sjl 	    PAGESIZE);
357325cf1a30Sjl 
3574e98fafb9Sjl 	prog->stat = (drmach_cr_stat_t *)(wp + DRMACH_FMEM_STAT_PAGE *
3575e98fafb9Sjl 	    PAGESIZE);
357625cf1a30Sjl 
357725cf1a30Sjl 	/* LINTED */
3578e98fafb9Sjl 	ASSERT(sizeof (drmach_cr_stat_t) <= ((DRMACH_FMEM_LOCKED_PAGES -
3579e98fafb9Sjl 	    DRMACH_FMEM_STAT_PAGE) * PAGESIZE));
358025cf1a30Sjl 
358125cf1a30Sjl 	prog->critical->scf_reg_base = (uint64_t)-1;
358225cf1a30Sjl 	prog->critical->scf_td[0] = (s_bd & 0xff);
358325cf1a30Sjl 	prog->critical->scf_td[1] = (t_bd & 0xff);
358425cf1a30Sjl 	for (i = 2; i < 15; i++) {
358525cf1a30Sjl 		prog->critical->scf_td[i]   = 0;
358625cf1a30Sjl 	}
358725cf1a30Sjl 	prog->critical->scf_td[15] = ((0xaa + s_bd + t_bd) & 0xff);
358825cf1a30Sjl 
358925cf1a30Sjl 	bp = (caddr_t)prog->critical;
359025cf1a30Sjl 	len = sizeof (drmach_copy_rename_critical_t);
359125cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
359225cf1a30Sjl 
359325cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_copy_rename_end -
3594e98fafb9Sjl 	    (ulong_t)drmach_copy_rename_prog__relocatable);
359525cf1a30Sjl 
359625cf1a30Sjl 	/*
359725cf1a30Sjl 	 * We always leave 1K nop's to prevent the processor from
359825cf1a30Sjl 	 * speculative execution that causes memory access
359925cf1a30Sjl 	 */
360025cf1a30Sjl 	wp = wp + len + 1024;
360125cf1a30Sjl 
360225cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_fmem_exec_script_end -
3603e98fafb9Sjl 	    (ulong_t)drmach_fmem_exec_script);
360425cf1a30Sjl 	/* this is the entry point of the loop script */
360525cf1a30Sjl 	wp = wp + len + 1024;
360625cf1a30Sjl 
360725cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_fmem_exec_script -
3608e98fafb9Sjl 	    (ulong_t)drmach_fmem_loop_script);
360925cf1a30Sjl 	wp = wp + len + 1024;
361025cf1a30Sjl 
361125cf1a30Sjl 	/* now we make sure there is 1K extra */
361225cf1a30Sjl 
361325cf1a30Sjl 	if ((wp - bp) > PAGESIZE) {
36148eafe49bSbm 		err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
361568ac2337Sjl 		goto out;
361625cf1a30Sjl 	}
361725cf1a30Sjl 
361825cf1a30Sjl 	bp = (caddr_t)prog->critical;
361925cf1a30Sjl 	len = sizeof (drmach_copy_rename_critical_t);
362025cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
362125cf1a30Sjl 
362225cf1a30Sjl 	prog->critical->run = (int (*)())(wp);
362325cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_copy_rename_end -
3624e98fafb9Sjl 	    (ulong_t)drmach_copy_rename_prog__relocatable);
362525cf1a30Sjl 
362625cf1a30Sjl 	bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len);
362725cf1a30Sjl 
362825cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
362925cf1a30Sjl 
363025cf1a30Sjl 	prog->critical->fmem = (int (*)())(wp);
363125cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_exec_script_end -
3632e98fafb9Sjl 	    (ulong_t)drmach_fmem_exec_script);
363325cf1a30Sjl 	bcopy((caddr_t)drmach_fmem_exec_script, wp, len);
363425cf1a30Sjl 
363525cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_exec_script_end -
3636e98fafb9Sjl 	    (ulong_t)drmach_fmem_exec_script);
363725cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
363825cf1a30Sjl 
363925cf1a30Sjl 	prog->critical->loop = (int (*)())(wp);
364025cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_exec_script -
3641e98fafb9Sjl 	    (ulong_t)drmach_fmem_loop_script);
364225cf1a30Sjl 	bcopy((caddr_t)drmach_fmem_loop_script, (void *)wp, len);
364325cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_loop_script_rtn-
3644e98fafb9Sjl 	    (ulong_t)drmach_fmem_loop_script);
364525cf1a30Sjl 	prog->critical->loop_rtn = (void (*)()) (wp+len);
364625cf1a30Sjl 
3647b307f191Sbm 	prog->data->fmem_status.error = ESBD_NOERROR;
3648b307f191Sbm 
364925cf1a30Sjl 	/* now we are committed, call SCF, soft suspend mac patrol */
365025cf1a30Sjl 	if ((*scf_fmem_start)(s_bd, t_bd)) {
36518eafe49bSbm 		err = drerr_new(1, EOPL_SCF_FMEM_START, NULL);
365268ac2337Sjl 		goto out;
365325cf1a30Sjl 	}
365425cf1a30Sjl 	prog->data->scf_fmem_end = scf_fmem_end;
365525cf1a30Sjl 	prog->data->scf_fmem_cancel = scf_fmem_cancel;
365668ac2337Sjl 	prog->data->scf_get_base_addr = scf_get_base_addr;
365725cf1a30Sjl 	prog->data->fmem_status.op |= OPL_FMEM_SCF_START;
3658b307f191Sbm 
365925cf1a30Sjl 	/* soft suspend mac patrol */
366025cf1a30Sjl 	(*mc_suspend)();
366125cf1a30Sjl 	prog->data->fmem_status.op |= OPL_FMEM_MC_SUSPEND;
366225cf1a30Sjl 	prog->data->mc_resume = mc_resume;
366325cf1a30Sjl 
366425cf1a30Sjl 	prog->critical->inst_loop_ret  =
3665e98fafb9Sjl 	    *(uint64_t *)(prog->critical->loop_rtn);
366625cf1a30Sjl 
366725cf1a30Sjl 	/*
366825cf1a30Sjl 	 * 0x30800000 is op code "ba,a	+0"
366925cf1a30Sjl 	 */
367025cf1a30Sjl 
367125cf1a30Sjl 	*(uint_t *)(prog->critical->loop_rtn) = (uint_t)(0x30800000);
367225cf1a30Sjl 
367325cf1a30Sjl 	/*
367425cf1a30Sjl 	 * set the value of SCF FMEM TIMEOUT
367525cf1a30Sjl 	 */
367625cf1a30Sjl 	prog->critical->delay = fmem_timeout * system_clock_freq;
367725cf1a30Sjl 
367825cf1a30Sjl 	prog->data->s_mem = (drmachid_t)s_mem;
367925cf1a30Sjl 	prog->data->t_mem = (drmachid_t)t_mem;
368025cf1a30Sjl 
368125cf1a30Sjl 	cpuid = CPU->cpu_id;
368225cf1a30Sjl 	prog->data->cpuid = cpuid;
368325cf1a30Sjl 	prog->data->cpu_ready_set = cpu_ready_set;
368425cf1a30Sjl 	prog->data->cpu_slave_set = cpu_ready_set;
368525cf1a30Sjl 	prog->data->slowest_cpuid = (processorid_t)-1;
368625cf1a30Sjl 	prog->data->copy_wait_time = 0;
36876534c6f0Swh 	prog->data->copy_rename_count = 0;
368825cf1a30Sjl 	CPUSET_DEL(prog->data->cpu_slave_set, cpuid);
368925cf1a30Sjl 
369025cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
369125cf1a30Sjl 		prog->data->cpu_ml[i] = NULL;
369225cf1a30Sjl 	}
369325cf1a30Sjl 
36946534c6f0Swh 	/*
36956534c6f0Swh 	 * max_elms -	max number of memlist structures that
3696bbe1232eSToomas Soome 	 *		may be allocated for the CPU memory list.
36976534c6f0Swh 	 *		If there are too many memory span (because
36986534c6f0Swh 	 *		of fragmentation) than number of memlist
36996534c6f0Swh 	 *		available, we should return error.
37006534c6f0Swh 	 */
37016534c6f0Swh 	max_elms = drmach_setup_memlist(prog);
37026534c6f0Swh 	if (max_elms < mlist_size) {
37036534c6f0Swh 		err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
37046534c6f0Swh 		goto err_out;
37056534c6f0Swh 	}
37066534c6f0Swh 
370725cf1a30Sjl 	active_cpus = 0;
370825cf1a30Sjl 	if (drmach_disable_mcopy) {
370925cf1a30Sjl 		active_cpus = 1;
371025cf1a30Sjl 		CPUSET_ADD(prog->data->cpu_copy_set, cpuid);
371125cf1a30Sjl 	} else {
37126534c6f0Swh 		int max_cpu_num;
37136534c6f0Swh 		/*
37146534c6f0Swh 		 * The parallel copy procedure is going to split some
37156534c6f0Swh 		 * of the elements of the original memory copy list.
37166534c6f0Swh 		 * The number of added elements can be up to
37176534c6f0Swh 		 * (max_cpu_num - 1).  It means that max_cpu_num
37186534c6f0Swh 		 * should satisfy the following condition:
37196534c6f0Swh 		 * (max_cpu_num - 1) + mlist_size <= max_elms.
37206534c6f0Swh 		 */
37216534c6f0Swh 		max_cpu_num = max_elms - mlist_size + 1;
37226534c6f0Swh 
372325cf1a30Sjl 		for (i = 0; i < NCPU; i++) {
372425cf1a30Sjl 			if (CPU_IN_SET(cpu_ready_set, i) &&
3725e98fafb9Sjl 			    CPU_ACTIVE(cpu[i])) {
37266534c6f0Swh 				/*
37276534c6f0Swh 				 * To reduce the level-2 cache contention only
37286534c6f0Swh 				 * one strand per core will participate
37296534c6f0Swh 				 * in the copy. If the strand with even cpu_id
37306534c6f0Swh 				 * number is present in the ready set, we will
37316534c6f0Swh 				 * include this strand in the copy set. If it
37326534c6f0Swh 				 * is not present in the ready set, we check for
37336534c6f0Swh 				 * the strand with the consecutive odd cpu_id
37346534c6f0Swh 				 * and include it, provided that it is
37356534c6f0Swh 				 * present in the ready set.
37366534c6f0Swh 				 */
37376534c6f0Swh 				if (!(i & 0x1) ||
37386534c6f0Swh 				    !CPU_IN_SET(prog->data->cpu_copy_set,
37396534c6f0Swh 				    i - 1)) {
37406534c6f0Swh 					CPUSET_ADD(prog->data->cpu_copy_set, i);
37416534c6f0Swh 					active_cpus++;
37426534c6f0Swh 					/*
37436534c6f0Swh 					 * We cannot have more than
37446534c6f0Swh 					 * max_cpu_num CPUs in the copy
37456534c6f0Swh 					 * set, because each CPU has to
37466534c6f0Swh 					 * have at least one element
37476534c6f0Swh 					 * long memory copy list.
37486534c6f0Swh 					 */
37496534c6f0Swh 					if (active_cpus >= max_cpu_num)
37506534c6f0Swh 						break;
37516534c6f0Swh 
37526534c6f0Swh 				}
375325cf1a30Sjl 			}
375425cf1a30Sjl 		}
375525cf1a30Sjl 	}
375625cf1a30Sjl 
375725cf1a30Sjl 	x_ml = c_ml;
375825cf1a30Sjl 	sz = 0;
375925cf1a30Sjl 	while (x_ml != NULL) {
376056f33205SJonathan Adams 		sz += x_ml->ml_size;
376156f33205SJonathan Adams 		x_ml = x_ml->ml_next;
376225cf1a30Sjl 	}
376325cf1a30Sjl 
376425cf1a30Sjl 	copy_sz = sz/active_cpus;
376525cf1a30Sjl 	copy_sz = roundup(copy_sz, MMU_PAGESIZE4M);
376625cf1a30Sjl 
376725cf1a30Sjl 	while (sz > copy_sz*active_cpus) {
376825cf1a30Sjl 		copy_sz += MMU_PAGESIZE4M;
376925cf1a30Sjl 	}
377025cf1a30Sjl 
377125cf1a30Sjl 	prog->data->stick_freq = system_clock_freq;
377225cf1a30Sjl 	prog->data->copy_delay = ((copy_sz / min_copy_size_per_sec) + 2) *
3773e98fafb9Sjl 	    system_clock_freq;
377425cf1a30Sjl 
377525cf1a30Sjl 	x_ml = c_ml;
377656f33205SJonathan Adams 	c_addr = x_ml->ml_address;
377756f33205SJonathan Adams 	c_size = x_ml->ml_size;
377825cf1a30Sjl 
377925cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
378025cf1a30Sjl 		prog->stat->nbytes[i] = 0;
378125cf1a30Sjl 		if (!CPU_IN_SET(prog->data->cpu_copy_set, i)) {
378225cf1a30Sjl 			continue;
378325cf1a30Sjl 		}
378425cf1a30Sjl 		sz = copy_sz;
378525cf1a30Sjl 
378625cf1a30Sjl 		while (sz) {
378725cf1a30Sjl 			if (c_size > sz) {
37886534c6f0Swh 				if ((prog->data->cpu_ml[i] =
3789e98fafb9Sjl 				    drmach_memlist_add_span(prog,
37906534c6f0Swh 				    prog->data->cpu_ml[i],
37916534c6f0Swh 				    c_addr, sz)) == NULL) {
37926534c6f0Swh 					cmn_err(CE_WARN,
37936534c6f0Swh 					    "Unexpected drmach_memlist_add_span"
37946534c6f0Swh 					    " failure.");
37956534c6f0Swh 					err = drerr_new(1, EOPL_FMEM_SETUP,
37966534c6f0Swh 					    NULL);
37976534c6f0Swh 					mc_resume();
37986534c6f0Swh 					goto out;
37996534c6f0Swh 				}
380025cf1a30Sjl 				c_addr += sz;
380125cf1a30Sjl 				c_size -= sz;
380225cf1a30Sjl 				break;
380325cf1a30Sjl 			} else {
380425cf1a30Sjl 				sz -= c_size;
38056534c6f0Swh 				if ((prog->data->cpu_ml[i] =
3806e98fafb9Sjl 				    drmach_memlist_add_span(prog,
38076534c6f0Swh 				    prog->data->cpu_ml[i],
38086534c6f0Swh 				    c_addr, c_size)) == NULL) {
38096534c6f0Swh 					cmn_err(CE_WARN,
38106534c6f0Swh 					    "Unexpected drmach_memlist_add_span"
38116534c6f0Swh 					    " failure.");
38126534c6f0Swh 					err = drerr_new(1, EOPL_FMEM_SETUP,
38136534c6f0Swh 					    NULL);
38146534c6f0Swh 					mc_resume();
38156534c6f0Swh 					goto out;
38166534c6f0Swh 				}
38176534c6f0Swh 
381856f33205SJonathan Adams 				x_ml = x_ml->ml_next;
381925cf1a30Sjl 				if (x_ml != NULL) {
382056f33205SJonathan Adams 					c_addr = x_ml->ml_address;
382156f33205SJonathan Adams 					c_size = x_ml->ml_size;
382225cf1a30Sjl 				} else {
382325cf1a30Sjl 					goto end;
382425cf1a30Sjl 				}
382525cf1a30Sjl 			}
382625cf1a30Sjl 		}
382725cf1a30Sjl 	}
382825cf1a30Sjl end:
382925cf1a30Sjl 	prog->data->s_copybasepa = s_copybasepa;
383025cf1a30Sjl 	prog->data->t_copybasepa = t_copybasepa;
383125cf1a30Sjl 	prog->data->c_ml = c_ml;
383268ac2337Sjl 	*pgm_id = prog_kmem;
383325cf1a30Sjl 
383468ac2337Sjl 	/* Unmap the alternate space.  It will have to be remapped again */
383568ac2337Sjl 	drmach_unlock_critical((caddr_t)prog);
383625cf1a30Sjl 	return (NULL);
38376534c6f0Swh 
38386534c6f0Swh err_out:
38396534c6f0Swh 	mc_resume();
38406534c6f0Swh 	rv = (*prog->data->scf_fmem_cancel)();
38416534c6f0Swh 	if (rv) {
38426534c6f0Swh 		cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x", rv);
38436534c6f0Swh 	}
384468ac2337Sjl out:
384568ac2337Sjl 	if (prog != NULL) {
384668ac2337Sjl 		drmach_unlock_critical((caddr_t)prog);
3847e98fafb9Sjl 		vmem_free(heap_arena, prog, DRMACH_FMEM_LOCKED_PAGES *
3848e98fafb9Sjl 		    PAGESIZE);
384968ac2337Sjl 	}
385068ac2337Sjl 	if (prog_kmem != NULL) {
385168ac2337Sjl 		kmem_free(prog_kmem, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
385268ac2337Sjl 	}
385368ac2337Sjl 	return (err);
385425cf1a30Sjl }
385525cf1a30Sjl 
385625cf1a30Sjl sbd_error_t *
drmach_copy_rename_fini(drmachid_t id)385725cf1a30Sjl drmach_copy_rename_fini(drmachid_t id)
385825cf1a30Sjl {
385925cf1a30Sjl 	drmach_copy_rename_program_t	*prog = id;
386025cf1a30Sjl 	sbd_error_t			*err = NULL;
386125cf1a30Sjl 	int				rv;
3862b307f191Sbm 	uint_t				fmem_error;
386325cf1a30Sjl 
386425cf1a30Sjl 	/*
386525cf1a30Sjl 	 * Note that we have to delay calling SCF to find out the
386625cf1a30Sjl 	 * status of the FMEM operation here because SCF cannot
386725cf1a30Sjl 	 * respond while it is suspended.
386825cf1a30Sjl 	 * This create a small window when we are sure about the
386925cf1a30Sjl 	 * base address of the system board.
387025cf1a30Sjl 	 * If there is any call to mc-opl to get memory unum,
387125cf1a30Sjl 	 * mc-opl will return UNKNOWN as the unum.
387225cf1a30Sjl 	 */
387325cf1a30Sjl 
387468ac2337Sjl 	/*
387568ac2337Sjl 	 * we have to remap again because all the pointer like data,
387668ac2337Sjl 	 * critical in prog are based on the alternate vmem space.
387768ac2337Sjl 	 */
387868ac2337Sjl 	(void) drmach_lock_critical((caddr_t)prog, (caddr_t)prog->locked_prog);
387968ac2337Sjl 
388025cf1a30Sjl 	if (prog->data->c_ml != NULL)
388125cf1a30Sjl 		memlist_delete(prog->data->c_ml);
388225cf1a30Sjl 
388325cf1a30Sjl 	if ((prog->data->fmem_status.op &
3884e98fafb9Sjl 	    (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) !=
3885e98fafb9Sjl 	    (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) {
3886e98fafb9Sjl 		cmn_err(CE_PANIC, "drmach_copy_rename_fini: invalid op "
3887e98fafb9Sjl 		    "code %x\n", prog->data->fmem_status.op);
388825cf1a30Sjl 	}
388925cf1a30Sjl 
3890b307f191Sbm 	fmem_error = prog->data->fmem_status.error;
3891b307f191Sbm 	if (fmem_error != ESBD_NOERROR) {
3892b307f191Sbm 		err = drerr_new(1, fmem_error, NULL);
3893b307f191Sbm 	}
3894b307f191Sbm 
389525cf1a30Sjl 	/* possible ops are SCF_START, MC_SUSPEND */
389625cf1a30Sjl 	if (prog->critical->fmem_issued) {
3897b307f191Sbm 		if (fmem_error != ESBD_NOERROR) {
3898e98fafb9Sjl 			cmn_err(CE_PANIC, "Irrecoverable FMEM error %d\n",
3899e98fafb9Sjl 			    fmem_error);
3900b307f191Sbm 		}
390125cf1a30Sjl 		rv = (*prog->data->scf_fmem_end)();
390225cf1a30Sjl 		if (rv) {
390368ac2337Sjl 			cmn_err(CE_PANIC, "scf_fmem_end() failed rv=%d", rv);
390425cf1a30Sjl 		}
390525cf1a30Sjl 		/*
390625cf1a30Sjl 		 * If we get here, rename is successful.
390725cf1a30Sjl 		 * Do all the copy rename post processing.
390825cf1a30Sjl 		 */
390925cf1a30Sjl 		drmach_swap_pa((drmach_mem_t *)prog->data->s_mem,
3910e98fafb9Sjl 		    (drmach_mem_t *)prog->data->t_mem);
391125cf1a30Sjl 	} else {
391225cf1a30Sjl 		rv = (*prog->data->scf_fmem_cancel)();
391325cf1a30Sjl 		if (rv) {
3914e98fafb9Sjl 			cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x",
3915e98fafb9Sjl 			    rv);
3916e98fafb9Sjl 			if (!err) {
3917e98fafb9Sjl 				err = drerr_new(1, EOPL_SCF_FMEM_CANCEL,
3918e98fafb9Sjl 				    "scf_fmem_cancel() failed. rv = 0x%x", rv);
3919e98fafb9Sjl 			}
392025cf1a30Sjl 		}
392125cf1a30Sjl 	}
392225cf1a30Sjl 	/* soft resume mac patrol */
392325cf1a30Sjl 	(*prog->data->mc_resume)();
392425cf1a30Sjl 
392568ac2337Sjl 	drmach_unlock_critical((caddr_t)prog->locked_prog);
392668ac2337Sjl 
392768ac2337Sjl 	vmem_free(heap_arena, prog->locked_prog,
3928e98fafb9Sjl 	    DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
392925cf1a30Sjl 	kmem_free(prog, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
393025cf1a30Sjl 	return (err);
393125cf1a30Sjl }
393225cf1a30Sjl 
393325cf1a30Sjl /*ARGSUSED*/
393425cf1a30Sjl static void
drmach_copy_rename_slave(struct regs * rp,drmachid_t id)393525cf1a30Sjl drmach_copy_rename_slave(struct regs *rp, drmachid_t id)
393625cf1a30Sjl {
393768ac2337Sjl 	drmach_copy_rename_program_t	*prog =
3938e98fafb9Sjl 	    (drmach_copy_rename_program_t *)id;
393925cf1a30Sjl 	register int			cpuid;
394025cf1a30Sjl 	extern void			drmach_flush();
394125cf1a30Sjl 	extern void			membar_sync_il();
394225cf1a30Sjl 	extern void			drmach_flush_icache();
394325cf1a30Sjl 	on_trap_data_t			otd;
394425cf1a30Sjl 
394525cf1a30Sjl 	cpuid = CPU->cpu_id;
394625cf1a30Sjl 
394725cf1a30Sjl 	if (on_trap(&otd, OT_DATA_EC)) {
394825cf1a30Sjl 		no_trap();
3949b307f191Sbm 		prog->data->error[cpuid] = EOPL_FMEM_COPY_ERROR;
395025cf1a30Sjl 		prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
395168ac2337Sjl 		drmach_flush_icache();
395268ac2337Sjl 		membar_sync_il();
395325cf1a30Sjl 		return;
395425cf1a30Sjl 	}
395525cf1a30Sjl 
395625cf1a30Sjl 
395725cf1a30Sjl 	/*
395825cf1a30Sjl 	 * jmp drmach_copy_rename_prog().
395925cf1a30Sjl 	 */
396025cf1a30Sjl 
396125cf1a30Sjl 	drmach_flush(prog->critical, PAGESIZE);
396225cf1a30Sjl 	(void) prog->critical->run(prog, cpuid);
396325cf1a30Sjl 	drmach_flush_icache();
396425cf1a30Sjl 
396525cf1a30Sjl 	no_trap();
396625cf1a30Sjl 
396725cf1a30Sjl 	prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
396868ac2337Sjl 
396925cf1a30Sjl 	membar_sync_il();
397025cf1a30Sjl }
397125cf1a30Sjl 
397225cf1a30Sjl static void
drmach_swap_pa(drmach_mem_t * s_mem,drmach_mem_t * t_mem)397325cf1a30Sjl drmach_swap_pa(drmach_mem_t *s_mem, drmach_mem_t *t_mem)
397425cf1a30Sjl {
397525cf1a30Sjl 	uint64_t s_base, t_base;
397625cf1a30Sjl 	drmach_board_t *s_board, *t_board;
397725cf1a30Sjl 	struct memlist *ml;
397825cf1a30Sjl 
397925cf1a30Sjl 	s_board = s_mem->dev.bp;
398025cf1a30Sjl 	t_board = t_mem->dev.bp;
398125cf1a30Sjl 	if (s_board == NULL || t_board == NULL) {
398225cf1a30Sjl 		cmn_err(CE_PANIC, "Cannot locate source or target board\n");
398325cf1a30Sjl 		return;
398425cf1a30Sjl 	}
398525cf1a30Sjl 	s_base = s_mem->slice_base;
398625cf1a30Sjl 	t_base = t_mem->slice_base;
398725cf1a30Sjl 
398825cf1a30Sjl 	s_mem->slice_base = t_base;
398925cf1a30Sjl 	s_mem->base_pa = (s_mem->base_pa - s_base) + t_base;
399025cf1a30Sjl 
399156f33205SJonathan Adams 	for (ml = s_mem->memlist; ml; ml = ml->ml_next) {
399256f33205SJonathan Adams 		ml->ml_address = ml->ml_address - s_base + t_base;
399325cf1a30Sjl 	}
399425cf1a30Sjl 
399525cf1a30Sjl 	t_mem->slice_base = s_base;
399625cf1a30Sjl 	t_mem->base_pa = (t_mem->base_pa - t_base) + s_base;
399725cf1a30Sjl 
399856f33205SJonathan Adams 	for (ml = t_mem->memlist; ml; ml = ml->ml_next) {
399956f33205SJonathan Adams 		ml->ml_address = ml->ml_address - t_base + s_base;
400025cf1a30Sjl 	}
400125cf1a30Sjl 
400225cf1a30Sjl 	/*
400325cf1a30Sjl 	 * IKP has to update the sb-mem-ranges for mac patrol driver
400425cf1a30Sjl 	 * when it resumes, it will re-read the sb-mem-range property
400525cf1a30Sjl 	 * to get the new base address
400625cf1a30Sjl 	 */
400725cf1a30Sjl 	if (oplcfg_pa_swap(s_board->bnum, t_board->bnum) != 0)
400825cf1a30Sjl 		cmn_err(CE_PANIC, "Could not update device nodes\n");
400925cf1a30Sjl }
401025cf1a30Sjl 
401125cf1a30Sjl void
drmach_copy_rename(drmachid_t id)401225cf1a30Sjl drmach_copy_rename(drmachid_t id)
401325cf1a30Sjl {
401468ac2337Sjl 	drmach_copy_rename_program_t	*prog_kmem = id;
401568ac2337Sjl 	drmach_copy_rename_program_t	*prog;
401625cf1a30Sjl 	cpuset_t	cpuset;
401725cf1a30Sjl 	int		cpuid;
401825cf1a30Sjl 	uint64_t	inst;
401925cf1a30Sjl 	register int	rtn;
402025cf1a30Sjl 	extern int	in_sync;
402125cf1a30Sjl 	int		old_in_sync;
402225cf1a30Sjl 	extern void	drmach_sys_trap();
402325cf1a30Sjl 	extern void	drmach_flush();
402425cf1a30Sjl 	extern void	drmach_flush_icache();
402525cf1a30Sjl 	extern uint64_t	patch_inst(uint64_t *, uint64_t);
402625cf1a30Sjl 	on_trap_data_t	otd;
402725cf1a30Sjl 
40288eafe49bSbm 
402968ac2337Sjl 	prog = prog_kmem->locked_prog;
403068ac2337Sjl 
40318eafe49bSbm 
403268ac2337Sjl 	/*
403368ac2337Sjl 	 * We must immediately drop in the TLB because all pointers
403468ac2337Sjl 	 * are based on the alternate vmem space.
403568ac2337Sjl 	 */
403668ac2337Sjl 
403768ac2337Sjl 	(void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
403868ac2337Sjl 
403968ac2337Sjl 	/*
404068ac2337Sjl 	 * we call scf to get the base address here becuase if scf
404168ac2337Sjl 	 * has not been suspended yet, the active path can be changing and
404268ac2337Sjl 	 * sometimes it is not even mapped.  We call the interface when
404368ac2337Sjl 	 * the OS has been quiesced.
404468ac2337Sjl 	 */
404568ac2337Sjl 	prog->critical->scf_reg_base = (*prog->data->scf_get_base_addr)();
404668ac2337Sjl 
404768ac2337Sjl 	if (prog->critical->scf_reg_base == (uint64_t)-1 ||
4048bbe1232eSToomas Soome 	    prog->critical->scf_reg_base == 0) {
4049b307f191Sbm 		prog->data->fmem_status.error = EOPL_FMEM_SCF_ERR;
405068ac2337Sjl 		drmach_unlock_critical((caddr_t)prog);
405125cf1a30Sjl 		return;
405225cf1a30Sjl 	}
405325cf1a30Sjl 
405425cf1a30Sjl 	cpuset = prog->data->cpu_ready_set;
405525cf1a30Sjl 
405625cf1a30Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
405725cf1a30Sjl 		if (CPU_IN_SET(cpuset, cpuid)) {
405825cf1a30Sjl 			prog->critical->stat[cpuid] = FMEM_LOOP_START;
4059b307f191Sbm 			prog->data->error[cpuid] = ESBD_NOERROR;
406025cf1a30Sjl 		}
406125cf1a30Sjl 	}
406225cf1a30Sjl 
406325cf1a30Sjl 	old_in_sync = in_sync;
406425cf1a30Sjl 	in_sync = 1;
406525cf1a30Sjl 	cpuid = CPU->cpu_id;
406625cf1a30Sjl 
406725cf1a30Sjl 	CPUSET_DEL(cpuset, cpuid);
406825cf1a30Sjl 
406968ac2337Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
407068ac2337Sjl 		if (CPU_IN_SET(cpuset, cpuid)) {
407168ac2337Sjl 			xc_one(cpuid, (xcfunc_t *)drmach_lock_critical,
4072e98fafb9Sjl 			    (uint64_t)prog_kmem, (uint64_t)prog);
407368ac2337Sjl 		}
407468ac2337Sjl 	}
407568ac2337Sjl 
407668ac2337Sjl 	cpuid = CPU->cpu_id;
407725cf1a30Sjl 
407825cf1a30Sjl 	xt_some(cpuset, (xcfunc_t *)drmach_sys_trap,
4079e98fafb9Sjl 	    (uint64_t)drmach_copy_rename_slave, (uint64_t)prog);
408025cf1a30Sjl 	xt_sync(cpuset);
408125cf1a30Sjl 
408225cf1a30Sjl 	if (on_trap(&otd, OT_DATA_EC)) {
4083b307f191Sbm 		rtn = EOPL_FMEM_COPY_ERROR;
408468ac2337Sjl 		drmach_flush_icache();
408525cf1a30Sjl 		goto done;
408625cf1a30Sjl 	}
408725cf1a30Sjl 
408825cf1a30Sjl 	/*
408925cf1a30Sjl 	 * jmp drmach_copy_rename_prog().
409025cf1a30Sjl 	 */
40918eafe49bSbm 
409225cf1a30Sjl 	drmach_flush(prog->critical, PAGESIZE);
409325cf1a30Sjl 	rtn = prog->critical->run(prog, cpuid);
4094e98fafb9Sjl 
409525cf1a30Sjl 	drmach_flush_icache();
409625cf1a30Sjl 
409725cf1a30Sjl 
409825cf1a30Sjl done:
409925cf1a30Sjl 	no_trap();
4100b307f191Sbm 	if (rtn == EOPL_FMEM_HW_ERROR) {
410125cf1a30Sjl 		kpreempt_enable();
4102e98fafb9Sjl 		prom_panic("URGENT_ERROR_TRAP is detected during FMEM.\n");
410325cf1a30Sjl 	}
410425cf1a30Sjl 
410525cf1a30Sjl 	/*
410625cf1a30Sjl 	 * In normal case, all slave CPU's are still spinning in
410725cf1a30Sjl 	 * the assembly code.  The master has to patch the instruction
410825cf1a30Sjl 	 * to get them out.
410925cf1a30Sjl 	 * In error case, e.g. COPY_ERROR, some slave CPU's might
411025cf1a30Sjl 	 * have aborted and already returned and sset LOOP_EXIT status.
411125cf1a30Sjl 	 * Some CPU might still be copying.
411225cf1a30Sjl 	 * In any case, some delay is necessary to give them
411325cf1a30Sjl 	 * enough time to set the LOOP_EXIT status.
411425cf1a30Sjl 	 */
411525cf1a30Sjl 
411625cf1a30Sjl 	for (;;) {
411725cf1a30Sjl 		inst = patch_inst((uint64_t *)prog->critical->loop_rtn,
4118e98fafb9Sjl 		    prog->critical->inst_loop_ret);
411925cf1a30Sjl 		if (prog->critical->inst_loop_ret == inst) {
412025cf1a30Sjl 			break;
412125cf1a30Sjl 		}
412225cf1a30Sjl 	}
412325cf1a30Sjl 
412425cf1a30Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
412525cf1a30Sjl 		uint64_t	last, now;
412625cf1a30Sjl 		if (!CPU_IN_SET(cpuset, cpuid)) {
412725cf1a30Sjl 			continue;
412825cf1a30Sjl 		}
412925cf1a30Sjl 		last = prog->stat->nbytes[cpuid];
413025cf1a30Sjl 		/*
413125cf1a30Sjl 		 * Wait for all CPU to exit.
413225cf1a30Sjl 		 * However we do not want an infinite loop
413325cf1a30Sjl 		 * so we detect hangup situation here.
413425cf1a30Sjl 		 * If the slave CPU is still copying data,
413525cf1a30Sjl 		 * we will continue to wait.
413625cf1a30Sjl 		 * In error cases, the master has already set
413725cf1a30Sjl 		 * fmem_status.error to abort the copying.
413825cf1a30Sjl 		 * 1 m.s delay for them to abort copying and
413925cf1a30Sjl 		 * return to drmach_copy_rename_slave to set
414025cf1a30Sjl 		 * FMEM_LOOP_EXIT status should be enough.
414125cf1a30Sjl 		 */
414225cf1a30Sjl 		for (;;) {
414325cf1a30Sjl 			if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT)
414425cf1a30Sjl 				break;
414525cf1a30Sjl 			drmach_sleep_il();
414625cf1a30Sjl 			drv_usecwait(1000);
414725cf1a30Sjl 			now = prog->stat->nbytes[cpuid];
414825cf1a30Sjl 			if (now <= last) {
4149e98fafb9Sjl 				drv_usecwait(1000);
4150e98fafb9Sjl 				if (prog->critical->stat[cpuid] ==
4151e98fafb9Sjl 				    FMEM_LOOP_EXIT)
4152e98fafb9Sjl 					break;
4153e98fafb9Sjl 				cmn_err(CE_PANIC, "CPU %d hang during Copy "
4154e98fafb9Sjl 				    "Rename", cpuid);
415525cf1a30Sjl 			}
415625cf1a30Sjl 			last = now;
415725cf1a30Sjl 		}
4158b307f191Sbm 		if (prog->data->error[cpuid] == EOPL_FMEM_HW_ERROR) {
4159e98fafb9Sjl 			prom_panic("URGENT_ERROR_TRAP is detected during "
4160e98fafb9Sjl 			    "FMEM.\n");
416125cf1a30Sjl 		}
416225cf1a30Sjl 	}
416368ac2337Sjl 
416468ac2337Sjl 	/*
416568ac2337Sjl 	 * This must be done after all strands have exit.
416668ac2337Sjl 	 * Removing the TLB entry will affect both strands
416768ac2337Sjl 	 * in the same core.
416868ac2337Sjl 	 */
416968ac2337Sjl 
417068ac2337Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
417168ac2337Sjl 		if (CPU_IN_SET(cpuset, cpuid)) {
417268ac2337Sjl 			xc_one(cpuid, (xcfunc_t *)drmach_unlock_critical,
4173e98fafb9Sjl 			    (uint64_t)prog, 0);
417468ac2337Sjl 		}
417568ac2337Sjl 	}
417625cf1a30Sjl 
417725cf1a30Sjl 	in_sync = old_in_sync;
417825cf1a30Sjl 
417968ac2337Sjl 	/*
418068ac2337Sjl 	 * we should unlock before the following lock to keep the kpreempt
418168ac2337Sjl 	 * count correct.
418268ac2337Sjl 	 */
418368ac2337Sjl 	(void) drmach_unlock_critical((caddr_t)prog);
418468ac2337Sjl 
418568ac2337Sjl 	/*
418668ac2337Sjl 	 * we must remap again.  TLB might have been removed in above xcall.
418768ac2337Sjl 	 */
418868ac2337Sjl 
418968ac2337Sjl 	(void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
419068ac2337Sjl 
4191b307f191Sbm 	if (prog->data->fmem_status.error == ESBD_NOERROR)
419225cf1a30Sjl 		prog->data->fmem_status.error = rtn;
419325cf1a30Sjl 
419425cf1a30Sjl 	if (prog->data->copy_wait_time > 0) {
419525cf1a30Sjl 		DRMACH_PR("Unexpected long wait time %ld seconds "
4196e98fafb9Sjl 		    "during copy rename on CPU %d\n",
4197e98fafb9Sjl 		    prog->data->copy_wait_time/prog->data->stick_freq,
4198e98fafb9Sjl 		    prog->data->slowest_cpuid);
419925cf1a30Sjl 	}
420068ac2337Sjl 	drmach_unlock_critical((caddr_t)prog);
420125cf1a30Sjl }
4202