xref: /illumos-gate/usr/src/uts/sun4u/opl/io/drmach.c (revision bbe1232e)
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 
2725cf1a30Sjl #include <sys/debug.h>
2825cf1a30Sjl #include <sys/types.h>
2925cf1a30Sjl #include <sys/varargs.h>
3025cf1a30Sjl #include <sys/errno.h>
3125cf1a30Sjl #include <sys/cred.h>
3225cf1a30Sjl #include <sys/dditypes.h>
3325cf1a30Sjl #include <sys/devops.h>
3425cf1a30Sjl #include <sys/modctl.h>
3525cf1a30Sjl #include <sys/poll.h>
3625cf1a30Sjl #include <sys/conf.h>
3725cf1a30Sjl #include <sys/ddi.h>
3825cf1a30Sjl #include <sys/sunddi.h>
3925cf1a30Sjl #include <sys/sunndi.h>
4025cf1a30Sjl #include <sys/ndi_impldefs.h>
4125cf1a30Sjl #include <sys/stat.h>
4225cf1a30Sjl #include <sys/kmem.h>
4325cf1a30Sjl #include <sys/vmem.h>
4425cf1a30Sjl #include <sys/opl_olympus_regs.h>
4525cf1a30Sjl #include <sys/cpuvar.h>
4625cf1a30Sjl #include <sys/cpupart.h>
4725cf1a30Sjl #include <sys/mem_config.h>
4825cf1a30Sjl #include <sys/ddi_impldefs.h>
4925cf1a30Sjl #include <sys/systm.h>
5025cf1a30Sjl #include <sys/machsystm.h>
5125cf1a30Sjl #include <sys/autoconf.h>
5225cf1a30Sjl #include <sys/cmn_err.h>
5325cf1a30Sjl #include <sys/sysmacros.h>
5425cf1a30Sjl #include <sys/x_call.h>
5525cf1a30Sjl #include <sys/promif.h>
5625cf1a30Sjl #include <sys/prom_plat.h>
5725cf1a30Sjl #include <sys/membar.h>
5825cf1a30Sjl #include <vm/seg_kmem.h>
5925cf1a30Sjl #include <sys/mem_cage.h>
6025cf1a30Sjl #include <sys/stack.h>
6125cf1a30Sjl #include <sys/archsystm.h>
6225cf1a30Sjl #include <vm/hat_sfmmu.h>
6325cf1a30Sjl #include <sys/pte.h>
6425cf1a30Sjl #include <sys/mmu.h>
6525cf1a30Sjl #include <sys/cpu_module.h>
6625cf1a30Sjl #include <sys/obpdefs.h>
6725cf1a30Sjl #include <sys/note.h>
6825cf1a30Sjl #include <sys/ontrap.h>
6925cf1a30Sjl #include <sys/cpu_sgnblk_defs.h>
7025cf1a30Sjl #include <sys/opl.h>
71e98fafb9Sjl #include <sys/cpu_impl.h>
7225cf1a30Sjl 
7325cf1a30Sjl 
7425cf1a30Sjl #include <sys/promimpl.h>
7525cf1a30Sjl #include <sys/prom_plat.h>
7625cf1a30Sjl #include <sys/kobj.h>
7725cf1a30Sjl 
7825cf1a30Sjl #include <sys/sysevent.h>
7925cf1a30Sjl #include <sys/sysevent/dr.h>
8025cf1a30Sjl #include <sys/sysevent/eventdefs.h>
8125cf1a30Sjl 
8225cf1a30Sjl #include <sys/drmach.h>
8325cf1a30Sjl #include <sys/dr_util.h>
8425cf1a30Sjl 
8525cf1a30Sjl #include <sys/fcode.h>
8625cf1a30Sjl #include <sys/opl_cfg.h>
8725cf1a30Sjl 
8825cf1a30Sjl extern void		bcopy32_il(uint64_t, uint64_t);
8925cf1a30Sjl extern void		flush_cache_il(void);
9025cf1a30Sjl extern void		drmach_sleep_il(void);
9125cf1a30Sjl 
9225cf1a30Sjl typedef struct {
9325cf1a30Sjl 	struct drmach_node	*node;
9425cf1a30Sjl 	void			*data;
9525cf1a30Sjl } drmach_node_walk_args_t;
9625cf1a30Sjl 
9725cf1a30Sjl typedef struct drmach_node {
9825cf1a30Sjl 	void		*here;
9925cf1a30Sjl 
10025cf1a30Sjl 	pnode_t		(*get_dnode)(struct drmach_node *node);
10125cf1a30Sjl 	int		(*walk)(struct drmach_node *node, void *data,
10225cf1a30Sjl 				int (*cb)(drmach_node_walk_args_t *args));
10325cf1a30Sjl 	dev_info_t	*(*n_getdip)(struct drmach_node *node);
10425cf1a30Sjl 	int		(*n_getproplen)(struct drmach_node *node, char *name,
10525cf1a30Sjl 				int *len);
10625cf1a30Sjl 	int		(*n_getprop)(struct drmach_node *node, char *name,
10725cf1a30Sjl 				void *buf, int len);
10825cf1a30Sjl 	int		(*get_parent)(struct drmach_node *node,
10925cf1a30Sjl 				struct drmach_node *pnode);
11025cf1a30Sjl } drmach_node_t;
11125cf1a30Sjl 
11225cf1a30Sjl typedef struct {
11325cf1a30Sjl 	int		 min_index;
11425cf1a30Sjl 	int		 max_index;
11525cf1a30Sjl 	int		 arr_sz;
11625cf1a30Sjl 	drmachid_t	*arr;
11725cf1a30Sjl } drmach_array_t;
11825cf1a30Sjl 
11925cf1a30Sjl typedef struct {
12025cf1a30Sjl 	void		*isa;
12125cf1a30Sjl 
12225cf1a30Sjl 	void		(*dispose)(drmachid_t);
12325cf1a30Sjl 	sbd_error_t	*(*release)(drmachid_t);
12425cf1a30Sjl 	sbd_error_t	*(*status)(drmachid_t, drmach_status_t *);
12525cf1a30Sjl 
12625cf1a30Sjl 	char		 name[MAXNAMELEN];
12725cf1a30Sjl } drmach_common_t;
12825cf1a30Sjl 
12925cf1a30Sjl typedef	struct {
13025cf1a30Sjl 	uint32_t	core_present;
13125cf1a30Sjl 	uint32_t	core_hotadded;
13225cf1a30Sjl 	uint32_t	core_started;
13325cf1a30Sjl } drmach_cmp_t;
13425cf1a30Sjl 
13525cf1a30Sjl typedef struct {
13625cf1a30Sjl 	drmach_common_t	 cm;
13725cf1a30Sjl 	int		 bnum;
13825cf1a30Sjl 	int		 assigned;
13925cf1a30Sjl 	int		 powered;
14025cf1a30Sjl 	int		 connected;
14125cf1a30Sjl 	int		 cond;
14225cf1a30Sjl 	drmach_node_t	*tree;
14325cf1a30Sjl 	drmach_array_t	*devices;
14425cf1a30Sjl 	int		boot_board;	/* if board exists on bootup */
14525cf1a30Sjl 	drmach_cmp_t	cores[OPL_MAX_COREID_PER_BOARD];
14625cf1a30Sjl } drmach_board_t;
14725cf1a30Sjl 
14825cf1a30Sjl typedef struct {
14925cf1a30Sjl 	drmach_common_t	 cm;
15025cf1a30Sjl 	drmach_board_t	*bp;
15125cf1a30Sjl 	int		 unum;
15225cf1a30Sjl 	int		portid;
15325cf1a30Sjl 	int		 busy;
15425cf1a30Sjl 	int		 powered;
15525cf1a30Sjl 	const char	*type;
15625cf1a30Sjl 	drmach_node_t	*node;
15725cf1a30Sjl } drmach_device_t;
15825cf1a30Sjl 
15925cf1a30Sjl typedef struct drmach_cpu {
16025cf1a30Sjl 	drmach_device_t  dev;
16125cf1a30Sjl 	processorid_t    cpuid;
16225cf1a30Sjl 	int		sb;
16325cf1a30Sjl 	int		chipid;
16425cf1a30Sjl 	int		coreid;
16525cf1a30Sjl 	int		strandid;
16625cf1a30Sjl 	int		status;
16725cf1a30Sjl #define	OPL_CPU_HOTADDED	1
16825cf1a30Sjl } drmach_cpu_t;
16925cf1a30Sjl 
17025cf1a30Sjl typedef struct drmach_mem {
17125cf1a30Sjl 	drmach_device_t  dev;
17225cf1a30Sjl 	uint64_t	slice_base;
17325cf1a30Sjl 	uint64_t	slice_size;
17425cf1a30Sjl 	uint64_t	base_pa;	/* lowest installed memory base */
17525cf1a30Sjl 	uint64_t	nbytes;		/* size of installed memory */
17625cf1a30Sjl 	struct memlist *memlist;
17725cf1a30Sjl } drmach_mem_t;
17825cf1a30Sjl 
17925cf1a30Sjl typedef struct drmach_io {
18025cf1a30Sjl 	drmach_device_t  dev;
18125cf1a30Sjl 	int	channel;
18225cf1a30Sjl 	int	leaf;
18325cf1a30Sjl } drmach_io_t;
18425cf1a30Sjl 
18525cf1a30Sjl typedef struct drmach_domain_info {
18625cf1a30Sjl 	uint32_t	floating;
18725cf1a30Sjl 	int		allow_dr;
18825cf1a30Sjl } drmach_domain_info_t;
18925cf1a30Sjl 
19025cf1a30Sjl drmach_domain_info_t drmach_domain;
19125cf1a30Sjl 
19225cf1a30Sjl typedef struct {
19325cf1a30Sjl 	int		 flags;
19425cf1a30Sjl 	drmach_device_t	*dp;
19525cf1a30Sjl 	sbd_error_t	*err;
19625cf1a30Sjl 	dev_info_t	*dip;
19725cf1a30Sjl } drmach_config_args_t;
19825cf1a30Sjl 
19925cf1a30Sjl typedef struct {
20025cf1a30Sjl 	drmach_board_t	*obj;
20125cf1a30Sjl 	int		 ndevs;
20225cf1a30Sjl 	void		*a;
20325cf1a30Sjl 	sbd_error_t	*(*found)(void *a, const char *, int, drmachid_t);
20425cf1a30Sjl 	sbd_error_t	*err;
20525cf1a30Sjl } drmach_board_cb_data_t;
20625cf1a30Sjl 
20725cf1a30Sjl static drmach_array_t	*drmach_boards;
20825cf1a30Sjl 
20925cf1a30Sjl static sbd_error_t	*drmach_device_new(drmach_node_t *,
21025cf1a30Sjl 				drmach_board_t *, int, drmachid_t *);
21125cf1a30Sjl static sbd_error_t	*drmach_cpu_new(drmach_device_t *, drmachid_t *);
21225cf1a30Sjl static sbd_error_t	*drmach_mem_new(drmach_device_t *, drmachid_t *);
21325cf1a30Sjl static sbd_error_t	*drmach_io_new(drmach_device_t *, drmachid_t *);
21425cf1a30Sjl 
21525cf1a30Sjl static dev_info_t	*drmach_node_ddi_get_dip(drmach_node_t *np);
21625cf1a30Sjl static int		 drmach_node_ddi_get_prop(drmach_node_t *np,
21725cf1a30Sjl 				char *name, void *buf, int len);
21825cf1a30Sjl static int		 drmach_node_ddi_get_proplen(drmach_node_t *np,
21925cf1a30Sjl 				char *name, int *len);
22025cf1a30Sjl 
221*bbe1232eSToomas Soome static int		drmach_get_portid(drmach_node_t *);
22225cf1a30Sjl static	sbd_error_t	*drmach_i_status(drmachid_t, drmach_status_t *);
22325cf1a30Sjl static int		opl_check_dr_status();
22425cf1a30Sjl static void		drmach_io_dispose(drmachid_t);
22525cf1a30Sjl static sbd_error_t	*drmach_io_release(drmachid_t);
22625cf1a30Sjl static sbd_error_t	*drmach_io_status(drmachid_t, drmach_status_t *);
227*bbe1232eSToomas Soome static int		drmach_init(void);
228*bbe1232eSToomas Soome static void		drmach_fini(void);
22925cf1a30Sjl static void		drmach_swap_pa(drmach_mem_t *, drmach_mem_t *);
23025cf1a30Sjl static drmach_board_t	*drmach_get_board_by_bnum(int);
23125cf1a30Sjl 
2328682d1efSRichard Lowe static sbd_error_t	*drmach_board_release(drmachid_t);
2338682d1efSRichard Lowe static sbd_error_t	*drmach_board_status(drmachid_t, drmach_status_t *);
2348682d1efSRichard Lowe static void		drmach_cpu_dispose(drmachid_t);
2358682d1efSRichard Lowe static sbd_error_t	*drmach_cpu_release(drmachid_t);
2368682d1efSRichard Lowe static sbd_error_t	*drmach_cpu_status(drmachid_t, drmach_status_t *);
2378682d1efSRichard Lowe static void		drmach_mem_dispose(drmachid_t);
2388682d1efSRichard Lowe static sbd_error_t	*drmach_mem_release(drmachid_t);
2398682d1efSRichard Lowe static sbd_error_t	*drmach_mem_status(drmachid_t, drmach_status_t *);
2408682d1efSRichard Lowe 
24125cf1a30Sjl /* options for the second argument in drmach_add_remove_cpu() */
24225cf1a30Sjl #define	HOTADD_CPU	1
24325cf1a30Sjl #define	HOTREMOVE_CPU	2
24425cf1a30Sjl 
24525cf1a30Sjl #define	ON_BOARD_CORE_NUM(x)	(((uint_t)(x) / OPL_MAX_STRANDID_PER_CORE) & \
24625cf1a30Sjl 	(OPL_MAX_COREID_PER_BOARD - 1))
24725cf1a30Sjl 
24825cf1a30Sjl extern struct cpu	*SIGBCPU;
24925cf1a30Sjl 
25025cf1a30Sjl static int		drmach_name2type_idx(char *);
25125cf1a30Sjl static drmach_board_t	*drmach_board_new(int, int);
25225cf1a30Sjl 
25325cf1a30Sjl #ifdef DEBUG
25425cf1a30Sjl 
25525cf1a30Sjl #define	DRMACH_PR		if (drmach_debug) printf
25625cf1a30Sjl int drmach_debug = 1;		 /* set to non-zero to enable debug messages */
25725cf1a30Sjl #else
25825cf1a30Sjl 
25925cf1a30Sjl #define	DRMACH_PR		_NOTE(CONSTANTCONDITION) if (0) printf
26025cf1a30Sjl #endif /* DEBUG */
26125cf1a30Sjl 
26225cf1a30Sjl 
26325cf1a30Sjl #define	DRMACH_OBJ(id)		((drmach_common_t *)id)
26425cf1a30Sjl 
265ddf95635Sbm #define	DRMACH_NULL_ID(id)	((id) == 0)
266ddf95635Sbm 
26725cf1a30Sjl #define	DRMACH_IS_BOARD_ID(id)	\
26825cf1a30Sjl 	((id != 0) &&		\
26925cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
27025cf1a30Sjl 
27125cf1a30Sjl #define	DRMACH_IS_CPU_ID(id)	\
27225cf1a30Sjl 	((id != 0) &&		\
27325cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
27425cf1a30Sjl 
27525cf1a30Sjl #define	DRMACH_IS_MEM_ID(id)	\
27625cf1a30Sjl 	((id != 0) &&		\
27725cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
27825cf1a30Sjl 
27925cf1a30Sjl #define	DRMACH_IS_IO_ID(id)	\
28025cf1a30Sjl 	((id != 0) &&		\
28125cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
28225cf1a30Sjl 
28325cf1a30Sjl #define	DRMACH_IS_DEVICE_ID(id)					\
28425cf1a30Sjl 	((id != 0) &&						\
28525cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
28625cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
28725cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
28825cf1a30Sjl 
28925cf1a30Sjl #define	DRMACH_IS_ID(id)					\
29025cf1a30Sjl 	((id != 0) &&						\
29125cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new ||	\
29225cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
29325cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
29425cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
29525cf1a30Sjl 
29625cf1a30Sjl #define	DRMACH_INTERNAL_ERROR() \
29725cf1a30Sjl 	drerr_new(1, EOPL_INTERNAL, drmach_ie_fmt, __LINE__)
29825cf1a30Sjl 
29925cf1a30Sjl static char		*drmach_ie_fmt = "drmach.c %d";
30025cf1a30Sjl 
30125cf1a30Sjl static struct {
30225cf1a30Sjl 	const char	*name;
30325cf1a30Sjl 	const char	*type;
30425cf1a30Sjl 	sbd_error_t	*(*new)(drmach_device_t *, drmachid_t *);
30525cf1a30Sjl } drmach_name2type[] = {
30625cf1a30Sjl 	{ "cpu",	DRMACH_DEVTYPE_CPU,		drmach_cpu_new },
30725cf1a30Sjl 	{ "pseudo-mc",	DRMACH_DEVTYPE_MEM,		drmach_mem_new },
30825cf1a30Sjl 	{ "pci",	DRMACH_DEVTYPE_PCI,		drmach_io_new  },
30925cf1a30Sjl };
31025cf1a30Sjl 
31125cf1a30Sjl /* utility */
31225cf1a30Sjl #define	MBYTE	(1048576ull)
31325cf1a30Sjl 
31425cf1a30Sjl /*
31525cf1a30Sjl  * drmach autoconfiguration data structures and interfaces
31625cf1a30Sjl  */
31725cf1a30Sjl 
31825cf1a30Sjl extern struct mod_ops mod_miscops;
31925cf1a30Sjl 
32025cf1a30Sjl static struct modlmisc modlmisc = {
32125cf1a30Sjl 	&mod_miscops,
32225cf1a30Sjl 	"OPL DR 1.1"
32325cf1a30Sjl };
32425cf1a30Sjl 
32525cf1a30Sjl static struct modlinkage modlinkage = {
32625cf1a30Sjl 	MODREV_1,
32725cf1a30Sjl 	(void *)&modlmisc,
32825cf1a30Sjl 	NULL
32925cf1a30Sjl };
33025cf1a30Sjl 
33125cf1a30Sjl static krwlock_t drmach_boards_rwlock;
33225cf1a30Sjl 
33325cf1a30Sjl typedef const char	*fn_t;
33425cf1a30Sjl 
33525cf1a30Sjl int
33625cf1a30Sjl _init(void)
33725cf1a30Sjl {
33825cf1a30Sjl 	int err;
33925cf1a30Sjl 
34025cf1a30Sjl 	if ((err = drmach_init()) != 0) {
34125cf1a30Sjl 		return (err);
34225cf1a30Sjl 	}
34325cf1a30Sjl 
34425cf1a30Sjl 	if ((err = mod_install(&modlinkage)) != 0) {
34525cf1a30Sjl 		drmach_fini();
34625cf1a30Sjl 	}
34725cf1a30Sjl 
34825cf1a30Sjl 	return (err);
34925cf1a30Sjl }
35025cf1a30Sjl 
35125cf1a30Sjl int
35225cf1a30Sjl _fini(void)
35325cf1a30Sjl {
35425cf1a30Sjl 	int	err;
35525cf1a30Sjl 
35625cf1a30Sjl 	if ((err = mod_remove(&modlinkage)) == 0)
35725cf1a30Sjl 		drmach_fini();
35825cf1a30Sjl 
35925cf1a30Sjl 	return (err);
36025cf1a30Sjl }
36125cf1a30Sjl 
36225cf1a30Sjl int
36325cf1a30Sjl _info(struct modinfo *modinfop)
36425cf1a30Sjl {
36525cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
36625cf1a30Sjl }
36725cf1a30Sjl 
36825cf1a30Sjl struct drmach_mc_lookup {
36925cf1a30Sjl 	int	bnum;
37025cf1a30Sjl 	drmach_board_t	*bp;
37125cf1a30Sjl 	dev_info_t *dip;	/* rv - set if found */
37225cf1a30Sjl };
37325cf1a30Sjl 
37425cf1a30Sjl #define	_ptob64(p) ((uint64_t)(p) << PAGESHIFT)
37525cf1a30Sjl #define	_b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
37625cf1a30Sjl 
37725cf1a30Sjl static int
37825cf1a30Sjl drmach_setup_mc_info(dev_info_t *dip, drmach_mem_t *mp)
37925cf1a30Sjl {
38025cf1a30Sjl 	uint64_t	memory_ranges[128];
38125cf1a30Sjl 	int len;
38225cf1a30Sjl 	struct memlist	*ml;
38325cf1a30Sjl 	int rv;
38425cf1a30Sjl 	hwd_sb_t *hwd;
38525cf1a30Sjl 	hwd_memory_t *pm;
38625cf1a30Sjl 
38725cf1a30Sjl 	len = sizeof (memory_ranges);
388e98fafb9Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
389e98fafb9Sjl 	    "sb-mem-ranges", (caddr_t)&memory_ranges[0], &len) !=
390e98fafb9Sjl 	    DDI_PROP_SUCCESS) {
39125cf1a30Sjl 		mp->slice_base = 0;
39225cf1a30Sjl 		mp->slice_size = 0;
39325cf1a30Sjl 		return (-1);
39425cf1a30Sjl 	}
39525cf1a30Sjl 	mp->slice_base = memory_ranges[0];
39625cf1a30Sjl 	mp->slice_size = memory_ranges[1];
39725cf1a30Sjl 
39825cf1a30Sjl 	if (!mp->dev.bp->boot_board) {
39925cf1a30Sjl 		int i;
40025cf1a30Sjl 
40125cf1a30Sjl 		rv = opl_read_hwd(mp->dev.bp->bnum, NULL,  NULL, NULL, &hwd);
40225cf1a30Sjl 
40325cf1a30Sjl 		if (rv != 0) {
40425cf1a30Sjl 			return (-1);
40525cf1a30Sjl 		}
40625cf1a30Sjl 
40725cf1a30Sjl 		ml = NULL;
40825cf1a30Sjl 		pm = &hwd->sb_cmu.cmu_memory;
40925cf1a30Sjl 		for (i = 0; i < HWD_MAX_MEM_CHUNKS; i++) {
41025cf1a30Sjl 			if (pm->mem_chunks[i].chnk_size > 0) {
41125cf1a30Sjl 				ml = memlist_add_span(ml,
412e98fafb9Sjl 				    pm->mem_chunks[i].chnk_start_address,
413e98fafb9Sjl 				    pm->mem_chunks[i].chnk_size);
41425cf1a30Sjl 			}
41525cf1a30Sjl 		}
41625cf1a30Sjl 	} else {
41725cf1a30Sjl 		/*
41825cf1a30Sjl 		 * we intersect phys_install to get base_pa.
41925cf1a30Sjl 		 * This only works at bootup time.
42025cf1a30Sjl 		 */
42125cf1a30Sjl 
42225cf1a30Sjl 		memlist_read_lock();
42325cf1a30Sjl 		ml = memlist_dup(phys_install);
42425cf1a30Sjl 		memlist_read_unlock();
42525cf1a30Sjl 
42625cf1a30Sjl 		ml = memlist_del_span(ml, 0ull, mp->slice_base);
42725cf1a30Sjl 		if (ml) {
42825cf1a30Sjl 			uint64_t basepa, endpa;
42925cf1a30Sjl 			endpa = _ptob64(physmax + 1);
43025cf1a30Sjl 
43125cf1a30Sjl 			basepa = mp->slice_base + mp->slice_size;
43225cf1a30Sjl 
43325cf1a30Sjl 			ml = memlist_del_span(ml, basepa, endpa - basepa);
43425cf1a30Sjl 		}
43525cf1a30Sjl 	}
43625cf1a30Sjl 
43725cf1a30Sjl 	if (ml) {
43825cf1a30Sjl 		uint64_t nbytes = 0;
43925cf1a30Sjl 		struct memlist *p;
44056f33205SJonathan Adams 		for (p = ml; p; p = p->ml_next) {
44156f33205SJonathan Adams 			nbytes += p->ml_size;
44225cf1a30Sjl 		}
44325cf1a30Sjl 		if ((mp->nbytes = nbytes) > 0)
44456f33205SJonathan Adams 			mp->base_pa = ml->ml_address;
44525cf1a30Sjl 		else
44625cf1a30Sjl 			mp->base_pa = 0;
44725cf1a30Sjl 		mp->memlist = ml;
44825cf1a30Sjl 	} else {
44925cf1a30Sjl 		mp->base_pa = 0;
45025cf1a30Sjl 		mp->nbytes = 0;
45125cf1a30Sjl 	}
45225cf1a30Sjl 	return (0);
45325cf1a30Sjl }
45425cf1a30Sjl 
45525cf1a30Sjl 
45625cf1a30Sjl struct drmach_hotcpu {
45725cf1a30Sjl 	drmach_board_t *bp;
45825cf1a30Sjl 	int	bnum;
45925cf1a30Sjl 	int	core_id;
460*bbe1232eSToomas Soome 	int	rv;
46125cf1a30Sjl 	int	option;
46225cf1a30Sjl };
46325cf1a30Sjl 
46425cf1a30Sjl static int
46525cf1a30Sjl drmach_cpu_cb(dev_info_t *dip, void *arg)
46625cf1a30Sjl {
46725cf1a30Sjl 	struct drmach_hotcpu *p = (struct drmach_hotcpu *)arg;
46825cf1a30Sjl 	char name[OBP_MAXDRVNAME];
46925cf1a30Sjl 	int len = OBP_MAXDRVNAME;
47025cf1a30Sjl 	int bnum, core_id, strand_id;
47125cf1a30Sjl 	drmach_board_t *bp;
47225cf1a30Sjl 
47325cf1a30Sjl 	if (dip == ddi_root_node()) {
47425cf1a30Sjl 		return (DDI_WALK_CONTINUE);
47525cf1a30Sjl 	}
47625cf1a30Sjl 
47725cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
47825cf1a30Sjl 	    DDI_PROP_DONTPASS, "name",
47925cf1a30Sjl 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
48025cf1a30Sjl 		return (DDI_WALK_PRUNECHILD);
48125cf1a30Sjl 	}
48225cf1a30Sjl 
48325cf1a30Sjl 	/* only cmp has board number */
48425cf1a30Sjl 	bnum = -1;
48525cf1a30Sjl 	len = sizeof (bnum);
48625cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
48725cf1a30Sjl 	    DDI_PROP_DONTPASS, OBP_BOARDNUM,
48825cf1a30Sjl 	    (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
48925cf1a30Sjl 		bnum = -1;
49025cf1a30Sjl 	}
49125cf1a30Sjl 
49225cf1a30Sjl 	if (strcmp(name, "cmp") == 0) {
49325cf1a30Sjl 		if (bnum != p->bnum)
49425cf1a30Sjl 			return (DDI_WALK_PRUNECHILD);
49525cf1a30Sjl 		return (DDI_WALK_CONTINUE);
49625cf1a30Sjl 	}
49725cf1a30Sjl 	/* we have already pruned all unwanted cores and cpu's above */
49825cf1a30Sjl 	if (strcmp(name, "core") == 0) {
49925cf1a30Sjl 		return (DDI_WALK_CONTINUE);
50025cf1a30Sjl 	}
50125cf1a30Sjl 	if (strcmp(name, "cpu") == 0) {
50225cf1a30Sjl 		processorid_t cpuid;
50325cf1a30Sjl 		len = sizeof (cpuid);
50425cf1a30Sjl 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
50525cf1a30Sjl 		    DDI_PROP_DONTPASS, "cpuid",
50625cf1a30Sjl 		    (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
50725cf1a30Sjl 			p->rv = -1;
50825cf1a30Sjl 			return (DDI_WALK_TERMINATE);
50925cf1a30Sjl 		}
51025cf1a30Sjl 
51125cf1a30Sjl 		core_id = p->core_id;
51225cf1a30Sjl 
51325cf1a30Sjl 		bnum = LSB_ID(cpuid);
51425cf1a30Sjl 
51525cf1a30Sjl 		if (ON_BOARD_CORE_NUM(cpuid) != core_id)
51625cf1a30Sjl 			return (DDI_WALK_CONTINUE);
51725cf1a30Sjl 
51825cf1a30Sjl 		bp = p->bp;
51925cf1a30Sjl 		ASSERT(bnum == bp->bnum);
52025cf1a30Sjl 
52125cf1a30Sjl 		if (p->option == HOTADD_CPU) {
52225cf1a30Sjl 			if (prom_hotaddcpu(cpuid) != 0) {
52325cf1a30Sjl 				p->rv = -1;
52425cf1a30Sjl 				return (DDI_WALK_TERMINATE);
52525cf1a30Sjl 			}
52625cf1a30Sjl 			strand_id = STRAND_ID(cpuid);
52725cf1a30Sjl 			bp->cores[core_id].core_hotadded |= (1 << strand_id);
52825cf1a30Sjl 		} else if (p->option == HOTREMOVE_CPU) {
52925cf1a30Sjl 			if (prom_hotremovecpu(cpuid) != 0) {
53025cf1a30Sjl 				p->rv = -1;
53125cf1a30Sjl 				return (DDI_WALK_TERMINATE);
53225cf1a30Sjl 			}
53325cf1a30Sjl 			strand_id = STRAND_ID(cpuid);
53425cf1a30Sjl 			bp->cores[core_id].core_hotadded &= ~(1 << strand_id);
53525cf1a30Sjl 		}
53625cf1a30Sjl 		return (DDI_WALK_CONTINUE);
53725cf1a30Sjl 	}
53825cf1a30Sjl 
53925cf1a30Sjl 	return (DDI_WALK_PRUNECHILD);
54025cf1a30Sjl }
54125cf1a30Sjl 
54225cf1a30Sjl 
54325cf1a30Sjl static int
54425cf1a30Sjl drmach_add_remove_cpu(int bnum, int core_id, int option)
54525cf1a30Sjl {
54625cf1a30Sjl 	struct drmach_hotcpu arg;
54725cf1a30Sjl 	drmach_board_t *bp;
54825cf1a30Sjl 
54925cf1a30Sjl 	bp = drmach_get_board_by_bnum(bnum);
55025cf1a30Sjl 	ASSERT(bp);
55125cf1a30Sjl 
55225cf1a30Sjl 	arg.bp = bp;
55325cf1a30Sjl 	arg.bnum = bnum;
55425cf1a30Sjl 	arg.core_id = core_id;
55525cf1a30Sjl 	arg.rv = 0;
55625cf1a30Sjl 	arg.option = option;
55725cf1a30Sjl 	ddi_walk_devs(ddi_root_node(), drmach_cpu_cb, (void *)&arg);
55825cf1a30Sjl 	return (arg.rv);
55925cf1a30Sjl }
56025cf1a30Sjl 
56125cf1a30Sjl struct drmach_setup_core_arg {
56225cf1a30Sjl 	drmach_board_t *bp;
56325cf1a30Sjl };
56425cf1a30Sjl 
56525cf1a30Sjl static int
56625cf1a30Sjl drmach_setup_core_cb(dev_info_t *dip, void *arg)
56725cf1a30Sjl {
56825cf1a30Sjl 	struct drmach_setup_core_arg *p = (struct drmach_setup_core_arg *)arg;
56925cf1a30Sjl 	char name[OBP_MAXDRVNAME];
57025cf1a30Sjl 	int len = OBP_MAXDRVNAME;
57125cf1a30Sjl 	int bnum;
57225cf1a30Sjl 	int core_id, strand_id;
57325cf1a30Sjl 
57425cf1a30Sjl 	if (dip == ddi_root_node()) {
57525cf1a30Sjl 		return (DDI_WALK_CONTINUE);
57625cf1a30Sjl 	}
57725cf1a30Sjl 
57825cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
57925cf1a30Sjl 	    DDI_PROP_DONTPASS, "name",
58025cf1a30Sjl 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
58125cf1a30Sjl 		return (DDI_WALK_PRUNECHILD);
58225cf1a30Sjl 	}
58325cf1a30Sjl 
58425cf1a30Sjl 	/* only cmp has board number */
58525cf1a30Sjl 	bnum = -1;
58625cf1a30Sjl 	len = sizeof (bnum);
58725cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
58825cf1a30Sjl 	    DDI_PROP_DONTPASS, OBP_BOARDNUM,
58925cf1a30Sjl 	    (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
59025cf1a30Sjl 		bnum = -1;
59125cf1a30Sjl 	}
59225cf1a30Sjl 
59325cf1a30Sjl 	if (strcmp(name, "cmp") == 0) {
59425cf1a30Sjl 		if (bnum != p->bp->bnum)
59525cf1a30Sjl 			return (DDI_WALK_PRUNECHILD);
59625cf1a30Sjl 		return (DDI_WALK_CONTINUE);
59725cf1a30Sjl 	}
59825cf1a30Sjl 	/* we have already pruned all unwanted cores and cpu's above */
59925cf1a30Sjl 	if (strcmp(name, "core") == 0) {
60025cf1a30Sjl 		return (DDI_WALK_CONTINUE);
60125cf1a30Sjl 	}
60225cf1a30Sjl 	if (strcmp(name, "cpu") == 0) {
60325cf1a30Sjl 		processorid_t cpuid;
60425cf1a30Sjl 		len = sizeof (cpuid);
60525cf1a30Sjl 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
60625cf1a30Sjl 		    DDI_PROP_DONTPASS, "cpuid",
60725cf1a30Sjl 		    (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
60825cf1a30Sjl 			return (DDI_WALK_TERMINATE);
60925cf1a30Sjl 		}
61025cf1a30Sjl 		bnum = LSB_ID(cpuid);
61125cf1a30Sjl 		ASSERT(bnum == p->bp->bnum);
61225cf1a30Sjl 		core_id = ON_BOARD_CORE_NUM(cpuid);
61325cf1a30Sjl 		strand_id = STRAND_ID(cpuid);
61425cf1a30Sjl 		p->bp->cores[core_id].core_present |= (1 << strand_id);
61525cf1a30Sjl 		return (DDI_WALK_CONTINUE);
61625cf1a30Sjl 	}
61725cf1a30Sjl 
61825cf1a30Sjl 	return (DDI_WALK_PRUNECHILD);
61925cf1a30Sjl }
62025cf1a30Sjl 
62125cf1a30Sjl 
62225cf1a30Sjl static void
62325cf1a30Sjl drmach_setup_core_info(drmach_board_t *obj)
62425cf1a30Sjl {
62525cf1a30Sjl 	struct drmach_setup_core_arg arg;
62625cf1a30Sjl 	int i;
62725cf1a30Sjl 
62825cf1a30Sjl 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
62925cf1a30Sjl 		obj->cores[i].core_present = 0;
63025cf1a30Sjl 		obj->cores[i].core_hotadded = 0;
63125cf1a30Sjl 		obj->cores[i].core_started = 0;
63225cf1a30Sjl 	}
63325cf1a30Sjl 	arg.bp = obj;
63425cf1a30Sjl 	ddi_walk_devs(ddi_root_node(), drmach_setup_core_cb, (void *)&arg);
63525cf1a30Sjl 
63625cf1a30Sjl 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
63725cf1a30Sjl 		if (obj->boot_board) {
63825cf1a30Sjl 			obj->cores[i].core_hotadded =
639e98fafb9Sjl 			    obj->cores[i].core_started =
640e98fafb9Sjl 			    obj->cores[i].core_present;
64125cf1a30Sjl 		}
64225cf1a30Sjl 	}
64325cf1a30Sjl }
64425cf1a30Sjl 
64525cf1a30Sjl /*
64625cf1a30Sjl  * drmach_node_* routines serve the purpose of separating the
64725cf1a30Sjl  * rest of the code from the device tree and OBP.  This is necessary
64825cf1a30Sjl  * because of In-Kernel-Probing.  Devices probed after stod, are probed
64925cf1a30Sjl  * by the in-kernel-prober, not OBP.  These devices, therefore, do not
65025cf1a30Sjl  * have dnode ids.
65125cf1a30Sjl  */
65225cf1a30Sjl 
65325cf1a30Sjl typedef struct {
65425cf1a30Sjl 	drmach_node_walk_args_t	*nwargs;
655*bbe1232eSToomas Soome 	int			(*cb)(drmach_node_walk_args_t *args);
65625cf1a30Sjl 	int			err;
65725cf1a30Sjl } drmach_node_ddi_walk_args_t;
65825cf1a30Sjl 
65925cf1a30Sjl static int
66025cf1a30Sjl drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
66125cf1a30Sjl {
66225cf1a30Sjl 	drmach_node_ddi_walk_args_t	*nargs;
66325cf1a30Sjl 
66425cf1a30Sjl 	nargs = (drmach_node_ddi_walk_args_t *)arg;
66525cf1a30Sjl 
66625cf1a30Sjl 	/*
66725cf1a30Sjl 	 * dip doesn't have to be held here as we are called
66825cf1a30Sjl 	 * from ddi_walk_devs() which holds the dip.
66925cf1a30Sjl 	 */
67025cf1a30Sjl 	nargs->nwargs->node->here = (void *)dip;
67125cf1a30Sjl 
67225cf1a30Sjl 	nargs->err = nargs->cb(nargs->nwargs);
67325cf1a30Sjl 
67425cf1a30Sjl 
67525cf1a30Sjl 	/*
67625cf1a30Sjl 	 * Set "here" to NULL so that unheld dip is not accessible
67725cf1a30Sjl 	 * outside ddi_walk_devs()
67825cf1a30Sjl 	 */
67925cf1a30Sjl 	nargs->nwargs->node->here = NULL;
68025cf1a30Sjl 
68125cf1a30Sjl 	if (nargs->err)
68225cf1a30Sjl 		return (DDI_WALK_TERMINATE);
68325cf1a30Sjl 	else
68425cf1a30Sjl 		return (DDI_WALK_CONTINUE);
68525cf1a30Sjl }
68625cf1a30Sjl 
68725cf1a30Sjl static int
68825cf1a30Sjl drmach_node_ddi_walk(drmach_node_t *np, void *data,
689*bbe1232eSToomas Soome     int (*cb)(drmach_node_walk_args_t *args))
69025cf1a30Sjl {
69125cf1a30Sjl 	drmach_node_walk_args_t		args;
69225cf1a30Sjl 	drmach_node_ddi_walk_args_t	nargs;
69325cf1a30Sjl 
69425cf1a30Sjl 
69525cf1a30Sjl 	/* initialized args structure for callback */
69625cf1a30Sjl 	args.node = np;
69725cf1a30Sjl 	args.data = data;
69825cf1a30Sjl 
69925cf1a30Sjl 	nargs.nwargs = &args;
70025cf1a30Sjl 	nargs.cb = cb;
70125cf1a30Sjl 	nargs.err = 0;
70225cf1a30Sjl 
70325cf1a30Sjl 	/*
70425cf1a30Sjl 	 * Root node doesn't have to be held in any way.
70525cf1a30Sjl 	 */
706e98fafb9Sjl 	ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
70725cf1a30Sjl 
70825cf1a30Sjl 	return (nargs.err);
70925cf1a30Sjl }
71025cf1a30Sjl 
71125cf1a30Sjl static int
71225cf1a30Sjl drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
71325cf1a30Sjl {
71425cf1a30Sjl 	dev_info_t	*ndip;
71525cf1a30Sjl 	static char	*fn = "drmach_node_ddi_get_parent";
71625cf1a30Sjl 
71725cf1a30Sjl 	ndip = np->n_getdip(np);
71825cf1a30Sjl 	if (ndip == NULL) {
71925cf1a30Sjl 		cmn_err(CE_WARN, "%s: NULL dip", fn);
72025cf1a30Sjl 		return (-1);
72125cf1a30Sjl 	}
72225cf1a30Sjl 
72325cf1a30Sjl 	bcopy(np, pp, sizeof (drmach_node_t));
72425cf1a30Sjl 
72525cf1a30Sjl 	pp->here = (void *)ddi_get_parent(ndip);
72625cf1a30Sjl 	if (pp->here == NULL) {
72725cf1a30Sjl 		cmn_err(CE_WARN, "%s: NULL parent dip", fn);
72825cf1a30Sjl 		return (-1);
72925cf1a30Sjl 	}
73025cf1a30Sjl 
73125cf1a30Sjl 	return (0);
73225cf1a30Sjl }
73325cf1a30Sjl 
73425cf1a30Sjl /*ARGSUSED*/
73525cf1a30Sjl static pnode_t
73625cf1a30Sjl drmach_node_ddi_get_dnode(drmach_node_t *np)
73725cf1a30Sjl {
738*bbe1232eSToomas Soome 	return (0);
73925cf1a30Sjl }
74025cf1a30Sjl 
74125cf1a30Sjl static drmach_node_t *
74225cf1a30Sjl drmach_node_new(void)
74325cf1a30Sjl {
74425cf1a30Sjl 	drmach_node_t *np;
74525cf1a30Sjl 
74625cf1a30Sjl 	np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
74725cf1a30Sjl 
74825cf1a30Sjl 	np->get_dnode = drmach_node_ddi_get_dnode;
74925cf1a30Sjl 	np->walk = drmach_node_ddi_walk;
75025cf1a30Sjl 	np->n_getdip = drmach_node_ddi_get_dip;
75125cf1a30Sjl 	np->n_getproplen = drmach_node_ddi_get_proplen;
75225cf1a30Sjl 	np->n_getprop = drmach_node_ddi_get_prop;
75325cf1a30Sjl 	np->get_parent = drmach_node_ddi_get_parent;
75425cf1a30Sjl 
75525cf1a30Sjl 	return (np);
75625cf1a30Sjl }
75725cf1a30Sjl 
75825cf1a30Sjl static void
75925cf1a30Sjl drmach_node_dispose(drmach_node_t *np)
76025cf1a30Sjl {
76125cf1a30Sjl 	kmem_free(np, sizeof (*np));
76225cf1a30Sjl }
76325cf1a30Sjl 
76425cf1a30Sjl static dev_info_t *
76525cf1a30Sjl drmach_node_ddi_get_dip(drmach_node_t *np)
76625cf1a30Sjl {
76725cf1a30Sjl 	return ((dev_info_t *)np->here);
76825cf1a30Sjl }
76925cf1a30Sjl 
77025cf1a30Sjl static int
77125cf1a30Sjl drmach_node_walk(drmach_node_t *np, void *param,
772*bbe1232eSToomas Soome     int (*cb)(drmach_node_walk_args_t *args))
77325cf1a30Sjl {
77425cf1a30Sjl 	return (np->walk(np, param, cb));
77525cf1a30Sjl }
77625cf1a30Sjl 
77725cf1a30Sjl static int
77825cf1a30Sjl drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
77925cf1a30Sjl {
78025cf1a30Sjl 	int		rv = 0;
78125cf1a30Sjl 	dev_info_t	*ndip;
78225cf1a30Sjl 	static char	*fn = "drmach_node_ddi_get_prop";
78325cf1a30Sjl 
78425cf1a30Sjl 
78525cf1a30Sjl 	ndip = np->n_getdip(np);
78625cf1a30Sjl 	if (ndip == NULL) {
78725cf1a30Sjl 		cmn_err(CE_WARN, "%s: NULL dip", fn);
78825cf1a30Sjl 		rv = -1;
78925cf1a30Sjl 	} else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
79025cf1a30Sjl 	    DDI_PROP_DONTPASS, name,
79125cf1a30Sjl 	    (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
79225cf1a30Sjl 		rv = -1;
79325cf1a30Sjl 	}
79425cf1a30Sjl 
79525cf1a30Sjl 	return (rv);
79625cf1a30Sjl }
79725cf1a30Sjl 
79825cf1a30Sjl static int
79925cf1a30Sjl drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
80025cf1a30Sjl {
80125cf1a30Sjl 	int		rv = 0;
80225cf1a30Sjl 	dev_info_t	*ndip;
80325cf1a30Sjl 
80425cf1a30Sjl 	ndip = np->n_getdip(np);
80525cf1a30Sjl 	if (ndip == NULL) {
80625cf1a30Sjl 		rv = -1;
807e98fafb9Sjl 	} else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS, name,
808e98fafb9Sjl 	    len) != DDI_PROP_SUCCESS) {
80925cf1a30Sjl 		rv = -1;
81025cf1a30Sjl 	}
81125cf1a30Sjl 
81225cf1a30Sjl 	return (rv);
81325cf1a30Sjl }
81425cf1a30Sjl 
81525cf1a30Sjl static drmachid_t
81625cf1a30Sjl drmach_node_dup(drmach_node_t *np)
81725cf1a30Sjl {
81825cf1a30Sjl 	drmach_node_t *dup;
81925cf1a30Sjl 
82025cf1a30Sjl 	dup = drmach_node_new();
82125cf1a30Sjl 	dup->here = np->here;
82225cf1a30Sjl 	dup->get_dnode = np->get_dnode;
82325cf1a30Sjl 	dup->walk = np->walk;
82425cf1a30Sjl 	dup->n_getdip = np->n_getdip;
82525cf1a30Sjl 	dup->n_getproplen = np->n_getproplen;
82625cf1a30Sjl 	dup->n_getprop = np->n_getprop;
82725cf1a30Sjl 	dup->get_parent = np->get_parent;
82825cf1a30Sjl 
82925cf1a30Sjl 	return (dup);
83025cf1a30Sjl }
83125cf1a30Sjl 
83225cf1a30Sjl /*
83325cf1a30Sjl  * drmach_array provides convenient array construction, access,
83425cf1a30Sjl  * bounds checking and array destruction logic.
83525cf1a30Sjl  */
83625cf1a30Sjl 
83725cf1a30Sjl static drmach_array_t *
83825cf1a30Sjl drmach_array_new(int min_index, int max_index)
83925cf1a30Sjl {
84025cf1a30Sjl 	drmach_array_t *arr;
84125cf1a30Sjl 
84225cf1a30Sjl 	arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
84325cf1a30Sjl 
84425cf1a30Sjl 	arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
84525cf1a30Sjl 	if (arr->arr_sz > 0) {
84625cf1a30Sjl 		arr->min_index = min_index;
84725cf1a30Sjl 		arr->max_index = max_index;
84825cf1a30Sjl 
84925cf1a30Sjl 		arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
85025cf1a30Sjl 		return (arr);
85125cf1a30Sjl 	} else {
85225cf1a30Sjl 		kmem_free(arr, sizeof (*arr));
85325cf1a30Sjl 		return (0);
85425cf1a30Sjl 	}
85525cf1a30Sjl }
85625cf1a30Sjl 
85725cf1a30Sjl static int
85825cf1a30Sjl drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
85925cf1a30Sjl {
86025cf1a30Sjl 	if (idx < arr->min_index || idx > arr->max_index)
86125cf1a30Sjl 		return (-1);
86225cf1a30Sjl 	else {
86325cf1a30Sjl 		arr->arr[idx - arr->min_index] = val;
86425cf1a30Sjl 		return (0);
86525cf1a30Sjl 	}
86625cf1a30Sjl 	/*NOTREACHED*/
86725cf1a30Sjl }
86825cf1a30Sjl 
86925cf1a30Sjl static int
87025cf1a30Sjl drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
87125cf1a30Sjl {
87225cf1a30Sjl 	if (idx < arr->min_index || idx > arr->max_index)
87325cf1a30Sjl 		return (-1);
87425cf1a30Sjl 	else {
87525cf1a30Sjl 		*val = arr->arr[idx - arr->min_index];
87625cf1a30Sjl 		return (0);
87725cf1a30Sjl 	}
87825cf1a30Sjl 	/*NOTREACHED*/
87925cf1a30Sjl }
88025cf1a30Sjl 
88125cf1a30Sjl static int
88225cf1a30Sjl drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
88325cf1a30Sjl {
88425cf1a30Sjl 	int rv;
88525cf1a30Sjl 
88625cf1a30Sjl 	*idx = arr->min_index;
88725cf1a30Sjl 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
88825cf1a30Sjl 		*idx += 1;
88925cf1a30Sjl 
89025cf1a30Sjl 	return (rv);
89125cf1a30Sjl }
89225cf1a30Sjl 
89325cf1a30Sjl static int
89425cf1a30Sjl drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
89525cf1a30Sjl {
89625cf1a30Sjl 	int rv;
89725cf1a30Sjl 
89825cf1a30Sjl 	*idx += 1;
89925cf1a30Sjl 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
90025cf1a30Sjl 		*idx += 1;
90125cf1a30Sjl 
90225cf1a30Sjl 	return (rv);
90325cf1a30Sjl }
90425cf1a30Sjl 
90525cf1a30Sjl static void
90625cf1a30Sjl drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
90725cf1a30Sjl {
90825cf1a30Sjl 	drmachid_t	val;
90925cf1a30Sjl 	int		idx;
91025cf1a30Sjl 	int		rv;
91125cf1a30Sjl 
91225cf1a30Sjl 	rv = drmach_array_first(arr, &idx, &val);
91325cf1a30Sjl 	while (rv == 0) {
91425cf1a30Sjl 		(*disposer)(val);
91525cf1a30Sjl 		rv = drmach_array_next(arr, &idx, &val);
91625cf1a30Sjl 	}
91725cf1a30Sjl 
91825cf1a30Sjl 	kmem_free(arr->arr, arr->arr_sz);
91925cf1a30Sjl 	kmem_free(arr, sizeof (*arr));
92025cf1a30Sjl }
92125cf1a30Sjl 
92225cf1a30Sjl static drmach_board_t *
92325cf1a30Sjl drmach_get_board_by_bnum(int bnum)
92425cf1a30Sjl {
92525cf1a30Sjl 	drmachid_t id;
92625cf1a30Sjl 
92725cf1a30Sjl 	if (drmach_array_get(drmach_boards, bnum, &id) == 0)
92825cf1a30Sjl 		return ((drmach_board_t *)id);
92925cf1a30Sjl 	else
93025cf1a30Sjl 		return (NULL);
93125cf1a30Sjl }
93225cf1a30Sjl 
93325cf1a30Sjl static pnode_t
93425cf1a30Sjl drmach_node_get_dnode(drmach_node_t *np)
93525cf1a30Sjl {
93625cf1a30Sjl 	return (np->get_dnode(np));
93725cf1a30Sjl }
93825cf1a30Sjl 
93925cf1a30Sjl /*ARGSUSED*/
94025cf1a30Sjl sbd_error_t *
94125cf1a30Sjl drmach_configure(drmachid_t id, int flags)
94225cf1a30Sjl {
94325cf1a30Sjl 	drmach_device_t		*dp;
94425cf1a30Sjl 	sbd_error_t		*err = NULL;
94525cf1a30Sjl 	dev_info_t		*rdip;
94625cf1a30Sjl 	dev_info_t		*fdip = NULL;
94725cf1a30Sjl 
94825cf1a30Sjl 	if (DRMACH_IS_CPU_ID(id)) {
94925cf1a30Sjl 		return (NULL);
95025cf1a30Sjl 	}
95125cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
95225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
95325cf1a30Sjl 	dp = id;
95425cf1a30Sjl 	rdip = dp->node->n_getdip(dp->node);
95525cf1a30Sjl 
95625cf1a30Sjl 	ASSERT(rdip);
95725cf1a30Sjl 
95825cf1a30Sjl 	ASSERT(e_ddi_branch_held(rdip));
95925cf1a30Sjl 
96025cf1a30Sjl 	if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
96125cf1a30Sjl 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
96225cf1a30Sjl 		dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
96325cf1a30Sjl 
96425cf1a30Sjl 		(void) ddi_pathname(dip, path);
96525cf1a30Sjl 		err = drerr_new(1,  EOPL_DRVFAIL, path);
96625cf1a30Sjl 
96725cf1a30Sjl 		kmem_free(path, MAXPATHLEN);
96825cf1a30Sjl 
96925cf1a30Sjl 		/* If non-NULL, fdip is returned held and must be released */
97025cf1a30Sjl 		if (fdip != NULL)
97125cf1a30Sjl 			ddi_release_devi(fdip);
97225cf1a30Sjl 	}
97325cf1a30Sjl 
97425cf1a30Sjl 	return (err);
97525cf1a30Sjl }
97625cf1a30Sjl 
97725cf1a30Sjl 
97825cf1a30Sjl static sbd_error_t *
97925cf1a30Sjl drmach_device_new(drmach_node_t *node,
980*bbe1232eSToomas Soome     drmach_board_t *bp, int portid, drmachid_t *idp)
98125cf1a30Sjl {
98225cf1a30Sjl 	int		 i;
98325cf1a30Sjl 	int		 rv;
98425cf1a30Sjl 	drmach_device_t	proto;
98525cf1a30Sjl 	sbd_error_t	*err;
98625cf1a30Sjl 	char		 name[OBP_MAXDRVNAME];
98725cf1a30Sjl 
98825cf1a30Sjl 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
98925cf1a30Sjl 	if (rv) {
99025cf1a30Sjl 		/* every node is expected to have a name */
991e98fafb9Sjl 		err = drerr_new(1, EOPL_GETPROP, "device node %s: property %s",
992e98fafb9Sjl 		    ddi_node_name(node->n_getdip(node)), "name");
99325cf1a30Sjl 		return (err);
99425cf1a30Sjl 	}
99525cf1a30Sjl 
99625cf1a30Sjl 	/*
99725cf1a30Sjl 	 * The node currently being examined is not listed in the name2type[]
99825cf1a30Sjl 	 * array.  In this case, the node is no interest to drmach.  Both
99925cf1a30Sjl 	 * dp and err are initialized here to yield nothing (no device or
100025cf1a30Sjl 	 * error structure) for this case.
100125cf1a30Sjl 	 */
100225cf1a30Sjl 	i = drmach_name2type_idx(name);
100325cf1a30Sjl 
100425cf1a30Sjl 
100525cf1a30Sjl 	if (i < 0) {
100625cf1a30Sjl 		*idp = (drmachid_t)0;
100725cf1a30Sjl 		return (NULL);
100825cf1a30Sjl 	}
100925cf1a30Sjl 
101025cf1a30Sjl 	/* device specific new function will set unum */
101125cf1a30Sjl 
101225cf1a30Sjl 	bzero(&proto, sizeof (proto));
101325cf1a30Sjl 	proto.type = drmach_name2type[i].type;
101425cf1a30Sjl 	proto.bp = bp;
101525cf1a30Sjl 	proto.node = node;
101625cf1a30Sjl 	proto.portid = portid;
101725cf1a30Sjl 
101825cf1a30Sjl 	return (drmach_name2type[i].new(&proto, idp));
101925cf1a30Sjl }
102025cf1a30Sjl 
102125cf1a30Sjl static void
102225cf1a30Sjl drmach_device_dispose(drmachid_t id)
102325cf1a30Sjl {
102425cf1a30Sjl 	drmach_device_t *self = id;
102525cf1a30Sjl 
102625cf1a30Sjl 	self->cm.dispose(id);
102725cf1a30Sjl }
102825cf1a30Sjl 
102925cf1a30Sjl 
103025cf1a30Sjl static drmach_board_t *
103125cf1a30Sjl drmach_board_new(int bnum, int boot_board)
103225cf1a30Sjl {
103325cf1a30Sjl 	drmach_board_t	*bp;
103425cf1a30Sjl 
103525cf1a30Sjl 	bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
103625cf1a30Sjl 
103725cf1a30Sjl 	bp->cm.isa = (void *)drmach_board_new;
103825cf1a30Sjl 	bp->cm.release = drmach_board_release;
103925cf1a30Sjl 	bp->cm.status = drmach_board_status;
104025cf1a30Sjl 
104125cf1a30Sjl 	(void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
104225cf1a30Sjl 
104325cf1a30Sjl 	bp->bnum = bnum;
104425cf1a30Sjl 	bp->devices = NULL;
104525cf1a30Sjl 	bp->connected = boot_board;
104625cf1a30Sjl 	bp->tree = drmach_node_new();
104725cf1a30Sjl 	bp->assigned = boot_board;
104825cf1a30Sjl 	bp->powered = boot_board;
104925cf1a30Sjl 	bp->boot_board = boot_board;
105025cf1a30Sjl 
105125cf1a30Sjl 	/*
105225cf1a30Sjl 	 * If this is not bootup initialization, we have to wait till
105325cf1a30Sjl 	 * IKP sets up the device nodes in drmach_board_connect().
105425cf1a30Sjl 	 */
105525cf1a30Sjl 	if (boot_board)
105625cf1a30Sjl 		drmach_setup_core_info(bp);
105725cf1a30Sjl 
105807d06da5SSurya Prakki 	(void) drmach_array_set(drmach_boards, bnum, bp);
105925cf1a30Sjl 	return (bp);
106025cf1a30Sjl }
106125cf1a30Sjl 
106225cf1a30Sjl static void
106325cf1a30Sjl drmach_board_dispose(drmachid_t id)
106425cf1a30Sjl {
106525cf1a30Sjl 	drmach_board_t *bp;
106625cf1a30Sjl 
106725cf1a30Sjl 	ASSERT(DRMACH_IS_BOARD_ID(id));
106825cf1a30Sjl 	bp = id;
106925cf1a30Sjl 
107025cf1a30Sjl 	if (bp->tree)
107125cf1a30Sjl 		drmach_node_dispose(bp->tree);
107225cf1a30Sjl 
107325cf1a30Sjl 	if (bp->devices)
107425cf1a30Sjl 		drmach_array_dispose(bp->devices, drmach_device_dispose);
107525cf1a30Sjl 
107625cf1a30Sjl 	kmem_free(bp, sizeof (*bp));
107725cf1a30Sjl }
107825cf1a30Sjl 
107925cf1a30Sjl static sbd_error_t *
108025cf1a30Sjl drmach_board_status(drmachid_t id, drmach_status_t *stat)
108125cf1a30Sjl {
108225cf1a30Sjl 	sbd_error_t	*err = NULL;
108325cf1a30Sjl 	drmach_board_t	*bp;
108425cf1a30Sjl 
108525cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
108625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
108725cf1a30Sjl 	bp = id;
108825cf1a30Sjl 
108925cf1a30Sjl 	stat->assigned = bp->assigned;
109025cf1a30Sjl 	stat->powered = bp->powered;
109125cf1a30Sjl 	stat->busy = 0;			/* assume not busy */
109225cf1a30Sjl 	stat->configured = 0;		/* assume not configured */
109325cf1a30Sjl 	stat->empty = 0;
109425cf1a30Sjl 	stat->cond = bp->cond = SBD_COND_OK;
109507d06da5SSurya Prakki 	(void) strncpy(stat->type, "System Brd", sizeof (stat->type));
109625cf1a30Sjl 	stat->info[0] = '\0';
109725cf1a30Sjl 
109825cf1a30Sjl 	if (bp->devices) {
109925cf1a30Sjl 		int		 rv;
110025cf1a30Sjl 		int		 d_idx;
110125cf1a30Sjl 		drmachid_t	 d_id;
110225cf1a30Sjl 
110325cf1a30Sjl 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
110425cf1a30Sjl 		while (rv == 0) {
110525cf1a30Sjl 			drmach_status_t	d_stat;
110625cf1a30Sjl 
110725cf1a30Sjl 			err = drmach_i_status(d_id, &d_stat);
110825cf1a30Sjl 			if (err)
110925cf1a30Sjl 				break;
111025cf1a30Sjl 
111125cf1a30Sjl 			stat->busy |= d_stat.busy;
111225cf1a30Sjl 			stat->configured |= d_stat.configured;
111325cf1a30Sjl 
111425cf1a30Sjl 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
111525cf1a30Sjl 		}
111625cf1a30Sjl 	}
111725cf1a30Sjl 
111825cf1a30Sjl 	return (err);
111925cf1a30Sjl }
112025cf1a30Sjl 
112125cf1a30Sjl int
112225cf1a30Sjl drmach_board_is_floating(drmachid_t id)
112325cf1a30Sjl {
112425cf1a30Sjl 	drmach_board_t *bp;
112525cf1a30Sjl 
112625cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
112725cf1a30Sjl 		return (0);
112825cf1a30Sjl 
112925cf1a30Sjl 	bp = (drmach_board_t *)id;
113025cf1a30Sjl 
113125cf1a30Sjl 	return ((drmach_domain.floating & (1 << bp->bnum)) ? 1 : 0);
113225cf1a30Sjl }
113325cf1a30Sjl 
113425cf1a30Sjl static int
113525cf1a30Sjl drmach_init(void)
113625cf1a30Sjl {
113725cf1a30Sjl 	dev_info_t	*rdip;
113825cf1a30Sjl 	int		i, rv, len;
113925cf1a30Sjl 	int		*floating;
114025cf1a30Sjl 
114125cf1a30Sjl 	rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
114225cf1a30Sjl 
114325cf1a30Sjl 	drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
114425cf1a30Sjl 
114525cf1a30Sjl 	rdip = ddi_root_node();
114625cf1a30Sjl 
114725cf1a30Sjl 	if (ddi_getproplen(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1148e98fafb9Sjl 	    "floating-boards", &len) != DDI_PROP_SUCCESS) {
114925cf1a30Sjl 		cmn_err(CE_WARN, "Cannot get floating-boards proplen\n");
115025cf1a30Sjl 	} else {
115125cf1a30Sjl 		floating = (int *)kmem_alloc(len, KM_SLEEP);
1152e98fafb9Sjl 		rv = ddi_prop_op(DDI_DEV_T_ANY, rdip, PROP_LEN_AND_VAL_BUF,
1153e98fafb9Sjl 		    DDI_PROP_DONTPASS, "floating-boards", (caddr_t)floating,
1154e98fafb9Sjl 		    &len);
115525cf1a30Sjl 		if (rv != DDI_PROP_SUCCESS) {
115625cf1a30Sjl 			cmn_err(CE_WARN, "Cannot get floating-boards prop\n");
115725cf1a30Sjl 		} else {
115825cf1a30Sjl 			drmach_domain.floating = 0;
115925cf1a30Sjl 			for (i = 0; i < len / sizeof (int); i++) {
116025cf1a30Sjl 				drmach_domain.floating |= (1 << floating[i]);
116125cf1a30Sjl 			}
116225cf1a30Sjl 		}
116325cf1a30Sjl 		kmem_free(floating, len);
116425cf1a30Sjl 	}
116525cf1a30Sjl 	drmach_domain.allow_dr = opl_check_dr_status();
116625cf1a30Sjl 
116725cf1a30Sjl 	rdip = ddi_get_child(ddi_root_node());
116825cf1a30Sjl 	do {
116925cf1a30Sjl 		int		 bnum;
117025cf1a30Sjl 		drmachid_t	 id;
117125cf1a30Sjl 
117225cf1a30Sjl 		bnum = -1;
1173e98fafb9Sjl 		bnum = ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1174e98fafb9Sjl 		    OBP_BOARDNUM, -1);
117525cf1a30Sjl 		if (bnum == -1)
117625cf1a30Sjl 			continue;
117725cf1a30Sjl 
117825cf1a30Sjl 		if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
1179e98fafb9Sjl 			cmn_err(CE_WARN, "Device node 0x%p has invalid "
118007d06da5SSurya Prakki 			    "property value, %s=%d", (void *)rdip,
118107d06da5SSurya Prakki 			    OBP_BOARDNUM, bnum);
118225cf1a30Sjl 			goto error;
118325cf1a30Sjl 		} else if (id == NULL) {
118425cf1a30Sjl 			(void) drmach_board_new(bnum, 1);
118525cf1a30Sjl 		}
118625cf1a30Sjl 	} while ((rdip = ddi_get_next_sibling(rdip)) != NULL);
118725cf1a30Sjl 
118825cf1a30Sjl 	opl_hold_devtree();
118925cf1a30Sjl 
119025cf1a30Sjl 	/*
119125cf1a30Sjl 	 * Initialize the IKP feature.
119225cf1a30Sjl 	 *
119325cf1a30Sjl 	 * This can be done only after DR has acquired a hold on all the
119425cf1a30Sjl 	 * device nodes that are interesting to IKP.
119525cf1a30Sjl 	 */
119625cf1a30Sjl 	if (opl_init_cfg() != 0) {
119725cf1a30Sjl 		cmn_err(CE_WARN, "DR - IKP initialization failed");
119825cf1a30Sjl 
119925cf1a30Sjl 		opl_release_devtree();
120025cf1a30Sjl 
120125cf1a30Sjl 		goto error;
120225cf1a30Sjl 	}
120325cf1a30Sjl 
120425cf1a30Sjl 	return (0);
120525cf1a30Sjl error:
120625cf1a30Sjl 	drmach_array_dispose(drmach_boards, drmach_board_dispose);
120725cf1a30Sjl 	rw_destroy(&drmach_boards_rwlock);
120825cf1a30Sjl 	return (ENXIO);
120925cf1a30Sjl }
121025cf1a30Sjl 
121125cf1a30Sjl static void
121225cf1a30Sjl drmach_fini(void)
121325cf1a30Sjl {
121425cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
121525cf1a30Sjl 	drmach_array_dispose(drmach_boards, drmach_board_dispose);
121625cf1a30Sjl 	drmach_boards = NULL;
121725cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
121825cf1a30Sjl 
121925cf1a30Sjl 	/*
122025cf1a30Sjl 	 * Walk immediate children of the root devinfo node
122125cf1a30Sjl 	 * releasing holds acquired on branches in drmach_init()
122225cf1a30Sjl 	 */
122325cf1a30Sjl 
122425cf1a30Sjl 	opl_release_devtree();
122525cf1a30Sjl 
122625cf1a30Sjl 	rw_destroy(&drmach_boards_rwlock);
122725cf1a30Sjl }
122825cf1a30Sjl 
122925cf1a30Sjl /*
123025cf1a30Sjl  *	Each system board contains 2 Oberon PCI bridge and
123125cf1a30Sjl  *	1 CMUCH.
123225cf1a30Sjl  *	Each oberon has 2 channels.
123325cf1a30Sjl  *	Each channel has 2 pci-ex leaf.
123425cf1a30Sjl  *	Each CMUCH has 1 pci bus.
123525cf1a30Sjl  *
123625cf1a30Sjl  *
123725cf1a30Sjl  *	Device Path:
123825cf1a30Sjl  *	/pci@<portid>,reg
123925cf1a30Sjl  *
124025cf1a30Sjl  *	where
124125cf1a30Sjl  *	portid[10] = 0
124225cf1a30Sjl  *	portid[9:0] = LLEAF_ID[9:0] of the Oberon Channel
124325cf1a30Sjl  *
124425cf1a30Sjl  *	LLEAF_ID[9:8] = 0
124525cf1a30Sjl  *	LLEAF_ID[8:4] = LSB_ID[4:0]
124625cf1a30Sjl  *	LLEAF_ID[3:1] = IO Channel#[2:0] (0,1,2,3 for Oberon)
124725cf1a30Sjl  *			channel 4 is pcicmu
124825cf1a30Sjl  *	LLEAF_ID[0] = PCI Leaf Number (0 for leaf-A, 1 for leaf-B)
124925cf1a30Sjl  *
125025cf1a30Sjl  *	Properties:
125125cf1a30Sjl  *	name = pci
125225cf1a30Sjl  *	device_type = "pciex"
125325cf1a30Sjl  *	board# = LSBID
125425cf1a30Sjl  *	reg = int32 * 2, Oberon CSR space of the leaf and the UBC space
125525cf1a30Sjl  *	portid = Jupiter Bus Device ID ((LSB_ID << 3)|pciport#)
125625cf1a30Sjl  */
125725cf1a30Sjl 
125825cf1a30Sjl static sbd_error_t *
125925cf1a30Sjl drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
126025cf1a30Sjl {
126125cf1a30Sjl 	drmach_io_t	*ip;
126225cf1a30Sjl 
126325cf1a30Sjl 	int		 portid;
126425cf1a30Sjl 
126525cf1a30Sjl 	portid = proto->portid;
126625cf1a30Sjl 	ASSERT(portid != -1);
126725cf1a30Sjl 	proto->unum = portid & (MAX_IO_UNITS_PER_BOARD - 1);
126825cf1a30Sjl 
126925cf1a30Sjl 	ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
127025cf1a30Sjl 	bcopy(proto, &ip->dev, sizeof (ip->dev));
127125cf1a30Sjl 	ip->dev.node = drmach_node_dup(proto->node);
127225cf1a30Sjl 	ip->dev.cm.isa = (void *)drmach_io_new;
127325cf1a30Sjl 	ip->dev.cm.dispose = drmach_io_dispose;
127425cf1a30Sjl 	ip->dev.cm.release = drmach_io_release;
127525cf1a30Sjl 	ip->dev.cm.status = drmach_io_status;
127625cf1a30Sjl 	ip->channel = (portid >> 1) & 0x7;
127725cf1a30Sjl 	ip->leaf = (portid & 0x1);
127825cf1a30Sjl 
127907d06da5SSurya Prakki 	(void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
1280e98fafb9Sjl 	    ip->dev.type, ip->dev.unum);
128125cf1a30Sjl 
128225cf1a30Sjl 	*idp = (drmachid_t)ip;
128325cf1a30Sjl 	return (NULL);
128425cf1a30Sjl }
128525cf1a30Sjl 
128625cf1a30Sjl 
128725cf1a30Sjl static void
128825cf1a30Sjl drmach_io_dispose(drmachid_t id)
128925cf1a30Sjl {
129025cf1a30Sjl 	drmach_io_t *self;
129125cf1a30Sjl 
129225cf1a30Sjl 	ASSERT(DRMACH_IS_IO_ID(id));
129325cf1a30Sjl 
129425cf1a30Sjl 	self = id;
129525cf1a30Sjl 	if (self->dev.node)
129625cf1a30Sjl 		drmach_node_dispose(self->dev.node);
129725cf1a30Sjl 
129825cf1a30Sjl 	kmem_free(self, sizeof (*self));
129925cf1a30Sjl }
130025cf1a30Sjl 
130125cf1a30Sjl /*ARGSUSED*/
130225cf1a30Sjl sbd_error_t *
130325cf1a30Sjl drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
130425cf1a30Sjl {
130525cf1a30Sjl 	drmach_board_t	*bp = (drmach_board_t *)id;
130625cf1a30Sjl 	sbd_error_t	*err = NULL;
130725cf1a30Sjl 
130825cf1a30Sjl 	/* allow status and ncm operations to always succeed */
130925cf1a30Sjl 	if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) {
131025cf1a30Sjl 		return (NULL);
131125cf1a30Sjl 	}
131225cf1a30Sjl 
131325cf1a30Sjl 	/* check all other commands for the required option string */
131425cf1a30Sjl 
131525cf1a30Sjl 	if ((opts->size > 0) && (opts->copts != NULL)) {
131625cf1a30Sjl 
131725cf1a30Sjl 		DRMACH_PR("platform options: %s\n", opts->copts);
131825cf1a30Sjl 
131925cf1a30Sjl 		if (strstr(opts->copts, "opldr") == NULL) {
132025cf1a30Sjl 			err = drerr_new(1, EOPL_SUPPORT, NULL);
132125cf1a30Sjl 		}
132225cf1a30Sjl 	} else {
132325cf1a30Sjl 		err = drerr_new(1, EOPL_SUPPORT, NULL);
132425cf1a30Sjl 	}
132525cf1a30Sjl 
132625cf1a30Sjl 	if (!err && id && DRMACH_IS_BOARD_ID(id)) {
132725cf1a30Sjl 		switch (cmd) {
132825cf1a30Sjl 			case SBD_CMD_TEST:
132925cf1a30Sjl 			case SBD_CMD_STATUS:
133025cf1a30Sjl 			case SBD_CMD_GETNCM:
133125cf1a30Sjl 				break;
133225cf1a30Sjl 			case SBD_CMD_CONNECT:
133325cf1a30Sjl 				if (bp->connected)
133425cf1a30Sjl 					err = drerr_new(0, ESBD_STATE, NULL);
133525cf1a30Sjl 				else if (!drmach_domain.allow_dr)
1336e98fafb9Sjl 					err = drerr_new(1, EOPL_SUPPORT, NULL);
133725cf1a30Sjl 				break;
133825cf1a30Sjl 			case SBD_CMD_DISCONNECT:
133925cf1a30Sjl 				if (!bp->connected)
134025cf1a30Sjl 					err = drerr_new(0, ESBD_STATE, NULL);
134125cf1a30Sjl 				else if (!drmach_domain.allow_dr)
1342e98fafb9Sjl 					err = drerr_new(1, EOPL_SUPPORT, NULL);
134325cf1a30Sjl 				break;
134425cf1a30Sjl 			default:
134525cf1a30Sjl 				if (!drmach_domain.allow_dr)
1346e98fafb9Sjl 					err = drerr_new(1, EOPL_SUPPORT, NULL);
134725cf1a30Sjl 				break;
134825cf1a30Sjl 
134925cf1a30Sjl 		}
135025cf1a30Sjl 	}
135125cf1a30Sjl 
135225cf1a30Sjl 	return (err);
135325cf1a30Sjl }
135425cf1a30Sjl 
135525cf1a30Sjl /*ARGSUSED*/
135625cf1a30Sjl sbd_error_t *
135725cf1a30Sjl drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
135825cf1a30Sjl {
135925cf1a30Sjl 	return (NULL);
136025cf1a30Sjl }
136125cf1a30Sjl 
136225cf1a30Sjl sbd_error_t *
136325cf1a30Sjl drmach_board_assign(int bnum, drmachid_t *id)
136425cf1a30Sjl {
136525cf1a30Sjl 	sbd_error_t	*err = NULL;
136625cf1a30Sjl 
136725cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
136825cf1a30Sjl 
136925cf1a30Sjl 	if (drmach_array_get(drmach_boards, bnum, id) == -1) {
137025cf1a30Sjl 		err = drerr_new(1, EOPL_BNUM, "%d", bnum);
137125cf1a30Sjl 	} else {
137225cf1a30Sjl 		drmach_board_t	*bp;
137325cf1a30Sjl 
137425cf1a30Sjl 		if (*id)
137525cf1a30Sjl 			rw_downgrade(&drmach_boards_rwlock);
137625cf1a30Sjl 
137725cf1a30Sjl 		bp = *id;
137825cf1a30Sjl 		if (!(*id))
137925cf1a30Sjl 			bp = *id  =
1380e98fafb9Sjl 			    (drmachid_t)drmach_board_new(bnum, 0);
138125cf1a30Sjl 		bp->assigned = 1;
138225cf1a30Sjl 	}
138325cf1a30Sjl 
138425cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
138525cf1a30Sjl 
138625cf1a30Sjl 	return (err);
138725cf1a30Sjl }
138825cf1a30Sjl 
138925cf1a30Sjl /*ARGSUSED*/
139025cf1a30Sjl sbd_error_t *
139125cf1a30Sjl drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
139225cf1a30Sjl {
1393e98fafb9Sjl 	extern int	cpu_alljupiter;
139425cf1a30Sjl 	drmach_board_t	*obj = (drmach_board_t *)id;
1395e98fafb9Sjl 	unsigned	cpu_impl;
139625cf1a30Sjl 
139725cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
139825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
139925cf1a30Sjl 
1400e98fafb9Sjl 	if (opl_probe_sb(obj->bnum, &cpu_impl) != 0)
14018eafe49bSbm 		return (drerr_new(1, EOPL_PROBE, NULL));
140225cf1a30Sjl 
1403e98fafb9Sjl 	if (cpu_alljupiter) {
14048a040953Swh 		if (cpu_impl & (1 << OLYMPUS_C_IMPL)) {
14058a040953Swh 			(void) opl_unprobe_sb(obj->bnum);
1406e98fafb9Sjl 			return (drerr_new(1, EOPL_MIXED_CPU, NULL));
14078a040953Swh 		}
1408e98fafb9Sjl 	}
1409e98fafb9Sjl 
141025cf1a30Sjl 	(void) prom_attach_notice(obj->bnum);
141125cf1a30Sjl 
141225cf1a30Sjl 	drmach_setup_core_info(obj);
141325cf1a30Sjl 
141425cf1a30Sjl 	obj->connected = 1;
141525cf1a30Sjl 
141625cf1a30Sjl 	return (NULL);
141725cf1a30Sjl }
141825cf1a30Sjl 
141925cf1a30Sjl static int drmach_cache_flush_flag[NCPU];
142025cf1a30Sjl 
142125cf1a30Sjl /*ARGSUSED*/
142225cf1a30Sjl static void
142325cf1a30Sjl drmach_flush_cache(uint64_t id, uint64_t dummy)
142425cf1a30Sjl {
142525cf1a30Sjl 	extern void cpu_flush_ecache(void);
142625cf1a30Sjl 
142725cf1a30Sjl 	cpu_flush_ecache();
142825cf1a30Sjl 	drmach_cache_flush_flag[id] = 0;
142925cf1a30Sjl }
143025cf1a30Sjl 
143125cf1a30Sjl static void
143225cf1a30Sjl drmach_flush_all()
143325cf1a30Sjl {
143425cf1a30Sjl 	cpuset_t	xc_cpuset;
143525cf1a30Sjl 	int		i;
143625cf1a30Sjl 
143725cf1a30Sjl 	xc_cpuset = cpu_ready_set;
143825cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
143925cf1a30Sjl 		if (CPU_IN_SET(xc_cpuset, i)) {
144025cf1a30Sjl 			drmach_cache_flush_flag[i] = 1;
144125cf1a30Sjl 			xc_one(i, drmach_flush_cache, i, 0);
144225cf1a30Sjl 			while (drmach_cache_flush_flag[i]) {
144325cf1a30Sjl 				DELAY(1000);
144425cf1a30Sjl 			}
144525cf1a30Sjl 		}
144625cf1a30Sjl 	}
144725cf1a30Sjl }
144825cf1a30Sjl 
144925cf1a30Sjl static int
145025cf1a30Sjl drmach_disconnect_cpus(drmach_board_t *bp)
145125cf1a30Sjl {
145225cf1a30Sjl 	int i, bnum;
145325cf1a30Sjl 
145425cf1a30Sjl 	bnum = bp->bnum;
145525cf1a30Sjl 
145625cf1a30Sjl 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
1457e98fafb9Sjl 		if (bp->cores[i].core_present) {
1458e98fafb9Sjl 			if (bp->cores[i].core_started)
1459e98fafb9Sjl 				return (-1);
1460e98fafb9Sjl 			if (bp->cores[i].core_hotadded) {
1461e98fafb9Sjl 				if (drmach_add_remove_cpu(bnum, i,
1462e98fafb9Sjl 				    HOTREMOVE_CPU)) {
1463e98fafb9Sjl 					cmn_err(CE_WARN, "Failed to remove "
1464e98fafb9Sjl 					    "CMP %d on board %d\n", i, bnum);
1465e98fafb9Sjl 					return (-1);
1466e98fafb9Sjl 				}
1467e98fafb9Sjl 			}
146825cf1a30Sjl 		}
146925cf1a30Sjl 	}
147025cf1a30Sjl 	return (0);
147125cf1a30Sjl }
147225cf1a30Sjl 
147325cf1a30Sjl /*ARGSUSED*/
147425cf1a30Sjl sbd_error_t *
147525cf1a30Sjl drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
147625cf1a30Sjl {
147725cf1a30Sjl 	drmach_board_t *obj;
147825cf1a30Sjl 	int rv = 0;
147925cf1a30Sjl 	sbd_error_t		*err = NULL;
148025cf1a30Sjl 
1481ddf95635Sbm 	if (DRMACH_NULL_ID(id))
1482ddf95635Sbm 		return (NULL);
148325cf1a30Sjl 
148425cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
148525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
148625cf1a30Sjl 
148725cf1a30Sjl 	obj = (drmach_board_t *)id;
148825cf1a30Sjl 
148925cf1a30Sjl 	if (drmach_disconnect_cpus(obj)) {
14908eafe49bSbm 		err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
149125cf1a30Sjl 		return (err);
149225cf1a30Sjl 	}
149325cf1a30Sjl 
149425cf1a30Sjl 	rv = opl_unprobe_sb(obj->bnum);
149525cf1a30Sjl 
149625cf1a30Sjl 	if (rv == 0) {
149707d06da5SSurya Prakki 		(void) prom_detach_notice(obj->bnum);
149825cf1a30Sjl 		obj->connected = 0;
149925cf1a30Sjl 
150025cf1a30Sjl 	} else
15018eafe49bSbm 		err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
150225cf1a30Sjl 
150325cf1a30Sjl 	return (err);
150425cf1a30Sjl }
150525cf1a30Sjl 
150625cf1a30Sjl static int
150725cf1a30Sjl drmach_get_portid(drmach_node_t *np)
150825cf1a30Sjl {
150925cf1a30Sjl 	int		portid;
151025cf1a30Sjl 	char		type[OBP_MAXPROPNAME];
151125cf1a30Sjl 
151225cf1a30Sjl 	if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
151325cf1a30Sjl 		return (portid);
151425cf1a30Sjl 
151525cf1a30Sjl 	/*
151625cf1a30Sjl 	 * Get the device_type property to see if we should
151725cf1a30Sjl 	 * continue processing this node.
151825cf1a30Sjl 	 */
151925cf1a30Sjl 	if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
152025cf1a30Sjl 		return (-1);
152125cf1a30Sjl 
152225cf1a30Sjl 	if (strcmp(type, OPL_CPU_NODE) == 0) {
152325cf1a30Sjl 		/*
152425cf1a30Sjl 		 * We return cpuid because it has no portid
152525cf1a30Sjl 		 */
152625cf1a30Sjl 		if (np->n_getprop(np, "cpuid", &portid, sizeof (portid)) == 0)
152725cf1a30Sjl 			return (portid);
152825cf1a30Sjl 	}
152925cf1a30Sjl 
153025cf1a30Sjl 	return (-1);
153125cf1a30Sjl }
153225cf1a30Sjl 
153325cf1a30Sjl /*
153425cf1a30Sjl  * This is a helper function to determine if a given
153525cf1a30Sjl  * node should be considered for a dr operation according
153625cf1a30Sjl  * to predefined dr type nodes and the node's name.
153725cf1a30Sjl  * Formal Parameter : The name of a device node.
153825cf1a30Sjl  * Return Value: -1, name does not map to a valid dr type.
153925cf1a30Sjl  *		 A value greater or equal to 0, name is a valid dr type.
154025cf1a30Sjl  */
154125cf1a30Sjl static int
154225cf1a30Sjl drmach_name2type_idx(char *name)
154325cf1a30Sjl {
1544*bbe1232eSToomas Soome 	int	index, ntypes;
154525cf1a30Sjl 
154625cf1a30Sjl 	if (name == NULL)
154725cf1a30Sjl 		return (-1);
154825cf1a30Sjl 
154925cf1a30Sjl 	/*
155025cf1a30Sjl 	 * Determine how many possible types are currently supported
155125cf1a30Sjl 	 * for dr.
155225cf1a30Sjl 	 */
155325cf1a30Sjl 	ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
155425cf1a30Sjl 
155525cf1a30Sjl 	/* Determine if the node's name correspond to a predefined type. */
155625cf1a30Sjl 	for (index = 0; index < ntypes; index++) {
155725cf1a30Sjl 		if (strcmp(drmach_name2type[index].name, name) == 0)
155825cf1a30Sjl 			/* The node is an allowed type for dr. */
155925cf1a30Sjl 			return (index);
156025cf1a30Sjl 	}
156125cf1a30Sjl 
156225cf1a30Sjl 	/*
156325cf1a30Sjl 	 * If the name of the node does not map to any of the
156425cf1a30Sjl 	 * types in the array drmach_name2type then the node is not of
156525cf1a30Sjl 	 * interest to dr.
156625cf1a30Sjl 	 */
156725cf1a30Sjl 	return (-1);
156825cf1a30Sjl }
156925cf1a30Sjl 
157025cf1a30Sjl /*
157125cf1a30Sjl  * there is some complication on OPL:
157225cf1a30Sjl  * - pseudo-mc nodes do not have portid property
157325cf1a30Sjl  * - portid[9:5] of cmp node is LSB #, portid[7:3] of pci is LSB#
157425cf1a30Sjl  * - cmp has board#
157525cf1a30Sjl  * - core and cpu nodes do not have portid and board# properties
157625cf1a30Sjl  * starcat uses portid to derive the board# but that does not work
157725cf1a30Sjl  * for us.  starfire reads board# property to filter the devices.
157825cf1a30Sjl  * That does not work either.  So for these specific device,
157925cf1a30Sjl  * we use specific hard coded methods to get the board# -
158025cf1a30Sjl  * cpu: LSB# = CPUID[9:5]
158125cf1a30Sjl  */
158225cf1a30Sjl 
158325cf1a30Sjl static int
158425cf1a30Sjl drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
158525cf1a30Sjl {
158625cf1a30Sjl 	drmach_node_t			*node = args->node;
158725cf1a30Sjl 	drmach_board_cb_data_t		*data = args->data;
158825cf1a30Sjl 	drmach_board_t			*obj = data->obj;
158925cf1a30Sjl 
159025cf1a30Sjl 	int		rv, portid;
159125cf1a30Sjl 	int		bnum;
159225cf1a30Sjl 	drmachid_t	id;
159325cf1a30Sjl 	drmach_device_t	*device;
159425cf1a30Sjl 	char name[OBP_MAXDRVNAME];
159525cf1a30Sjl 
159625cf1a30Sjl 	portid = drmach_get_portid(node);
159725cf1a30Sjl 	/*
159825cf1a30Sjl 	 * core, cpu and pseudo-mc do not have portid
159925cf1a30Sjl 	 * we use cpuid as the portid of the cpu node
160025cf1a30Sjl 	 * for pseudo-mc, we do not use portid info.
160125cf1a30Sjl 	 */
160225cf1a30Sjl 
160325cf1a30Sjl 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
160425cf1a30Sjl 	if (rv)
160525cf1a30Sjl 		return (0);
160625cf1a30Sjl 
160725cf1a30Sjl 
160825cf1a30Sjl 	rv = node->n_getprop(node, OBP_BOARDNUM, &bnum, sizeof (bnum));
160925cf1a30Sjl 
161025cf1a30Sjl 	if (rv) {
161125cf1a30Sjl 		/*
161225cf1a30Sjl 		 * cpu does not have board# property.  We use
161325cf1a30Sjl 		 * CPUID[9:5]
161425cf1a30Sjl 		 */
161525cf1a30Sjl 		if (strcmp("cpu", name) == 0) {
161625cf1a30Sjl 			bnum = (portid >> 5) & 0x1f;
161725cf1a30Sjl 		} else
161825cf1a30Sjl 			return (0);
161925cf1a30Sjl 	}
162025cf1a30Sjl 
162125cf1a30Sjl 
162225cf1a30Sjl 	if (bnum != obj->bnum)
162325cf1a30Sjl 		return (0);
162425cf1a30Sjl 
162525cf1a30Sjl 	if (drmach_name2type_idx(name) < 0) {
162625cf1a30Sjl 		return (0);
162725cf1a30Sjl 	}
162825cf1a30Sjl 
162925cf1a30Sjl 	/*
163025cf1a30Sjl 	 * Create a device data structure from this node data.
163125cf1a30Sjl 	 * The call may yield nothing if the node is not of interest
163225cf1a30Sjl 	 * to drmach.
163325cf1a30Sjl 	 */
163425cf1a30Sjl 	data->err = drmach_device_new(node, obj, portid, &id);
163525cf1a30Sjl 	if (data->err)
163625cf1a30Sjl 		return (-1);
163725cf1a30Sjl 	else if (!id) {
163825cf1a30Sjl 		/*
163925cf1a30Sjl 		 * drmach_device_new examined the node we passed in
164025cf1a30Sjl 		 * and determined that it was one not of interest to
164125cf1a30Sjl 		 * drmach.  So, it is skipped.
164225cf1a30Sjl 		 */
164325cf1a30Sjl 		return (0);
164425cf1a30Sjl 	}
164525cf1a30Sjl 
164625cf1a30Sjl 	rv = drmach_array_set(obj->devices, data->ndevs++, id);
164725cf1a30Sjl 	if (rv) {
164825cf1a30Sjl 		data->err = DRMACH_INTERNAL_ERROR();
164925cf1a30Sjl 		return (-1);
165025cf1a30Sjl 	}
165125cf1a30Sjl 	device = id;
165225cf1a30Sjl 
165325cf1a30Sjl 	data->err = (*data->found)(data->a, device->type, device->unum, id);
165425cf1a30Sjl 	return (data->err == NULL ? 0 : -1);
165525cf1a30Sjl }
165625cf1a30Sjl 
165725cf1a30Sjl sbd_error_t *
165825cf1a30Sjl drmach_board_find_devices(drmachid_t id, void *a,
1659*bbe1232eSToomas Soome     sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
166025cf1a30Sjl {
166125cf1a30Sjl 	drmach_board_t		*bp = (drmach_board_t *)id;
166225cf1a30Sjl 	sbd_error_t		*err;
166325cf1a30Sjl 	int			 max_devices;
166425cf1a30Sjl 	int			 rv;
166525cf1a30Sjl 	drmach_board_cb_data_t	data;
166625cf1a30Sjl 
166725cf1a30Sjl 
166825cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
166925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
167025cf1a30Sjl 
167125cf1a30Sjl 	max_devices  = MAX_CPU_UNITS_PER_BOARD;
167225cf1a30Sjl 	max_devices += MAX_MEM_UNITS_PER_BOARD;
167325cf1a30Sjl 	max_devices += MAX_IO_UNITS_PER_BOARD;
167425cf1a30Sjl 
167525cf1a30Sjl 	bp->devices = drmach_array_new(0, max_devices);
167625cf1a30Sjl 
167725cf1a30Sjl 	if (bp->tree == NULL)
167825cf1a30Sjl 		bp->tree = drmach_node_new();
167925cf1a30Sjl 
168025cf1a30Sjl 	data.obj = bp;
168125cf1a30Sjl 	data.ndevs = 0;
168225cf1a30Sjl 	data.found = found;
168325cf1a30Sjl 	data.a = a;
168425cf1a30Sjl 	data.err = NULL;
168525cf1a30Sjl 
168625cf1a30Sjl 	rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
168725cf1a30Sjl 	if (rv == 0)
168825cf1a30Sjl 		err = NULL;
168925cf1a30Sjl 	else {
169025cf1a30Sjl 		drmach_array_dispose(bp->devices, drmach_device_dispose);
169125cf1a30Sjl 		bp->devices = NULL;
169225cf1a30Sjl 
169325cf1a30Sjl 		if (data.err)
169425cf1a30Sjl 			err = data.err;
169525cf1a30Sjl 		else
169625cf1a30Sjl 			err = DRMACH_INTERNAL_ERROR();
169725cf1a30Sjl 	}
169825cf1a30Sjl 
169925cf1a30Sjl 	return (err);
170025cf1a30Sjl }
170125cf1a30Sjl 
170225cf1a30Sjl int
170325cf1a30Sjl drmach_board_lookup(int bnum, drmachid_t *id)
170425cf1a30Sjl {
170525cf1a30Sjl 	int	rv = 0;
170625cf1a30Sjl 
170725cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_READER);
170825cf1a30Sjl 	if (drmach_array_get(drmach_boards, bnum, id)) {
170925cf1a30Sjl 		*id = 0;
171025cf1a30Sjl 		rv = -1;
171125cf1a30Sjl 	}
171225cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
171325cf1a30Sjl 	return (rv);
171425cf1a30Sjl }
171525cf1a30Sjl 
171625cf1a30Sjl sbd_error_t *
171725cf1a30Sjl drmach_board_name(int bnum, char *buf, int buflen)
171825cf1a30Sjl {
171907d06da5SSurya Prakki 	(void) snprintf(buf, buflen, "SB%d", bnum);
172025cf1a30Sjl 	return (NULL);
172125cf1a30Sjl }
172225cf1a30Sjl 
172325cf1a30Sjl sbd_error_t *
172425cf1a30Sjl drmach_board_poweroff(drmachid_t id)
172525cf1a30Sjl {
172625cf1a30Sjl 	drmach_board_t	*bp;
172725cf1a30Sjl 	sbd_error_t	*err;
172825cf1a30Sjl 	drmach_status_t	 stat;
172925cf1a30Sjl 
1730ddf95635Sbm 	if (DRMACH_NULL_ID(id))
1731ddf95635Sbm 		return (NULL);
1732ddf95635Sbm 
173325cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
173425cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
173525cf1a30Sjl 	bp = id;
173625cf1a30Sjl 
173725cf1a30Sjl 	err = drmach_board_status(id, &stat);
173825cf1a30Sjl 
173925cf1a30Sjl 	if (!err) {
174025cf1a30Sjl 		if (stat.configured || stat.busy)
174125cf1a30Sjl 			err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
174225cf1a30Sjl 		else {
174325cf1a30Sjl 			bp->powered = 0;
174425cf1a30Sjl 		}
174525cf1a30Sjl 	}
174625cf1a30Sjl 	return (err);
174725cf1a30Sjl }
174825cf1a30Sjl 
174925cf1a30Sjl sbd_error_t *
175025cf1a30Sjl drmach_board_poweron(drmachid_t id)
175125cf1a30Sjl {
175225cf1a30Sjl 	drmach_board_t	*bp;
175325cf1a30Sjl 
175425cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
175525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
175625cf1a30Sjl 	bp = id;
175725cf1a30Sjl 
175825cf1a30Sjl 	bp->powered = 1;
175925cf1a30Sjl 
176025cf1a30Sjl 	return (NULL);
176125cf1a30Sjl }
176225cf1a30Sjl 
176325cf1a30Sjl static sbd_error_t *
176425cf1a30Sjl drmach_board_release(drmachid_t id)
176525cf1a30Sjl {
176625cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
176725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
176825cf1a30Sjl 	return (NULL);
176925cf1a30Sjl }
177025cf1a30Sjl 
177125cf1a30Sjl /*ARGSUSED*/
177225cf1a30Sjl sbd_error_t *
177325cf1a30Sjl drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
177425cf1a30Sjl {
177525cf1a30Sjl 	return (NULL);
177625cf1a30Sjl }
177725cf1a30Sjl 
177825cf1a30Sjl sbd_error_t *
177925cf1a30Sjl drmach_board_unassign(drmachid_t id)
178025cf1a30Sjl {
178125cf1a30Sjl 	drmach_board_t	*bp;
178225cf1a30Sjl 	sbd_error_t	*err;
178325cf1a30Sjl 	drmach_status_t	 stat;
178425cf1a30Sjl 
1785ddf95635Sbm 	if (DRMACH_NULL_ID(id))
1786ddf95635Sbm 		return (NULL);
178725cf1a30Sjl 
178825cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id)) {
178925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
179025cf1a30Sjl 	}
179125cf1a30Sjl 	bp = id;
179225cf1a30Sjl 
179325cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
179425cf1a30Sjl 
179525cf1a30Sjl 	err = drmach_board_status(id, &stat);
179625cf1a30Sjl 	if (err) {
179725cf1a30Sjl 		rw_exit(&drmach_boards_rwlock);
179825cf1a30Sjl 		return (err);
179925cf1a30Sjl 	}
180025cf1a30Sjl 	if (stat.configured || stat.busy) {
180125cf1a30Sjl 		err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
180225cf1a30Sjl 	} else {
180325cf1a30Sjl 		if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
180425cf1a30Sjl 			err = DRMACH_INTERNAL_ERROR();
180525cf1a30Sjl 		else
180625cf1a30Sjl 			drmach_board_dispose(bp);
180725cf1a30Sjl 	}
180825cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
180925cf1a30Sjl 	return (err);
181025cf1a30Sjl }
181125cf1a30Sjl 
181225cf1a30Sjl /*
181325cf1a30Sjl  * We have to do more on OPL - e.g. set up sram tte, read cpuid, strand id,
181425cf1a30Sjl  * implementation #, etc
181525cf1a30Sjl  */
181625cf1a30Sjl 
181725cf1a30Sjl static sbd_error_t *
181825cf1a30Sjl drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
181925cf1a30Sjl {
182025cf1a30Sjl 	int		 portid;
182125cf1a30Sjl 	drmach_cpu_t	*cp = NULL;
182225cf1a30Sjl 
182325cf1a30Sjl 	/* portid is CPUID of the node */
182425cf1a30Sjl 	portid = proto->portid;
182525cf1a30Sjl 	ASSERT(portid != -1);
182625cf1a30Sjl 
182725cf1a30Sjl 	/* unum = (CMP/CHIP ID) + (ON_BOARD_CORE_NUM * MAX_CMPID_PER_BOARD) */
182825cf1a30Sjl 	proto->unum = ((portid/OPL_MAX_CPUID_PER_CMP) &
1829e98fafb9Sjl 	    (OPL_MAX_CMPID_PER_BOARD - 1)) +
1830e98fafb9Sjl 	    ((portid & (OPL_MAX_CPUID_PER_CMP - 1)) *
1831e98fafb9Sjl 	    (OPL_MAX_CMPID_PER_BOARD));
183225cf1a30Sjl 
183325cf1a30Sjl 	cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
183425cf1a30Sjl 	bcopy(proto, &cp->dev, sizeof (cp->dev));
183525cf1a30Sjl 	cp->dev.node = drmach_node_dup(proto->node);
183625cf1a30Sjl 	cp->dev.cm.isa = (void *)drmach_cpu_new;
183725cf1a30Sjl 	cp->dev.cm.dispose = drmach_cpu_dispose;
183825cf1a30Sjl 	cp->dev.cm.release = drmach_cpu_release;
183925cf1a30Sjl 	cp->dev.cm.status = drmach_cpu_status;
184025cf1a30Sjl 
184107d06da5SSurya Prakki 	(void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
1842e98fafb9Sjl 	    cp->dev.type, cp->dev.unum);
184325cf1a30Sjl 
184425cf1a30Sjl /*
184525cf1a30Sjl  *	CPU ID representation
184625cf1a30Sjl  *	CPUID[9:5] = SB#
184725cf1a30Sjl  *	CPUID[4:3] = Chip#
184825cf1a30Sjl  *	CPUID[2:1] = Core# (Only 2 core for OPL)
184925cf1a30Sjl  *	CPUID[0:0] = Strand#
185025cf1a30Sjl  */
185125cf1a30Sjl 
185225cf1a30Sjl /*
185325cf1a30Sjl  *	reg property of the strand contains strand ID
185425cf1a30Sjl  *	reg property of the parent node contains core ID
185525cf1a30Sjl  *	We should use them.
185625cf1a30Sjl  */
185725cf1a30Sjl 	cp->cpuid = portid;
185825cf1a30Sjl 	cp->sb = (portid >> 5) & 0x1f;
185925cf1a30Sjl 	cp->chipid = (portid >> 3) & 0x3;
186025cf1a30Sjl 	cp->coreid = (portid >> 1) & 0x3;
186125cf1a30Sjl 	cp->strandid = portid & 0x1;
186225cf1a30Sjl 
186325cf1a30Sjl 	*idp = (drmachid_t)cp;
186425cf1a30Sjl 	return (NULL);
186525cf1a30Sjl }
186625cf1a30Sjl 
186725cf1a30Sjl 
186825cf1a30Sjl static void
186925cf1a30Sjl drmach_cpu_dispose(drmachid_t id)
187025cf1a30Sjl {
187125cf1a30Sjl 	drmach_cpu_t	*self;
187225cf1a30Sjl 
187325cf1a30Sjl 	ASSERT(DRMACH_IS_CPU_ID(id));
187425cf1a30Sjl 
187525cf1a30Sjl 	self = id;
187625cf1a30Sjl 	if (self->dev.node)
187725cf1a30Sjl 		drmach_node_dispose(self->dev.node);
187825cf1a30Sjl 
187925cf1a30Sjl 	kmem_free(self, sizeof (*self));
188025cf1a30Sjl }
188125cf1a30Sjl 
188225cf1a30Sjl static int
188325cf1a30Sjl drmach_cpu_start(struct cpu *cp)
188425cf1a30Sjl {
188525cf1a30Sjl 	int		cpuid = cp->cpu_id;
188625cf1a30Sjl 	extern int	restart_other_cpu(int);
188725cf1a30Sjl 
188825cf1a30Sjl 	ASSERT(MUTEX_HELD(&cpu_lock));
188925cf1a30Sjl 	ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
189025cf1a30Sjl 
189125cf1a30Sjl 	cp->cpu_flags &= ~CPU_POWEROFF;
189225cf1a30Sjl 
189325cf1a30Sjl 	/*
189425cf1a30Sjl 	 * NOTE: restart_other_cpu pauses cpus during the
189525cf1a30Sjl 	 *	 slave cpu start.  This helps to quiesce the
189625cf1a30Sjl 	 *	 bus traffic a bit which makes the tick sync
189725cf1a30Sjl 	 *	 routine in the prom more robust.
189825cf1a30Sjl 	 */
189925cf1a30Sjl 	DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
190025cf1a30Sjl 
190107d06da5SSurya Prakki 	(void) restart_other_cpu(cpuid);
190225cf1a30Sjl 
190325cf1a30Sjl 	return (0);
190425cf1a30Sjl }
190525cf1a30Sjl 
190625cf1a30Sjl static sbd_error_t *
190725cf1a30Sjl drmach_cpu_release(drmachid_t id)
190825cf1a30Sjl {
190925cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
191025cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
191125cf1a30Sjl 
191225cf1a30Sjl 	return (NULL);
191325cf1a30Sjl }
191425cf1a30Sjl 
191525cf1a30Sjl static sbd_error_t *
191625cf1a30Sjl drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
191725cf1a30Sjl {
191825cf1a30Sjl 	drmach_cpu_t *cp;
191925cf1a30Sjl 	drmach_device_t *dp;
192025cf1a30Sjl 
192125cf1a30Sjl 	ASSERT(DRMACH_IS_CPU_ID(id));
192225cf1a30Sjl 	cp = (drmach_cpu_t *)id;
192325cf1a30Sjl 	dp = &cp->dev;
192425cf1a30Sjl 
192525cf1a30Sjl 	stat->assigned = dp->bp->assigned;
192625cf1a30Sjl 	stat->powered = dp->bp->powered;
192725cf1a30Sjl 	mutex_enter(&cpu_lock);
192825cf1a30Sjl 	stat->configured = (cpu_get(cp->cpuid) != NULL);
192925cf1a30Sjl 	mutex_exit(&cpu_lock);
193025cf1a30Sjl 	stat->busy = dp->busy;
193107d06da5SSurya Prakki 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
193225cf1a30Sjl 	stat->info[0] = '\0';
193325cf1a30Sjl 
193425cf1a30Sjl 	return (NULL);
193525cf1a30Sjl }
193625cf1a30Sjl 
193725cf1a30Sjl sbd_error_t *
193825cf1a30Sjl drmach_cpu_disconnect(drmachid_t id)
193925cf1a30Sjl {
194025cf1a30Sjl 
194125cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
194225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
194325cf1a30Sjl 
194425cf1a30Sjl 	return (NULL);
194525cf1a30Sjl }
194625cf1a30Sjl 
194725cf1a30Sjl sbd_error_t *
194825cf1a30Sjl drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
194925cf1a30Sjl {
195025cf1a30Sjl 	drmach_cpu_t *cpu;
195125cf1a30Sjl 
195225cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
195325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
195425cf1a30Sjl 	cpu = (drmach_cpu_t *)id;
195525cf1a30Sjl 
195625cf1a30Sjl 	/* get from cpu directly on OPL */
195725cf1a30Sjl 	*cpuid = cpu->cpuid;
195825cf1a30Sjl 	return (NULL);
195925cf1a30Sjl }
196025cf1a30Sjl 
196125cf1a30Sjl sbd_error_t *
196225cf1a30Sjl drmach_cpu_get_impl(drmachid_t id, int *ip)
196325cf1a30Sjl {
196425cf1a30Sjl 	drmach_device_t *cpu;
196525cf1a30Sjl 	drmach_node_t	*np;
196625cf1a30Sjl 	drmach_node_t	pp;
196725cf1a30Sjl 	int		impl;
196825cf1a30Sjl 	char		type[OBP_MAXPROPNAME];
196925cf1a30Sjl 
197025cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
197125cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
197225cf1a30Sjl 
197325cf1a30Sjl 	cpu = id;
197425cf1a30Sjl 	np = cpu->node;
197525cf1a30Sjl 
197625cf1a30Sjl 	if (np->get_parent(np, &pp) != 0) {
197725cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
197825cf1a30Sjl 	}
197925cf1a30Sjl 
198025cf1a30Sjl 	/* the parent should be core */
198125cf1a30Sjl 
198225cf1a30Sjl 	if (pp.n_getprop(&pp, "device_type", &type, sizeof (type)) != 0) {
198325cf1a30Sjl 		return (drerr_new(0, EOPL_GETPROP, NULL));
198425cf1a30Sjl 	}
198525cf1a30Sjl 
198625cf1a30Sjl 	if (strcmp(type, OPL_CORE_NODE) == 0) {
1987e98fafb9Sjl 		if (pp.n_getprop(&pp, "implementation#", &impl,
1988e98fafb9Sjl 		    sizeof (impl)) != 0) {
198925cf1a30Sjl 			return (drerr_new(0, EOPL_GETPROP, NULL));
199025cf1a30Sjl 		}
199125cf1a30Sjl 	} else {
199225cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
199325cf1a30Sjl 	}
199425cf1a30Sjl 
199525cf1a30Sjl 	*ip = impl;
199625cf1a30Sjl 
199725cf1a30Sjl 	return (NULL);
199825cf1a30Sjl }
199925cf1a30Sjl 
200025cf1a30Sjl sbd_error_t *
200125cf1a30Sjl drmach_get_dip(drmachid_t id, dev_info_t **dip)
200225cf1a30Sjl {
200325cf1a30Sjl 	drmach_device_t	*dp;
200425cf1a30Sjl 
200525cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
200625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
200725cf1a30Sjl 	dp = id;
200825cf1a30Sjl 
200925cf1a30Sjl 	*dip = dp->node->n_getdip(dp->node);
201025cf1a30Sjl 	return (NULL);
201125cf1a30Sjl }
201225cf1a30Sjl 
201325cf1a30Sjl sbd_error_t *
201425cf1a30Sjl drmach_io_is_attached(drmachid_t id, int *yes)
201525cf1a30Sjl {
201625cf1a30Sjl 	drmach_device_t *dp;
201725cf1a30Sjl 	dev_info_t	*dip;
201825cf1a30Sjl 	int		state;
201925cf1a30Sjl 
202025cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
202125cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
202225cf1a30Sjl 	dp = id;
202325cf1a30Sjl 
202425cf1a30Sjl 	dip = dp->node->n_getdip(dp->node);
202525cf1a30Sjl 	if (dip == NULL) {
202625cf1a30Sjl 		*yes = 0;
202725cf1a30Sjl 		return (NULL);
202825cf1a30Sjl 	}
202925cf1a30Sjl 
203025cf1a30Sjl 	state = ddi_get_devstate(dip);
203125cf1a30Sjl 	*yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) ||
203225cf1a30Sjl 	    (state == DDI_DEVSTATE_UP));
203325cf1a30Sjl 
203425cf1a30Sjl 	return (NULL);
203525cf1a30Sjl }
203625cf1a30Sjl 
203725cf1a30Sjl struct drmach_io_cb {
203825cf1a30Sjl 	char	*name;	/* name of the node */
203925cf1a30Sjl 	int	(*func)(dev_info_t *);
204025cf1a30Sjl 	int	rv;
204168ac2337Sjl 	dev_info_t *dip;
204225cf1a30Sjl };
204325cf1a30Sjl 
204425cf1a30Sjl #define	DRMACH_IO_POST_ATTACH	0
204525cf1a30Sjl #define	DRMACH_IO_PRE_RELEASE	1
204625cf1a30Sjl 
204725cf1a30Sjl static int
204825cf1a30Sjl drmach_io_cb_check(dev_info_t *dip, void *arg)
204925cf1a30Sjl {
205025cf1a30Sjl 	struct drmach_io_cb *p = (struct drmach_io_cb *)arg;
205125cf1a30Sjl 	char name[OBP_MAXDRVNAME];
205225cf1a30Sjl 	int len = OBP_MAXDRVNAME;
205325cf1a30Sjl 
2054e98fafb9Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "name",
205525cf1a30Sjl 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
205625cf1a30Sjl 		return (DDI_WALK_PRUNECHILD);
205725cf1a30Sjl 	}
205825cf1a30Sjl 
205925cf1a30Sjl 	if (strcmp(name, p->name) == 0) {
206068ac2337Sjl 		ndi_hold_devi(dip);
206168ac2337Sjl 		p->dip = dip;
206225cf1a30Sjl 		return (DDI_WALK_TERMINATE);
206325cf1a30Sjl 	}
206425cf1a30Sjl 
206525cf1a30Sjl 	return (DDI_WALK_CONTINUE);
206625cf1a30Sjl }
206725cf1a30Sjl 
206825cf1a30Sjl 
206925cf1a30Sjl static int
207025cf1a30Sjl drmach_console_ops(drmachid_t *id, int state)
207125cf1a30Sjl {
207225cf1a30Sjl 	drmach_io_t *obj = (drmach_io_t *)id;
207325cf1a30Sjl 	struct drmach_io_cb arg;
207425cf1a30Sjl 	int (*msudetp)(dev_info_t *);
207525cf1a30Sjl 	int (*msuattp)(dev_info_t *);
207625cf1a30Sjl 	dev_info_t *dip, *pdip;
207725cf1a30Sjl 	int circ;
207825cf1a30Sjl 
207925cf1a30Sjl 	/* 4 is pcicmu channel */
208025cf1a30Sjl 	if (obj->channel != 4)
208125cf1a30Sjl 		return (0);
208225cf1a30Sjl 
208325cf1a30Sjl 	arg.name = "serial";
208425cf1a30Sjl 	arg.func = NULL;
208525cf1a30Sjl 	if (state == DRMACH_IO_PRE_RELEASE) {
208625cf1a30Sjl 		msudetp = (int (*)(dev_info_t *))
208725cf1a30Sjl 		    modgetsymvalue("oplmsu_dr_detach", 0);
208825cf1a30Sjl 		if (msudetp != NULL)
208925cf1a30Sjl 			arg.func = msudetp;
209025cf1a30Sjl 	} else if (state == DRMACH_IO_POST_ATTACH) {
209125cf1a30Sjl 		msuattp = (int (*)(dev_info_t *))
209225cf1a30Sjl 		    modgetsymvalue("oplmsu_dr_attach", 0);
209325cf1a30Sjl 		if (msuattp != NULL)
209425cf1a30Sjl 			arg.func = msuattp;
209568ac2337Sjl 	} else {
209625cf1a30Sjl 		return (0);
209768ac2337Sjl 	}
209825cf1a30Sjl 
209925cf1a30Sjl 	if (arg.func == NULL) {
210025cf1a30Sjl 		return (0);
210125cf1a30Sjl 	}
210225cf1a30Sjl 
210325cf1a30Sjl 	arg.rv = 0;
210468ac2337Sjl 	arg.dip = NULL;
210525cf1a30Sjl 
210625cf1a30Sjl 	dip = obj->dev.node->n_getdip(obj->dev.node);
210725cf1a30Sjl 	if (pdip = ddi_get_parent(dip)) {
210825cf1a30Sjl 		ndi_hold_devi(pdip);
210925cf1a30Sjl 		ndi_devi_enter(pdip, &circ);
211025cf1a30Sjl 	} else {
211125cf1a30Sjl 		/* this cannot happen unless something bad happens */
211225cf1a30Sjl 		return (-1);
211325cf1a30Sjl 	}
211425cf1a30Sjl 
211525cf1a30Sjl 	ddi_walk_devs(dip, drmach_io_cb_check, (void *)&arg);
211625cf1a30Sjl 
211768ac2337Sjl 	ndi_devi_exit(pdip, circ);
211868ac2337Sjl 	ndi_rele_devi(pdip);
211968ac2337Sjl 
212068ac2337Sjl 	if (arg.dip) {
212168ac2337Sjl 		arg.rv = (*arg.func)(arg.dip);
212268ac2337Sjl 		ndi_rele_devi(arg.dip);
212368ac2337Sjl 	} else {
212468ac2337Sjl 		arg.rv = -1;
212525cf1a30Sjl 	}
212625cf1a30Sjl 
212725cf1a30Sjl 	return (arg.rv);
212825cf1a30Sjl }
212925cf1a30Sjl 
213025cf1a30Sjl sbd_error_t *
213125cf1a30Sjl drmach_io_pre_release(drmachid_t id)
213225cf1a30Sjl {
213325cf1a30Sjl 	int rv;
213425cf1a30Sjl 
213525cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
213625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
213725cf1a30Sjl 
213825cf1a30Sjl 	rv = drmach_console_ops(id, DRMACH_IO_PRE_RELEASE);
213925cf1a30Sjl 
214025cf1a30Sjl 	if (rv != 0)
214125cf1a30Sjl 		cmn_err(CE_WARN, "IO callback failed in pre-release\n");
214225cf1a30Sjl 
214325cf1a30Sjl 	return (NULL);
214425cf1a30Sjl }
214525cf1a30Sjl 
214625cf1a30Sjl static sbd_error_t *
214725cf1a30Sjl drmach_io_release(drmachid_t id)
214825cf1a30Sjl {
214925cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
215025cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
215125cf1a30Sjl 	return (NULL);
215225cf1a30Sjl }
215325cf1a30Sjl 
215425cf1a30Sjl sbd_error_t *
215525cf1a30Sjl drmach_io_unrelease(drmachid_t id)
215625cf1a30Sjl {
215725cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
215825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
215925cf1a30Sjl 	return (NULL);
216025cf1a30Sjl }
216125cf1a30Sjl 
216225cf1a30Sjl /*ARGSUSED*/
216325cf1a30Sjl sbd_error_t *
216425cf1a30Sjl drmach_io_post_release(drmachid_t id)
216525cf1a30Sjl {
216625cf1a30Sjl 	return (NULL);
216725cf1a30Sjl }
216825cf1a30Sjl 
216925cf1a30Sjl /*ARGSUSED*/
217025cf1a30Sjl sbd_error_t *
217125cf1a30Sjl drmach_io_post_attach(drmachid_t id)
217225cf1a30Sjl {
217325cf1a30Sjl 	int rv;
217425cf1a30Sjl 
217525cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
217625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
217725cf1a30Sjl 
217825cf1a30Sjl 	rv = drmach_console_ops(id, DRMACH_IO_POST_ATTACH);
217925cf1a30Sjl 
218025cf1a30Sjl 	if (rv != 0)
218125cf1a30Sjl 		cmn_err(CE_WARN, "IO callback failed in post-attach\n");
218225cf1a30Sjl 
218325cf1a30Sjl 	return (0);
218425cf1a30Sjl }
218525cf1a30Sjl 
218625cf1a30Sjl static sbd_error_t *
218725cf1a30Sjl drmach_io_status(drmachid_t id, drmach_status_t *stat)
218825cf1a30Sjl {
218925cf1a30Sjl 	drmach_device_t *dp;
219025cf1a30Sjl 	sbd_error_t	*err;
219125cf1a30Sjl 	int		 configured;
219225cf1a30Sjl 
219325cf1a30Sjl 	ASSERT(DRMACH_IS_IO_ID(id));
219425cf1a30Sjl 	dp = id;
219525cf1a30Sjl 
219625cf1a30Sjl 	err = drmach_io_is_attached(id, &configured);
219725cf1a30Sjl 	if (err)
219825cf1a30Sjl 		return (err);
219925cf1a30Sjl 
220025cf1a30Sjl 	stat->assigned = dp->bp->assigned;
220125cf1a30Sjl 	stat->powered = dp->bp->powered;
220225cf1a30Sjl 	stat->configured = (configured != 0);
220325cf1a30Sjl 	stat->busy = dp->busy;
220407d06da5SSurya Prakki 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
220525cf1a30Sjl 	stat->info[0] = '\0';
220625cf1a30Sjl 
220725cf1a30Sjl 	return (NULL);
220825cf1a30Sjl }
220925cf1a30Sjl 
221025cf1a30Sjl static sbd_error_t *
221125cf1a30Sjl drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
221225cf1a30Sjl {
221325cf1a30Sjl 	dev_info_t *dip;
221468ac2337Sjl 	int rv;
221525cf1a30Sjl 
221625cf1a30Sjl 	drmach_mem_t	*mp;
221725cf1a30Sjl 
221868ac2337Sjl 	rv = 0;
221968ac2337Sjl 
222068ac2337Sjl 	if ((proto->node->n_getproplen(proto->node, "mc-addr", &rv) < 0) ||
2221e98fafb9Sjl 	    (rv <= 0)) {
222268ac2337Sjl 		*idp = (drmachid_t)0;
222368ac2337Sjl 		return (NULL);
222468ac2337Sjl 	}
222568ac2337Sjl 
222625cf1a30Sjl 	mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
222725cf1a30Sjl 	proto->unum = 0;
222825cf1a30Sjl 
222925cf1a30Sjl 	bcopy(proto, &mp->dev, sizeof (mp->dev));
223025cf1a30Sjl 	mp->dev.node = drmach_node_dup(proto->node);
223125cf1a30Sjl 	mp->dev.cm.isa = (void *)drmach_mem_new;
223225cf1a30Sjl 	mp->dev.cm.dispose = drmach_mem_dispose;
223325cf1a30Sjl 	mp->dev.cm.release = drmach_mem_release;
223425cf1a30Sjl 	mp->dev.cm.status = drmach_mem_status;
223525cf1a30Sjl 
223607d06da5SSurya Prakki 	(void) snprintf(mp->dev.cm.name, sizeof (mp->dev.cm.name), "%s",
223707d06da5SSurya Prakki 	    mp->dev.type);
223825cf1a30Sjl 
223925cf1a30Sjl 	dip = mp->dev.node->n_getdip(mp->dev.node);
224025cf1a30Sjl 	if (drmach_setup_mc_info(dip, mp) != 0) {
22418eafe49bSbm 		return (drerr_new(1, EOPL_MC_SETUP, NULL));
224225cf1a30Sjl 	}
224325cf1a30Sjl 
224468ac2337Sjl 	/* make sure we do not create memoryless nodes */
224568ac2337Sjl 	if (mp->nbytes == 0) {
224668ac2337Sjl 		*idp = (drmachid_t)NULL;
224768ac2337Sjl 		kmem_free(mp, sizeof (drmach_mem_t));
224868ac2337Sjl 	} else
224968ac2337Sjl 		*idp = (drmachid_t)mp;
225068ac2337Sjl 
225125cf1a30Sjl 	return (NULL);
225225cf1a30Sjl }
225325cf1a30Sjl 
225425cf1a30Sjl static void
225525cf1a30Sjl drmach_mem_dispose(drmachid_t id)
225625cf1a30Sjl {
225725cf1a30Sjl 	drmach_mem_t *mp;
225825cf1a30Sjl 
225925cf1a30Sjl 	ASSERT(DRMACH_IS_MEM_ID(id));
226025cf1a30Sjl 
226125cf1a30Sjl 
226225cf1a30Sjl 	mp = id;
226325cf1a30Sjl 
226425cf1a30Sjl 	if (mp->dev.node)
226525cf1a30Sjl 		drmach_node_dispose(mp->dev.node);
226625cf1a30Sjl 
226725cf1a30Sjl 	if (mp->memlist) {
226825cf1a30Sjl 		memlist_delete(mp->memlist);
226925cf1a30Sjl 		mp->memlist = NULL;
227025cf1a30Sjl 	}
227168ac2337Sjl 
227268ac2337Sjl 	kmem_free(mp, sizeof (*mp));
227325cf1a30Sjl }
227425cf1a30Sjl 
227525cf1a30Sjl sbd_error_t *
227625cf1a30Sjl drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
227725cf1a30Sjl {
227825cf1a30Sjl 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
227925cf1a30Sjl 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
228025cf1a30Sjl 	int		rv;
228125cf1a30Sjl 
228225cf1a30Sjl 	ASSERT(size != 0);
228325cf1a30Sjl 
228425cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
228525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
228625cf1a30Sjl 
228785f58038Sdp 	rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
228825cf1a30Sjl 	if (rv == ENOMEM) {
228907d06da5SSurya Prakki 		cmn_err(CE_WARN, "%lu megabytes not available to kernel cage",
229007d06da5SSurya Prakki 		    (ulong_t)(size == 0 ? 0 : size / MBYTE));
229125cf1a30Sjl 	} else if (rv != 0) {
229225cf1a30Sjl 		/* catch this in debug kernels */
229325cf1a30Sjl 		ASSERT(0);
229425cf1a30Sjl 
2295e98fafb9Sjl 		cmn_err(CE_WARN, "unexpected kcage_range_add return value %d",
2296e98fafb9Sjl 		    rv);
229725cf1a30Sjl 	}
229825cf1a30Sjl 
229925cf1a30Sjl 	if (rv) {
230025cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
230125cf1a30Sjl 	}
230225cf1a30Sjl 	else
230325cf1a30Sjl 		return (NULL);
230425cf1a30Sjl }
230525cf1a30Sjl 
230625cf1a30Sjl sbd_error_t *
230725cf1a30Sjl drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
230825cf1a30Sjl {
230925cf1a30Sjl 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
231025cf1a30Sjl 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
231125cf1a30Sjl 	int		rv;
231225cf1a30Sjl 
231325cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
231425cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
231525cf1a30Sjl 
231625cf1a30Sjl 	if (size > 0) {
231725cf1a30Sjl 		rv = kcage_range_delete_post_mem_del(basepfn, npages);
231825cf1a30Sjl 		if (rv != 0) {
231925cf1a30Sjl 			cmn_err(CE_WARN,
232025cf1a30Sjl 			    "unexpected kcage_range_delete_post_mem_del"
232125cf1a30Sjl 			    " return value %d", rv);
232225cf1a30Sjl 			return (DRMACH_INTERNAL_ERROR());
232325cf1a30Sjl 		}
232425cf1a30Sjl 	}
232525cf1a30Sjl 
232625cf1a30Sjl 	return (NULL);
232725cf1a30Sjl }
232825cf1a30Sjl 
232925cf1a30Sjl sbd_error_t *
233025cf1a30Sjl drmach_mem_disable(drmachid_t id)
233125cf1a30Sjl {
233225cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
233325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
233425cf1a30Sjl 	else {
233525cf1a30Sjl 		drmach_flush_all();
233625cf1a30Sjl 		return (NULL);
233725cf1a30Sjl 	}
233825cf1a30Sjl }
233925cf1a30Sjl 
234025cf1a30Sjl sbd_error_t *
234125cf1a30Sjl drmach_mem_enable(drmachid_t id)
234225cf1a30Sjl {
234325cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
234425cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
234525cf1a30Sjl 	else
234625cf1a30Sjl 		return (NULL);
234725cf1a30Sjl }
234825cf1a30Sjl 
234925cf1a30Sjl sbd_error_t *
235025cf1a30Sjl drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem)
235125cf1a30Sjl {
235225cf1a30Sjl 	drmach_mem_t *mp;
235325cf1a30Sjl 
235425cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
235525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
235625cf1a30Sjl 
235725cf1a30Sjl 	mp = (drmach_mem_t *)id;
235825cf1a30Sjl 
235925cf1a30Sjl 	/*
236025cf1a30Sjl 	 * This is only used by dr to round up/down the memory
236125cf1a30Sjl 	 * for copying. Our unit of memory isolation is 64 MB.
236225cf1a30Sjl 	 */
236325cf1a30Sjl 
236425cf1a30Sjl 	mem->mi_alignment_mask = (64 * 1024 * 1024 - 1);
236525cf1a30Sjl 	mem->mi_basepa = mp->base_pa;
236625cf1a30Sjl 	mem->mi_size = mp->nbytes;
236725cf1a30Sjl 	mem->mi_slice_size = mp->slice_size;
236825cf1a30Sjl 
236925cf1a30Sjl 	return (NULL);
237025cf1a30Sjl }
237125cf1a30Sjl 
237225cf1a30Sjl sbd_error_t *
237325cf1a30Sjl drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa)
237425cf1a30Sjl {
237525cf1a30Sjl 	drmach_mem_t *mp;
237625cf1a30Sjl 
237725cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
237825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
237925cf1a30Sjl 
238025cf1a30Sjl 	mp = (drmach_mem_t *)id;
238125cf1a30Sjl 
238225cf1a30Sjl 	*pa = mp->base_pa;
238325cf1a30Sjl 	return (NULL);
238425cf1a30Sjl }
238525cf1a30Sjl 
238625cf1a30Sjl sbd_error_t *
238725cf1a30Sjl drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
238825cf1a30Sjl {
238925cf1a30Sjl 	drmach_mem_t	*mem;
2390b307f191Sbm #ifdef	DEBUG
239125cf1a30Sjl 	int		rv;
2392b307f191Sbm #endif
239325cf1a30Sjl 	struct memlist	*mlist;
239425cf1a30Sjl 
239525cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
239625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
239725cf1a30Sjl 
239825cf1a30Sjl 	mem = (drmach_mem_t *)id;
239925cf1a30Sjl 	mlist = memlist_dup(mem->memlist);
240025cf1a30Sjl 
240125cf1a30Sjl #ifdef DEBUG
240225cf1a30Sjl 	/*
240325cf1a30Sjl 	 * Make sure the incoming memlist doesn't already
240425cf1a30Sjl 	 * intersect with what's present in the system (phys_install).
240525cf1a30Sjl 	 */
240625cf1a30Sjl 	memlist_read_lock();
240725cf1a30Sjl 	rv = memlist_intersect(phys_install, mlist);
240825cf1a30Sjl 	memlist_read_unlock();
240925cf1a30Sjl 	if (rv) {
2410e98fafb9Sjl 		DRMACH_PR("Derived memlist intersects with phys_install\n");
241125cf1a30Sjl 		memlist_dump(mlist);
241225cf1a30Sjl 
241325cf1a30Sjl 		DRMACH_PR("phys_install memlist:\n");
241425cf1a30Sjl 		memlist_dump(phys_install);
241525cf1a30Sjl 
241625cf1a30Sjl 		memlist_delete(mlist);
241725cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
241825cf1a30Sjl 	}
241925cf1a30Sjl 
242025cf1a30Sjl 	DRMACH_PR("Derived memlist:");
242125cf1a30Sjl 	memlist_dump(mlist);
242225cf1a30Sjl #endif
242325cf1a30Sjl 	*ml = mlist;
242425cf1a30Sjl 
242525cf1a30Sjl 	return (NULL);
242625cf1a30Sjl }
242725cf1a30Sjl 
242825cf1a30Sjl sbd_error_t *
242925cf1a30Sjl drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
243025cf1a30Sjl {
243125cf1a30Sjl 	drmach_mem_t	*mem;
243225cf1a30Sjl 
243325cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
243425cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
243525cf1a30Sjl 
243625cf1a30Sjl 	mem = (drmach_mem_t *)id;
243725cf1a30Sjl 
243825cf1a30Sjl 	*bytes = mem->slice_size;
243925cf1a30Sjl 
244025cf1a30Sjl 	return (NULL);
244125cf1a30Sjl }
244225cf1a30Sjl 
244325cf1a30Sjl 
244425cf1a30Sjl /* ARGSUSED */
244525cf1a30Sjl processorid_t
244625cf1a30Sjl drmach_mem_cpu_affinity(drmachid_t id)
244725cf1a30Sjl {
244825cf1a30Sjl 	return (CPU_CURRENT);
244925cf1a30Sjl }
245025cf1a30Sjl 
245125cf1a30Sjl static sbd_error_t *
245225cf1a30Sjl drmach_mem_release(drmachid_t id)
245325cf1a30Sjl {
245425cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
245525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
245625cf1a30Sjl 	return (NULL);
245725cf1a30Sjl }
245825cf1a30Sjl 
245925cf1a30Sjl static sbd_error_t *
246025cf1a30Sjl drmach_mem_status(drmachid_t id, drmach_status_t *stat)
246125cf1a30Sjl {
246225cf1a30Sjl 	drmach_mem_t *dp;
246325cf1a30Sjl 	uint64_t	 pa, slice_size;
246425cf1a30Sjl 	struct memlist	*ml;
246525cf1a30Sjl 
246625cf1a30Sjl 	ASSERT(DRMACH_IS_MEM_ID(id));
246725cf1a30Sjl 	dp = id;
246825cf1a30Sjl 
246925cf1a30Sjl 	/* get starting physical address of target memory */
247025cf1a30Sjl 	pa = dp->base_pa;
247125cf1a30Sjl 
247225cf1a30Sjl 	/* round down to slice boundary */
247325cf1a30Sjl 	slice_size = dp->slice_size;
247425cf1a30Sjl 	pa &= ~(slice_size - 1);
247525cf1a30Sjl 
247625cf1a30Sjl 	/* stop at first span that is in slice */
247725cf1a30Sjl 	memlist_read_lock();
247856f33205SJonathan Adams 	for (ml = phys_install; ml; ml = ml->ml_next)
247956f33205SJonathan Adams 		if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
248025cf1a30Sjl 			break;
248125cf1a30Sjl 	memlist_read_unlock();
248225cf1a30Sjl 
248325cf1a30Sjl 	stat->assigned = dp->dev.bp->assigned;
248425cf1a30Sjl 	stat->powered = dp->dev.bp->powered;
248525cf1a30Sjl 	stat->configured = (ml != NULL);
248625cf1a30Sjl 	stat->busy = dp->dev.busy;
248707d06da5SSurya Prakki 	(void) strncpy(stat->type, dp->dev.type, sizeof (stat->type));
248825cf1a30Sjl 	stat->info[0] = '\0';
248925cf1a30Sjl 
249025cf1a30Sjl 	return (NULL);
249125cf1a30Sjl }
249225cf1a30Sjl 
249325cf1a30Sjl 
249425cf1a30Sjl sbd_error_t *
249525cf1a30Sjl drmach_board_deprobe(drmachid_t id)
249625cf1a30Sjl {
249725cf1a30Sjl 	drmach_board_t	*bp;
249825cf1a30Sjl 
249925cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
250025cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
250125cf1a30Sjl 
250225cf1a30Sjl 	bp = id;
250325cf1a30Sjl 
250468ac2337Sjl 	cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum);
250525cf1a30Sjl 
250625cf1a30Sjl 	if (bp->tree) {
250725cf1a30Sjl 		drmach_node_dispose(bp->tree);
250825cf1a30Sjl 		bp->tree = NULL;
250925cf1a30Sjl 	}
251025cf1a30Sjl 	if (bp->devices) {
251125cf1a30Sjl 		drmach_array_dispose(bp->devices, drmach_device_dispose);
251225cf1a30Sjl 		bp->devices = NULL;
251325cf1a30Sjl 	}
251425cf1a30Sjl 
251525cf1a30Sjl 	bp->boot_board = 0;
251625cf1a30Sjl 
251725cf1a30Sjl 	return (NULL);
251825cf1a30Sjl }
251925cf1a30Sjl 
252025cf1a30Sjl /*ARGSUSED*/
252125cf1a30Sjl static sbd_error_t *
252225cf1a30Sjl drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
252325cf1a30Sjl {
252425cf1a30Sjl 	drmach_board_t		*bp = (drmach_board_t *)id;
252525cf1a30Sjl 	sbd_error_t		*err = NULL;
252625cf1a30Sjl 	int	rv;
2527e98fafb9Sjl 	unsigned cpu_impl;
252825cf1a30Sjl 
252925cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
253025cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
253125cf1a30Sjl 
253225cf1a30Sjl 	DRMACH_PR("calling opl_probe_board for bnum=%d\n", bp->bnum);
2533e98fafb9Sjl 	rv = opl_probe_sb(bp->bnum, &cpu_impl);
253425cf1a30Sjl 	if (rv != 0) {
25358eafe49bSbm 		err = drerr_new(1, EOPL_PROBE, bp->cm.name);
253625cf1a30Sjl 		return (err);
253725cf1a30Sjl 	}
253825cf1a30Sjl 	return (err);
253925cf1a30Sjl }
254025cf1a30Sjl 
254125cf1a30Sjl /*ARGSUSED*/
254225cf1a30Sjl static sbd_error_t *
254325cf1a30Sjl drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
254425cf1a30Sjl {
254525cf1a30Sjl 	drmach_board_t	*bp;
254625cf1a30Sjl 	sbd_error_t	*err = NULL;
254725cf1a30Sjl 	int	rv;
254825cf1a30Sjl 
254925cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
255025cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
255125cf1a30Sjl 	bp = (drmach_board_t *)id;
255225cf1a30Sjl 
255325cf1a30Sjl 	cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
255425cf1a30Sjl 
255525cf1a30Sjl 	rv = opl_unprobe_sb(bp->bnum);
255625cf1a30Sjl 	if (rv != 0) {
25578eafe49bSbm 		err = drerr_new(1, EOPL_DEPROBE, bp->cm.name);
255825cf1a30Sjl 	}
255925cf1a30Sjl 
256025cf1a30Sjl 	return (err);
256125cf1a30Sjl }
256225cf1a30Sjl 
256325cf1a30Sjl 
256425cf1a30Sjl /*ARGSUSED*/
256525cf1a30Sjl sbd_error_t *
256625cf1a30Sjl drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
256725cf1a30Sjl {
256825cf1a30Sjl 	struct memlist	*ml;
256925cf1a30Sjl 	uint64_t	src_pa;
257025cf1a30Sjl 	uint64_t	dst_pa;
257125cf1a30Sjl 	uint64_t	dst;
257225cf1a30Sjl 
257325cf1a30Sjl 	dst_pa = va_to_pa(&dst);
257425cf1a30Sjl 
257525cf1a30Sjl 	memlist_read_lock();
257656f33205SJonathan Adams 	for (ml = phys_install; ml; ml = ml->ml_next) {
257725cf1a30Sjl 		uint64_t	nbytes;
257825cf1a30Sjl 
257956f33205SJonathan Adams 		src_pa = ml->ml_address;
258056f33205SJonathan Adams 		nbytes = ml->ml_size;
258125cf1a30Sjl 
258225cf1a30Sjl 		while (nbytes != 0ull) {
258325cf1a30Sjl 
258425cf1a30Sjl 			/* copy 32 bytes at arc_pa to dst_pa */
258525cf1a30Sjl 			bcopy32_il(src_pa, dst_pa);
258625cf1a30Sjl 
258725cf1a30Sjl 			/* increment by 32 bytes */
258825cf1a30Sjl 			src_pa += (4 * sizeof (uint64_t));
258925cf1a30Sjl 
259025cf1a30Sjl 			/* decrement by 32 bytes */
259125cf1a30Sjl 			nbytes -= (4 * sizeof (uint64_t));
259225cf1a30Sjl 		}
259325cf1a30Sjl 	}
259425cf1a30Sjl 	memlist_read_unlock();
259525cf1a30Sjl 
259625cf1a30Sjl 	return (NULL);
259725cf1a30Sjl }
259825cf1a30Sjl 
259925cf1a30Sjl static struct {
260025cf1a30Sjl 	const char	*name;
260125cf1a30Sjl 	sbd_error_t	*(*handler)(drmachid_t id, drmach_opts_t *opts);
260225cf1a30Sjl } drmach_pt_arr[] = {
260325cf1a30Sjl 	{ "readmem",		drmach_pt_readmem		},
260425cf1a30Sjl 	{ "ikprobe",	drmach_pt_ikprobe	},
260525cf1a30Sjl 	{ "ikdeprobe",	drmach_pt_ikdeprobe	},
260625cf1a30Sjl 
260725cf1a30Sjl 	/* the following line must always be last */
260825cf1a30Sjl 	{ NULL,			NULL				}
260925cf1a30Sjl };
261025cf1a30Sjl 
261125cf1a30Sjl /*ARGSUSED*/
261225cf1a30Sjl sbd_error_t *
261325cf1a30Sjl drmach_passthru(drmachid_t id, drmach_opts_t *opts)
261425cf1a30Sjl {
261525cf1a30Sjl 	int		i;
261625cf1a30Sjl 	sbd_error_t	*err;
261725cf1a30Sjl 
261825cf1a30Sjl 	i = 0;
261925cf1a30Sjl 	while (drmach_pt_arr[i].name != NULL) {
262025cf1a30Sjl 		int len = strlen(drmach_pt_arr[i].name);
262125cf1a30Sjl 
262225cf1a30Sjl 		if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
262325cf1a30Sjl 			break;
262425cf1a30Sjl 
262525cf1a30Sjl 		i += 1;
262625cf1a30Sjl 	}
262725cf1a30Sjl 
262825cf1a30Sjl 	if (drmach_pt_arr[i].name == NULL)
262925cf1a30Sjl 		err = drerr_new(0, EOPL_UNKPTCMD, opts->copts);
263025cf1a30Sjl 	else
263125cf1a30Sjl 		err = (*drmach_pt_arr[i].handler)(id, opts);
263225cf1a30Sjl 
263325cf1a30Sjl 	return (err);
263425cf1a30Sjl }
263525cf1a30Sjl 
263625cf1a30Sjl sbd_error_t *
263725cf1a30Sjl drmach_release(drmachid_t id)
263825cf1a30Sjl {
263925cf1a30Sjl 	drmach_common_t *cp;
264025cf1a30Sjl 
264125cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
264225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
264325cf1a30Sjl 	cp = id;
264425cf1a30Sjl 
264525cf1a30Sjl 	return (cp->release(id));
264625cf1a30Sjl }
264725cf1a30Sjl 
264825cf1a30Sjl sbd_error_t *
264925cf1a30Sjl drmach_status(drmachid_t id, drmach_status_t *stat)
265025cf1a30Sjl {
265125cf1a30Sjl 	drmach_common_t *cp;
265225cf1a30Sjl 	sbd_error_t	*err;
265325cf1a30Sjl 
265425cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_READER);
265525cf1a30Sjl 
265625cf1a30Sjl 	if (!DRMACH_IS_ID(id)) {
265725cf1a30Sjl 		rw_exit(&drmach_boards_rwlock);
265825cf1a30Sjl 		return (drerr_new(0, EOPL_NOTID, NULL));
265925cf1a30Sjl 	}
266025cf1a30Sjl 	cp = (drmach_common_t *)id;
266125cf1a30Sjl 	err = cp->status(id, stat);
266225cf1a30Sjl 
266325cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
266425cf1a30Sjl 
266525cf1a30Sjl 	return (err);
266625cf1a30Sjl }
266725cf1a30Sjl 
266825cf1a30Sjl static sbd_error_t *
266925cf1a30Sjl drmach_i_status(drmachid_t id, drmach_status_t *stat)
267025cf1a30Sjl {
267125cf1a30Sjl 	drmach_common_t *cp;
267225cf1a30Sjl 
267325cf1a30Sjl 	if (!DRMACH_IS_ID(id))
267425cf1a30Sjl 		return (drerr_new(0, EOPL_NOTID, NULL));
267525cf1a30Sjl 	cp = id;
267625cf1a30Sjl 
267725cf1a30Sjl 	return (cp->status(id, stat));
267825cf1a30Sjl }
267925cf1a30Sjl 
268025cf1a30Sjl /*ARGSUSED*/
268125cf1a30Sjl sbd_error_t *
268225cf1a30Sjl drmach_unconfigure(drmachid_t id, int flags)
268325cf1a30Sjl {
268425cf1a30Sjl 	drmach_device_t *dp;
268525cf1a30Sjl 	dev_info_t	*rdip, *fdip = NULL;
268625cf1a30Sjl 	char name[OBP_MAXDRVNAME];
268725cf1a30Sjl 	int rv;
268825cf1a30Sjl 
268925cf1a30Sjl 	if (DRMACH_IS_CPU_ID(id))
269025cf1a30Sjl 		return (NULL);
269125cf1a30Sjl 
269225cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
269325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
269425cf1a30Sjl 
269525cf1a30Sjl 	dp = id;
269625cf1a30Sjl 
269725cf1a30Sjl 	rdip = dp->node->n_getdip(dp->node);
269825cf1a30Sjl 
269925cf1a30Sjl 	ASSERT(rdip);
270025cf1a30Sjl 
270125cf1a30Sjl 	rv = dp->node->n_getprop(dp->node, "name", name, OBP_MAXDRVNAME);
270225cf1a30Sjl 
270325cf1a30Sjl 	if (rv)
270425cf1a30Sjl 		return (NULL);
270525cf1a30Sjl 
270625cf1a30Sjl 	/*
270725cf1a30Sjl 	 * Note: FORCE flag is no longer necessary under devfs
270825cf1a30Sjl 	 */
270925cf1a30Sjl 
271025cf1a30Sjl 	ASSERT(e_ddi_branch_held(rdip));
271125cf1a30Sjl 	if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) {
271225cf1a30Sjl 		sbd_error_t	*err;
271325cf1a30Sjl 		char		*path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
271425cf1a30Sjl 
271525cf1a30Sjl 		/*
271625cf1a30Sjl 		 * If non-NULL, fdip is returned held and must be released.
271725cf1a30Sjl 		 */
271825cf1a30Sjl 		if (fdip != NULL) {
271925cf1a30Sjl 			(void) ddi_pathname(fdip, path);
272025cf1a30Sjl 			ndi_rele_devi(fdip);
272125cf1a30Sjl 		} else {
272225cf1a30Sjl 			(void) ddi_pathname(rdip, path);
272325cf1a30Sjl 		}
272425cf1a30Sjl 
272525cf1a30Sjl 		err = drerr_new(1, EOPL_DRVFAIL, path);
272625cf1a30Sjl 
272725cf1a30Sjl 		kmem_free(path, MAXPATHLEN);
272825cf1a30Sjl 
272925cf1a30Sjl 		return (err);
273025cf1a30Sjl 	}
273125cf1a30Sjl 
273225cf1a30Sjl 	return (NULL);
273325cf1a30Sjl }
273425cf1a30Sjl 
273525cf1a30Sjl 
273625cf1a30Sjl int
273725cf1a30Sjl drmach_cpu_poweron(struct cpu *cp)
273825cf1a30Sjl {
273925cf1a30Sjl 	int bnum, cpuid, onb_core_num, strand_id;
274025cf1a30Sjl 	drmach_board_t *bp;
274125cf1a30Sjl 
274225cf1a30Sjl 	DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
274325cf1a30Sjl 
274425cf1a30Sjl 	cpuid = cp->cpu_id;
274525cf1a30Sjl 	bnum = LSB_ID(cpuid);
274625cf1a30Sjl 	onb_core_num = ON_BOARD_CORE_NUM(cpuid);
274725cf1a30Sjl 	strand_id = STRAND_ID(cpuid);
274825cf1a30Sjl 	bp = drmach_get_board_by_bnum(bnum);
274925cf1a30Sjl 
275025cf1a30Sjl 	ASSERT(bp);
275125cf1a30Sjl 	if (bp->cores[onb_core_num].core_hotadded == 0) {
275225cf1a30Sjl 		if (drmach_add_remove_cpu(bnum, onb_core_num,
2753e98fafb9Sjl 		    HOTADD_CPU) != 0) {
275425cf1a30Sjl 			cmn_err(CE_WARN, "Failed to add CMP %d on board %d\n",
2755e98fafb9Sjl 			    onb_core_num, bnum);
275625cf1a30Sjl 			return (EIO);
275725cf1a30Sjl 		}
275825cf1a30Sjl 	}
275925cf1a30Sjl 
276025cf1a30Sjl 	ASSERT(MUTEX_HELD(&cpu_lock));
276125cf1a30Sjl 
276225cf1a30Sjl 	if (drmach_cpu_start(cp) != 0) {
276325cf1a30Sjl 		if (bp->cores[onb_core_num].core_started == 0) {
276425cf1a30Sjl 			/*
276525cf1a30Sjl 			 * we must undo the hotadd or no one will do that
276625cf1a30Sjl 			 * If this fails, we will do this again in
276725cf1a30Sjl 			 * drmach_board_disconnect.
276825cf1a30Sjl 			 */
276925cf1a30Sjl 			if (drmach_add_remove_cpu(bnum, onb_core_num,
2770e98fafb9Sjl 			    HOTREMOVE_CPU) != 0) {
277125cf1a30Sjl 				cmn_err(CE_WARN, "Failed to remove CMP %d "
2772e98fafb9Sjl 				    "on board %d\n", onb_core_num, bnum);
277325cf1a30Sjl 			}
277425cf1a30Sjl 		}
277525cf1a30Sjl 		return (EBUSY);
277625cf1a30Sjl 	} else {
277725cf1a30Sjl 		bp->cores[onb_core_num].core_started |= (1 << strand_id);
277825cf1a30Sjl 		return (0);
277925cf1a30Sjl 	}
278025cf1a30Sjl }
278125cf1a30Sjl 
278225cf1a30Sjl int
278325cf1a30Sjl drmach_cpu_poweroff(struct cpu *cp)
278425cf1a30Sjl {
2785*bbe1232eSToomas Soome 	int		rv = 0;
278625cf1a30Sjl 	processorid_t	cpuid = cp->cpu_id;
278725cf1a30Sjl 
278825cf1a30Sjl 	DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
278925cf1a30Sjl 
279025cf1a30Sjl 	ASSERT(MUTEX_HELD(&cpu_lock));
279125cf1a30Sjl 
279225cf1a30Sjl 	/*
279325cf1a30Sjl 	 * Capture all CPUs (except for detaching proc) to prevent
279425cf1a30Sjl 	 * crosscalls to the detaching proc until it has cleared its
279525cf1a30Sjl 	 * bit in cpu_ready_set.
279625cf1a30Sjl 	 *
279725cf1a30Sjl 	 * The CPU's remain paused and the prom_mutex is known to be free.
279825cf1a30Sjl 	 * This prevents the x-trap victim from blocking when doing prom
279925cf1a30Sjl 	 * IEEE-1275 calls at a high PIL level.
280025cf1a30Sjl 	 */
280125cf1a30Sjl 
280225cf1a30Sjl 	promsafe_pause_cpus();
280325cf1a30Sjl 
280425cf1a30Sjl 	/*
280525cf1a30Sjl 	 * Quiesce interrupts on the target CPU. We do this by setting
280625cf1a30Sjl 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
280725cf1a30Sjl 	 * prevent it from receiving cross calls and cross traps.
280825cf1a30Sjl 	 * This prevents the processor from receiving any new soft interrupts.
280925cf1a30Sjl 	 */
281025cf1a30Sjl 	mp_cpu_quiesce(cp);
281125cf1a30Sjl 
281225cf1a30Sjl 	rv = prom_stopcpu_bycpuid(cpuid);
281325cf1a30Sjl 	if (rv == 0)
281425cf1a30Sjl 		cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
281525cf1a30Sjl 
281625cf1a30Sjl 	start_cpus();
281725cf1a30Sjl 
281825cf1a30Sjl 	if (rv == 0) {
281925cf1a30Sjl 		int bnum, onb_core_num, strand_id;
282025cf1a30Sjl 		drmach_board_t *bp;
282125cf1a30Sjl 
282225cf1a30Sjl 		CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
282325cf1a30Sjl 
282425cf1a30Sjl 		bnum = LSB_ID(cpuid);
282525cf1a30Sjl 		onb_core_num = ON_BOARD_CORE_NUM(cpuid);
282625cf1a30Sjl 		strand_id = STRAND_ID(cpuid);
282725cf1a30Sjl 		bp = drmach_get_board_by_bnum(bnum);
282825cf1a30Sjl 		ASSERT(bp);
282925cf1a30Sjl 
283025cf1a30Sjl 		bp->cores[onb_core_num].core_started &= ~(1 << strand_id);
283125cf1a30Sjl 		if (bp->cores[onb_core_num].core_started == 0) {
283225cf1a30Sjl 			if (drmach_add_remove_cpu(bnum, onb_core_num,
2833e98fafb9Sjl 			    HOTREMOVE_CPU) != 0) {
2834e98fafb9Sjl 				cmn_err(CE_WARN, "Failed to remove CMP %d LSB "
2835e98fafb9Sjl 				    "%d\n", onb_core_num, bnum);
283625cf1a30Sjl 				return (EIO);
283725cf1a30Sjl 			}
283825cf1a30Sjl 		}
283925cf1a30Sjl 	}
284025cf1a30Sjl 
284125cf1a30Sjl 	return (rv);
284225cf1a30Sjl }
284325cf1a30Sjl 
284425cf1a30Sjl /*ARGSUSED*/
284525cf1a30Sjl int
284625cf1a30Sjl drmach_verify_sr(dev_info_t *dip, int sflag)
284725cf1a30Sjl {
284825cf1a30Sjl 	return (0);
284925cf1a30Sjl }
285025cf1a30Sjl 
285125cf1a30Sjl void
285225cf1a30Sjl drmach_suspend_last(void)
285325cf1a30Sjl {
285425cf1a30Sjl }
285525cf1a30Sjl 
285625cf1a30Sjl void
285725cf1a30Sjl drmach_resume_first(void)
285825cf1a30Sjl {
285925cf1a30Sjl }
286025cf1a30Sjl 
286125cf1a30Sjl /*
286225cf1a30Sjl  * Log a DR sysevent.
286325cf1a30Sjl  * Return value: 0 success, non-zero failure.
286425cf1a30Sjl  */
286525cf1a30Sjl int
286625cf1a30Sjl drmach_log_sysevent(int board, char *hint, int flag, int verbose)
286725cf1a30Sjl {
286825cf1a30Sjl 	sysevent_t			*ev;
286925cf1a30Sjl 	sysevent_id_t			eid;
287025cf1a30Sjl 	int				rv, km_flag;
287125cf1a30Sjl 	sysevent_value_t		evnt_val;
287225cf1a30Sjl 	sysevent_attr_list_t		*evnt_attr_list = NULL;
287325cf1a30Sjl 	char				attach_pnt[MAXNAMELEN];
287425cf1a30Sjl 
287525cf1a30Sjl 	km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
287625cf1a30Sjl 	attach_pnt[0] = '\0';
287725cf1a30Sjl 	if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
287825cf1a30Sjl 		rv = -1;
287925cf1a30Sjl 		goto logexit;
288025cf1a30Sjl 	}
2881b307f191Sbm 	if (verbose) {
288225cf1a30Sjl 		DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
2883e98fafb9Sjl 		    attach_pnt, hint, flag, verbose);
2884b307f191Sbm 	}
288525cf1a30Sjl 
288625cf1a30Sjl 	if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
2887e98fafb9Sjl 	    SUNW_KERN_PUB"dr", km_flag)) == NULL) {
288825cf1a30Sjl 		rv = -2;
288925cf1a30Sjl 		goto logexit;
289025cf1a30Sjl 	}
289125cf1a30Sjl 	evnt_val.value_type = SE_DATA_TYPE_STRING;
289225cf1a30Sjl 	evnt_val.value.sv_string = attach_pnt;
2893e98fafb9Sjl 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
2894e98fafb9Sjl 	    km_flag)) != 0)
289525cf1a30Sjl 		goto logexit;
289625cf1a30Sjl 
289725cf1a30Sjl 	evnt_val.value_type = SE_DATA_TYPE_STRING;
289825cf1a30Sjl 	evnt_val.value.sv_string = hint;
2899e98fafb9Sjl 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
2900e98fafb9Sjl 	    km_flag)) != 0) {
290125cf1a30Sjl 		sysevent_free_attr(evnt_attr_list);
290225cf1a30Sjl 		goto logexit;
290325cf1a30Sjl 	}
290425cf1a30Sjl 
290525cf1a30Sjl 	(void) sysevent_attach_attributes(ev, evnt_attr_list);
290625cf1a30Sjl 
290725cf1a30Sjl 	/*
290825cf1a30Sjl 	 * Log the event but do not sleep waiting for its
290925cf1a30Sjl 	 * delivery. This provides insulation from syseventd.
291025cf1a30Sjl 	 */
291125cf1a30Sjl 	rv = log_sysevent(ev, SE_NOSLEEP, &eid);
291225cf1a30Sjl 
291325cf1a30Sjl logexit:
291425cf1a30Sjl 	if (ev)
291525cf1a30Sjl 		sysevent_free(ev);
291625cf1a30Sjl 	if ((rv != 0) && verbose)
2917e98fafb9Sjl 		cmn_err(CE_WARN, "drmach_log_sysevent failed (rv %d) for %s "
2918e98fafb9Sjl 		    " %s\n", rv, attach_pnt, hint);
291925cf1a30Sjl 
292025cf1a30Sjl 	return (rv);
292125cf1a30Sjl }
292225cf1a30Sjl 
292325cf1a30Sjl #define	OPL_DR_STATUS_PROP "dr-status"
292425cf1a30Sjl 
292525cf1a30Sjl static int
292625cf1a30Sjl opl_check_dr_status()
292725cf1a30Sjl {
292825cf1a30Sjl 	pnode_t	node;
292925cf1a30Sjl 	int	rtn, len;
293025cf1a30Sjl 	char	*str;
293125cf1a30Sjl 
293225cf1a30Sjl 	node = prom_rootnode();
293325cf1a30Sjl 	if (node == OBP_BADNODE) {
293425cf1a30Sjl 		return (1);
293525cf1a30Sjl 	}
293625cf1a30Sjl 
293725cf1a30Sjl 	len = prom_getproplen(node, OPL_DR_STATUS_PROP);
293825cf1a30Sjl 	if (len == -1) {
293925cf1a30Sjl 		/*
294025cf1a30Sjl 		 * dr-status doesn't exist when DR is activated and
294125cf1a30Sjl 		 * any warning messages aren't needed.
294225cf1a30Sjl 		 */
294325cf1a30Sjl 		return (1);
294425cf1a30Sjl 	}
294525cf1a30Sjl 
294625cf1a30Sjl 	str = (char *)kmem_zalloc(len+1, KM_SLEEP);
294725cf1a30Sjl 	rtn = prom_getprop(node, OPL_DR_STATUS_PROP, str);
294825cf1a30Sjl 	kmem_free(str, len + 1);
294925cf1a30Sjl 	if (rtn == -1) {
295025cf1a30Sjl 		return (1);
295125cf1a30Sjl 	} else {
295225cf1a30Sjl 		return (0);
295325cf1a30Sjl 	}
295425cf1a30Sjl }
295525cf1a30Sjl 
295625cf1a30Sjl /* we are allocating memlist from TLB locked pages to avoid tlbmisses */
295725cf1a30Sjl 
295825cf1a30Sjl static struct memlist *
295925cf1a30Sjl drmach_memlist_add_span(drmach_copy_rename_program_t *p,
2960*bbe1232eSToomas Soome     struct memlist *mlist, uint64_t base, uint64_t len)
296125cf1a30Sjl {
296225cf1a30Sjl 	struct memlist	*ml, *tl, *nl;
296325cf1a30Sjl 
296425cf1a30Sjl 	if (len == 0ull)
296525cf1a30Sjl 		return (NULL);
296625cf1a30Sjl 
296725cf1a30Sjl 	if (mlist == NULL) {
296825cf1a30Sjl 		mlist = p->free_mlist;
296925cf1a30Sjl 		if (mlist == NULL)
297025cf1a30Sjl 			return (NULL);
297156f33205SJonathan Adams 		p->free_mlist = mlist->ml_next;
297256f33205SJonathan Adams 		mlist->ml_address = base;
297356f33205SJonathan Adams 		mlist->ml_size = len;
297456f33205SJonathan Adams 		mlist->ml_next = mlist->ml_prev = NULL;
297525cf1a30Sjl 
297625cf1a30Sjl 		return (mlist);
297725cf1a30Sjl 	}
297825cf1a30Sjl 
297956f33205SJonathan Adams 	for (tl = ml = mlist; ml; tl = ml, ml = ml->ml_next) {
298056f33205SJonathan Adams 		if (base < ml->ml_address) {
298156f33205SJonathan Adams 			if ((base + len) < ml->ml_address) {
298225cf1a30Sjl 				nl = p->free_mlist;
298325cf1a30Sjl 				if (nl == NULL)
298425cf1a30Sjl 					return (NULL);
298556f33205SJonathan Adams 				p->free_mlist = nl->ml_next;
298656f33205SJonathan Adams 				nl->ml_address = base;
298756f33205SJonathan Adams 				nl->ml_size = len;
298856f33205SJonathan Adams 				nl->ml_next = ml;
298956f33205SJonathan Adams 				if ((nl->ml_prev = ml->ml_prev) != NULL)
299056f33205SJonathan Adams 					nl->ml_prev->ml_next = nl;
299156f33205SJonathan Adams 				ml->ml_prev = nl;
299225cf1a30Sjl 				if (mlist == ml)
299325cf1a30Sjl 					mlist = nl;
299425cf1a30Sjl 			} else {
299556f33205SJonathan Adams 				ml->ml_size = MAX((base + len),
299656f33205SJonathan Adams 				    (ml->ml_address + ml->ml_size)) - base;
299756f33205SJonathan Adams 				ml->ml_address = base;
299825cf1a30Sjl 			}
299925cf1a30Sjl 			break;
300025cf1a30Sjl 
300156f33205SJonathan Adams 		} else if (base <= (ml->ml_address + ml->ml_size)) {
300256f33205SJonathan Adams 			ml->ml_size =
300356f33205SJonathan Adams 			    MAX((base + len), (ml->ml_address + ml->ml_size)) -
300456f33205SJonathan Adams 			    MIN(ml->ml_address, base);
300556f33205SJonathan Adams 			ml->ml_address = MIN(ml->ml_address, base);
300625cf1a30Sjl 			break;
300725cf1a30Sjl 		}
300825cf1a30Sjl 	}
300925cf1a30Sjl 	if (ml == NULL) {
301025cf1a30Sjl 		nl = p->free_mlist;
301125cf1a30Sjl 		if (nl == NULL)
301225cf1a30Sjl 			return (NULL);
301356f33205SJonathan Adams 		p->free_mlist = nl->ml_next;
301456f33205SJonathan Adams 		nl->ml_address = base;
301556f33205SJonathan Adams 		nl->ml_size = len;
301656f33205SJonathan Adams 		nl->ml_next = NULL;
301756f33205SJonathan Adams 		nl->ml_prev = tl;
301856f33205SJonathan Adams 		tl->ml_next = nl;
301925cf1a30Sjl 	}
302025cf1a30Sjl 
302125cf1a30Sjl 	return (mlist);
302225cf1a30Sjl }
302325cf1a30Sjl 
302425cf1a30Sjl /*
302525cf1a30Sjl  * The routine performs the necessary memory COPY and MC adr SWITCH.
302625cf1a30Sjl  * Both operations MUST be at the same "level" so that the stack is
302725cf1a30Sjl  * maintained correctly between the copy and switch.  The switch
302825cf1a30Sjl  * portion implements a caching mechanism to guarantee the code text
302925cf1a30Sjl  * is cached prior to execution.  This is to guard against possible
303025cf1a30Sjl  * memory access while the MC adr's are being modified.
303125cf1a30Sjl  *
303225cf1a30Sjl  * IMPORTANT: The _drmach_copy_rename_end() function must immediately
303325cf1a30Sjl  * follow drmach_copy_rename_prog__relocatable() so that the correct
303425cf1a30Sjl  * "length" of the drmach_copy_rename_prog__relocatable can be
303525cf1a30Sjl  * calculated.  This routine MUST be a LEAF function, i.e. it can
303625cf1a30Sjl  * make NO function calls, primarily for two reasons:
303725cf1a30Sjl  *
303825cf1a30Sjl  *	1. We must keep the stack consistent across the "switch".
303925cf1a30Sjl  *	2. Function calls are compiled to relative offsets, and
304025cf1a30Sjl  *	   we execute this function we'll be executing it from
304125cf1a30Sjl  *	   a copied version in a different area of memory, thus
304225cf1a30Sjl  *	   the relative offsets will be bogus.
304325cf1a30Sjl  *
304425cf1a30Sjl  * Moreover, it must have the "__relocatable" suffix to inform DTrace
304525cf1a30Sjl  * providers (and anything else, for that matter) that this
304625cf1a30Sjl  * function's text is manually relocated elsewhere before it is
304725cf1a30Sjl  * executed.  That is, it cannot be safely instrumented with any
304825cf1a30Sjl  * methodology that is PC-relative.
304925cf1a30Sjl  */
305025cf1a30Sjl 
305125cf1a30Sjl /*
305225cf1a30Sjl  * We multiply this to system_clock_frequency so we
305325cf1a30Sjl  * are setting a delay of fmem_timeout second for
3054b307f191Sbm  * the rename command.
3055b307f191Sbm  *
3056b307f191Sbm  * FMEM command itself should complete within 15 sec.
3057b307f191Sbm  * We add 2 more sec to be conservative.
3058b307f191Sbm  *
3059b307f191Sbm  * Note that there is also a SCF BUSY bit checking
3060b307f191Sbm  * in drmach_asm.s right before FMEM command is
3061b307f191Sbm  * issued.  XSCF sets the SCF BUSY bit when the
3062b307f191Sbm  * other domain on the same PSB reboots and it
3063b307f191Sbm  * will not be able to service the FMEM command
3064b307f191Sbm  * within 15 sec.   After setting the SCF BUSY
3065b307f191Sbm  * bit, XSCF will wait a while before servicing
3066b307f191Sbm  * other reboot command so there is no race
3067b307f191Sbm  * condition.
306825cf1a30Sjl  */
3069b307f191Sbm 
307025cf1a30Sjl static int	fmem_timeout = 17;
3071b307f191Sbm 
3072b307f191Sbm /*
3073b307f191Sbm  *	The empirical data on some OPL system shows that
3074b307f191Sbm  *	we can copy 250 MB per second.  We set it to
3075*bbe1232eSToomas Soome  *	80 MB to be conservative.  In normal case,
3076b307f191Sbm  *	this timeout does not affect anything.
3077b307f191Sbm  */
3078b307f191Sbm 
3079b307f191Sbm static int	min_copy_size_per_sec = 80 * 1024 * 1024;
3080b307f191Sbm 
3081b307f191Sbm /*
3082b307f191Sbm  *	This is the timeout value for the xcall synchronization
3083b307f191Sbm  *	to get all the CPU ready to do the parallel copying.
3084b307f191Sbm  *	Even on a fully loaded system, 10 sec. should be long
3085b307f191Sbm  *	enough.
3086b307f191Sbm  */
3087b307f191Sbm 
3088b307f191Sbm static int	cpu_xcall_delay = 10;
308925cf1a30Sjl int drmach_disable_mcopy = 0;
309025cf1a30Sjl 
309168ac2337Sjl /*
309268ac2337Sjl  * The following delay loop executes sleep instruction to yield the
309368ac2337Sjl  * CPU to other strands.  If this is not done, some strand will tie
309468ac2337Sjl  * up the CPU in busy loops while the other strand cannot do useful
309568ac2337Sjl  * work.  The copy procedure will take a much longer time without this.
309668ac2337Sjl  */
309725cf1a30Sjl #define	DR_DELAY_IL(ms, freq)					\
309825cf1a30Sjl 	{							\
309925cf1a30Sjl 		uint64_t start;					\
310025cf1a30Sjl 		uint64_t nstick;				\
310125cf1a30Sjl 		volatile uint64_t now;				\
310225cf1a30Sjl 		nstick = ((uint64_t)ms * freq)/1000;		\
310325cf1a30Sjl 		start = drmach_get_stick_il();			\
310425cf1a30Sjl 		now = start;					\
310525cf1a30Sjl 		while ((now - start) <= nstick) {		\
310625cf1a30Sjl 			drmach_sleep_il();			\
310725cf1a30Sjl 			now = drmach_get_stick_il();		\
310825cf1a30Sjl 		}						\
310925cf1a30Sjl 	}
311025cf1a30Sjl 
31116534c6f0Swh /* Each loop is 2ms, timeout at 1000ms */
31126534c6f0Swh static int drmach_copy_rename_timeout = 500;
31136534c6f0Swh 
311425cf1a30Sjl static int
311525cf1a30Sjl drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog,
3116*bbe1232eSToomas Soome     int cpuid)
311725cf1a30Sjl {
311825cf1a30Sjl 	struct memlist		*ml;
311925cf1a30Sjl 	register int		rtn;
312025cf1a30Sjl 	int			i;
312125cf1a30Sjl 	register uint64_t	curr, limit;
312225cf1a30Sjl 	extern uint64_t		drmach_get_stick_il();
312325cf1a30Sjl 	extern void		membar_sync_il();
312425cf1a30Sjl 	extern void		flush_instr_mem_il(void*);
312568ac2337Sjl 	extern void		flush_windows_il(void);
312625cf1a30Sjl 	uint64_t		copy_start;
312725cf1a30Sjl 
312868ac2337Sjl 	/*
312968ac2337Sjl 	 * flush_windows is moved here to make sure all
313068ac2337Sjl 	 * registers used in the callers are flushed to
313168ac2337Sjl 	 * memory before the copy.
313268ac2337Sjl 	 *
313368ac2337Sjl 	 * If flush_windows() is called too early in the
313468ac2337Sjl 	 * calling function, the compiler might put some
313568ac2337Sjl 	 * data in the local registers after flush_windows().
313668ac2337Sjl 	 * After FMA, if there is any fill trap, the registers
313768ac2337Sjl 	 * will contain stale data.
313868ac2337Sjl 	 */
313968ac2337Sjl 
314068ac2337Sjl 	flush_windows_il();
314168ac2337Sjl 
314225cf1a30Sjl 	prog->critical->stat[cpuid] = FMEM_LOOP_COPY_READY;
314325cf1a30Sjl 	membar_sync_il();
314425cf1a30Sjl 
314525cf1a30Sjl 	if (prog->data->cpuid == cpuid) {
314625cf1a30Sjl 		limit = drmach_get_stick_il();
3147b307f191Sbm 		limit += cpu_xcall_delay * system_clock_freq;
314825cf1a30Sjl 		for (i = 0; i < NCPU; i++) {
314925cf1a30Sjl 			if (CPU_IN_SET(prog->data->cpu_slave_set, i)) {
3150e98fafb9Sjl 				/* wait for all CPU's to be ready */
3151e98fafb9Sjl 				for (;;) {
3152e98fafb9Sjl 					if (prog->critical->stat[i] ==
3153e98fafb9Sjl 					    FMEM_LOOP_COPY_READY) {
3154e98fafb9Sjl 						break;
3155e98fafb9Sjl 					}
3156e98fafb9Sjl 					DR_DELAY_IL(1, prog->data->stick_freq);
3157e98fafb9Sjl 				}
3158e98fafb9Sjl 				curr = drmach_get_stick_il();
3159e98fafb9Sjl 				if (curr > limit) {
3160e98fafb9Sjl 					prog->data->fmem_status.error =
3161e98fafb9Sjl 					    EOPL_FMEM_XC_TIMEOUT;
3162e98fafb9Sjl 					return (EOPL_FMEM_XC_TIMEOUT);
316325cf1a30Sjl 				}
316425cf1a30Sjl 			}
316525cf1a30Sjl 		}
316625cf1a30Sjl 		prog->data->fmem_status.stat = FMEM_LOOP_COPY_READY;
316725cf1a30Sjl 		membar_sync_il();
316825cf1a30Sjl 		copy_start = drmach_get_stick_il();
316925cf1a30Sjl 	} else {
317025cf1a30Sjl 		for (;;) {
317125cf1a30Sjl 			if (prog->data->fmem_status.stat ==
3172e98fafb9Sjl 			    FMEM_LOOP_COPY_READY) {
317325cf1a30Sjl 				break;
317425cf1a30Sjl 			}
317525cf1a30Sjl 			if (prog->data->fmem_status.error) {
3176e98fafb9Sjl 				prog->data->error[cpuid] = EOPL_FMEM_TERMINATE;
3177b307f191Sbm 				return (EOPL_FMEM_TERMINATE);
317825cf1a30Sjl 			}
317968ac2337Sjl 			DR_DELAY_IL(1, prog->data->stick_freq);
318025cf1a30Sjl 		}
318125cf1a30Sjl 	}
318225cf1a30Sjl 
318325cf1a30Sjl 	/*
318425cf1a30Sjl 	 * DO COPY.
318525cf1a30Sjl 	 */
318625cf1a30Sjl 	if (CPU_IN_SET(prog->data->cpu_copy_set, cpuid)) {
318756f33205SJonathan Adams 		for (ml = prog->data->cpu_ml[cpuid]; ml; ml = ml->ml_next) {
3188e98fafb9Sjl 			uint64_t	s_pa, t_pa;
3189e98fafb9Sjl 			uint64_t	nbytes;
3190e98fafb9Sjl 
319156f33205SJonathan Adams 			s_pa = prog->data->s_copybasepa + ml->ml_address;
319256f33205SJonathan Adams 			t_pa = prog->data->t_copybasepa + ml->ml_address;
319356f33205SJonathan Adams 			nbytes = ml->ml_size;
3194e98fafb9Sjl 
3195e98fafb9Sjl 			while (nbytes != 0ull) {
3196e98fafb9Sjl 				/*
3197e98fafb9Sjl 				 * If the master has detected error, we just
3198e98fafb9Sjl 				 * bail out
3199e98fafb9Sjl 				 */
3200e98fafb9Sjl 				if (prog->data->fmem_status.error !=
3201e98fafb9Sjl 				    ESBD_NOERROR) {
3202e98fafb9Sjl 					prog->data->error[cpuid] =
3203e98fafb9Sjl 					    EOPL_FMEM_TERMINATE;
3204e98fafb9Sjl 					return (EOPL_FMEM_TERMINATE);
3205e98fafb9Sjl 				}
3206e98fafb9Sjl 				/*
3207e98fafb9Sjl 				 * This copy does NOT use an ASI
3208e98fafb9Sjl 				 * that avoids the Ecache, therefore
3209e98fafb9Sjl 				 * the dst_pa addresses may remain
3210e98fafb9Sjl 				 * in our Ecache after the dst_pa
3211e98fafb9Sjl 				 * has been removed from the system.
3212e98fafb9Sjl 				 * A subsequent write-back to memory
3213e98fafb9Sjl 				 * will cause an ARB-stop because the
3214e98fafb9Sjl 				 * physical address no longer exists
3215e98fafb9Sjl 				 * in the system. Therefore we must
3216e98fafb9Sjl 				 * flush out local Ecache after we
3217e98fafb9Sjl 				 * finish the copy.
3218e98fafb9Sjl 				 */
3219e98fafb9Sjl 
3220e98fafb9Sjl 				/* copy 32 bytes at src_pa to dst_pa */
3221e98fafb9Sjl 				bcopy32_il(s_pa, t_pa);
3222e98fafb9Sjl 
3223e98fafb9Sjl 				/*
3224e98fafb9Sjl 				 * increment the counter to signal that we are
3225e98fafb9Sjl 				 * alive
3226e98fafb9Sjl 				 */
3227e98fafb9Sjl 				prog->stat->nbytes[cpuid] += 32;
3228e98fafb9Sjl 
3229e98fafb9Sjl 				/* increment by 32 bytes */
3230e98fafb9Sjl 				s_pa += (4 * sizeof (uint64_t));
3231e98fafb9Sjl 				t_pa += (4 * sizeof (uint64_t));
3232e98fafb9Sjl 
3233e98fafb9Sjl 				/* decrement by 32 bytes */
3234e98fafb9Sjl 				nbytes -= (4 * sizeof (uint64_t));
323525cf1a30Sjl 			}
323625cf1a30Sjl 		}
3237e98fafb9Sjl 		prog->critical->stat[cpuid] = FMEM_LOOP_COPY_DONE;
3238e98fafb9Sjl 		membar_sync_il();
323925cf1a30Sjl 	}
324025cf1a30Sjl 
324125cf1a30Sjl 	/*
324225cf1a30Sjl 	 * Since bcopy32_il() does NOT use an ASI to bypass
324325cf1a30Sjl 	 * the Ecache, we need to flush our Ecache after
324425cf1a30Sjl 	 * the copy is complete.
324525cf1a30Sjl 	 */
324625cf1a30Sjl 	flush_cache_il();
324725cf1a30Sjl 
324825cf1a30Sjl 	/*
324925cf1a30Sjl 	 * drmach_fmem_exec_script()
325025cf1a30Sjl 	 */
325125cf1a30Sjl 	if (prog->data->cpuid == cpuid) {
325225cf1a30Sjl 		uint64_t	last, now;
325325cf1a30Sjl 
325425cf1a30Sjl 		limit = copy_start + prog->data->copy_delay;
325525cf1a30Sjl 		for (i = 0; i < NCPU; i++) {
3256e98fafb9Sjl 			if (!CPU_IN_SET(prog->data->cpu_slave_set, i))
3257e98fafb9Sjl 				continue;
3258e98fafb9Sjl 
3259e98fafb9Sjl 			for (;;) {
3260e98fafb9Sjl 				/*
3261e98fafb9Sjl 				 * we get FMEM_LOOP_FMEM_READY in
3262e98fafb9Sjl 				 * normal case
3263e98fafb9Sjl 				 */
326425cf1a30Sjl 				if (prog->critical->stat[i] ==
3265e98fafb9Sjl 				    FMEM_LOOP_FMEM_READY) {
326625cf1a30Sjl 					break;
326725cf1a30Sjl 				}
326825cf1a30Sjl 				/* got error traps */
3269b307f191Sbm 				if (prog->data->error[i] ==
3270e98fafb9Sjl 				    EOPL_FMEM_COPY_ERROR) {
327125cf1a30Sjl 					prog->data->fmem_status.error =
3272e98fafb9Sjl 					    EOPL_FMEM_COPY_ERROR;
3273b307f191Sbm 					return (EOPL_FMEM_COPY_ERROR);
327425cf1a30Sjl 				}
3275e98fafb9Sjl 				/*
3276e98fafb9Sjl 				 * if we have not reached limit, wait
3277e98fafb9Sjl 				 * more
3278e98fafb9Sjl 				 */
327925cf1a30Sjl 				curr = drmach_get_stick_il();
328025cf1a30Sjl 				if (curr <= limit)
328125cf1a30Sjl 					continue;
328225cf1a30Sjl 
328325cf1a30Sjl 				prog->data->slowest_cpuid = i;
3284e98fafb9Sjl 				prog->data->copy_wait_time = curr - copy_start;
328525cf1a30Sjl 
328625cf1a30Sjl 				/* now check if slave is alive */
328725cf1a30Sjl 				last = prog->stat->nbytes[i];
328825cf1a30Sjl 
328925cf1a30Sjl 				DR_DELAY_IL(1, prog->data->stick_freq);
329025cf1a30Sjl 
329125cf1a30Sjl 				now = prog->stat->nbytes[i];
329225cf1a30Sjl 				if (now <= last) {
3293e98fafb9Sjl 					/*
3294e98fafb9Sjl 					 * no progress, perhaps just
3295e98fafb9Sjl 					 * finished
3296e98fafb9Sjl 					 */
329725cf1a30Sjl 					DR_DELAY_IL(1, prog->data->stick_freq);
329825cf1a30Sjl 					if (prog->critical->stat[i] ==
3299e98fafb9Sjl 					    FMEM_LOOP_FMEM_READY)
330025cf1a30Sjl 						break;
330125cf1a30Sjl 					/* copy error */
3302b307f191Sbm 					if (prog->data->error[i] ==
3303e98fafb9Sjl 					    EOPL_FMEM_COPY_ERROR) {
3304e98fafb9Sjl 						prog->data-> fmem_status.error =
3305e98fafb9Sjl 						    EOPL_FMEM_COPY_ERROR;
3306b307f191Sbm 						return (EOPL_FMEM_COPY_ERROR);
330725cf1a30Sjl 					}
33086534c6f0Swh 
33096534c6f0Swh 					prog->data->copy_rename_count++;
33106534c6f0Swh 					if (prog->data->copy_rename_count
33116534c6f0Swh 					    < drmach_copy_rename_timeout) {
33126534c6f0Swh 						continue;
33136534c6f0Swh 					} else {
33146534c6f0Swh 						prog->data->fmem_status.error =
33156534c6f0Swh 						    EOPL_FMEM_COPY_TIMEOUT;
33166534c6f0Swh 						return (EOPL_FMEM_COPY_TIMEOUT);
33176534c6f0Swh 					}
331825cf1a30Sjl 				}
331925cf1a30Sjl 			}
332025cf1a30Sjl 		}
3321b307f191Sbm 
332225cf1a30Sjl 		prog->critical->stat[cpuid] = FMEM_LOOP_FMEM_READY;
332325cf1a30Sjl 		prog->data->fmem_status.stat  = FMEM_LOOP_FMEM_READY;
332425cf1a30Sjl 
332525cf1a30Sjl 		membar_sync_il();
332625cf1a30Sjl 		flush_instr_mem_il((void*) (prog->critical));
332725cf1a30Sjl 		/*
332825cf1a30Sjl 		 * drmach_fmem_exec_script()
332925cf1a30Sjl 		 */
333025cf1a30Sjl 		rtn = prog->critical->fmem((void *)prog->critical, PAGESIZE);
333125cf1a30Sjl 		return (rtn);
333225cf1a30Sjl 	} else {
333325cf1a30Sjl 		flush_instr_mem_il((void*) (prog->critical));
333425cf1a30Sjl 		/*
333525cf1a30Sjl 		 * drmach_fmem_loop_script()
333625cf1a30Sjl 		 */
3337e98fafb9Sjl 		rtn = prog->critical->loop((void *)(prog->critical), PAGESIZE,
3338e98fafb9Sjl 		    (void *)&(prog->critical->stat[cpuid]));
333925cf1a30Sjl 		prog->data->error[cpuid] = rtn;
334025cf1a30Sjl 		/* slave thread does not care the rv */
334125cf1a30Sjl 		return (0);
334225cf1a30Sjl 	}
334325cf1a30Sjl }
334425cf1a30Sjl 
334525cf1a30Sjl static void
334625cf1a30Sjl drmach_copy_rename_end(void)
334725cf1a30Sjl {
334825cf1a30Sjl 	/*
334925cf1a30Sjl 	 * IMPORTANT:	This function's location MUST be located immediately
335025cf1a30Sjl 	 *		following drmach_copy_rename_prog__relocatable to
335125cf1a30Sjl 	 *		accurately estimate its size.  Note that this assumes
335225cf1a30Sjl 	 *		the compiler keeps these functions in the order in
335325cf1a30Sjl 	 *		which they appear :-o
335425cf1a30Sjl 	 */
335525cf1a30Sjl }
335625cf1a30Sjl 
335725cf1a30Sjl 
33586534c6f0Swh static int
335925cf1a30Sjl drmach_setup_memlist(drmach_copy_rename_program_t *p)
336025cf1a30Sjl {
336125cf1a30Sjl 	struct memlist *ml;
336225cf1a30Sjl 	caddr_t buf;
33636534c6f0Swh 	int nbytes, s, n_elements;
336425cf1a30Sjl 
336525cf1a30Sjl 	nbytes = PAGESIZE;
33666534c6f0Swh 	n_elements = 0;
336725cf1a30Sjl 	s = roundup(sizeof (struct memlist), sizeof (void *));
336825cf1a30Sjl 	p->free_mlist = NULL;
336925cf1a30Sjl 	buf = p->memlist_buffer;
337025cf1a30Sjl 	while (nbytes >= sizeof (struct memlist)) {
337125cf1a30Sjl 		ml = (struct memlist *)buf;
337256f33205SJonathan Adams 		ml->ml_next = p->free_mlist;
337325cf1a30Sjl 		p->free_mlist = ml;
337425cf1a30Sjl 		buf += s;
33756534c6f0Swh 		n_elements++;
337625cf1a30Sjl 		nbytes -= s;
337725cf1a30Sjl 	}
33786534c6f0Swh 	return (n_elements);
337925cf1a30Sjl }
338025cf1a30Sjl 
338168ac2337Sjl static void
338268ac2337Sjl drmach_lock_critical(caddr_t va, caddr_t new_va)
338368ac2337Sjl {
338468ac2337Sjl 	tte_t tte;
338568ac2337Sjl 	int i;
338668ac2337Sjl 
338768ac2337Sjl 	kpreempt_disable();
338868ac2337Sjl 
338968ac2337Sjl 	for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
339068ac2337Sjl 		vtag_flushpage(new_va, (uint64_t)ksfmmup);
3391e98fafb9Sjl 		sfmmu_memtte(&tte, va_to_pfn(va), PROC_DATA|HAT_NOSYNC, TTE8K);
339268ac2337Sjl 		tte.tte_intlo |= TTE_LCK_INT;
339368ac2337Sjl 		sfmmu_dtlb_ld_kva(new_va, &tte);
339468ac2337Sjl 		sfmmu_itlb_ld_kva(new_va, &tte);
339568ac2337Sjl 		va += PAGESIZE;
339668ac2337Sjl 		new_va += PAGESIZE;
339768ac2337Sjl 	}
339868ac2337Sjl }
339968ac2337Sjl 
340068ac2337Sjl static void
340168ac2337Sjl drmach_unlock_critical(caddr_t va)
340268ac2337Sjl {
340368ac2337Sjl 	int i;
340468ac2337Sjl 
340568ac2337Sjl 	for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
340668ac2337Sjl 		vtag_flushpage(va, (uint64_t)ksfmmup);
340768ac2337Sjl 		va += PAGESIZE;
340868ac2337Sjl 	}
340968ac2337Sjl 
341068ac2337Sjl 	kpreempt_enable();
341168ac2337Sjl }
341268ac2337Sjl 
341325cf1a30Sjl sbd_error_t *
341425cf1a30Sjl drmach_copy_rename_init(drmachid_t t_id, drmachid_t s_id,
3415*bbe1232eSToomas Soome     struct memlist *c_ml, drmachid_t *pgm_id)
341625cf1a30Sjl {
341725cf1a30Sjl 	drmach_mem_t	*s_mem;
341825cf1a30Sjl 	drmach_mem_t	*t_mem;
341925cf1a30Sjl 	struct memlist	*x_ml;
342025cf1a30Sjl 	uint64_t	s_copybasepa, t_copybasepa;
342125cf1a30Sjl 	uint_t		len;
342225cf1a30Sjl 	caddr_t		bp, wp;
34236534c6f0Swh 	int		s_bd, t_bd, cpuid, active_cpus, i;
34246534c6f0Swh 	int		max_elms, mlist_size, rv;
34256534c6f0Swh 	uint64_t	c_addr;
34266534c6f0Swh 	size_t		c_size, copy_sz, sz;
34276534c6f0Swh 	extern void	drmach_fmem_loop_script();
34286534c6f0Swh 	extern void	drmach_fmem_loop_script_rtn();
34296534c6f0Swh 	extern int	drmach_fmem_exec_script();
34306534c6f0Swh 	extern void	drmach_fmem_exec_script_end();
343125cf1a30Sjl 	sbd_error_t	*err;
343268ac2337Sjl 	drmach_copy_rename_program_t *prog = NULL;
343368ac2337Sjl 	drmach_copy_rename_program_t *prog_kmem = NULL;
343425cf1a30Sjl 	void		(*mc_suspend)(void);
343525cf1a30Sjl 	void		(*mc_resume)(void);
343625cf1a30Sjl 	int		(*scf_fmem_start)(int, int);
343725cf1a30Sjl 	int		(*scf_fmem_end)(void);
343825cf1a30Sjl 	int		(*scf_fmem_cancel)(void);
343968ac2337Sjl 	uint64_t	(*scf_get_base_addr)(void);
344025cf1a30Sjl 
344125cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(s_id))
344225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
344325cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(t_id))
344425cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
344525cf1a30Sjl 
344625cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
344725cf1a30Sjl 		int lsb_id, onb_core_num, strand_id;
344825cf1a30Sjl 		drmach_board_t *bp;
344925cf1a30Sjl 
345025cf1a30Sjl 		/*
345125cf1a30Sjl 		 * this kind of CPU will spin in cache
345225cf1a30Sjl 		 */
345325cf1a30Sjl 		if (CPU_IN_SET(cpu_ready_set, i))
345425cf1a30Sjl 			continue;
345525cf1a30Sjl 
345625cf1a30Sjl 		/*
345725cf1a30Sjl 		 * Now check for any inactive CPU's that
345825cf1a30Sjl 		 * have been hotadded.  This can only occur in
345925cf1a30Sjl 		 * error condition in drmach_cpu_poweron().
346025cf1a30Sjl 		 */
346125cf1a30Sjl 		lsb_id = LSB_ID(i);
346225cf1a30Sjl 		onb_core_num = ON_BOARD_CORE_NUM(i);
346325cf1a30Sjl 		strand_id = STRAND_ID(i);
346425cf1a30Sjl 		bp = drmach_get_board_by_bnum(lsb_id);
346525cf1a30Sjl 		if (bp == NULL)
346625cf1a30Sjl 			continue;
346725cf1a30Sjl 		if (bp->cores[onb_core_num].core_hotadded &
346825cf1a30Sjl 		    (1 << strand_id)) {
3469e98fafb9Sjl 			if (!(bp->cores[onb_core_num].core_started &
3470e98fafb9Sjl 			    (1 << strand_id))) {
3471e98fafb9Sjl 				return (drerr_new(1, EOPL_CPU_STATE, NULL));
3472e98fafb9Sjl 			}
347325cf1a30Sjl 		}
347425cf1a30Sjl 	}
347525cf1a30Sjl 
347625cf1a30Sjl 	mc_suspend = (void (*)(void))
347725cf1a30Sjl 	    modgetsymvalue("opl_mc_suspend", 0);
347825cf1a30Sjl 	mc_resume = (void (*)(void))
347925cf1a30Sjl 	    modgetsymvalue("opl_mc_resume", 0);
348025cf1a30Sjl 
348125cf1a30Sjl 	if (mc_suspend == NULL || mc_resume == NULL) {
34828eafe49bSbm 		return (drerr_new(1, EOPL_MC_OPL, NULL));
348325cf1a30Sjl 	}
348425cf1a30Sjl 
348525cf1a30Sjl 	scf_fmem_start = (int (*)(int, int))
348625cf1a30Sjl 	    modgetsymvalue("scf_fmem_start", 0);
348725cf1a30Sjl 	if (scf_fmem_start == NULL) {
34888eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
348925cf1a30Sjl 	}
349025cf1a30Sjl 	scf_fmem_end = (int (*)(void))
349125cf1a30Sjl 	    modgetsymvalue("scf_fmem_end", 0);
349225cf1a30Sjl 	if (scf_fmem_end == NULL) {
34938eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
349425cf1a30Sjl 	}
349525cf1a30Sjl 	scf_fmem_cancel = (int (*)(void))
349625cf1a30Sjl 	    modgetsymvalue("scf_fmem_cancel", 0);
349725cf1a30Sjl 	if (scf_fmem_cancel == NULL) {
34988eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
349968ac2337Sjl 	}
350068ac2337Sjl 	scf_get_base_addr = (uint64_t (*)(void))
350168ac2337Sjl 	    modgetsymvalue("scf_get_base_addr", 0);
350268ac2337Sjl 	if (scf_get_base_addr == NULL) {
35038eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
350425cf1a30Sjl 	}
350525cf1a30Sjl 	s_mem = s_id;
350625cf1a30Sjl 	t_mem = t_id;
350725cf1a30Sjl 
350825cf1a30Sjl 	s_bd = s_mem->dev.bp->bnum;
350925cf1a30Sjl 	t_bd = t_mem->dev.bp->bnum;
351025cf1a30Sjl 
351125cf1a30Sjl 	/* calculate source and target base pa */
351225cf1a30Sjl 
351325cf1a30Sjl 	s_copybasepa = s_mem->slice_base;
351425cf1a30Sjl 	t_copybasepa = t_mem->slice_base;
351525cf1a30Sjl 
351625cf1a30Sjl 	/* adjust copy memlist addresses to be relative to copy base pa */
351725cf1a30Sjl 	x_ml = c_ml;
35186534c6f0Swh 	mlist_size = 0;
351925cf1a30Sjl 	while (x_ml != NULL) {
352056f33205SJonathan Adams 		x_ml->ml_address -= s_copybasepa;
352156f33205SJonathan Adams 		x_ml = x_ml->ml_next;
35226534c6f0Swh 		mlist_size++;
352325cf1a30Sjl 	}
352425cf1a30Sjl 
352525cf1a30Sjl 	/*
352625cf1a30Sjl 	 * bp will be page aligned, since we're calling
352725cf1a30Sjl 	 * kmem_zalloc() with an exact multiple of PAGESIZE.
352825cf1a30Sjl 	 */
352925cf1a30Sjl 
353068ac2337Sjl 	prog_kmem = (drmach_copy_rename_program_t *)kmem_zalloc(
3531e98fafb9Sjl 	    DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, KM_SLEEP);
353268ac2337Sjl 
353368ac2337Sjl 	prog_kmem->prog = prog_kmem;
353468ac2337Sjl 
353568ac2337Sjl 	/*
353668ac2337Sjl 	 * To avoid MTLB hit, we allocate a new VM space and remap
353768ac2337Sjl 	 * the kmem_alloc buffer to that address.  This solves
353868ac2337Sjl 	 * 2 problems we found:
353968ac2337Sjl 	 * - the kmem_alloc buffer can be just a chunk inside
354068ac2337Sjl 	 *   a much larger, e.g. 4MB buffer and MTLB will occur
354168ac2337Sjl 	 *   if there are both a 4MB and a 8K TLB mapping to
354268ac2337Sjl 	 *   the same VA range.
354368ac2337Sjl 	 * - the kmem mapping got dropped into the TLB by other
354468ac2337Sjl 	 *   strands, unintentionally.
354568ac2337Sjl 	 * Note that the pointers like data, critical, memlist_buffer,
354668ac2337Sjl 	 * and stat inside the copy rename structure are mapped to this
354768ac2337Sjl 	 * alternate VM space so we must make sure we lock the TLB mapping
354868ac2337Sjl 	 * whenever we access data pointed to by these pointers.
354968ac2337Sjl 	 */
355068ac2337Sjl 
355168ac2337Sjl 	prog = prog_kmem->locked_prog = vmem_alloc(heap_arena,
3552e98fafb9Sjl 	    DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, VM_SLEEP);
355368ac2337Sjl 	wp = bp = (caddr_t)prog;
355468ac2337Sjl 
355568ac2337Sjl 	/* Now remap prog_kmem to prog */
355668ac2337Sjl 	drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
355768ac2337Sjl 
355868ac2337Sjl 	/* All pointers in prog are based on the alternate mapping */
355925cf1a30Sjl 	prog->data = (drmach_copy_rename_data_t *)roundup(((uint64_t)prog +
3560e98fafb9Sjl 	    sizeof (drmach_copy_rename_program_t)), sizeof (void *));
356125cf1a30Sjl 
356225cf1a30Sjl 	ASSERT(((uint64_t)prog->data + sizeof (drmach_copy_rename_data_t))
3563e98fafb9Sjl 	    <= ((uint64_t)prog + PAGESIZE));
356425cf1a30Sjl 
356525cf1a30Sjl 	prog->critical = (drmach_copy_rename_critical_t *)
3566e98fafb9Sjl 	    (wp + DRMACH_FMEM_CRITICAL_PAGE * PAGESIZE);
356725cf1a30Sjl 
3568e98fafb9Sjl 	prog->memlist_buffer = (caddr_t)(wp + DRMACH_FMEM_MLIST_PAGE *
3569e98fafb9Sjl 	    PAGESIZE);
357025cf1a30Sjl 
3571e98fafb9Sjl 	prog->stat = (drmach_cr_stat_t *)(wp + DRMACH_FMEM_STAT_PAGE *
3572e98fafb9Sjl 	    PAGESIZE);
357325cf1a30Sjl 
357425cf1a30Sjl 	/* LINTED */
3575e98fafb9Sjl 	ASSERT(sizeof (drmach_cr_stat_t) <= ((DRMACH_FMEM_LOCKED_PAGES -
3576e98fafb9Sjl 	    DRMACH_FMEM_STAT_PAGE) * PAGESIZE));
357725cf1a30Sjl 
357825cf1a30Sjl 	prog->critical->scf_reg_base = (uint64_t)-1;
357925cf1a30Sjl 	prog->critical->scf_td[0] = (s_bd & 0xff);
358025cf1a30Sjl 	prog->critical->scf_td[1] = (t_bd & 0xff);
358125cf1a30Sjl 	for (i = 2; i < 15; i++) {
358225cf1a30Sjl 		prog->critical->scf_td[i]   = 0;
358325cf1a30Sjl 	}
358425cf1a30Sjl 	prog->critical->scf_td[15] = ((0xaa + s_bd + t_bd) & 0xff);
358525cf1a30Sjl 
358625cf1a30Sjl 	bp = (caddr_t)prog->critical;
358725cf1a30Sjl 	len = sizeof (drmach_copy_rename_critical_t);
358825cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
358925cf1a30Sjl 
359025cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_copy_rename_end -
3591e98fafb9Sjl 	    (ulong_t)drmach_copy_rename_prog__relocatable);
359225cf1a30Sjl 
359325cf1a30Sjl 	/*
359425cf1a30Sjl 	 * We always leave 1K nop's to prevent the processor from
359525cf1a30Sjl 	 * speculative execution that causes memory access
359625cf1a30Sjl 	 */
359725cf1a30Sjl 	wp = wp + len + 1024;
359825cf1a30Sjl 
359925cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_fmem_exec_script_end -
3600e98fafb9Sjl 	    (ulong_t)drmach_fmem_exec_script);
360125cf1a30Sjl 	/* this is the entry point of the loop script */
360225cf1a30Sjl 	wp = wp + len + 1024;
360325cf1a30Sjl 
360425cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_fmem_exec_script -
3605e98fafb9Sjl 	    (ulong_t)drmach_fmem_loop_script);
360625cf1a30Sjl 	wp = wp + len + 1024;
360725cf1a30Sjl 
360825cf1a30Sjl 	/* now we make sure there is 1K extra */
360925cf1a30Sjl 
361025cf1a30Sjl 	if ((wp - bp) > PAGESIZE) {
36118eafe49bSbm 		err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
361268ac2337Sjl 		goto out;
361325cf1a30Sjl 	}
361425cf1a30Sjl 
361525cf1a30Sjl 	bp = (caddr_t)prog->critical;
361625cf1a30Sjl 	len = sizeof (drmach_copy_rename_critical_t);
361725cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
361825cf1a30Sjl 
361925cf1a30Sjl 	prog->critical->run = (int (*)())(wp);
362025cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_copy_rename_end -
3621e98fafb9Sjl 	    (ulong_t)drmach_copy_rename_prog__relocatable);
362225cf1a30Sjl 
362325cf1a30Sjl 	bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len);
362425cf1a30Sjl 
362525cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
362625cf1a30Sjl 
362725cf1a30Sjl 	prog->critical->fmem = (int (*)())(wp);
362825cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_exec_script_end -
3629e98fafb9Sjl 	    (ulong_t)drmach_fmem_exec_script);
363025cf1a30Sjl 	bcopy((caddr_t)drmach_fmem_exec_script, wp, len);
363125cf1a30Sjl 
363225cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_exec_script_end -
3633e98fafb9Sjl 	    (ulong_t)drmach_fmem_exec_script);
363425cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
363525cf1a30Sjl 
363625cf1a30Sjl 	prog->critical->loop = (int (*)())(wp);
363725cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_exec_script -
3638e98fafb9Sjl 	    (ulong_t)drmach_fmem_loop_script);
363925cf1a30Sjl 	bcopy((caddr_t)drmach_fmem_loop_script, (void *)wp, len);
364025cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_loop_script_rtn-
3641e98fafb9Sjl 	    (ulong_t)drmach_fmem_loop_script);
364225cf1a30Sjl 	prog->critical->loop_rtn = (void (*)()) (wp+len);
364325cf1a30Sjl 
3644b307f191Sbm 	prog->data->fmem_status.error = ESBD_NOERROR;
3645b307f191Sbm 
364625cf1a30Sjl 	/* now we are committed, call SCF, soft suspend mac patrol */
364725cf1a30Sjl 	if ((*scf_fmem_start)(s_bd, t_bd)) {
36488eafe49bSbm 		err = drerr_new(1, EOPL_SCF_FMEM_START, NULL);
364968ac2337Sjl 		goto out;
365025cf1a30Sjl 	}
365125cf1a30Sjl 	prog->data->scf_fmem_end = scf_fmem_end;
365225cf1a30Sjl 	prog->data->scf_fmem_cancel = scf_fmem_cancel;
365368ac2337Sjl 	prog->data->scf_get_base_addr = scf_get_base_addr;
365425cf1a30Sjl 	prog->data->fmem_status.op |= OPL_FMEM_SCF_START;
3655b307f191Sbm 
365625cf1a30Sjl 	/* soft suspend mac patrol */
365725cf1a30Sjl 	(*mc_suspend)();
365825cf1a30Sjl 	prog->data->fmem_status.op |= OPL_FMEM_MC_SUSPEND;
365925cf1a30Sjl 	prog->data->mc_resume = mc_resume;
366025cf1a30Sjl 
366125cf1a30Sjl 	prog->critical->inst_loop_ret  =
3662e98fafb9Sjl 	    *(uint64_t *)(prog->critical->loop_rtn);
366325cf1a30Sjl 
366425cf1a30Sjl 	/*
366525cf1a30Sjl 	 * 0x30800000 is op code "ba,a	+0"
366625cf1a30Sjl 	 */
366725cf1a30Sjl 
366825cf1a30Sjl 	*(uint_t *)(prog->critical->loop_rtn) = (uint_t)(0x30800000);
366925cf1a30Sjl 
367025cf1a30Sjl 	/*
367125cf1a30Sjl 	 * set the value of SCF FMEM TIMEOUT
367225cf1a30Sjl 	 */
367325cf1a30Sjl 	prog->critical->delay = fmem_timeout * system_clock_freq;
367425cf1a30Sjl 
367525cf1a30Sjl 	prog->data->s_mem = (drmachid_t)s_mem;
367625cf1a30Sjl 	prog->data->t_mem = (drmachid_t)t_mem;
367725cf1a30Sjl 
367825cf1a30Sjl 	cpuid = CPU->cpu_id;
367925cf1a30Sjl 	prog->data->cpuid = cpuid;
368025cf1a30Sjl 	prog->data->cpu_ready_set = cpu_ready_set;
368125cf1a30Sjl 	prog->data->cpu_slave_set = cpu_ready_set;
368225cf1a30Sjl 	prog->data->slowest_cpuid = (processorid_t)-1;
368325cf1a30Sjl 	prog->data->copy_wait_time = 0;
36846534c6f0Swh 	prog->data->copy_rename_count = 0;
368525cf1a30Sjl 	CPUSET_DEL(prog->data->cpu_slave_set, cpuid);
368625cf1a30Sjl 
368725cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
368825cf1a30Sjl 		prog->data->cpu_ml[i] = NULL;
368925cf1a30Sjl 	}
369025cf1a30Sjl 
36916534c6f0Swh 	/*
36926534c6f0Swh 	 * max_elms -	max number of memlist structures that
3693*bbe1232eSToomas Soome 	 *		may be allocated for the CPU memory list.
36946534c6f0Swh 	 *		If there are too many memory span (because
36956534c6f0Swh 	 *		of fragmentation) than number of memlist
36966534c6f0Swh 	 *		available, we should return error.
36976534c6f0Swh 	 */
36986534c6f0Swh 	max_elms = drmach_setup_memlist(prog);
36996534c6f0Swh 	if (max_elms < mlist_size) {
37006534c6f0Swh 		err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
37016534c6f0Swh 		goto err_out;
37026534c6f0Swh 	}
37036534c6f0Swh 
370425cf1a30Sjl 	active_cpus = 0;
370525cf1a30Sjl 	if (drmach_disable_mcopy) {
370625cf1a30Sjl 		active_cpus = 1;
370725cf1a30Sjl 		CPUSET_ADD(prog->data->cpu_copy_set, cpuid);
370825cf1a30Sjl 	} else {
37096534c6f0Swh 		int max_cpu_num;
37106534c6f0Swh 		/*
37116534c6f0Swh 		 * The parallel copy procedure is going to split some
37126534c6f0Swh 		 * of the elements of the original memory copy list.
37136534c6f0Swh 		 * The number of added elements can be up to
37146534c6f0Swh 		 * (max_cpu_num - 1).  It means that max_cpu_num
37156534c6f0Swh 		 * should satisfy the following condition:
37166534c6f0Swh 		 * (max_cpu_num - 1) + mlist_size <= max_elms.
37176534c6f0Swh 		 */
37186534c6f0Swh 		max_cpu_num = max_elms - mlist_size + 1;
37196534c6f0Swh 
372025cf1a30Sjl 		for (i = 0; i < NCPU; i++) {
372125cf1a30Sjl 			if (CPU_IN_SET(cpu_ready_set, i) &&
3722e98fafb9Sjl 			    CPU_ACTIVE(cpu[i])) {
37236534c6f0Swh 				/*
37246534c6f0Swh 				 * To reduce the level-2 cache contention only
37256534c6f0Swh 				 * one strand per core will participate
37266534c6f0Swh 				 * in the copy. If the strand with even cpu_id
37276534c6f0Swh 				 * number is present in the ready set, we will
37286534c6f0Swh 				 * include this strand in the copy set. If it
37296534c6f0Swh 				 * is not present in the ready set, we check for
37306534c6f0Swh 				 * the strand with the consecutive odd cpu_id
37316534c6f0Swh 				 * and include it, provided that it is
37326534c6f0Swh 				 * present in the ready set.
37336534c6f0Swh 				 */
37346534c6f0Swh 				if (!(i & 0x1) ||
37356534c6f0Swh 				    !CPU_IN_SET(prog->data->cpu_copy_set,
37366534c6f0Swh 				    i - 1)) {
37376534c6f0Swh 					CPUSET_ADD(prog->data->cpu_copy_set, i);
37386534c6f0Swh 					active_cpus++;
37396534c6f0Swh 					/*
37406534c6f0Swh 					 * We cannot have more than
37416534c6f0Swh 					 * max_cpu_num CPUs in the copy
37426534c6f0Swh 					 * set, because each CPU has to
37436534c6f0Swh 					 * have at least one element
37446534c6f0Swh 					 * long memory copy list.
37456534c6f0Swh 					 */
37466534c6f0Swh 					if (active_cpus >= max_cpu_num)
37476534c6f0Swh 						break;
37486534c6f0Swh 
37496534c6f0Swh 				}
375025cf1a30Sjl 			}
375125cf1a30Sjl 		}
375225cf1a30Sjl 	}
375325cf1a30Sjl 
375425cf1a30Sjl 	x_ml = c_ml;
375525cf1a30Sjl 	sz = 0;
375625cf1a30Sjl 	while (x_ml != NULL) {
375756f33205SJonathan Adams 		sz += x_ml->ml_size;
375856f33205SJonathan Adams 		x_ml = x_ml->ml_next;
375925cf1a30Sjl 	}
376025cf1a30Sjl 
376125cf1a30Sjl 	copy_sz = sz/active_cpus;
376225cf1a30Sjl 	copy_sz = roundup(copy_sz, MMU_PAGESIZE4M);
376325cf1a30Sjl 
376425cf1a30Sjl 	while (sz > copy_sz*active_cpus) {
376525cf1a30Sjl 		copy_sz += MMU_PAGESIZE4M;
376625cf1a30Sjl 	}
376725cf1a30Sjl 
376825cf1a30Sjl 	prog->data->stick_freq = system_clock_freq;
376925cf1a30Sjl 	prog->data->copy_delay = ((copy_sz / min_copy_size_per_sec) + 2) *
3770e98fafb9Sjl 	    system_clock_freq;
377125cf1a30Sjl 
377225cf1a30Sjl 	x_ml = c_ml;
377356f33205SJonathan Adams 	c_addr = x_ml->ml_address;
377456f33205SJonathan Adams 	c_size = x_ml->ml_size;
377525cf1a30Sjl 
377625cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
377725cf1a30Sjl 		prog->stat->nbytes[i] = 0;
377825cf1a30Sjl 		if (!CPU_IN_SET(prog->data->cpu_copy_set, i)) {
377925cf1a30Sjl 			continue;
378025cf1a30Sjl 		}
378125cf1a30Sjl 		sz = copy_sz;
378225cf1a30Sjl 
378325cf1a30Sjl 		while (sz) {
378425cf1a30Sjl 			if (c_size > sz) {
37856534c6f0Swh 				if ((prog->data->cpu_ml[i] =
3786e98fafb9Sjl 				    drmach_memlist_add_span(prog,
37876534c6f0Swh 				    prog->data->cpu_ml[i],
37886534c6f0Swh 				    c_addr, sz)) == NULL) {
37896534c6f0Swh 					cmn_err(CE_WARN,
37906534c6f0Swh 					    "Unexpected drmach_memlist_add_span"
37916534c6f0Swh 					    " failure.");
37926534c6f0Swh 					err = drerr_new(1, EOPL_FMEM_SETUP,
37936534c6f0Swh 					    NULL);
37946534c6f0Swh 					mc_resume();
37956534c6f0Swh 					goto out;
37966534c6f0Swh 				}
379725cf1a30Sjl 				c_addr += sz;
379825cf1a30Sjl 				c_size -= sz;
379925cf1a30Sjl 				break;
380025cf1a30Sjl 			} else {
380125cf1a30Sjl 				sz -= c_size;
38026534c6f0Swh 				if ((prog->data->cpu_ml[i] =
3803e98fafb9Sjl 				    drmach_memlist_add_span(prog,
38046534c6f0Swh 				    prog->data->cpu_ml[i],
38056534c6f0Swh 				    c_addr, c_size)) == NULL) {
38066534c6f0Swh 					cmn_err(CE_WARN,
38076534c6f0Swh 					    "Unexpected drmach_memlist_add_span"
38086534c6f0Swh 					    " failure.");
38096534c6f0Swh 					err = drerr_new(1, EOPL_FMEM_SETUP,
38106534c6f0Swh 					    NULL);
38116534c6f0Swh 					mc_resume();
38126534c6f0Swh 					goto out;
38136534c6f0Swh 				}
38146534c6f0Swh 
381556f33205SJonathan Adams 				x_ml = x_ml->ml_next;
381625cf1a30Sjl 				if (x_ml != NULL) {
381756f33205SJonathan Adams 					c_addr = x_ml->ml_address;
381856f33205SJonathan Adams 					c_size = x_ml->ml_size;
381925cf1a30Sjl 				} else {
382025cf1a30Sjl 					goto end;
382125cf1a30Sjl 				}
382225cf1a30Sjl 			}
382325cf1a30Sjl 		}
382425cf1a30Sjl 	}
382525cf1a30Sjl end:
382625cf1a30Sjl 	prog->data->s_copybasepa = s_copybasepa;
382725cf1a30Sjl 	prog->data->t_copybasepa = t_copybasepa;
382825cf1a30Sjl 	prog->data->c_ml = c_ml;
382968ac2337Sjl 	*pgm_id = prog_kmem;
383025cf1a30Sjl 
383168ac2337Sjl 	/* Unmap the alternate space.  It will have to be remapped again */
383268ac2337Sjl 	drmach_unlock_critical((caddr_t)prog);
383325cf1a30Sjl 	return (NULL);
38346534c6f0Swh 
38356534c6f0Swh err_out:
38366534c6f0Swh 	mc_resume();
38376534c6f0Swh 	rv = (*prog->data->scf_fmem_cancel)();
38386534c6f0Swh 	if (rv) {
38396534c6f0Swh 		cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x", rv);
38406534c6f0Swh 	}
384168ac2337Sjl out:
384268ac2337Sjl 	if (prog != NULL) {
384368ac2337Sjl 		drmach_unlock_critical((caddr_t)prog);
3844e98fafb9Sjl 		vmem_free(heap_arena, prog, DRMACH_FMEM_LOCKED_PAGES *
3845e98fafb9Sjl 		    PAGESIZE);
384668ac2337Sjl 	}
384768ac2337Sjl 	if (prog_kmem != NULL) {
384868ac2337Sjl 		kmem_free(prog_kmem, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
384968ac2337Sjl 	}
385068ac2337Sjl 	return (err);
385125cf1a30Sjl }
385225cf1a30Sjl 
385325cf1a30Sjl sbd_error_t *
385425cf1a30Sjl drmach_copy_rename_fini(drmachid_t id)
385525cf1a30Sjl {
385625cf1a30Sjl 	drmach_copy_rename_program_t	*prog = id;
385725cf1a30Sjl 	sbd_error_t			*err = NULL;
385825cf1a30Sjl 	int				rv;
3859b307f191Sbm 	uint_t				fmem_error;
386025cf1a30Sjl 
386125cf1a30Sjl 	/*
386225cf1a30Sjl 	 * Note that we have to delay calling SCF to find out the
386325cf1a30Sjl 	 * status of the FMEM operation here because SCF cannot
386425cf1a30Sjl 	 * respond while it is suspended.
386525cf1a30Sjl 	 * This create a small window when we are sure about the
386625cf1a30Sjl 	 * base address of the system board.
386725cf1a30Sjl 	 * If there is any call to mc-opl to get memory unum,
386825cf1a30Sjl 	 * mc-opl will return UNKNOWN as the unum.
386925cf1a30Sjl 	 */
387025cf1a30Sjl 
387168ac2337Sjl 	/*
387268ac2337Sjl 	 * we have to remap again because all the pointer like data,
387368ac2337Sjl 	 * critical in prog are based on the alternate vmem space.
387468ac2337Sjl 	 */
387568ac2337Sjl 	(void) drmach_lock_critical((caddr_t)prog, (caddr_t)prog->locked_prog);
387668ac2337Sjl 
387725cf1a30Sjl 	if (prog->data->c_ml != NULL)
387825cf1a30Sjl 		memlist_delete(prog->data->c_ml);
387925cf1a30Sjl 
388025cf1a30Sjl 	if ((prog->data->fmem_status.op &
3881e98fafb9Sjl 	    (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) !=
3882e98fafb9Sjl 	    (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) {
3883e98fafb9Sjl 		cmn_err(CE_PANIC, "drmach_copy_rename_fini: invalid op "
3884e98fafb9Sjl 		    "code %x\n", prog->data->fmem_status.op);
388525cf1a30Sjl 	}
388625cf1a30Sjl 
3887b307f191Sbm 	fmem_error = prog->data->fmem_status.error;
3888b307f191Sbm 	if (fmem_error != ESBD_NOERROR) {
3889b307f191Sbm 		err = drerr_new(1, fmem_error, NULL);
3890b307f191Sbm 	}
3891b307f191Sbm 
389225cf1a30Sjl 	/* possible ops are SCF_START, MC_SUSPEND */
389325cf1a30Sjl 	if (prog->critical->fmem_issued) {
3894b307f191Sbm 		if (fmem_error != ESBD_NOERROR) {
3895e98fafb9Sjl 			cmn_err(CE_PANIC, "Irrecoverable FMEM error %d\n",
3896e98fafb9Sjl 			    fmem_error);
3897b307f191Sbm 		}
389825cf1a30Sjl 		rv = (*prog->data->scf_fmem_end)();
389925cf1a30Sjl 		if (rv) {
390068ac2337Sjl 			cmn_err(CE_PANIC, "scf_fmem_end() failed rv=%d", rv);
390125cf1a30Sjl 		}
390225cf1a30Sjl 		/*
390325cf1a30Sjl 		 * If we get here, rename is successful.
390425cf1a30Sjl 		 * Do all the copy rename post processing.
390525cf1a30Sjl 		 */
390625cf1a30Sjl 		drmach_swap_pa((drmach_mem_t *)prog->data->s_mem,
3907e98fafb9Sjl 		    (drmach_mem_t *)prog->data->t_mem);
390825cf1a30Sjl 	} else {
390925cf1a30Sjl 		rv = (*prog->data->scf_fmem_cancel)();
391025cf1a30Sjl 		if (rv) {
3911e98fafb9Sjl 			cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x",
3912e98fafb9Sjl 			    rv);
3913e98fafb9Sjl 			if (!err) {
3914e98fafb9Sjl 				err = drerr_new(1, EOPL_SCF_FMEM_CANCEL,
3915e98fafb9Sjl 				    "scf_fmem_cancel() failed. rv = 0x%x", rv);
3916e98fafb9Sjl 			}
391725cf1a30Sjl 		}
391825cf1a30Sjl 	}
391925cf1a30Sjl 	/* soft resume mac patrol */
392025cf1a30Sjl 	(*prog->data->mc_resume)();
392125cf1a30Sjl 
392268ac2337Sjl 	drmach_unlock_critical((caddr_t)prog->locked_prog);
392368ac2337Sjl 
392468ac2337Sjl 	vmem_free(heap_arena, prog->locked_prog,
3925e98fafb9Sjl 	    DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
392625cf1a30Sjl 	kmem_free(prog, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
392725cf1a30Sjl 	return (err);
392825cf1a30Sjl }
392925cf1a30Sjl 
393025cf1a30Sjl /*ARGSUSED*/
393125cf1a30Sjl static void
393225cf1a30Sjl drmach_copy_rename_slave(struct regs *rp, drmachid_t id)
393325cf1a30Sjl {
393468ac2337Sjl 	drmach_copy_rename_program_t	*prog =
3935e98fafb9Sjl 	    (drmach_copy_rename_program_t *)id;
393625cf1a30Sjl 	register int			cpuid;
393725cf1a30Sjl 	extern void			drmach_flush();
393825cf1a30Sjl 	extern void			membar_sync_il();
393925cf1a30Sjl 	extern void			drmach_flush_icache();
394025cf1a30Sjl 	on_trap_data_t			otd;
394125cf1a30Sjl 
394225cf1a30Sjl 	cpuid = CPU->cpu_id;
394325cf1a30Sjl 
394425cf1a30Sjl 	if (on_trap(&otd, OT_DATA_EC)) {
394525cf1a30Sjl 		no_trap();
3946b307f191Sbm 		prog->data->error[cpuid] = EOPL_FMEM_COPY_ERROR;
394725cf1a30Sjl 		prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
394868ac2337Sjl 		drmach_flush_icache();
394968ac2337Sjl 		membar_sync_il();
395025cf1a30Sjl 		return;
395125cf1a30Sjl 	}
395225cf1a30Sjl 
395325cf1a30Sjl 
395425cf1a30Sjl 	/*
395525cf1a30Sjl 	 * jmp drmach_copy_rename_prog().
395625cf1a30Sjl 	 */
395725cf1a30Sjl 
395825cf1a30Sjl 	drmach_flush(prog->critical, PAGESIZE);
395925cf1a30Sjl 	(void) prog->critical->run(prog, cpuid);
396025cf1a30Sjl 	drmach_flush_icache();
396125cf1a30Sjl 
396225cf1a30Sjl 	no_trap();
396325cf1a30Sjl 
396425cf1a30Sjl 	prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
396568ac2337Sjl 
396625cf1a30Sjl 	membar_sync_il();
396725cf1a30Sjl }
396825cf1a30Sjl 
396925cf1a30Sjl static void
397025cf1a30Sjl drmach_swap_pa(drmach_mem_t *s_mem, drmach_mem_t *t_mem)
397125cf1a30Sjl {
397225cf1a30Sjl 	uint64_t s_base, t_base;
397325cf1a30Sjl 	drmach_board_t *s_board, *t_board;
397425cf1a30Sjl 	struct memlist *ml;
397525cf1a30Sjl 
397625cf1a30Sjl 	s_board = s_mem->dev.bp;
397725cf1a30Sjl 	t_board = t_mem->dev.bp;
397825cf1a30Sjl 	if (s_board == NULL || t_board == NULL) {
397925cf1a30Sjl 		cmn_err(CE_PANIC, "Cannot locate source or target board\n");
398025cf1a30Sjl 		return;
398125cf1a30Sjl 	}
398225cf1a30Sjl 	s_base = s_mem->slice_base;
398325cf1a30Sjl 	t_base = t_mem->slice_base;
398425cf1a30Sjl 
398525cf1a30Sjl 	s_mem->slice_base = t_base;
398625cf1a30Sjl 	s_mem->base_pa = (s_mem->base_pa - s_base) + t_base;
398725cf1a30Sjl 
398856f33205SJonathan Adams 	for (ml = s_mem->memlist; ml; ml = ml->ml_next) {
398956f33205SJonathan Adams 		ml->ml_address = ml->ml_address - s_base + t_base;
399025cf1a30Sjl 	}
399125cf1a30Sjl 
399225cf1a30Sjl 	t_mem->slice_base = s_base;
399325cf1a30Sjl 	t_mem->base_pa = (t_mem->base_pa - t_base) + s_base;
399425cf1a30Sjl 
399556f33205SJonathan Adams 	for (ml = t_mem->memlist; ml; ml = ml->ml_next) {
399656f33205SJonathan Adams 		ml->ml_address = ml->ml_address - t_base + s_base;
399725cf1a30Sjl 	}
399825cf1a30Sjl 
399925cf1a30Sjl 	/*
400025cf1a30Sjl 	 * IKP has to update the sb-mem-ranges for mac patrol driver
400125cf1a30Sjl 	 * when it resumes, it will re-read the sb-mem-range property
400225cf1a30Sjl 	 * to get the new base address
400325cf1a30Sjl 	 */
400425cf1a30Sjl 	if (oplcfg_pa_swap(s_board->bnum, t_board->bnum) != 0)
400525cf1a30Sjl 		cmn_err(CE_PANIC, "Could not update device nodes\n");
400625cf1a30Sjl }
400725cf1a30Sjl 
400825cf1a30Sjl void
400925cf1a30Sjl drmach_copy_rename(drmachid_t id)
401025cf1a30Sjl {
401168ac2337Sjl 	drmach_copy_rename_program_t	*prog_kmem = id;
401268ac2337Sjl 	drmach_copy_rename_program_t	*prog;
401325cf1a30Sjl 	cpuset_t	cpuset;
401425cf1a30Sjl 	int		cpuid;
401525cf1a30Sjl 	uint64_t	inst;
401625cf1a30Sjl 	register int	rtn;
401725cf1a30Sjl 	extern int	in_sync;
401825cf1a30Sjl 	int		old_in_sync;
401925cf1a30Sjl 	extern void	drmach_sys_trap();
402025cf1a30Sjl 	extern void	drmach_flush();
402125cf1a30Sjl 	extern void	drmach_flush_icache();
402225cf1a30Sjl 	extern uint64_t	patch_inst(uint64_t *, uint64_t);
402325cf1a30Sjl 	on_trap_data_t	otd;
402425cf1a30Sjl 
40258eafe49bSbm 
402668ac2337Sjl 	prog = prog_kmem->locked_prog;
402768ac2337Sjl 
40288eafe49bSbm 
402968ac2337Sjl 	/*
403068ac2337Sjl 	 * We must immediately drop in the TLB because all pointers
403168ac2337Sjl 	 * are based on the alternate vmem space.
403268ac2337Sjl 	 */
403368ac2337Sjl 
403468ac2337Sjl 	(void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
403568ac2337Sjl 
403668ac2337Sjl 	/*
403768ac2337Sjl 	 * we call scf to get the base address here becuase if scf
403868ac2337Sjl 	 * has not been suspended yet, the active path can be changing and
403968ac2337Sjl 	 * sometimes it is not even mapped.  We call the interface when
404068ac2337Sjl 	 * the OS has been quiesced.
404168ac2337Sjl 	 */
404268ac2337Sjl 	prog->critical->scf_reg_base = (*prog->data->scf_get_base_addr)();
404368ac2337Sjl 
404468ac2337Sjl 	if (prog->critical->scf_reg_base == (uint64_t)-1 ||
4045*bbe1232eSToomas Soome 	    prog->critical->scf_reg_base == 0) {
4046b307f191Sbm 		prog->data->fmem_status.error = EOPL_FMEM_SCF_ERR;
404768ac2337Sjl 		drmach_unlock_critical((caddr_t)prog);
404825cf1a30Sjl 		return;
404925cf1a30Sjl 	}
405025cf1a30Sjl 
405125cf1a30Sjl 	cpuset = prog->data->cpu_ready_set;
405225cf1a30Sjl 
405325cf1a30Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
405425cf1a30Sjl 		if (CPU_IN_SET(cpuset, cpuid)) {
405525cf1a30Sjl 			prog->critical->stat[cpuid] = FMEM_LOOP_START;
4056b307f191Sbm 			prog->data->error[cpuid] = ESBD_NOERROR;
405725cf1a30Sjl 		}
405825cf1a30Sjl 	}
405925cf1a30Sjl 
406025cf1a30Sjl 	old_in_sync = in_sync;
406125cf1a30Sjl 	in_sync = 1;
406225cf1a30Sjl 	cpuid = CPU->cpu_id;
406325cf1a30Sjl 
406425cf1a30Sjl 	CPUSET_DEL(cpuset, cpuid);
406525cf1a30Sjl 
406668ac2337Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
406768ac2337Sjl 		if (CPU_IN_SET(cpuset, cpuid)) {
406868ac2337Sjl 			xc_one(cpuid, (xcfunc_t *)drmach_lock_critical,
4069e98fafb9Sjl 			    (uint64_t)prog_kmem, (uint64_t)prog);
407068ac2337Sjl 		}
407168ac2337Sjl 	}
407268ac2337Sjl 
407368ac2337Sjl 	cpuid = CPU->cpu_id;
407425cf1a30Sjl 
407525cf1a30Sjl 	xt_some(cpuset, (xcfunc_t *)drmach_sys_trap,
4076e98fafb9Sjl 	    (uint64_t)drmach_copy_rename_slave, (uint64_t)prog);
407725cf1a30Sjl 	xt_sync(cpuset);
407825cf1a30Sjl 
407925cf1a30Sjl 	if (on_trap(&otd, OT_DATA_EC)) {
4080b307f191Sbm 		rtn = EOPL_FMEM_COPY_ERROR;
408168ac2337Sjl 		drmach_flush_icache();
408225cf1a30Sjl 		goto done;
408325cf1a30Sjl 	}
408425cf1a30Sjl 
408525cf1a30Sjl 	/*
408625cf1a30Sjl 	 * jmp drmach_copy_rename_prog().
408725cf1a30Sjl 	 */
40888eafe49bSbm 
408925cf1a30Sjl 	drmach_flush(prog->critical, PAGESIZE);
409025cf1a30Sjl 	rtn = prog->critical->run(prog, cpuid);
4091e98fafb9Sjl 
409225cf1a30Sjl 	drmach_flush_icache();
409325cf1a30Sjl 
409425cf1a30Sjl 
409525cf1a30Sjl done:
409625cf1a30Sjl 	no_trap();
4097b307f191Sbm 	if (rtn == EOPL_FMEM_HW_ERROR) {
409825cf1a30Sjl 		kpreempt_enable();
4099e98fafb9Sjl 		prom_panic("URGENT_ERROR_TRAP is detected during FMEM.\n");
410025cf1a30Sjl 	}
410125cf1a30Sjl 
410225cf1a30Sjl 	/*
410325cf1a30Sjl 	 * In normal case, all slave CPU's are still spinning in
410425cf1a30Sjl 	 * the assembly code.  The master has to patch the instruction
410525cf1a30Sjl 	 * to get them out.
410625cf1a30Sjl 	 * In error case, e.g. COPY_ERROR, some slave CPU's might
410725cf1a30Sjl 	 * have aborted and already returned and sset LOOP_EXIT status.
410825cf1a30Sjl 	 * Some CPU might still be copying.
410925cf1a30Sjl 	 * In any case, some delay is necessary to give them
411025cf1a30Sjl 	 * enough time to set the LOOP_EXIT status.
411125cf1a30Sjl 	 */
411225cf1a30Sjl 
411325cf1a30Sjl 	for (;;) {
411425cf1a30Sjl 		inst = patch_inst((uint64_t *)prog->critical->loop_rtn,
4115e98fafb9Sjl 		    prog->critical->inst_loop_ret);
411625cf1a30Sjl 		if (prog->critical->inst_loop_ret == inst) {
411725cf1a30Sjl 			break;
411825cf1a30Sjl 		}
411925cf1a30Sjl 	}
412025cf1a30Sjl 
412125cf1a30Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
412225cf1a30Sjl 		uint64_t	last, now;
412325cf1a30Sjl 		if (!CPU_IN_SET(cpuset, cpuid)) {
412425cf1a30Sjl 			continue;
412525cf1a30Sjl 		}
412625cf1a30Sjl 		last = prog->stat->nbytes[cpuid];
412725cf1a30Sjl 		/*
412825cf1a30Sjl 		 * Wait for all CPU to exit.
412925cf1a30Sjl 		 * However we do not want an infinite loop
413025cf1a30Sjl 		 * so we detect hangup situation here.
413125cf1a30Sjl 		 * If the slave CPU is still copying data,
413225cf1a30Sjl 		 * we will continue to wait.
413325cf1a30Sjl 		 * In error cases, the master has already set
413425cf1a30Sjl 		 * fmem_status.error to abort the copying.
413525cf1a30Sjl 		 * 1 m.s delay for them to abort copying and
413625cf1a30Sjl 		 * return to drmach_copy_rename_slave to set
413725cf1a30Sjl 		 * FMEM_LOOP_EXIT status should be enough.
413825cf1a30Sjl 		 */
413925cf1a30Sjl 		for (;;) {
414025cf1a30Sjl 			if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT)
414125cf1a30Sjl 				break;
414225cf1a30Sjl 			drmach_sleep_il();
414325cf1a30Sjl 			drv_usecwait(1000);
414425cf1a30Sjl 			now = prog->stat->nbytes[cpuid];
414525cf1a30Sjl 			if (now <= last) {
4146e98fafb9Sjl 				drv_usecwait(1000);
4147e98fafb9Sjl 				if (prog->critical->stat[cpuid] ==
4148e98fafb9Sjl 				    FMEM_LOOP_EXIT)
4149e98fafb9Sjl 					break;
4150e98fafb9Sjl 				cmn_err(CE_PANIC, "CPU %d hang during Copy "
4151e98fafb9Sjl 				    "Rename", cpuid);
415225cf1a30Sjl 			}
415325cf1a30Sjl 			last = now;
415425cf1a30Sjl 		}
4155b307f191Sbm 		if (prog->data->error[cpuid] == EOPL_FMEM_HW_ERROR) {
4156e98fafb9Sjl 			prom_panic("URGENT_ERROR_TRAP is detected during "
4157e98fafb9Sjl 			    "FMEM.\n");
415825cf1a30Sjl 		}
415925cf1a30Sjl 	}
416068ac2337Sjl 
416168ac2337Sjl 	/*
416268ac2337Sjl 	 * This must be done after all strands have exit.
416368ac2337Sjl 	 * Removing the TLB entry will affect both strands
416468ac2337Sjl 	 * in the same core.
416568ac2337Sjl 	 */
416668ac2337Sjl 
416768ac2337Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
416868ac2337Sjl 		if (CPU_IN_SET(cpuset, cpuid)) {
416968ac2337Sjl 			xc_one(cpuid, (xcfunc_t *)drmach_unlock_critical,
4170e98fafb9Sjl 			    (uint64_t)prog, 0);
417168ac2337Sjl 		}
417268ac2337Sjl 	}
417325cf1a30Sjl 
417425cf1a30Sjl 	in_sync = old_in_sync;
417525cf1a30Sjl 
417668ac2337Sjl 	/*
417768ac2337Sjl 	 * we should unlock before the following lock to keep the kpreempt
417868ac2337Sjl 	 * count correct.
417968ac2337Sjl 	 */
418068ac2337Sjl 	(void) drmach_unlock_critical((caddr_t)prog);
418168ac2337Sjl 
418268ac2337Sjl 	/*
418368ac2337Sjl 	 * we must remap again.  TLB might have been removed in above xcall.
418468ac2337Sjl 	 */
418568ac2337Sjl 
418668ac2337Sjl 	(void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
418768ac2337Sjl 
4188b307f191Sbm 	if (prog->data->fmem_status.error == ESBD_NOERROR)
418925cf1a30Sjl 		prog->data->fmem_status.error = rtn;
419025cf1a30Sjl 
419125cf1a30Sjl 	if (prog->data->copy_wait_time > 0) {
419225cf1a30Sjl 		DRMACH_PR("Unexpected long wait time %ld seconds "
4193e98fafb9Sjl 		    "during copy rename on CPU %d\n",
4194e98fafb9Sjl 		    prog->data->copy_wait_time/prog->data->stick_freq,
4195e98fafb9Sjl 		    prog->data->slowest_cpuid);
419625cf1a30Sjl 	}
419768ac2337Sjl 	drmach_unlock_critical((caddr_t)prog);
419825cf1a30Sjl }
4199