xref: /illumos-gate/usr/src/uts/sun4u/opl/io/drmach.c (revision e98fafb9)
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  */
2125cf1a30Sjl /*
2268ac2337Sjl  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
2325cf1a30Sjl  * Use is subject to license terms.
2425cf1a30Sjl  */
2525cf1a30Sjl 
2625cf1a30Sjl #pragma ident	"%Z%%M%	%I%	%E% SMI"
2725cf1a30Sjl 
2868ac2337Sjl 
2925cf1a30Sjl #include <sys/debug.h>
3025cf1a30Sjl #include <sys/types.h>
3125cf1a30Sjl #include <sys/varargs.h>
3225cf1a30Sjl #include <sys/errno.h>
3325cf1a30Sjl #include <sys/cred.h>
3425cf1a30Sjl #include <sys/dditypes.h>
3525cf1a30Sjl #include <sys/devops.h>
3625cf1a30Sjl #include <sys/modctl.h>
3725cf1a30Sjl #include <sys/poll.h>
3825cf1a30Sjl #include <sys/conf.h>
3925cf1a30Sjl #include <sys/ddi.h>
4025cf1a30Sjl #include <sys/sunddi.h>
4125cf1a30Sjl #include <sys/sunndi.h>
4225cf1a30Sjl #include <sys/ndi_impldefs.h>
4325cf1a30Sjl #include <sys/stat.h>
4425cf1a30Sjl #include <sys/kmem.h>
4525cf1a30Sjl #include <sys/vmem.h>
4625cf1a30Sjl #include <sys/opl_olympus_regs.h>
4725cf1a30Sjl #include <sys/cpuvar.h>
4825cf1a30Sjl #include <sys/cpupart.h>
4925cf1a30Sjl #include <sys/mem_config.h>
5025cf1a30Sjl #include <sys/ddi_impldefs.h>
5125cf1a30Sjl #include <sys/systm.h>
5225cf1a30Sjl #include <sys/machsystm.h>
5325cf1a30Sjl #include <sys/autoconf.h>
5425cf1a30Sjl #include <sys/cmn_err.h>
5525cf1a30Sjl #include <sys/sysmacros.h>
5625cf1a30Sjl #include <sys/x_call.h>
5725cf1a30Sjl #include <sys/promif.h>
5825cf1a30Sjl #include <sys/prom_plat.h>
5925cf1a30Sjl #include <sys/membar.h>
6025cf1a30Sjl #include <vm/seg_kmem.h>
6125cf1a30Sjl #include <sys/mem_cage.h>
6225cf1a30Sjl #include <sys/stack.h>
6325cf1a30Sjl #include <sys/archsystm.h>
6425cf1a30Sjl #include <vm/hat_sfmmu.h>
6525cf1a30Sjl #include <sys/pte.h>
6625cf1a30Sjl #include <sys/mmu.h>
6725cf1a30Sjl #include <sys/cpu_module.h>
6825cf1a30Sjl #include <sys/obpdefs.h>
6925cf1a30Sjl #include <sys/note.h>
7025cf1a30Sjl #include <sys/ontrap.h>
7125cf1a30Sjl #include <sys/cpu_sgnblk_defs.h>
7225cf1a30Sjl #include <sys/opl.h>
73*e98fafb9Sjl #include <sys/cpu_impl.h>
7425cf1a30Sjl 
7525cf1a30Sjl 
7625cf1a30Sjl #include <sys/promimpl.h>
7725cf1a30Sjl #include <sys/prom_plat.h>
7825cf1a30Sjl #include <sys/kobj.h>
7925cf1a30Sjl 
8025cf1a30Sjl #include <sys/sysevent.h>
8125cf1a30Sjl #include <sys/sysevent/dr.h>
8225cf1a30Sjl #include <sys/sysevent/eventdefs.h>
8325cf1a30Sjl 
8425cf1a30Sjl #include <sys/drmach.h>
8525cf1a30Sjl #include <sys/dr_util.h>
8625cf1a30Sjl 
8725cf1a30Sjl #include <sys/fcode.h>
8825cf1a30Sjl #include <sys/opl_cfg.h>
8925cf1a30Sjl 
9025cf1a30Sjl extern void		bcopy32_il(uint64_t, uint64_t);
9125cf1a30Sjl extern void		flush_cache_il(void);
9225cf1a30Sjl extern void		drmach_sleep_il(void);
9325cf1a30Sjl 
9425cf1a30Sjl typedef struct {
9525cf1a30Sjl 	struct drmach_node	*node;
9625cf1a30Sjl 	void			*data;
9725cf1a30Sjl } drmach_node_walk_args_t;
9825cf1a30Sjl 
9925cf1a30Sjl typedef struct drmach_node {
10025cf1a30Sjl 	void		*here;
10125cf1a30Sjl 
10225cf1a30Sjl 	pnode_t		(*get_dnode)(struct drmach_node *node);
10325cf1a30Sjl 	int		(*walk)(struct drmach_node *node, void *data,
10425cf1a30Sjl 				int (*cb)(drmach_node_walk_args_t *args));
10525cf1a30Sjl 	dev_info_t	*(*n_getdip)(struct drmach_node *node);
10625cf1a30Sjl 	int		(*n_getproplen)(struct drmach_node *node, char *name,
10725cf1a30Sjl 				int *len);
10825cf1a30Sjl 	int		(*n_getprop)(struct drmach_node *node, char *name,
10925cf1a30Sjl 				void *buf, int len);
11025cf1a30Sjl 	int		(*get_parent)(struct drmach_node *node,
11125cf1a30Sjl 				struct drmach_node *pnode);
11225cf1a30Sjl } drmach_node_t;
11325cf1a30Sjl 
11425cf1a30Sjl typedef struct {
11525cf1a30Sjl 	int		 min_index;
11625cf1a30Sjl 	int		 max_index;
11725cf1a30Sjl 	int		 arr_sz;
11825cf1a30Sjl 	drmachid_t	*arr;
11925cf1a30Sjl } drmach_array_t;
12025cf1a30Sjl 
12125cf1a30Sjl typedef struct {
12225cf1a30Sjl 	void		*isa;
12325cf1a30Sjl 
12425cf1a30Sjl 	void		(*dispose)(drmachid_t);
12525cf1a30Sjl 	sbd_error_t	*(*release)(drmachid_t);
12625cf1a30Sjl 	sbd_error_t	*(*status)(drmachid_t, drmach_status_t *);
12725cf1a30Sjl 
12825cf1a30Sjl 	char		 name[MAXNAMELEN];
12925cf1a30Sjl } drmach_common_t;
13025cf1a30Sjl 
13125cf1a30Sjl typedef	struct {
13225cf1a30Sjl 	uint32_t	core_present;
13325cf1a30Sjl 	uint32_t	core_hotadded;
13425cf1a30Sjl 	uint32_t	core_started;
13525cf1a30Sjl } drmach_cmp_t;
13625cf1a30Sjl 
13725cf1a30Sjl typedef struct {
13825cf1a30Sjl 	drmach_common_t	 cm;
13925cf1a30Sjl 	int		 bnum;
14025cf1a30Sjl 	int		 assigned;
14125cf1a30Sjl 	int		 powered;
14225cf1a30Sjl 	int		 connected;
14325cf1a30Sjl 	int		 cond;
14425cf1a30Sjl 	drmach_node_t	*tree;
14525cf1a30Sjl 	drmach_array_t	*devices;
14625cf1a30Sjl 	int		boot_board;	/* if board exists on bootup */
14725cf1a30Sjl 	drmach_cmp_t	cores[OPL_MAX_COREID_PER_BOARD];
14825cf1a30Sjl } drmach_board_t;
14925cf1a30Sjl 
15025cf1a30Sjl typedef struct {
15125cf1a30Sjl 	drmach_common_t	 cm;
15225cf1a30Sjl 	drmach_board_t	*bp;
15325cf1a30Sjl 	int		 unum;
15425cf1a30Sjl 	int		portid;
15525cf1a30Sjl 	int		 busy;
15625cf1a30Sjl 	int		 powered;
15725cf1a30Sjl 	const char	*type;
15825cf1a30Sjl 	drmach_node_t	*node;
15925cf1a30Sjl } drmach_device_t;
16025cf1a30Sjl 
16125cf1a30Sjl typedef struct drmach_cpu {
16225cf1a30Sjl 	drmach_device_t  dev;
16325cf1a30Sjl 	processorid_t    cpuid;
16425cf1a30Sjl 	int		sb;
16525cf1a30Sjl 	int		chipid;
16625cf1a30Sjl 	int		coreid;
16725cf1a30Sjl 	int		strandid;
16825cf1a30Sjl 	int		status;
16925cf1a30Sjl #define	OPL_CPU_HOTADDED	1
17025cf1a30Sjl } drmach_cpu_t;
17125cf1a30Sjl 
17225cf1a30Sjl typedef struct drmach_mem {
17325cf1a30Sjl 	drmach_device_t  dev;
17425cf1a30Sjl 	uint64_t	slice_base;
17525cf1a30Sjl 	uint64_t	slice_size;
17625cf1a30Sjl 	uint64_t	base_pa;	/* lowest installed memory base */
17725cf1a30Sjl 	uint64_t	nbytes;		/* size of installed memory */
17825cf1a30Sjl 	struct memlist *memlist;
17925cf1a30Sjl } drmach_mem_t;
18025cf1a30Sjl 
18125cf1a30Sjl typedef struct drmach_io {
18225cf1a30Sjl 	drmach_device_t  dev;
18325cf1a30Sjl 	int	channel;
18425cf1a30Sjl 	int	leaf;
18525cf1a30Sjl } drmach_io_t;
18625cf1a30Sjl 
18725cf1a30Sjl typedef struct drmach_domain_info {
18825cf1a30Sjl 	uint32_t	floating;
18925cf1a30Sjl 	int		allow_dr;
19025cf1a30Sjl } drmach_domain_info_t;
19125cf1a30Sjl 
19225cf1a30Sjl drmach_domain_info_t drmach_domain;
19325cf1a30Sjl 
19425cf1a30Sjl typedef struct {
19525cf1a30Sjl 	int		 flags;
19625cf1a30Sjl 	drmach_device_t	*dp;
19725cf1a30Sjl 	sbd_error_t	*err;
19825cf1a30Sjl 	dev_info_t	*dip;
19925cf1a30Sjl } drmach_config_args_t;
20025cf1a30Sjl 
20125cf1a30Sjl typedef struct {
20225cf1a30Sjl 	drmach_board_t	*obj;
20325cf1a30Sjl 	int		 ndevs;
20425cf1a30Sjl 	void		*a;
20525cf1a30Sjl 	sbd_error_t	*(*found)(void *a, const char *, int, drmachid_t);
20625cf1a30Sjl 	sbd_error_t	*err;
20725cf1a30Sjl } drmach_board_cb_data_t;
20825cf1a30Sjl 
20925cf1a30Sjl static drmach_array_t	*drmach_boards;
21025cf1a30Sjl 
21125cf1a30Sjl static sbd_error_t	*drmach_device_new(drmach_node_t *,
21225cf1a30Sjl 				drmach_board_t *, int, drmachid_t *);
21325cf1a30Sjl static sbd_error_t	*drmach_cpu_new(drmach_device_t *, drmachid_t *);
21425cf1a30Sjl static sbd_error_t	*drmach_mem_new(drmach_device_t *, drmachid_t *);
21525cf1a30Sjl static sbd_error_t	*drmach_io_new(drmach_device_t *, drmachid_t *);
21625cf1a30Sjl 
21725cf1a30Sjl static dev_info_t	*drmach_node_ddi_get_dip(drmach_node_t *np);
21825cf1a30Sjl static int		 drmach_node_ddi_get_prop(drmach_node_t *np,
21925cf1a30Sjl 				char *name, void *buf, int len);
22025cf1a30Sjl static int		 drmach_node_ddi_get_proplen(drmach_node_t *np,
22125cf1a30Sjl 				char *name, int *len);
22225cf1a30Sjl 
22325cf1a30Sjl static int 		drmach_get_portid(drmach_node_t *);
22425cf1a30Sjl static	sbd_error_t	*drmach_i_status(drmachid_t, drmach_status_t *);
22525cf1a30Sjl static int		opl_check_dr_status();
22625cf1a30Sjl static void		drmach_io_dispose(drmachid_t);
22725cf1a30Sjl static sbd_error_t	*drmach_io_release(drmachid_t);
22825cf1a30Sjl static sbd_error_t	*drmach_io_status(drmachid_t, drmach_status_t *);
22925cf1a30Sjl static int 		drmach_init(void);
23025cf1a30Sjl static void 		drmach_fini(void);
23125cf1a30Sjl static void		drmach_swap_pa(drmach_mem_t *, drmach_mem_t *);
23225cf1a30Sjl static drmach_board_t	*drmach_get_board_by_bnum(int);
23325cf1a30Sjl 
23425cf1a30Sjl /* options for the second argument in drmach_add_remove_cpu() */
23525cf1a30Sjl #define	HOTADD_CPU	1
23625cf1a30Sjl #define	HOTREMOVE_CPU	2
23725cf1a30Sjl 
23825cf1a30Sjl #define	ON_BOARD_CORE_NUM(x)	(((uint_t)(x) / OPL_MAX_STRANDID_PER_CORE) & \
23925cf1a30Sjl 	(OPL_MAX_COREID_PER_BOARD - 1))
24025cf1a30Sjl 
24125cf1a30Sjl extern struct cpu	*SIGBCPU;
24225cf1a30Sjl 
24325cf1a30Sjl static int		drmach_name2type_idx(char *);
24425cf1a30Sjl static drmach_board_t	*drmach_board_new(int, int);
24525cf1a30Sjl 
24625cf1a30Sjl #ifdef DEBUG
24725cf1a30Sjl 
24825cf1a30Sjl #define	DRMACH_PR		if (drmach_debug) printf
24925cf1a30Sjl int drmach_debug = 1;		 /* set to non-zero to enable debug messages */
25025cf1a30Sjl #else
25125cf1a30Sjl 
25225cf1a30Sjl #define	DRMACH_PR		_NOTE(CONSTANTCONDITION) if (0) printf
25325cf1a30Sjl #endif /* DEBUG */
25425cf1a30Sjl 
25525cf1a30Sjl 
25625cf1a30Sjl #define	DRMACH_OBJ(id)		((drmach_common_t *)id)
25725cf1a30Sjl 
258ddf95635Sbm #define	DRMACH_NULL_ID(id)	((id) == 0)
259ddf95635Sbm 
26025cf1a30Sjl #define	DRMACH_IS_BOARD_ID(id)	\
26125cf1a30Sjl 	((id != 0) &&		\
26225cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
26325cf1a30Sjl 
26425cf1a30Sjl #define	DRMACH_IS_CPU_ID(id)	\
26525cf1a30Sjl 	((id != 0) &&		\
26625cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
26725cf1a30Sjl 
26825cf1a30Sjl #define	DRMACH_IS_MEM_ID(id)	\
26925cf1a30Sjl 	((id != 0) &&		\
27025cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
27125cf1a30Sjl 
27225cf1a30Sjl #define	DRMACH_IS_IO_ID(id)	\
27325cf1a30Sjl 	((id != 0) &&		\
27425cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
27525cf1a30Sjl 
27625cf1a30Sjl #define	DRMACH_IS_DEVICE_ID(id)					\
27725cf1a30Sjl 	((id != 0) &&						\
27825cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
27925cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
28025cf1a30Sjl 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
28125cf1a30Sjl 
28225cf1a30Sjl #define	DRMACH_IS_ID(id)					\
28325cf1a30Sjl 	((id != 0) &&						\
28425cf1a30Sjl 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new ||	\
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_INTERNAL_ERROR() \
29025cf1a30Sjl 	drerr_new(1, EOPL_INTERNAL, drmach_ie_fmt, __LINE__)
29125cf1a30Sjl 
29225cf1a30Sjl static char		*drmach_ie_fmt = "drmach.c %d";
29325cf1a30Sjl 
29425cf1a30Sjl static struct {
29525cf1a30Sjl 	const char	*name;
29625cf1a30Sjl 	const char	*type;
29725cf1a30Sjl 	sbd_error_t	*(*new)(drmach_device_t *, drmachid_t *);
29825cf1a30Sjl } drmach_name2type[] = {
29925cf1a30Sjl 	{ "cpu",	DRMACH_DEVTYPE_CPU,		drmach_cpu_new },
30025cf1a30Sjl 	{ "pseudo-mc",	DRMACH_DEVTYPE_MEM,		drmach_mem_new },
30125cf1a30Sjl 	{ "pci",	DRMACH_DEVTYPE_PCI,		drmach_io_new  },
30225cf1a30Sjl };
30325cf1a30Sjl 
30425cf1a30Sjl /* utility */
30525cf1a30Sjl #define	MBYTE	(1048576ull)
30625cf1a30Sjl 
30725cf1a30Sjl /*
30825cf1a30Sjl  * drmach autoconfiguration data structures and interfaces
30925cf1a30Sjl  */
31025cf1a30Sjl 
31125cf1a30Sjl extern struct mod_ops mod_miscops;
31225cf1a30Sjl 
31325cf1a30Sjl static struct modlmisc modlmisc = {
31425cf1a30Sjl 	&mod_miscops,
31525cf1a30Sjl 	"OPL DR 1.1"
31625cf1a30Sjl };
31725cf1a30Sjl 
31825cf1a30Sjl static struct modlinkage modlinkage = {
31925cf1a30Sjl 	MODREV_1,
32025cf1a30Sjl 	(void *)&modlmisc,
32125cf1a30Sjl 	NULL
32225cf1a30Sjl };
32325cf1a30Sjl 
32425cf1a30Sjl static krwlock_t drmach_boards_rwlock;
32525cf1a30Sjl 
32625cf1a30Sjl typedef const char	*fn_t;
32725cf1a30Sjl 
32825cf1a30Sjl int
32925cf1a30Sjl _init(void)
33025cf1a30Sjl {
33125cf1a30Sjl 	int err;
33225cf1a30Sjl 
33325cf1a30Sjl 	if ((err = drmach_init()) != 0) {
33425cf1a30Sjl 		return (err);
33525cf1a30Sjl 	}
33625cf1a30Sjl 
33725cf1a30Sjl 	if ((err = mod_install(&modlinkage)) != 0) {
33825cf1a30Sjl 		drmach_fini();
33925cf1a30Sjl 	}
34025cf1a30Sjl 
34125cf1a30Sjl 	return (err);
34225cf1a30Sjl }
34325cf1a30Sjl 
34425cf1a30Sjl int
34525cf1a30Sjl _fini(void)
34625cf1a30Sjl {
34725cf1a30Sjl 	int	err;
34825cf1a30Sjl 
34925cf1a30Sjl 	if ((err = mod_remove(&modlinkage)) == 0)
35025cf1a30Sjl 		drmach_fini();
35125cf1a30Sjl 
35225cf1a30Sjl 	return (err);
35325cf1a30Sjl }
35425cf1a30Sjl 
35525cf1a30Sjl int
35625cf1a30Sjl _info(struct modinfo *modinfop)
35725cf1a30Sjl {
35825cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
35925cf1a30Sjl }
36025cf1a30Sjl 
36125cf1a30Sjl struct drmach_mc_lookup {
36225cf1a30Sjl 	int	bnum;
36325cf1a30Sjl 	drmach_board_t	*bp;
36425cf1a30Sjl 	dev_info_t *dip;	/* rv - set if found */
36525cf1a30Sjl };
36625cf1a30Sjl 
36725cf1a30Sjl #define	_ptob64(p) ((uint64_t)(p) << PAGESHIFT)
36825cf1a30Sjl #define	_b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
36925cf1a30Sjl 
37025cf1a30Sjl static int
37125cf1a30Sjl drmach_setup_mc_info(dev_info_t *dip, drmach_mem_t *mp)
37225cf1a30Sjl {
37325cf1a30Sjl 	uint64_t	memory_ranges[128];
37425cf1a30Sjl 	int len;
37525cf1a30Sjl 	struct memlist	*ml;
37625cf1a30Sjl 	int rv;
37725cf1a30Sjl 	hwd_sb_t *hwd;
37825cf1a30Sjl 	hwd_memory_t *pm;
37925cf1a30Sjl 
38025cf1a30Sjl 	len = sizeof (memory_ranges);
381*e98fafb9Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
382*e98fafb9Sjl 	    "sb-mem-ranges", (caddr_t)&memory_ranges[0], &len) !=
383*e98fafb9Sjl 	    DDI_PROP_SUCCESS) {
38425cf1a30Sjl 		mp->slice_base = 0;
38525cf1a30Sjl 		mp->slice_size = 0;
38625cf1a30Sjl 		return (-1);
38725cf1a30Sjl 	}
38825cf1a30Sjl 	mp->slice_base = memory_ranges[0];
38925cf1a30Sjl 	mp->slice_size = memory_ranges[1];
39025cf1a30Sjl 
39125cf1a30Sjl 	if (!mp->dev.bp->boot_board) {
39225cf1a30Sjl 		int i;
39325cf1a30Sjl 
39425cf1a30Sjl 		rv = opl_read_hwd(mp->dev.bp->bnum, NULL,  NULL, NULL, &hwd);
39525cf1a30Sjl 
39625cf1a30Sjl 		if (rv != 0) {
39725cf1a30Sjl 			return (-1);
39825cf1a30Sjl 		}
39925cf1a30Sjl 
40025cf1a30Sjl 		ml = NULL;
40125cf1a30Sjl 		pm = &hwd->sb_cmu.cmu_memory;
40225cf1a30Sjl 		for (i = 0; i < HWD_MAX_MEM_CHUNKS; i++) {
40325cf1a30Sjl 			if (pm->mem_chunks[i].chnk_size > 0) {
40425cf1a30Sjl 				ml = memlist_add_span(ml,
405*e98fafb9Sjl 				    pm->mem_chunks[i].chnk_start_address,
406*e98fafb9Sjl 				    pm->mem_chunks[i].chnk_size);
40725cf1a30Sjl 			}
40825cf1a30Sjl 		}
40925cf1a30Sjl 	} else {
41025cf1a30Sjl 		/*
41125cf1a30Sjl 		 * we intersect phys_install to get base_pa.
41225cf1a30Sjl 		 * This only works at bootup time.
41325cf1a30Sjl 		 */
41425cf1a30Sjl 
41525cf1a30Sjl 		memlist_read_lock();
41625cf1a30Sjl 		ml = memlist_dup(phys_install);
41725cf1a30Sjl 		memlist_read_unlock();
41825cf1a30Sjl 
41925cf1a30Sjl 		ml = memlist_del_span(ml, 0ull, mp->slice_base);
42025cf1a30Sjl 		if (ml) {
42125cf1a30Sjl 			uint64_t basepa, endpa;
42225cf1a30Sjl 			endpa = _ptob64(physmax + 1);
42325cf1a30Sjl 
42425cf1a30Sjl 			basepa = mp->slice_base + mp->slice_size;
42525cf1a30Sjl 
42625cf1a30Sjl 			ml = memlist_del_span(ml, basepa, endpa - basepa);
42725cf1a30Sjl 		}
42825cf1a30Sjl 	}
42925cf1a30Sjl 
43025cf1a30Sjl 	if (ml) {
43125cf1a30Sjl 		uint64_t nbytes = 0;
43225cf1a30Sjl 		struct memlist *p;
43325cf1a30Sjl 		for (p = ml; p; p = p->next) {
43425cf1a30Sjl 			nbytes += p->size;
43525cf1a30Sjl 		}
43625cf1a30Sjl 		if ((mp->nbytes = nbytes) > 0)
43725cf1a30Sjl 			mp->base_pa = ml->address;
43825cf1a30Sjl 		else
43925cf1a30Sjl 			mp->base_pa = 0;
44025cf1a30Sjl 		mp->memlist = ml;
44125cf1a30Sjl 	} else {
44225cf1a30Sjl 		mp->base_pa = 0;
44325cf1a30Sjl 		mp->nbytes = 0;
44425cf1a30Sjl 	}
44525cf1a30Sjl 	return (0);
44625cf1a30Sjl }
44725cf1a30Sjl 
44825cf1a30Sjl 
44925cf1a30Sjl struct drmach_hotcpu {
45025cf1a30Sjl 	drmach_board_t *bp;
45125cf1a30Sjl 	int	bnum;
45225cf1a30Sjl 	int	core_id;
45325cf1a30Sjl 	int 	rv;
45425cf1a30Sjl 	int	option;
45525cf1a30Sjl };
45625cf1a30Sjl 
45725cf1a30Sjl static int
45825cf1a30Sjl drmach_cpu_cb(dev_info_t *dip, void *arg)
45925cf1a30Sjl {
46025cf1a30Sjl 	struct drmach_hotcpu *p = (struct drmach_hotcpu *)arg;
46125cf1a30Sjl 	char name[OBP_MAXDRVNAME];
46225cf1a30Sjl 	int len = OBP_MAXDRVNAME;
46325cf1a30Sjl 	int bnum, core_id, strand_id;
46425cf1a30Sjl 	drmach_board_t *bp;
46525cf1a30Sjl 
46625cf1a30Sjl 	if (dip == ddi_root_node()) {
46725cf1a30Sjl 		return (DDI_WALK_CONTINUE);
46825cf1a30Sjl 	}
46925cf1a30Sjl 
47025cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
47125cf1a30Sjl 	    DDI_PROP_DONTPASS, "name",
47225cf1a30Sjl 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
47325cf1a30Sjl 		return (DDI_WALK_PRUNECHILD);
47425cf1a30Sjl 	}
47525cf1a30Sjl 
47625cf1a30Sjl 	/* only cmp has board number */
47725cf1a30Sjl 	bnum = -1;
47825cf1a30Sjl 	len = sizeof (bnum);
47925cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
48025cf1a30Sjl 	    DDI_PROP_DONTPASS, OBP_BOARDNUM,
48125cf1a30Sjl 	    (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
48225cf1a30Sjl 		bnum = -1;
48325cf1a30Sjl 	}
48425cf1a30Sjl 
48525cf1a30Sjl 	if (strcmp(name, "cmp") == 0) {
48625cf1a30Sjl 		if (bnum != p->bnum)
48725cf1a30Sjl 			return (DDI_WALK_PRUNECHILD);
48825cf1a30Sjl 		return (DDI_WALK_CONTINUE);
48925cf1a30Sjl 	}
49025cf1a30Sjl 	/* we have already pruned all unwanted cores and cpu's above */
49125cf1a30Sjl 	if (strcmp(name, "core") == 0) {
49225cf1a30Sjl 		return (DDI_WALK_CONTINUE);
49325cf1a30Sjl 	}
49425cf1a30Sjl 	if (strcmp(name, "cpu") == 0) {
49525cf1a30Sjl 		processorid_t cpuid;
49625cf1a30Sjl 		len = sizeof (cpuid);
49725cf1a30Sjl 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
49825cf1a30Sjl 		    DDI_PROP_DONTPASS, "cpuid",
49925cf1a30Sjl 		    (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
50025cf1a30Sjl 			p->rv = -1;
50125cf1a30Sjl 			return (DDI_WALK_TERMINATE);
50225cf1a30Sjl 		}
50325cf1a30Sjl 
50425cf1a30Sjl 		core_id = p->core_id;
50525cf1a30Sjl 
50625cf1a30Sjl 		bnum = LSB_ID(cpuid);
50725cf1a30Sjl 
50825cf1a30Sjl 		if (ON_BOARD_CORE_NUM(cpuid) != core_id)
50925cf1a30Sjl 			return (DDI_WALK_CONTINUE);
51025cf1a30Sjl 
51125cf1a30Sjl 		bp = p->bp;
51225cf1a30Sjl 		ASSERT(bnum == bp->bnum);
51325cf1a30Sjl 
51425cf1a30Sjl 		if (p->option == HOTADD_CPU) {
51525cf1a30Sjl 			if (prom_hotaddcpu(cpuid) != 0) {
51625cf1a30Sjl 				p->rv = -1;
51725cf1a30Sjl 				return (DDI_WALK_TERMINATE);
51825cf1a30Sjl 			}
51925cf1a30Sjl 			strand_id = STRAND_ID(cpuid);
52025cf1a30Sjl 			bp->cores[core_id].core_hotadded |= (1 << strand_id);
52125cf1a30Sjl 		} else if (p->option == HOTREMOVE_CPU) {
52225cf1a30Sjl 			if (prom_hotremovecpu(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 		}
52925cf1a30Sjl 		return (DDI_WALK_CONTINUE);
53025cf1a30Sjl 	}
53125cf1a30Sjl 
53225cf1a30Sjl 	return (DDI_WALK_PRUNECHILD);
53325cf1a30Sjl }
53425cf1a30Sjl 
53525cf1a30Sjl 
53625cf1a30Sjl static int
53725cf1a30Sjl drmach_add_remove_cpu(int bnum, int core_id, int option)
53825cf1a30Sjl {
53925cf1a30Sjl 	struct drmach_hotcpu arg;
54025cf1a30Sjl 	drmach_board_t *bp;
54125cf1a30Sjl 
54225cf1a30Sjl 	bp = drmach_get_board_by_bnum(bnum);
54325cf1a30Sjl 	ASSERT(bp);
54425cf1a30Sjl 
54525cf1a30Sjl 	arg.bp = bp;
54625cf1a30Sjl 	arg.bnum = bnum;
54725cf1a30Sjl 	arg.core_id = core_id;
54825cf1a30Sjl 	arg.rv = 0;
54925cf1a30Sjl 	arg.option = option;
55025cf1a30Sjl 	ddi_walk_devs(ddi_root_node(), drmach_cpu_cb, (void *)&arg);
55125cf1a30Sjl 	return (arg.rv);
55225cf1a30Sjl }
55325cf1a30Sjl 
55425cf1a30Sjl struct drmach_setup_core_arg {
55525cf1a30Sjl 	drmach_board_t *bp;
55625cf1a30Sjl };
55725cf1a30Sjl 
55825cf1a30Sjl static int
55925cf1a30Sjl drmach_setup_core_cb(dev_info_t *dip, void *arg)
56025cf1a30Sjl {
56125cf1a30Sjl 	struct drmach_setup_core_arg *p = (struct drmach_setup_core_arg *)arg;
56225cf1a30Sjl 	char name[OBP_MAXDRVNAME];
56325cf1a30Sjl 	int len = OBP_MAXDRVNAME;
56425cf1a30Sjl 	int bnum;
56525cf1a30Sjl 	int core_id, strand_id;
56625cf1a30Sjl 
56725cf1a30Sjl 	if (dip == ddi_root_node()) {
56825cf1a30Sjl 		return (DDI_WALK_CONTINUE);
56925cf1a30Sjl 	}
57025cf1a30Sjl 
57125cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
57225cf1a30Sjl 	    DDI_PROP_DONTPASS, "name",
57325cf1a30Sjl 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
57425cf1a30Sjl 		return (DDI_WALK_PRUNECHILD);
57525cf1a30Sjl 	}
57625cf1a30Sjl 
57725cf1a30Sjl 	/* only cmp has board number */
57825cf1a30Sjl 	bnum = -1;
57925cf1a30Sjl 	len = sizeof (bnum);
58025cf1a30Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
58125cf1a30Sjl 	    DDI_PROP_DONTPASS, OBP_BOARDNUM,
58225cf1a30Sjl 	    (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
58325cf1a30Sjl 		bnum = -1;
58425cf1a30Sjl 	}
58525cf1a30Sjl 
58625cf1a30Sjl 	if (strcmp(name, "cmp") == 0) {
58725cf1a30Sjl 		if (bnum != p->bp->bnum)
58825cf1a30Sjl 			return (DDI_WALK_PRUNECHILD);
58925cf1a30Sjl 		return (DDI_WALK_CONTINUE);
59025cf1a30Sjl 	}
59125cf1a30Sjl 	/* we have already pruned all unwanted cores and cpu's above */
59225cf1a30Sjl 	if (strcmp(name, "core") == 0) {
59325cf1a30Sjl 		return (DDI_WALK_CONTINUE);
59425cf1a30Sjl 	}
59525cf1a30Sjl 	if (strcmp(name, "cpu") == 0) {
59625cf1a30Sjl 		processorid_t cpuid;
59725cf1a30Sjl 		len = sizeof (cpuid);
59825cf1a30Sjl 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
59925cf1a30Sjl 		    DDI_PROP_DONTPASS, "cpuid",
60025cf1a30Sjl 		    (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
60125cf1a30Sjl 			return (DDI_WALK_TERMINATE);
60225cf1a30Sjl 		}
60325cf1a30Sjl 		bnum = LSB_ID(cpuid);
60425cf1a30Sjl 		ASSERT(bnum == p->bp->bnum);
60525cf1a30Sjl 		core_id = ON_BOARD_CORE_NUM(cpuid);
60625cf1a30Sjl 		strand_id = STRAND_ID(cpuid);
60725cf1a30Sjl 		p->bp->cores[core_id].core_present |= (1 << strand_id);
60825cf1a30Sjl 		return (DDI_WALK_CONTINUE);
60925cf1a30Sjl 	}
61025cf1a30Sjl 
61125cf1a30Sjl 	return (DDI_WALK_PRUNECHILD);
61225cf1a30Sjl }
61325cf1a30Sjl 
61425cf1a30Sjl 
61525cf1a30Sjl static void
61625cf1a30Sjl drmach_setup_core_info(drmach_board_t *obj)
61725cf1a30Sjl {
61825cf1a30Sjl 	struct drmach_setup_core_arg arg;
61925cf1a30Sjl 	int i;
62025cf1a30Sjl 
62125cf1a30Sjl 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
62225cf1a30Sjl 		obj->cores[i].core_present = 0;
62325cf1a30Sjl 		obj->cores[i].core_hotadded = 0;
62425cf1a30Sjl 		obj->cores[i].core_started = 0;
62525cf1a30Sjl 	}
62625cf1a30Sjl 	arg.bp = obj;
62725cf1a30Sjl 	ddi_walk_devs(ddi_root_node(), drmach_setup_core_cb, (void *)&arg);
62825cf1a30Sjl 
62925cf1a30Sjl 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
63025cf1a30Sjl 		if (obj->boot_board) {
63125cf1a30Sjl 			obj->cores[i].core_hotadded =
632*e98fafb9Sjl 			    obj->cores[i].core_started =
633*e98fafb9Sjl 			    obj->cores[i].core_present;
63425cf1a30Sjl 		}
63525cf1a30Sjl 	}
63625cf1a30Sjl }
63725cf1a30Sjl 
63825cf1a30Sjl /*
63925cf1a30Sjl  * drmach_node_* routines serve the purpose of separating the
64025cf1a30Sjl  * rest of the code from the device tree and OBP.  This is necessary
64125cf1a30Sjl  * because of In-Kernel-Probing.  Devices probed after stod, are probed
64225cf1a30Sjl  * by the in-kernel-prober, not OBP.  These devices, therefore, do not
64325cf1a30Sjl  * have dnode ids.
64425cf1a30Sjl  */
64525cf1a30Sjl 
64625cf1a30Sjl typedef struct {
64725cf1a30Sjl 	drmach_node_walk_args_t	*nwargs;
64825cf1a30Sjl 	int 			(*cb)(drmach_node_walk_args_t *args);
64925cf1a30Sjl 	int			err;
65025cf1a30Sjl } drmach_node_ddi_walk_args_t;
65125cf1a30Sjl 
65225cf1a30Sjl static int
65325cf1a30Sjl drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
65425cf1a30Sjl {
65525cf1a30Sjl 	drmach_node_ddi_walk_args_t	*nargs;
65625cf1a30Sjl 
65725cf1a30Sjl 	nargs = (drmach_node_ddi_walk_args_t *)arg;
65825cf1a30Sjl 
65925cf1a30Sjl 	/*
66025cf1a30Sjl 	 * dip doesn't have to be held here as we are called
66125cf1a30Sjl 	 * from ddi_walk_devs() which holds the dip.
66225cf1a30Sjl 	 */
66325cf1a30Sjl 	nargs->nwargs->node->here = (void *)dip;
66425cf1a30Sjl 
66525cf1a30Sjl 	nargs->err = nargs->cb(nargs->nwargs);
66625cf1a30Sjl 
66725cf1a30Sjl 
66825cf1a30Sjl 	/*
66925cf1a30Sjl 	 * Set "here" to NULL so that unheld dip is not accessible
67025cf1a30Sjl 	 * outside ddi_walk_devs()
67125cf1a30Sjl 	 */
67225cf1a30Sjl 	nargs->nwargs->node->here = NULL;
67325cf1a30Sjl 
67425cf1a30Sjl 	if (nargs->err)
67525cf1a30Sjl 		return (DDI_WALK_TERMINATE);
67625cf1a30Sjl 	else
67725cf1a30Sjl 		return (DDI_WALK_CONTINUE);
67825cf1a30Sjl }
67925cf1a30Sjl 
68025cf1a30Sjl static int
68125cf1a30Sjl drmach_node_ddi_walk(drmach_node_t *np, void *data,
68225cf1a30Sjl 		int (*cb)(drmach_node_walk_args_t *args))
68325cf1a30Sjl {
68425cf1a30Sjl 	drmach_node_walk_args_t		args;
68525cf1a30Sjl 	drmach_node_ddi_walk_args_t	nargs;
68625cf1a30Sjl 
68725cf1a30Sjl 
68825cf1a30Sjl 	/* initialized args structure for callback */
68925cf1a30Sjl 	args.node = np;
69025cf1a30Sjl 	args.data = data;
69125cf1a30Sjl 
69225cf1a30Sjl 	nargs.nwargs = &args;
69325cf1a30Sjl 	nargs.cb = cb;
69425cf1a30Sjl 	nargs.err = 0;
69525cf1a30Sjl 
69625cf1a30Sjl 	/*
69725cf1a30Sjl 	 * Root node doesn't have to be held in any way.
69825cf1a30Sjl 	 */
699*e98fafb9Sjl 	ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
70025cf1a30Sjl 
70125cf1a30Sjl 	return (nargs.err);
70225cf1a30Sjl }
70325cf1a30Sjl 
70425cf1a30Sjl static int
70525cf1a30Sjl drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
70625cf1a30Sjl {
70725cf1a30Sjl 	dev_info_t	*ndip;
70825cf1a30Sjl 	static char	*fn = "drmach_node_ddi_get_parent";
70925cf1a30Sjl 
71025cf1a30Sjl 	ndip = np->n_getdip(np);
71125cf1a30Sjl 	if (ndip == NULL) {
71225cf1a30Sjl 		cmn_err(CE_WARN, "%s: NULL dip", fn);
71325cf1a30Sjl 		return (-1);
71425cf1a30Sjl 	}
71525cf1a30Sjl 
71625cf1a30Sjl 	bcopy(np, pp, sizeof (drmach_node_t));
71725cf1a30Sjl 
71825cf1a30Sjl 	pp->here = (void *)ddi_get_parent(ndip);
71925cf1a30Sjl 	if (pp->here == NULL) {
72025cf1a30Sjl 		cmn_err(CE_WARN, "%s: NULL parent dip", fn);
72125cf1a30Sjl 		return (-1);
72225cf1a30Sjl 	}
72325cf1a30Sjl 
72425cf1a30Sjl 	return (0);
72525cf1a30Sjl }
72625cf1a30Sjl 
72725cf1a30Sjl /*ARGSUSED*/
72825cf1a30Sjl static pnode_t
72925cf1a30Sjl drmach_node_ddi_get_dnode(drmach_node_t *np)
73025cf1a30Sjl {
73125cf1a30Sjl 	return ((pnode_t)NULL);
73225cf1a30Sjl }
73325cf1a30Sjl 
73425cf1a30Sjl static drmach_node_t *
73525cf1a30Sjl drmach_node_new(void)
73625cf1a30Sjl {
73725cf1a30Sjl 	drmach_node_t *np;
73825cf1a30Sjl 
73925cf1a30Sjl 	np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
74025cf1a30Sjl 
74125cf1a30Sjl 	np->get_dnode = drmach_node_ddi_get_dnode;
74225cf1a30Sjl 	np->walk = drmach_node_ddi_walk;
74325cf1a30Sjl 	np->n_getdip = drmach_node_ddi_get_dip;
74425cf1a30Sjl 	np->n_getproplen = drmach_node_ddi_get_proplen;
74525cf1a30Sjl 	np->n_getprop = drmach_node_ddi_get_prop;
74625cf1a30Sjl 	np->get_parent = drmach_node_ddi_get_parent;
74725cf1a30Sjl 
74825cf1a30Sjl 	return (np);
74925cf1a30Sjl }
75025cf1a30Sjl 
75125cf1a30Sjl static void
75225cf1a30Sjl drmach_node_dispose(drmach_node_t *np)
75325cf1a30Sjl {
75425cf1a30Sjl 	kmem_free(np, sizeof (*np));
75525cf1a30Sjl }
75625cf1a30Sjl 
75725cf1a30Sjl static dev_info_t *
75825cf1a30Sjl drmach_node_ddi_get_dip(drmach_node_t *np)
75925cf1a30Sjl {
76025cf1a30Sjl 	return ((dev_info_t *)np->here);
76125cf1a30Sjl }
76225cf1a30Sjl 
76325cf1a30Sjl static int
76425cf1a30Sjl drmach_node_walk(drmach_node_t *np, void *param,
76525cf1a30Sjl 		int (*cb)(drmach_node_walk_args_t *args))
76625cf1a30Sjl {
76725cf1a30Sjl 	return (np->walk(np, param, cb));
76825cf1a30Sjl }
76925cf1a30Sjl 
77025cf1a30Sjl static int
77125cf1a30Sjl drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
77225cf1a30Sjl {
77325cf1a30Sjl 	int		rv = 0;
77425cf1a30Sjl 	dev_info_t	*ndip;
77525cf1a30Sjl 	static char	*fn = "drmach_node_ddi_get_prop";
77625cf1a30Sjl 
77725cf1a30Sjl 
77825cf1a30Sjl 	ndip = np->n_getdip(np);
77925cf1a30Sjl 	if (ndip == NULL) {
78025cf1a30Sjl 		cmn_err(CE_WARN, "%s: NULL dip", fn);
78125cf1a30Sjl 		rv = -1;
78225cf1a30Sjl 	} else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
78325cf1a30Sjl 	    DDI_PROP_DONTPASS, name,
78425cf1a30Sjl 	    (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
78525cf1a30Sjl 		rv = -1;
78625cf1a30Sjl 	}
78725cf1a30Sjl 
78825cf1a30Sjl 	return (rv);
78925cf1a30Sjl }
79025cf1a30Sjl 
79125cf1a30Sjl static int
79225cf1a30Sjl drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
79325cf1a30Sjl {
79425cf1a30Sjl 	int		rv = 0;
79525cf1a30Sjl 	dev_info_t	*ndip;
79625cf1a30Sjl 
79725cf1a30Sjl 	ndip = np->n_getdip(np);
79825cf1a30Sjl 	if (ndip == NULL) {
79925cf1a30Sjl 		rv = -1;
800*e98fafb9Sjl 	} else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS, name,
801*e98fafb9Sjl 	    len) != DDI_PROP_SUCCESS) {
80225cf1a30Sjl 		rv = -1;
80325cf1a30Sjl 	}
80425cf1a30Sjl 
80525cf1a30Sjl 	return (rv);
80625cf1a30Sjl }
80725cf1a30Sjl 
80825cf1a30Sjl static drmachid_t
80925cf1a30Sjl drmach_node_dup(drmach_node_t *np)
81025cf1a30Sjl {
81125cf1a30Sjl 	drmach_node_t *dup;
81225cf1a30Sjl 
81325cf1a30Sjl 	dup = drmach_node_new();
81425cf1a30Sjl 	dup->here = np->here;
81525cf1a30Sjl 	dup->get_dnode = np->get_dnode;
81625cf1a30Sjl 	dup->walk = np->walk;
81725cf1a30Sjl 	dup->n_getdip = np->n_getdip;
81825cf1a30Sjl 	dup->n_getproplen = np->n_getproplen;
81925cf1a30Sjl 	dup->n_getprop = np->n_getprop;
82025cf1a30Sjl 	dup->get_parent = np->get_parent;
82125cf1a30Sjl 
82225cf1a30Sjl 	return (dup);
82325cf1a30Sjl }
82425cf1a30Sjl 
82525cf1a30Sjl /*
82625cf1a30Sjl  * drmach_array provides convenient array construction, access,
82725cf1a30Sjl  * bounds checking and array destruction logic.
82825cf1a30Sjl  */
82925cf1a30Sjl 
83025cf1a30Sjl static drmach_array_t *
83125cf1a30Sjl drmach_array_new(int min_index, int max_index)
83225cf1a30Sjl {
83325cf1a30Sjl 	drmach_array_t *arr;
83425cf1a30Sjl 
83525cf1a30Sjl 	arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
83625cf1a30Sjl 
83725cf1a30Sjl 	arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
83825cf1a30Sjl 	if (arr->arr_sz > 0) {
83925cf1a30Sjl 		arr->min_index = min_index;
84025cf1a30Sjl 		arr->max_index = max_index;
84125cf1a30Sjl 
84225cf1a30Sjl 		arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
84325cf1a30Sjl 		return (arr);
84425cf1a30Sjl 	} else {
84525cf1a30Sjl 		kmem_free(arr, sizeof (*arr));
84625cf1a30Sjl 		return (0);
84725cf1a30Sjl 	}
84825cf1a30Sjl }
84925cf1a30Sjl 
85025cf1a30Sjl static int
85125cf1a30Sjl drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
85225cf1a30Sjl {
85325cf1a30Sjl 	if (idx < arr->min_index || idx > arr->max_index)
85425cf1a30Sjl 		return (-1);
85525cf1a30Sjl 	else {
85625cf1a30Sjl 		arr->arr[idx - arr->min_index] = val;
85725cf1a30Sjl 		return (0);
85825cf1a30Sjl 	}
85925cf1a30Sjl 	/*NOTREACHED*/
86025cf1a30Sjl }
86125cf1a30Sjl 
86225cf1a30Sjl static int
86325cf1a30Sjl drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
86425cf1a30Sjl {
86525cf1a30Sjl 	if (idx < arr->min_index || idx > arr->max_index)
86625cf1a30Sjl 		return (-1);
86725cf1a30Sjl 	else {
86825cf1a30Sjl 		*val = arr->arr[idx - arr->min_index];
86925cf1a30Sjl 		return (0);
87025cf1a30Sjl 	}
87125cf1a30Sjl 	/*NOTREACHED*/
87225cf1a30Sjl }
87325cf1a30Sjl 
87425cf1a30Sjl static int
87525cf1a30Sjl drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
87625cf1a30Sjl {
87725cf1a30Sjl 	int rv;
87825cf1a30Sjl 
87925cf1a30Sjl 	*idx = arr->min_index;
88025cf1a30Sjl 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
88125cf1a30Sjl 		*idx += 1;
88225cf1a30Sjl 
88325cf1a30Sjl 	return (rv);
88425cf1a30Sjl }
88525cf1a30Sjl 
88625cf1a30Sjl static int
88725cf1a30Sjl drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
88825cf1a30Sjl {
88925cf1a30Sjl 	int rv;
89025cf1a30Sjl 
89125cf1a30Sjl 	*idx += 1;
89225cf1a30Sjl 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
89325cf1a30Sjl 		*idx += 1;
89425cf1a30Sjl 
89525cf1a30Sjl 	return (rv);
89625cf1a30Sjl }
89725cf1a30Sjl 
89825cf1a30Sjl static void
89925cf1a30Sjl drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
90025cf1a30Sjl {
90125cf1a30Sjl 	drmachid_t	val;
90225cf1a30Sjl 	int		idx;
90325cf1a30Sjl 	int		rv;
90425cf1a30Sjl 
90525cf1a30Sjl 	rv = drmach_array_first(arr, &idx, &val);
90625cf1a30Sjl 	while (rv == 0) {
90725cf1a30Sjl 		(*disposer)(val);
90825cf1a30Sjl 		rv = drmach_array_next(arr, &idx, &val);
90925cf1a30Sjl 	}
91025cf1a30Sjl 
91125cf1a30Sjl 	kmem_free(arr->arr, arr->arr_sz);
91225cf1a30Sjl 	kmem_free(arr, sizeof (*arr));
91325cf1a30Sjl }
91425cf1a30Sjl 
91525cf1a30Sjl static drmach_board_t *
91625cf1a30Sjl drmach_get_board_by_bnum(int bnum)
91725cf1a30Sjl {
91825cf1a30Sjl 	drmachid_t id;
91925cf1a30Sjl 
92025cf1a30Sjl 	if (drmach_array_get(drmach_boards, bnum, &id) == 0)
92125cf1a30Sjl 		return ((drmach_board_t *)id);
92225cf1a30Sjl 	else
92325cf1a30Sjl 		return (NULL);
92425cf1a30Sjl }
92525cf1a30Sjl 
92625cf1a30Sjl static pnode_t
92725cf1a30Sjl drmach_node_get_dnode(drmach_node_t *np)
92825cf1a30Sjl {
92925cf1a30Sjl 	return (np->get_dnode(np));
93025cf1a30Sjl }
93125cf1a30Sjl 
93225cf1a30Sjl /*ARGSUSED*/
93325cf1a30Sjl sbd_error_t *
93425cf1a30Sjl drmach_configure(drmachid_t id, int flags)
93525cf1a30Sjl {
93625cf1a30Sjl 	drmach_device_t		*dp;
93725cf1a30Sjl 	sbd_error_t		*err = NULL;
93825cf1a30Sjl 	dev_info_t		*rdip;
93925cf1a30Sjl 	dev_info_t		*fdip = NULL;
94025cf1a30Sjl 
94125cf1a30Sjl 	if (DRMACH_IS_CPU_ID(id)) {
94225cf1a30Sjl 		return (NULL);
94325cf1a30Sjl 	}
94425cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
94525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
94625cf1a30Sjl 	dp = id;
94725cf1a30Sjl 	rdip = dp->node->n_getdip(dp->node);
94825cf1a30Sjl 
94925cf1a30Sjl 	ASSERT(rdip);
95025cf1a30Sjl 
95125cf1a30Sjl 	ASSERT(e_ddi_branch_held(rdip));
95225cf1a30Sjl 
95325cf1a30Sjl 	if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
95425cf1a30Sjl 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
95525cf1a30Sjl 		dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
95625cf1a30Sjl 
95725cf1a30Sjl 		(void) ddi_pathname(dip, path);
95825cf1a30Sjl 		err = drerr_new(1,  EOPL_DRVFAIL, path);
95925cf1a30Sjl 
96025cf1a30Sjl 		kmem_free(path, MAXPATHLEN);
96125cf1a30Sjl 
96225cf1a30Sjl 		/* If non-NULL, fdip is returned held and must be released */
96325cf1a30Sjl 		if (fdip != NULL)
96425cf1a30Sjl 			ddi_release_devi(fdip);
96525cf1a30Sjl 	}
96625cf1a30Sjl 
96725cf1a30Sjl 	return (err);
96825cf1a30Sjl }
96925cf1a30Sjl 
97025cf1a30Sjl 
97125cf1a30Sjl static sbd_error_t *
97225cf1a30Sjl drmach_device_new(drmach_node_t *node,
97325cf1a30Sjl 	drmach_board_t *bp, int portid, drmachid_t *idp)
97425cf1a30Sjl {
97525cf1a30Sjl 	int		 i;
97625cf1a30Sjl 	int		 rv;
97725cf1a30Sjl 	drmach_device_t	proto;
97825cf1a30Sjl 	sbd_error_t	*err;
97925cf1a30Sjl 	char		 name[OBP_MAXDRVNAME];
98025cf1a30Sjl 
98125cf1a30Sjl 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
98225cf1a30Sjl 	if (rv) {
98325cf1a30Sjl 		/* every node is expected to have a name */
984*e98fafb9Sjl 		err = drerr_new(1, EOPL_GETPROP, "device node %s: property %s",
985*e98fafb9Sjl 		    ddi_node_name(node->n_getdip(node)), "name");
98625cf1a30Sjl 		return (err);
98725cf1a30Sjl 	}
98825cf1a30Sjl 
98925cf1a30Sjl 	/*
99025cf1a30Sjl 	 * The node currently being examined is not listed in the name2type[]
99125cf1a30Sjl 	 * array.  In this case, the node is no interest to drmach.  Both
99225cf1a30Sjl 	 * dp and err are initialized here to yield nothing (no device or
99325cf1a30Sjl 	 * error structure) for this case.
99425cf1a30Sjl 	 */
99525cf1a30Sjl 	i = drmach_name2type_idx(name);
99625cf1a30Sjl 
99725cf1a30Sjl 
99825cf1a30Sjl 	if (i < 0) {
99925cf1a30Sjl 		*idp = (drmachid_t)0;
100025cf1a30Sjl 		return (NULL);
100125cf1a30Sjl 	}
100225cf1a30Sjl 
100325cf1a30Sjl 	/* device specific new function will set unum */
100425cf1a30Sjl 
100525cf1a30Sjl 	bzero(&proto, sizeof (proto));
100625cf1a30Sjl 	proto.type = drmach_name2type[i].type;
100725cf1a30Sjl 	proto.bp = bp;
100825cf1a30Sjl 	proto.node = node;
100925cf1a30Sjl 	proto.portid = portid;
101025cf1a30Sjl 
101125cf1a30Sjl 	return (drmach_name2type[i].new(&proto, idp));
101225cf1a30Sjl }
101325cf1a30Sjl 
101425cf1a30Sjl static void
101525cf1a30Sjl drmach_device_dispose(drmachid_t id)
101625cf1a30Sjl {
101725cf1a30Sjl 	drmach_device_t *self = id;
101825cf1a30Sjl 
101925cf1a30Sjl 	self->cm.dispose(id);
102025cf1a30Sjl }
102125cf1a30Sjl 
102225cf1a30Sjl 
102325cf1a30Sjl static drmach_board_t *
102425cf1a30Sjl drmach_board_new(int bnum, int boot_board)
102525cf1a30Sjl {
102625cf1a30Sjl 	static sbd_error_t *drmach_board_release(drmachid_t);
102725cf1a30Sjl 	static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
102825cf1a30Sjl 
102925cf1a30Sjl 	drmach_board_t	*bp;
103025cf1a30Sjl 
103125cf1a30Sjl 	bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
103225cf1a30Sjl 
103325cf1a30Sjl 	bp->cm.isa = (void *)drmach_board_new;
103425cf1a30Sjl 	bp->cm.release = drmach_board_release;
103525cf1a30Sjl 	bp->cm.status = drmach_board_status;
103625cf1a30Sjl 
103725cf1a30Sjl 	(void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
103825cf1a30Sjl 
103925cf1a30Sjl 	bp->bnum = bnum;
104025cf1a30Sjl 	bp->devices = NULL;
104125cf1a30Sjl 	bp->connected = boot_board;
104225cf1a30Sjl 	bp->tree = drmach_node_new();
104325cf1a30Sjl 	bp->assigned = boot_board;
104425cf1a30Sjl 	bp->powered = boot_board;
104525cf1a30Sjl 	bp->boot_board = boot_board;
104625cf1a30Sjl 
104725cf1a30Sjl 	/*
104825cf1a30Sjl 	 * If this is not bootup initialization, we have to wait till
104925cf1a30Sjl 	 * IKP sets up the device nodes in drmach_board_connect().
105025cf1a30Sjl 	 */
105125cf1a30Sjl 	if (boot_board)
105225cf1a30Sjl 		drmach_setup_core_info(bp);
105325cf1a30Sjl 
105425cf1a30Sjl 	drmach_array_set(drmach_boards, bnum, bp);
105525cf1a30Sjl 	return (bp);
105625cf1a30Sjl }
105725cf1a30Sjl 
105825cf1a30Sjl static void
105925cf1a30Sjl drmach_board_dispose(drmachid_t id)
106025cf1a30Sjl {
106125cf1a30Sjl 	drmach_board_t *bp;
106225cf1a30Sjl 
106325cf1a30Sjl 	ASSERT(DRMACH_IS_BOARD_ID(id));
106425cf1a30Sjl 	bp = id;
106525cf1a30Sjl 
106625cf1a30Sjl 	if (bp->tree)
106725cf1a30Sjl 		drmach_node_dispose(bp->tree);
106825cf1a30Sjl 
106925cf1a30Sjl 	if (bp->devices)
107025cf1a30Sjl 		drmach_array_dispose(bp->devices, drmach_device_dispose);
107125cf1a30Sjl 
107225cf1a30Sjl 	kmem_free(bp, sizeof (*bp));
107325cf1a30Sjl }
107425cf1a30Sjl 
107525cf1a30Sjl static sbd_error_t *
107625cf1a30Sjl drmach_board_status(drmachid_t id, drmach_status_t *stat)
107725cf1a30Sjl {
107825cf1a30Sjl 	sbd_error_t	*err = NULL;
107925cf1a30Sjl 	drmach_board_t	*bp;
108025cf1a30Sjl 
108125cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
108225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
108325cf1a30Sjl 	bp = id;
108425cf1a30Sjl 
108525cf1a30Sjl 	stat->assigned = bp->assigned;
108625cf1a30Sjl 	stat->powered = bp->powered;
108725cf1a30Sjl 	stat->busy = 0;			/* assume not busy */
108825cf1a30Sjl 	stat->configured = 0;		/* assume not configured */
108925cf1a30Sjl 	stat->empty = 0;
109025cf1a30Sjl 	stat->cond = bp->cond = SBD_COND_OK;
109125cf1a30Sjl 	strncpy(stat->type, "System Brd", sizeof (stat->type));
109225cf1a30Sjl 	stat->info[0] = '\0';
109325cf1a30Sjl 
109425cf1a30Sjl 	if (bp->devices) {
109525cf1a30Sjl 		int		 rv;
109625cf1a30Sjl 		int		 d_idx;
109725cf1a30Sjl 		drmachid_t	 d_id;
109825cf1a30Sjl 
109925cf1a30Sjl 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
110025cf1a30Sjl 		while (rv == 0) {
110125cf1a30Sjl 			drmach_status_t	d_stat;
110225cf1a30Sjl 
110325cf1a30Sjl 			err = drmach_i_status(d_id, &d_stat);
110425cf1a30Sjl 			if (err)
110525cf1a30Sjl 				break;
110625cf1a30Sjl 
110725cf1a30Sjl 			stat->busy |= d_stat.busy;
110825cf1a30Sjl 			stat->configured |= d_stat.configured;
110925cf1a30Sjl 
111025cf1a30Sjl 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
111125cf1a30Sjl 		}
111225cf1a30Sjl 	}
111325cf1a30Sjl 
111425cf1a30Sjl 	return (err);
111525cf1a30Sjl }
111625cf1a30Sjl 
111725cf1a30Sjl int
111825cf1a30Sjl drmach_board_is_floating(drmachid_t id)
111925cf1a30Sjl {
112025cf1a30Sjl 	drmach_board_t *bp;
112125cf1a30Sjl 
112225cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
112325cf1a30Sjl 		return (0);
112425cf1a30Sjl 
112525cf1a30Sjl 	bp = (drmach_board_t *)id;
112625cf1a30Sjl 
112725cf1a30Sjl 	return ((drmach_domain.floating & (1 << bp->bnum)) ? 1 : 0);
112825cf1a30Sjl }
112925cf1a30Sjl 
113025cf1a30Sjl static int
113125cf1a30Sjl drmach_init(void)
113225cf1a30Sjl {
113325cf1a30Sjl 	dev_info_t	*rdip;
113425cf1a30Sjl 	int		i, rv, len;
113525cf1a30Sjl 	int		*floating;
113625cf1a30Sjl 
113725cf1a30Sjl 	rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
113825cf1a30Sjl 
113925cf1a30Sjl 	drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
114025cf1a30Sjl 
114125cf1a30Sjl 	rdip = ddi_root_node();
114225cf1a30Sjl 
114325cf1a30Sjl 	if (ddi_getproplen(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1144*e98fafb9Sjl 	    "floating-boards", &len) != DDI_PROP_SUCCESS) {
114525cf1a30Sjl 		cmn_err(CE_WARN, "Cannot get floating-boards proplen\n");
114625cf1a30Sjl 	} else {
114725cf1a30Sjl 		floating = (int *)kmem_alloc(len, KM_SLEEP);
1148*e98fafb9Sjl 		rv = ddi_prop_op(DDI_DEV_T_ANY, rdip, PROP_LEN_AND_VAL_BUF,
1149*e98fafb9Sjl 		    DDI_PROP_DONTPASS, "floating-boards", (caddr_t)floating,
1150*e98fafb9Sjl 		    &len);
115125cf1a30Sjl 		if (rv != DDI_PROP_SUCCESS) {
115225cf1a30Sjl 			cmn_err(CE_WARN, "Cannot get floating-boards prop\n");
115325cf1a30Sjl 		} else {
115425cf1a30Sjl 			drmach_domain.floating = 0;
115525cf1a30Sjl 			for (i = 0; i < len / sizeof (int); i++) {
115625cf1a30Sjl 				drmach_domain.floating |= (1 << floating[i]);
115725cf1a30Sjl 			}
115825cf1a30Sjl 		}
115925cf1a30Sjl 		kmem_free(floating, len);
116025cf1a30Sjl 	}
116125cf1a30Sjl 	drmach_domain.allow_dr = opl_check_dr_status();
116225cf1a30Sjl 
116325cf1a30Sjl 	rdip = ddi_get_child(ddi_root_node());
116425cf1a30Sjl 	do {
116525cf1a30Sjl 		int		 bnum;
116625cf1a30Sjl 		drmachid_t	 id;
116725cf1a30Sjl 
116825cf1a30Sjl 		bnum = -1;
1169*e98fafb9Sjl 		bnum = ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1170*e98fafb9Sjl 		    OBP_BOARDNUM, -1);
117125cf1a30Sjl 		if (bnum == -1)
117225cf1a30Sjl 			continue;
117325cf1a30Sjl 
117425cf1a30Sjl 		if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
1175*e98fafb9Sjl 			cmn_err(CE_WARN, "Device node 0x%p has invalid "
1176*e98fafb9Sjl 			    "property value, %s=%d", rdip, OBP_BOARDNUM, bnum);
117725cf1a30Sjl 			goto error;
117825cf1a30Sjl 		} else if (id == NULL) {
117925cf1a30Sjl 			(void) drmach_board_new(bnum, 1);
118025cf1a30Sjl 		}
118125cf1a30Sjl 	} while ((rdip = ddi_get_next_sibling(rdip)) != NULL);
118225cf1a30Sjl 
118325cf1a30Sjl 	opl_hold_devtree();
118425cf1a30Sjl 
118525cf1a30Sjl 	/*
118625cf1a30Sjl 	 * Initialize the IKP feature.
118725cf1a30Sjl 	 *
118825cf1a30Sjl 	 * This can be done only after DR has acquired a hold on all the
118925cf1a30Sjl 	 * device nodes that are interesting to IKP.
119025cf1a30Sjl 	 */
119125cf1a30Sjl 	if (opl_init_cfg() != 0) {
119225cf1a30Sjl 		cmn_err(CE_WARN, "DR - IKP initialization failed");
119325cf1a30Sjl 
119425cf1a30Sjl 		opl_release_devtree();
119525cf1a30Sjl 
119625cf1a30Sjl 		goto error;
119725cf1a30Sjl 	}
119825cf1a30Sjl 
119925cf1a30Sjl 	return (0);
120025cf1a30Sjl error:
120125cf1a30Sjl 	drmach_array_dispose(drmach_boards, drmach_board_dispose);
120225cf1a30Sjl 	rw_destroy(&drmach_boards_rwlock);
120325cf1a30Sjl 	return (ENXIO);
120425cf1a30Sjl }
120525cf1a30Sjl 
120625cf1a30Sjl static void
120725cf1a30Sjl drmach_fini(void)
120825cf1a30Sjl {
120925cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
121025cf1a30Sjl 	drmach_array_dispose(drmach_boards, drmach_board_dispose);
121125cf1a30Sjl 	drmach_boards = NULL;
121225cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
121325cf1a30Sjl 
121425cf1a30Sjl 	/*
121525cf1a30Sjl 	 * Walk immediate children of the root devinfo node
121625cf1a30Sjl 	 * releasing holds acquired on branches in drmach_init()
121725cf1a30Sjl 	 */
121825cf1a30Sjl 
121925cf1a30Sjl 	opl_release_devtree();
122025cf1a30Sjl 
122125cf1a30Sjl 	rw_destroy(&drmach_boards_rwlock);
122225cf1a30Sjl }
122325cf1a30Sjl 
122425cf1a30Sjl /*
122525cf1a30Sjl  *	Each system board contains 2 Oberon PCI bridge and
122625cf1a30Sjl  *	1 CMUCH.
122725cf1a30Sjl  *	Each oberon has 2 channels.
122825cf1a30Sjl  *	Each channel has 2 pci-ex leaf.
122925cf1a30Sjl  *	Each CMUCH has 1 pci bus.
123025cf1a30Sjl  *
123125cf1a30Sjl  *
123225cf1a30Sjl  *	Device Path:
123325cf1a30Sjl  *	/pci@<portid>,reg
123425cf1a30Sjl  *
123525cf1a30Sjl  *	where
123625cf1a30Sjl  *	portid[10] = 0
123725cf1a30Sjl  *	portid[9:0] = LLEAF_ID[9:0] of the Oberon Channel
123825cf1a30Sjl  *
123925cf1a30Sjl  *	LLEAF_ID[9:8] = 0
124025cf1a30Sjl  *	LLEAF_ID[8:4] = LSB_ID[4:0]
124125cf1a30Sjl  *	LLEAF_ID[3:1] = IO Channel#[2:0] (0,1,2,3 for Oberon)
124225cf1a30Sjl  *			channel 4 is pcicmu
124325cf1a30Sjl  *	LLEAF_ID[0] = PCI Leaf Number (0 for leaf-A, 1 for leaf-B)
124425cf1a30Sjl  *
124525cf1a30Sjl  *	Properties:
124625cf1a30Sjl  *	name = pci
124725cf1a30Sjl  *	device_type = "pciex"
124825cf1a30Sjl  *	board# = LSBID
124925cf1a30Sjl  *	reg = int32 * 2, Oberon CSR space of the leaf and the UBC space
125025cf1a30Sjl  *	portid = Jupiter Bus Device ID ((LSB_ID << 3)|pciport#)
125125cf1a30Sjl  */
125225cf1a30Sjl 
125325cf1a30Sjl static sbd_error_t *
125425cf1a30Sjl drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
125525cf1a30Sjl {
125625cf1a30Sjl 	drmach_io_t	*ip;
125725cf1a30Sjl 
125825cf1a30Sjl 	int		 portid;
125925cf1a30Sjl 
126025cf1a30Sjl 	portid = proto->portid;
126125cf1a30Sjl 	ASSERT(portid != -1);
126225cf1a30Sjl 	proto->unum = portid & (MAX_IO_UNITS_PER_BOARD - 1);
126325cf1a30Sjl 
126425cf1a30Sjl 	ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
126525cf1a30Sjl 	bcopy(proto, &ip->dev, sizeof (ip->dev));
126625cf1a30Sjl 	ip->dev.node = drmach_node_dup(proto->node);
126725cf1a30Sjl 	ip->dev.cm.isa = (void *)drmach_io_new;
126825cf1a30Sjl 	ip->dev.cm.dispose = drmach_io_dispose;
126925cf1a30Sjl 	ip->dev.cm.release = drmach_io_release;
127025cf1a30Sjl 	ip->dev.cm.status = drmach_io_status;
127125cf1a30Sjl 	ip->channel = (portid >> 1) & 0x7;
127225cf1a30Sjl 	ip->leaf = (portid & 0x1);
127325cf1a30Sjl 
127425cf1a30Sjl 	snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
1275*e98fafb9Sjl 	    ip->dev.type, ip->dev.unum);
127625cf1a30Sjl 
127725cf1a30Sjl 	*idp = (drmachid_t)ip;
127825cf1a30Sjl 	return (NULL);
127925cf1a30Sjl }
128025cf1a30Sjl 
128125cf1a30Sjl 
128225cf1a30Sjl static void
128325cf1a30Sjl drmach_io_dispose(drmachid_t id)
128425cf1a30Sjl {
128525cf1a30Sjl 	drmach_io_t *self;
128625cf1a30Sjl 
128725cf1a30Sjl 	ASSERT(DRMACH_IS_IO_ID(id));
128825cf1a30Sjl 
128925cf1a30Sjl 	self = id;
129025cf1a30Sjl 	if (self->dev.node)
129125cf1a30Sjl 		drmach_node_dispose(self->dev.node);
129225cf1a30Sjl 
129325cf1a30Sjl 	kmem_free(self, sizeof (*self));
129425cf1a30Sjl }
129525cf1a30Sjl 
129625cf1a30Sjl /*ARGSUSED*/
129725cf1a30Sjl sbd_error_t *
129825cf1a30Sjl drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
129925cf1a30Sjl {
130025cf1a30Sjl 	drmach_board_t	*bp = (drmach_board_t *)id;
130125cf1a30Sjl 	sbd_error_t	*err = NULL;
130225cf1a30Sjl 
130325cf1a30Sjl 	/* allow status and ncm operations to always succeed */
130425cf1a30Sjl 	if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) {
130525cf1a30Sjl 		return (NULL);
130625cf1a30Sjl 	}
130725cf1a30Sjl 
130825cf1a30Sjl 	/* check all other commands for the required option string */
130925cf1a30Sjl 
131025cf1a30Sjl 	if ((opts->size > 0) && (opts->copts != NULL)) {
131125cf1a30Sjl 
131225cf1a30Sjl 		DRMACH_PR("platform options: %s\n", opts->copts);
131325cf1a30Sjl 
131425cf1a30Sjl 		if (strstr(opts->copts, "opldr") == NULL) {
131525cf1a30Sjl 			err = drerr_new(1, EOPL_SUPPORT, NULL);
131625cf1a30Sjl 		}
131725cf1a30Sjl 	} else {
131825cf1a30Sjl 		err = drerr_new(1, EOPL_SUPPORT, NULL);
131925cf1a30Sjl 	}
132025cf1a30Sjl 
132125cf1a30Sjl 	if (!err && id && DRMACH_IS_BOARD_ID(id)) {
132225cf1a30Sjl 		switch (cmd) {
132325cf1a30Sjl 			case SBD_CMD_TEST:
132425cf1a30Sjl 			case SBD_CMD_STATUS:
132525cf1a30Sjl 			case SBD_CMD_GETNCM:
132625cf1a30Sjl 				break;
132725cf1a30Sjl 			case SBD_CMD_CONNECT:
132825cf1a30Sjl 				if (bp->connected)
132925cf1a30Sjl 					err = drerr_new(0, ESBD_STATE, NULL);
133025cf1a30Sjl 				else if (!drmach_domain.allow_dr)
1331*e98fafb9Sjl 					err = drerr_new(1, EOPL_SUPPORT, NULL);
133225cf1a30Sjl 				break;
133325cf1a30Sjl 			case SBD_CMD_DISCONNECT:
133425cf1a30Sjl 				if (!bp->connected)
133525cf1a30Sjl 					err = drerr_new(0, ESBD_STATE, NULL);
133625cf1a30Sjl 				else if (!drmach_domain.allow_dr)
1337*e98fafb9Sjl 					err = drerr_new(1, EOPL_SUPPORT, NULL);
133825cf1a30Sjl 				break;
133925cf1a30Sjl 			default:
134025cf1a30Sjl 				if (!drmach_domain.allow_dr)
1341*e98fafb9Sjl 					err = drerr_new(1, EOPL_SUPPORT, NULL);
134225cf1a30Sjl 				break;
134325cf1a30Sjl 
134425cf1a30Sjl 		}
134525cf1a30Sjl 	}
134625cf1a30Sjl 
134725cf1a30Sjl 	return (err);
134825cf1a30Sjl }
134925cf1a30Sjl 
135025cf1a30Sjl /*ARGSUSED*/
135125cf1a30Sjl sbd_error_t *
135225cf1a30Sjl drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
135325cf1a30Sjl {
135425cf1a30Sjl 	return (NULL);
135525cf1a30Sjl }
135625cf1a30Sjl 
135725cf1a30Sjl sbd_error_t *
135825cf1a30Sjl drmach_board_assign(int bnum, drmachid_t *id)
135925cf1a30Sjl {
136025cf1a30Sjl 	sbd_error_t	*err = NULL;
136125cf1a30Sjl 
136225cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
136325cf1a30Sjl 
136425cf1a30Sjl 	if (drmach_array_get(drmach_boards, bnum, id) == -1) {
136525cf1a30Sjl 		err = drerr_new(1, EOPL_BNUM, "%d", bnum);
136625cf1a30Sjl 	} else {
136725cf1a30Sjl 		drmach_board_t	*bp;
136825cf1a30Sjl 
136925cf1a30Sjl 		if (*id)
137025cf1a30Sjl 			rw_downgrade(&drmach_boards_rwlock);
137125cf1a30Sjl 
137225cf1a30Sjl 		bp = *id;
137325cf1a30Sjl 		if (!(*id))
137425cf1a30Sjl 			bp = *id  =
1375*e98fafb9Sjl 			    (drmachid_t)drmach_board_new(bnum, 0);
137625cf1a30Sjl 		bp->assigned = 1;
137725cf1a30Sjl 	}
137825cf1a30Sjl 
137925cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
138025cf1a30Sjl 
138125cf1a30Sjl 	return (err);
138225cf1a30Sjl }
138325cf1a30Sjl 
138425cf1a30Sjl /*ARGSUSED*/
138525cf1a30Sjl sbd_error_t *
138625cf1a30Sjl drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
138725cf1a30Sjl {
1388*e98fafb9Sjl 	extern int	cpu_alljupiter;
138925cf1a30Sjl 	drmach_board_t	*obj = (drmach_board_t *)id;
1390*e98fafb9Sjl 	unsigned	cpu_impl;
139125cf1a30Sjl 
139225cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
139325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
139425cf1a30Sjl 
1395*e98fafb9Sjl 	if (opl_probe_sb(obj->bnum, &cpu_impl) != 0)
13968eafe49bSbm 		return (drerr_new(1, EOPL_PROBE, NULL));
139725cf1a30Sjl 
1398*e98fafb9Sjl 	if (cpu_alljupiter) {
1399*e98fafb9Sjl 		if (cpu_impl & (1 << OLYMPUS_C_IMPL))
1400*e98fafb9Sjl 			return (drerr_new(1, EOPL_MIXED_CPU, NULL));
1401*e98fafb9Sjl 	}
1402*e98fafb9Sjl 
140325cf1a30Sjl 	(void) prom_attach_notice(obj->bnum);
140425cf1a30Sjl 
140525cf1a30Sjl 	drmach_setup_core_info(obj);
140625cf1a30Sjl 
140725cf1a30Sjl 	obj->connected = 1;
140825cf1a30Sjl 
140925cf1a30Sjl 	return (NULL);
141025cf1a30Sjl }
141125cf1a30Sjl 
141225cf1a30Sjl static int drmach_cache_flush_flag[NCPU];
141325cf1a30Sjl 
141425cf1a30Sjl /*ARGSUSED*/
141525cf1a30Sjl static void
141625cf1a30Sjl drmach_flush_cache(uint64_t id, uint64_t dummy)
141725cf1a30Sjl {
141825cf1a30Sjl 	extern void cpu_flush_ecache(void);
141925cf1a30Sjl 
142025cf1a30Sjl 	cpu_flush_ecache();
142125cf1a30Sjl 	drmach_cache_flush_flag[id] = 0;
142225cf1a30Sjl }
142325cf1a30Sjl 
142425cf1a30Sjl static void
142525cf1a30Sjl drmach_flush_all()
142625cf1a30Sjl {
142725cf1a30Sjl 	cpuset_t	xc_cpuset;
142825cf1a30Sjl 	int		i;
142925cf1a30Sjl 
143025cf1a30Sjl 	xc_cpuset = cpu_ready_set;
143125cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
143225cf1a30Sjl 		if (CPU_IN_SET(xc_cpuset, i)) {
143325cf1a30Sjl 			drmach_cache_flush_flag[i] = 1;
143425cf1a30Sjl 			xc_one(i, drmach_flush_cache, i, 0);
143525cf1a30Sjl 			while (drmach_cache_flush_flag[i]) {
143625cf1a30Sjl 				DELAY(1000);
143725cf1a30Sjl 			}
143825cf1a30Sjl 		}
143925cf1a30Sjl 	}
144025cf1a30Sjl }
144125cf1a30Sjl 
144225cf1a30Sjl static int
144325cf1a30Sjl drmach_disconnect_cpus(drmach_board_t *bp)
144425cf1a30Sjl {
144525cf1a30Sjl 	int i, bnum;
144625cf1a30Sjl 
144725cf1a30Sjl 	bnum = bp->bnum;
144825cf1a30Sjl 
144925cf1a30Sjl 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
1450*e98fafb9Sjl 		if (bp->cores[i].core_present) {
1451*e98fafb9Sjl 			if (bp->cores[i].core_started)
1452*e98fafb9Sjl 				return (-1);
1453*e98fafb9Sjl 			if (bp->cores[i].core_hotadded) {
1454*e98fafb9Sjl 				if (drmach_add_remove_cpu(bnum, i,
1455*e98fafb9Sjl 				    HOTREMOVE_CPU)) {
1456*e98fafb9Sjl 					cmn_err(CE_WARN, "Failed to remove "
1457*e98fafb9Sjl 					    "CMP %d on board %d\n", i, bnum);
1458*e98fafb9Sjl 					return (-1);
1459*e98fafb9Sjl 				}
1460*e98fafb9Sjl 			}
146125cf1a30Sjl 		}
146225cf1a30Sjl 	}
146325cf1a30Sjl 	return (0);
146425cf1a30Sjl }
146525cf1a30Sjl 
146625cf1a30Sjl /*ARGSUSED*/
146725cf1a30Sjl sbd_error_t *
146825cf1a30Sjl drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
146925cf1a30Sjl {
147025cf1a30Sjl 	drmach_board_t *obj;
147125cf1a30Sjl 	int rv = 0;
147225cf1a30Sjl 	sbd_error_t		*err = NULL;
147325cf1a30Sjl 
1474ddf95635Sbm 	if (DRMACH_NULL_ID(id))
1475ddf95635Sbm 		return (NULL);
147625cf1a30Sjl 
147725cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
147825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
147925cf1a30Sjl 
148025cf1a30Sjl 	obj = (drmach_board_t *)id;
148125cf1a30Sjl 
148225cf1a30Sjl 	if (drmach_disconnect_cpus(obj)) {
14838eafe49bSbm 		err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
148425cf1a30Sjl 		return (err);
148525cf1a30Sjl 	}
148625cf1a30Sjl 
148725cf1a30Sjl 	rv = opl_unprobe_sb(obj->bnum);
148825cf1a30Sjl 
148925cf1a30Sjl 	if (rv == 0) {
149025cf1a30Sjl 		prom_detach_notice(obj->bnum);
149125cf1a30Sjl 		obj->connected = 0;
149225cf1a30Sjl 
149325cf1a30Sjl 	} else
14948eafe49bSbm 		err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
149525cf1a30Sjl 
149625cf1a30Sjl 	return (err);
149725cf1a30Sjl }
149825cf1a30Sjl 
149925cf1a30Sjl static int
150025cf1a30Sjl drmach_get_portid(drmach_node_t *np)
150125cf1a30Sjl {
150225cf1a30Sjl 	int		portid;
150325cf1a30Sjl 	char		type[OBP_MAXPROPNAME];
150425cf1a30Sjl 
150525cf1a30Sjl 	if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
150625cf1a30Sjl 		return (portid);
150725cf1a30Sjl 
150825cf1a30Sjl 	/*
150925cf1a30Sjl 	 * Get the device_type property to see if we should
151025cf1a30Sjl 	 * continue processing this node.
151125cf1a30Sjl 	 */
151225cf1a30Sjl 	if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
151325cf1a30Sjl 		return (-1);
151425cf1a30Sjl 
151525cf1a30Sjl 	if (strcmp(type, OPL_CPU_NODE) == 0) {
151625cf1a30Sjl 		/*
151725cf1a30Sjl 		 * We return cpuid because it has no portid
151825cf1a30Sjl 		 */
151925cf1a30Sjl 		if (np->n_getprop(np, "cpuid", &portid, sizeof (portid)) == 0)
152025cf1a30Sjl 			return (portid);
152125cf1a30Sjl 	}
152225cf1a30Sjl 
152325cf1a30Sjl 	return (-1);
152425cf1a30Sjl }
152525cf1a30Sjl 
152625cf1a30Sjl /*
152725cf1a30Sjl  * This is a helper function to determine if a given
152825cf1a30Sjl  * node should be considered for a dr operation according
152925cf1a30Sjl  * to predefined dr type nodes and the node's name.
153025cf1a30Sjl  * Formal Parameter : The name of a device node.
153125cf1a30Sjl  * Return Value: -1, name does not map to a valid dr type.
153225cf1a30Sjl  *		 A value greater or equal to 0, name is a valid dr type.
153325cf1a30Sjl  */
153425cf1a30Sjl static int
153525cf1a30Sjl drmach_name2type_idx(char *name)
153625cf1a30Sjl {
153725cf1a30Sjl 	int 	index, ntypes;
153825cf1a30Sjl 
153925cf1a30Sjl 	if (name == NULL)
154025cf1a30Sjl 		return (-1);
154125cf1a30Sjl 
154225cf1a30Sjl 	/*
154325cf1a30Sjl 	 * Determine how many possible types are currently supported
154425cf1a30Sjl 	 * for dr.
154525cf1a30Sjl 	 */
154625cf1a30Sjl 	ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
154725cf1a30Sjl 
154825cf1a30Sjl 	/* Determine if the node's name correspond to a predefined type. */
154925cf1a30Sjl 	for (index = 0; index < ntypes; index++) {
155025cf1a30Sjl 		if (strcmp(drmach_name2type[index].name, name) == 0)
155125cf1a30Sjl 			/* The node is an allowed type for dr. */
155225cf1a30Sjl 			return (index);
155325cf1a30Sjl 	}
155425cf1a30Sjl 
155525cf1a30Sjl 	/*
155625cf1a30Sjl 	 * If the name of the node does not map to any of the
155725cf1a30Sjl 	 * types in the array drmach_name2type then the node is not of
155825cf1a30Sjl 	 * interest to dr.
155925cf1a30Sjl 	 */
156025cf1a30Sjl 	return (-1);
156125cf1a30Sjl }
156225cf1a30Sjl 
156325cf1a30Sjl /*
156425cf1a30Sjl  * there is some complication on OPL:
156525cf1a30Sjl  * - pseudo-mc nodes do not have portid property
156625cf1a30Sjl  * - portid[9:5] of cmp node is LSB #, portid[7:3] of pci is LSB#
156725cf1a30Sjl  * - cmp has board#
156825cf1a30Sjl  * - core and cpu nodes do not have portid and board# properties
156925cf1a30Sjl  * starcat uses portid to derive the board# but that does not work
157025cf1a30Sjl  * for us.  starfire reads board# property to filter the devices.
157125cf1a30Sjl  * That does not work either.  So for these specific device,
157225cf1a30Sjl  * we use specific hard coded methods to get the board# -
157325cf1a30Sjl  * cpu: LSB# = CPUID[9:5]
157425cf1a30Sjl  */
157525cf1a30Sjl 
157625cf1a30Sjl static int
157725cf1a30Sjl drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
157825cf1a30Sjl {
157925cf1a30Sjl 	drmach_node_t			*node = args->node;
158025cf1a30Sjl 	drmach_board_cb_data_t		*data = args->data;
158125cf1a30Sjl 	drmach_board_t			*obj = data->obj;
158225cf1a30Sjl 
158325cf1a30Sjl 	int		rv, portid;
158425cf1a30Sjl 	int		bnum;
158525cf1a30Sjl 	drmachid_t	id;
158625cf1a30Sjl 	drmach_device_t	*device;
158725cf1a30Sjl 	char name[OBP_MAXDRVNAME];
158825cf1a30Sjl 
158925cf1a30Sjl 	portid = drmach_get_portid(node);
159025cf1a30Sjl 	/*
159125cf1a30Sjl 	 * core, cpu and pseudo-mc do not have portid
159225cf1a30Sjl 	 * we use cpuid as the portid of the cpu node
159325cf1a30Sjl 	 * for pseudo-mc, we do not use portid info.
159425cf1a30Sjl 	 */
159525cf1a30Sjl 
159625cf1a30Sjl 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
159725cf1a30Sjl 	if (rv)
159825cf1a30Sjl 		return (0);
159925cf1a30Sjl 
160025cf1a30Sjl 
160125cf1a30Sjl 	rv = node->n_getprop(node, OBP_BOARDNUM, &bnum, sizeof (bnum));
160225cf1a30Sjl 
160325cf1a30Sjl 	if (rv) {
160425cf1a30Sjl 		/*
160525cf1a30Sjl 		 * cpu does not have board# property.  We use
160625cf1a30Sjl 		 * CPUID[9:5]
160725cf1a30Sjl 		 */
160825cf1a30Sjl 		if (strcmp("cpu", name) == 0) {
160925cf1a30Sjl 			bnum = (portid >> 5) & 0x1f;
161025cf1a30Sjl 		} else
161125cf1a30Sjl 			return (0);
161225cf1a30Sjl 	}
161325cf1a30Sjl 
161425cf1a30Sjl 
161525cf1a30Sjl 	if (bnum != obj->bnum)
161625cf1a30Sjl 		return (0);
161725cf1a30Sjl 
161825cf1a30Sjl 	if (drmach_name2type_idx(name) < 0) {
161925cf1a30Sjl 		return (0);
162025cf1a30Sjl 	}
162125cf1a30Sjl 
162225cf1a30Sjl 	/*
162325cf1a30Sjl 	 * Create a device data structure from this node data.
162425cf1a30Sjl 	 * The call may yield nothing if the node is not of interest
162525cf1a30Sjl 	 * to drmach.
162625cf1a30Sjl 	 */
162725cf1a30Sjl 	data->err = drmach_device_new(node, obj, portid, &id);
162825cf1a30Sjl 	if (data->err)
162925cf1a30Sjl 		return (-1);
163025cf1a30Sjl 	else if (!id) {
163125cf1a30Sjl 		/*
163225cf1a30Sjl 		 * drmach_device_new examined the node we passed in
163325cf1a30Sjl 		 * and determined that it was one not of interest to
163425cf1a30Sjl 		 * drmach.  So, it is skipped.
163525cf1a30Sjl 		 */
163625cf1a30Sjl 		return (0);
163725cf1a30Sjl 	}
163825cf1a30Sjl 
163925cf1a30Sjl 	rv = drmach_array_set(obj->devices, data->ndevs++, id);
164025cf1a30Sjl 	if (rv) {
164125cf1a30Sjl 		data->err = DRMACH_INTERNAL_ERROR();
164225cf1a30Sjl 		return (-1);
164325cf1a30Sjl 	}
164425cf1a30Sjl 	device = id;
164525cf1a30Sjl 
164625cf1a30Sjl 	data->err = (*data->found)(data->a, device->type, device->unum, id);
164725cf1a30Sjl 	return (data->err == NULL ? 0 : -1);
164825cf1a30Sjl }
164925cf1a30Sjl 
165025cf1a30Sjl sbd_error_t *
165125cf1a30Sjl drmach_board_find_devices(drmachid_t id, void *a,
165225cf1a30Sjl 	sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
165325cf1a30Sjl {
165425cf1a30Sjl 	drmach_board_t		*bp = (drmach_board_t *)id;
165525cf1a30Sjl 	sbd_error_t		*err;
165625cf1a30Sjl 	int			 max_devices;
165725cf1a30Sjl 	int			 rv;
165825cf1a30Sjl 	drmach_board_cb_data_t	data;
165925cf1a30Sjl 
166025cf1a30Sjl 
166125cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
166225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
166325cf1a30Sjl 
166425cf1a30Sjl 	max_devices  = MAX_CPU_UNITS_PER_BOARD;
166525cf1a30Sjl 	max_devices += MAX_MEM_UNITS_PER_BOARD;
166625cf1a30Sjl 	max_devices += MAX_IO_UNITS_PER_BOARD;
166725cf1a30Sjl 
166825cf1a30Sjl 	bp->devices = drmach_array_new(0, max_devices);
166925cf1a30Sjl 
167025cf1a30Sjl 	if (bp->tree == NULL)
167125cf1a30Sjl 		bp->tree = drmach_node_new();
167225cf1a30Sjl 
167325cf1a30Sjl 	data.obj = bp;
167425cf1a30Sjl 	data.ndevs = 0;
167525cf1a30Sjl 	data.found = found;
167625cf1a30Sjl 	data.a = a;
167725cf1a30Sjl 	data.err = NULL;
167825cf1a30Sjl 
167925cf1a30Sjl 	rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
168025cf1a30Sjl 	if (rv == 0)
168125cf1a30Sjl 		err = NULL;
168225cf1a30Sjl 	else {
168325cf1a30Sjl 		drmach_array_dispose(bp->devices, drmach_device_dispose);
168425cf1a30Sjl 		bp->devices = NULL;
168525cf1a30Sjl 
168625cf1a30Sjl 		if (data.err)
168725cf1a30Sjl 			err = data.err;
168825cf1a30Sjl 		else
168925cf1a30Sjl 			err = DRMACH_INTERNAL_ERROR();
169025cf1a30Sjl 	}
169125cf1a30Sjl 
169225cf1a30Sjl 	return (err);
169325cf1a30Sjl }
169425cf1a30Sjl 
169525cf1a30Sjl int
169625cf1a30Sjl drmach_board_lookup(int bnum, drmachid_t *id)
169725cf1a30Sjl {
169825cf1a30Sjl 	int	rv = 0;
169925cf1a30Sjl 
170025cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_READER);
170125cf1a30Sjl 	if (drmach_array_get(drmach_boards, bnum, id)) {
170225cf1a30Sjl 		*id = 0;
170325cf1a30Sjl 		rv = -1;
170425cf1a30Sjl 	}
170525cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
170625cf1a30Sjl 	return (rv);
170725cf1a30Sjl }
170825cf1a30Sjl 
170925cf1a30Sjl sbd_error_t *
171025cf1a30Sjl drmach_board_name(int bnum, char *buf, int buflen)
171125cf1a30Sjl {
171225cf1a30Sjl 	snprintf(buf, buflen, "SB%d", bnum);
171325cf1a30Sjl 	return (NULL);
171425cf1a30Sjl }
171525cf1a30Sjl 
171625cf1a30Sjl sbd_error_t *
171725cf1a30Sjl drmach_board_poweroff(drmachid_t id)
171825cf1a30Sjl {
171925cf1a30Sjl 	drmach_board_t	*bp;
172025cf1a30Sjl 	sbd_error_t	*err;
172125cf1a30Sjl 	drmach_status_t	 stat;
172225cf1a30Sjl 
1723ddf95635Sbm 	if (DRMACH_NULL_ID(id))
1724ddf95635Sbm 		return (NULL);
1725ddf95635Sbm 
172625cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
172725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
172825cf1a30Sjl 	bp = id;
172925cf1a30Sjl 
173025cf1a30Sjl 	err = drmach_board_status(id, &stat);
173125cf1a30Sjl 
173225cf1a30Sjl 	if (!err) {
173325cf1a30Sjl 		if (stat.configured || stat.busy)
173425cf1a30Sjl 			err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
173525cf1a30Sjl 		else {
173625cf1a30Sjl 			bp->powered = 0;
173725cf1a30Sjl 		}
173825cf1a30Sjl 	}
173925cf1a30Sjl 	return (err);
174025cf1a30Sjl }
174125cf1a30Sjl 
174225cf1a30Sjl sbd_error_t *
174325cf1a30Sjl drmach_board_poweron(drmachid_t id)
174425cf1a30Sjl {
174525cf1a30Sjl 	drmach_board_t	*bp;
174625cf1a30Sjl 
174725cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
174825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
174925cf1a30Sjl 	bp = id;
175025cf1a30Sjl 
175125cf1a30Sjl 	bp->powered = 1;
175225cf1a30Sjl 
175325cf1a30Sjl 	return (NULL);
175425cf1a30Sjl }
175525cf1a30Sjl 
175625cf1a30Sjl static sbd_error_t *
175725cf1a30Sjl drmach_board_release(drmachid_t id)
175825cf1a30Sjl {
175925cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
176025cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
176125cf1a30Sjl 	return (NULL);
176225cf1a30Sjl }
176325cf1a30Sjl 
176425cf1a30Sjl /*ARGSUSED*/
176525cf1a30Sjl sbd_error_t *
176625cf1a30Sjl drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
176725cf1a30Sjl {
176825cf1a30Sjl 	return (NULL);
176925cf1a30Sjl }
177025cf1a30Sjl 
177125cf1a30Sjl sbd_error_t *
177225cf1a30Sjl drmach_board_unassign(drmachid_t id)
177325cf1a30Sjl {
177425cf1a30Sjl 	drmach_board_t	*bp;
177525cf1a30Sjl 	sbd_error_t	*err;
177625cf1a30Sjl 	drmach_status_t	 stat;
177725cf1a30Sjl 
1778ddf95635Sbm 	if (DRMACH_NULL_ID(id))
1779ddf95635Sbm 		return (NULL);
178025cf1a30Sjl 
178125cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id)) {
178225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
178325cf1a30Sjl 	}
178425cf1a30Sjl 	bp = id;
178525cf1a30Sjl 
178625cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
178725cf1a30Sjl 
178825cf1a30Sjl 	err = drmach_board_status(id, &stat);
178925cf1a30Sjl 	if (err) {
179025cf1a30Sjl 		rw_exit(&drmach_boards_rwlock);
179125cf1a30Sjl 		return (err);
179225cf1a30Sjl 	}
179325cf1a30Sjl 	if (stat.configured || stat.busy) {
179425cf1a30Sjl 		err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
179525cf1a30Sjl 	} else {
179625cf1a30Sjl 		if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
179725cf1a30Sjl 			err = DRMACH_INTERNAL_ERROR();
179825cf1a30Sjl 		else
179925cf1a30Sjl 			drmach_board_dispose(bp);
180025cf1a30Sjl 	}
180125cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
180225cf1a30Sjl 	return (err);
180325cf1a30Sjl }
180425cf1a30Sjl 
180525cf1a30Sjl /*
180625cf1a30Sjl  * We have to do more on OPL - e.g. set up sram tte, read cpuid, strand id,
180725cf1a30Sjl  * implementation #, etc
180825cf1a30Sjl  */
180925cf1a30Sjl 
181025cf1a30Sjl static sbd_error_t *
181125cf1a30Sjl drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
181225cf1a30Sjl {
181325cf1a30Sjl 	static void drmach_cpu_dispose(drmachid_t);
181425cf1a30Sjl 	static sbd_error_t *drmach_cpu_release(drmachid_t);
181525cf1a30Sjl 	static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
181625cf1a30Sjl 
181725cf1a30Sjl 	int		 portid;
181825cf1a30Sjl 	drmach_cpu_t	*cp = NULL;
181925cf1a30Sjl 
182025cf1a30Sjl 	/* portid is CPUID of the node */
182125cf1a30Sjl 	portid = proto->portid;
182225cf1a30Sjl 	ASSERT(portid != -1);
182325cf1a30Sjl 
182425cf1a30Sjl 	/* unum = (CMP/CHIP ID) + (ON_BOARD_CORE_NUM * MAX_CMPID_PER_BOARD) */
182525cf1a30Sjl 	proto->unum = ((portid/OPL_MAX_CPUID_PER_CMP) &
1826*e98fafb9Sjl 	    (OPL_MAX_CMPID_PER_BOARD - 1)) +
1827*e98fafb9Sjl 	    ((portid & (OPL_MAX_CPUID_PER_CMP - 1)) *
1828*e98fafb9Sjl 	    (OPL_MAX_CMPID_PER_BOARD));
182925cf1a30Sjl 
183025cf1a30Sjl 	cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
183125cf1a30Sjl 	bcopy(proto, &cp->dev, sizeof (cp->dev));
183225cf1a30Sjl 	cp->dev.node = drmach_node_dup(proto->node);
183325cf1a30Sjl 	cp->dev.cm.isa = (void *)drmach_cpu_new;
183425cf1a30Sjl 	cp->dev.cm.dispose = drmach_cpu_dispose;
183525cf1a30Sjl 	cp->dev.cm.release = drmach_cpu_release;
183625cf1a30Sjl 	cp->dev.cm.status = drmach_cpu_status;
183725cf1a30Sjl 
183825cf1a30Sjl 	snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
1839*e98fafb9Sjl 	    cp->dev.type, cp->dev.unum);
184025cf1a30Sjl 
184125cf1a30Sjl /*
184225cf1a30Sjl  *	CPU ID representation
184325cf1a30Sjl  *	CPUID[9:5] = SB#
184425cf1a30Sjl  *	CPUID[4:3] = Chip#
184525cf1a30Sjl  *	CPUID[2:1] = Core# (Only 2 core for OPL)
184625cf1a30Sjl  *	CPUID[0:0] = Strand#
184725cf1a30Sjl  */
184825cf1a30Sjl 
184925cf1a30Sjl /*
185025cf1a30Sjl  *	reg property of the strand contains strand ID
185125cf1a30Sjl  *	reg property of the parent node contains core ID
185225cf1a30Sjl  *	We should use them.
185325cf1a30Sjl  */
185425cf1a30Sjl 	cp->cpuid = portid;
185525cf1a30Sjl 	cp->sb = (portid >> 5) & 0x1f;
185625cf1a30Sjl 	cp->chipid = (portid >> 3) & 0x3;
185725cf1a30Sjl 	cp->coreid = (portid >> 1) & 0x3;
185825cf1a30Sjl 	cp->strandid = portid & 0x1;
185925cf1a30Sjl 
186025cf1a30Sjl 	*idp = (drmachid_t)cp;
186125cf1a30Sjl 	return (NULL);
186225cf1a30Sjl }
186325cf1a30Sjl 
186425cf1a30Sjl 
186525cf1a30Sjl static void
186625cf1a30Sjl drmach_cpu_dispose(drmachid_t id)
186725cf1a30Sjl {
186825cf1a30Sjl 	drmach_cpu_t	*self;
186925cf1a30Sjl 
187025cf1a30Sjl 	ASSERT(DRMACH_IS_CPU_ID(id));
187125cf1a30Sjl 
187225cf1a30Sjl 	self = id;
187325cf1a30Sjl 	if (self->dev.node)
187425cf1a30Sjl 		drmach_node_dispose(self->dev.node);
187525cf1a30Sjl 
187625cf1a30Sjl 	kmem_free(self, sizeof (*self));
187725cf1a30Sjl }
187825cf1a30Sjl 
187925cf1a30Sjl static int
188025cf1a30Sjl drmach_cpu_start(struct cpu *cp)
188125cf1a30Sjl {
188225cf1a30Sjl 	int		cpuid = cp->cpu_id;
188325cf1a30Sjl 	extern int	restart_other_cpu(int);
188425cf1a30Sjl 
188525cf1a30Sjl 	ASSERT(MUTEX_HELD(&cpu_lock));
188625cf1a30Sjl 	ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
188725cf1a30Sjl 
188825cf1a30Sjl 	cp->cpu_flags &= ~CPU_POWEROFF;
188925cf1a30Sjl 
189025cf1a30Sjl 	/*
189125cf1a30Sjl 	 * NOTE: restart_other_cpu pauses cpus during the
189225cf1a30Sjl 	 *	 slave cpu start.  This helps to quiesce the
189325cf1a30Sjl 	 *	 bus traffic a bit which makes the tick sync
189425cf1a30Sjl 	 *	 routine in the prom more robust.
189525cf1a30Sjl 	 */
189625cf1a30Sjl 	DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
189725cf1a30Sjl 
189825cf1a30Sjl 	restart_other_cpu(cpuid);
189925cf1a30Sjl 
190025cf1a30Sjl 	return (0);
190125cf1a30Sjl }
190225cf1a30Sjl 
190325cf1a30Sjl static sbd_error_t *
190425cf1a30Sjl drmach_cpu_release(drmachid_t id)
190525cf1a30Sjl {
190625cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
190725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
190825cf1a30Sjl 
190925cf1a30Sjl 	return (NULL);
191025cf1a30Sjl }
191125cf1a30Sjl 
191225cf1a30Sjl static sbd_error_t *
191325cf1a30Sjl drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
191425cf1a30Sjl {
191525cf1a30Sjl 	drmach_cpu_t *cp;
191625cf1a30Sjl 	drmach_device_t *dp;
191725cf1a30Sjl 
191825cf1a30Sjl 	ASSERT(DRMACH_IS_CPU_ID(id));
191925cf1a30Sjl 	cp = (drmach_cpu_t *)id;
192025cf1a30Sjl 	dp = &cp->dev;
192125cf1a30Sjl 
192225cf1a30Sjl 	stat->assigned = dp->bp->assigned;
192325cf1a30Sjl 	stat->powered = dp->bp->powered;
192425cf1a30Sjl 	mutex_enter(&cpu_lock);
192525cf1a30Sjl 	stat->configured = (cpu_get(cp->cpuid) != NULL);
192625cf1a30Sjl 	mutex_exit(&cpu_lock);
192725cf1a30Sjl 	stat->busy = dp->busy;
192825cf1a30Sjl 	strncpy(stat->type, dp->type, sizeof (stat->type));
192925cf1a30Sjl 	stat->info[0] = '\0';
193025cf1a30Sjl 
193125cf1a30Sjl 	return (NULL);
193225cf1a30Sjl }
193325cf1a30Sjl 
193425cf1a30Sjl sbd_error_t *
193525cf1a30Sjl drmach_cpu_disconnect(drmachid_t id)
193625cf1a30Sjl {
193725cf1a30Sjl 
193825cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
193925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
194025cf1a30Sjl 
194125cf1a30Sjl 	return (NULL);
194225cf1a30Sjl }
194325cf1a30Sjl 
194425cf1a30Sjl sbd_error_t *
194525cf1a30Sjl drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
194625cf1a30Sjl {
194725cf1a30Sjl 	drmach_cpu_t *cpu;
194825cf1a30Sjl 
194925cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
195025cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
195125cf1a30Sjl 	cpu = (drmach_cpu_t *)id;
195225cf1a30Sjl 
195325cf1a30Sjl 	/* get from cpu directly on OPL */
195425cf1a30Sjl 	*cpuid = cpu->cpuid;
195525cf1a30Sjl 	return (NULL);
195625cf1a30Sjl }
195725cf1a30Sjl 
195825cf1a30Sjl sbd_error_t *
195925cf1a30Sjl drmach_cpu_get_impl(drmachid_t id, int *ip)
196025cf1a30Sjl {
196125cf1a30Sjl 	drmach_device_t *cpu;
196225cf1a30Sjl 	drmach_node_t	*np;
196325cf1a30Sjl 	drmach_node_t	pp;
196425cf1a30Sjl 	int		impl;
196525cf1a30Sjl 	char		type[OBP_MAXPROPNAME];
196625cf1a30Sjl 
196725cf1a30Sjl 	if (!DRMACH_IS_CPU_ID(id))
196825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
196925cf1a30Sjl 
197025cf1a30Sjl 	cpu = id;
197125cf1a30Sjl 	np = cpu->node;
197225cf1a30Sjl 
197325cf1a30Sjl 	if (np->get_parent(np, &pp) != 0) {
197425cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
197525cf1a30Sjl 	}
197625cf1a30Sjl 
197725cf1a30Sjl 	/* the parent should be core */
197825cf1a30Sjl 
197925cf1a30Sjl 	if (pp.n_getprop(&pp, "device_type", &type, sizeof (type)) != 0) {
198025cf1a30Sjl 		return (drerr_new(0, EOPL_GETPROP, NULL));
198125cf1a30Sjl 	}
198225cf1a30Sjl 
198325cf1a30Sjl 	if (strcmp(type, OPL_CORE_NODE) == 0) {
1984*e98fafb9Sjl 		if (pp.n_getprop(&pp, "implementation#", &impl,
1985*e98fafb9Sjl 		    sizeof (impl)) != 0) {
198625cf1a30Sjl 			return (drerr_new(0, EOPL_GETPROP, NULL));
198725cf1a30Sjl 		}
198825cf1a30Sjl 	} else {
198925cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
199025cf1a30Sjl 	}
199125cf1a30Sjl 
199225cf1a30Sjl 	*ip = impl;
199325cf1a30Sjl 
199425cf1a30Sjl 	return (NULL);
199525cf1a30Sjl }
199625cf1a30Sjl 
199725cf1a30Sjl sbd_error_t *
199825cf1a30Sjl drmach_get_dip(drmachid_t id, dev_info_t **dip)
199925cf1a30Sjl {
200025cf1a30Sjl 	drmach_device_t	*dp;
200125cf1a30Sjl 
200225cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
200325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
200425cf1a30Sjl 	dp = id;
200525cf1a30Sjl 
200625cf1a30Sjl 	*dip = dp->node->n_getdip(dp->node);
200725cf1a30Sjl 	return (NULL);
200825cf1a30Sjl }
200925cf1a30Sjl 
201025cf1a30Sjl sbd_error_t *
201125cf1a30Sjl drmach_io_is_attached(drmachid_t id, int *yes)
201225cf1a30Sjl {
201325cf1a30Sjl 	drmach_device_t *dp;
201425cf1a30Sjl 	dev_info_t	*dip;
201525cf1a30Sjl 	int		state;
201625cf1a30Sjl 
201725cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
201825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
201925cf1a30Sjl 	dp = id;
202025cf1a30Sjl 
202125cf1a30Sjl 	dip = dp->node->n_getdip(dp->node);
202225cf1a30Sjl 	if (dip == NULL) {
202325cf1a30Sjl 		*yes = 0;
202425cf1a30Sjl 		return (NULL);
202525cf1a30Sjl 	}
202625cf1a30Sjl 
202725cf1a30Sjl 	state = ddi_get_devstate(dip);
202825cf1a30Sjl 	*yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) ||
202925cf1a30Sjl 	    (state == DDI_DEVSTATE_UP));
203025cf1a30Sjl 
203125cf1a30Sjl 	return (NULL);
203225cf1a30Sjl }
203325cf1a30Sjl 
203425cf1a30Sjl struct drmach_io_cb {
203525cf1a30Sjl 	char	*name;	/* name of the node */
203625cf1a30Sjl 	int	(*func)(dev_info_t *);
203725cf1a30Sjl 	int	rv;
203868ac2337Sjl 	dev_info_t *dip;
203925cf1a30Sjl };
204025cf1a30Sjl 
204125cf1a30Sjl #define	DRMACH_IO_POST_ATTACH	0
204225cf1a30Sjl #define	DRMACH_IO_PRE_RELEASE	1
204325cf1a30Sjl 
204425cf1a30Sjl static int
204525cf1a30Sjl drmach_io_cb_check(dev_info_t *dip, void *arg)
204625cf1a30Sjl {
204725cf1a30Sjl 	struct drmach_io_cb *p = (struct drmach_io_cb *)arg;
204825cf1a30Sjl 	char name[OBP_MAXDRVNAME];
204925cf1a30Sjl 	int len = OBP_MAXDRVNAME;
205025cf1a30Sjl 
2051*e98fafb9Sjl 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "name",
205225cf1a30Sjl 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
205325cf1a30Sjl 		return (DDI_WALK_PRUNECHILD);
205425cf1a30Sjl 	}
205525cf1a30Sjl 
205625cf1a30Sjl 	if (strcmp(name, p->name) == 0) {
205768ac2337Sjl 		ndi_hold_devi(dip);
205868ac2337Sjl 		p->dip = dip;
205925cf1a30Sjl 		return (DDI_WALK_TERMINATE);
206025cf1a30Sjl 	}
206125cf1a30Sjl 
206225cf1a30Sjl 	return (DDI_WALK_CONTINUE);
206325cf1a30Sjl }
206425cf1a30Sjl 
206525cf1a30Sjl 
206625cf1a30Sjl static int
206725cf1a30Sjl drmach_console_ops(drmachid_t *id, int state)
206825cf1a30Sjl {
206925cf1a30Sjl 	drmach_io_t *obj = (drmach_io_t *)id;
207025cf1a30Sjl 	struct drmach_io_cb arg;
207125cf1a30Sjl 	int (*msudetp)(dev_info_t *);
207225cf1a30Sjl 	int (*msuattp)(dev_info_t *);
207325cf1a30Sjl 	dev_info_t *dip, *pdip;
207425cf1a30Sjl 	int circ;
207525cf1a30Sjl 
207625cf1a30Sjl 	/* 4 is pcicmu channel */
207725cf1a30Sjl 	if (obj->channel != 4)
207825cf1a30Sjl 		return (0);
207925cf1a30Sjl 
208025cf1a30Sjl 	arg.name = "serial";
208125cf1a30Sjl 	arg.func = NULL;
208225cf1a30Sjl 	if (state == DRMACH_IO_PRE_RELEASE) {
208325cf1a30Sjl 		msudetp = (int (*)(dev_info_t *))
208425cf1a30Sjl 		    modgetsymvalue("oplmsu_dr_detach", 0);
208525cf1a30Sjl 		if (msudetp != NULL)
208625cf1a30Sjl 			arg.func = msudetp;
208725cf1a30Sjl 	} else if (state == DRMACH_IO_POST_ATTACH) {
208825cf1a30Sjl 		msuattp = (int (*)(dev_info_t *))
208925cf1a30Sjl 		    modgetsymvalue("oplmsu_dr_attach", 0);
209025cf1a30Sjl 		if (msuattp != NULL)
209125cf1a30Sjl 			arg.func = msuattp;
209268ac2337Sjl 	} else {
209325cf1a30Sjl 		return (0);
209468ac2337Sjl 	}
209525cf1a30Sjl 
209625cf1a30Sjl 	if (arg.func == NULL) {
209725cf1a30Sjl 		return (0);
209825cf1a30Sjl 	}
209925cf1a30Sjl 
210025cf1a30Sjl 	arg.rv = 0;
210168ac2337Sjl 	arg.dip = NULL;
210225cf1a30Sjl 
210325cf1a30Sjl 	dip = obj->dev.node->n_getdip(obj->dev.node);
210425cf1a30Sjl 	if (pdip = ddi_get_parent(dip)) {
210525cf1a30Sjl 		ndi_hold_devi(pdip);
210625cf1a30Sjl 		ndi_devi_enter(pdip, &circ);
210725cf1a30Sjl 	} else {
210825cf1a30Sjl 		/* this cannot happen unless something bad happens */
210925cf1a30Sjl 		return (-1);
211025cf1a30Sjl 	}
211125cf1a30Sjl 
211225cf1a30Sjl 	ddi_walk_devs(dip, drmach_io_cb_check, (void *)&arg);
211325cf1a30Sjl 
211468ac2337Sjl 	ndi_devi_exit(pdip, circ);
211568ac2337Sjl 	ndi_rele_devi(pdip);
211668ac2337Sjl 
211768ac2337Sjl 	if (arg.dip) {
211868ac2337Sjl 		arg.rv = (*arg.func)(arg.dip);
211968ac2337Sjl 		ndi_rele_devi(arg.dip);
212068ac2337Sjl 	} else {
212168ac2337Sjl 		arg.rv = -1;
212225cf1a30Sjl 	}
212325cf1a30Sjl 
212425cf1a30Sjl 	return (arg.rv);
212525cf1a30Sjl }
212625cf1a30Sjl 
212725cf1a30Sjl sbd_error_t *
212825cf1a30Sjl drmach_io_pre_release(drmachid_t id)
212925cf1a30Sjl {
213025cf1a30Sjl 	int rv;
213125cf1a30Sjl 
213225cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
213325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
213425cf1a30Sjl 
213525cf1a30Sjl 	rv = drmach_console_ops(id, DRMACH_IO_PRE_RELEASE);
213625cf1a30Sjl 
213725cf1a30Sjl 	if (rv != 0)
213825cf1a30Sjl 		cmn_err(CE_WARN, "IO callback failed in pre-release\n");
213925cf1a30Sjl 
214025cf1a30Sjl 	return (NULL);
214125cf1a30Sjl }
214225cf1a30Sjl 
214325cf1a30Sjl static sbd_error_t *
214425cf1a30Sjl drmach_io_release(drmachid_t id)
214525cf1a30Sjl {
214625cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
214725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
214825cf1a30Sjl 	return (NULL);
214925cf1a30Sjl }
215025cf1a30Sjl 
215125cf1a30Sjl sbd_error_t *
215225cf1a30Sjl drmach_io_unrelease(drmachid_t id)
215325cf1a30Sjl {
215425cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
215525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
215625cf1a30Sjl 	return (NULL);
215725cf1a30Sjl }
215825cf1a30Sjl 
215925cf1a30Sjl /*ARGSUSED*/
216025cf1a30Sjl sbd_error_t *
216125cf1a30Sjl drmach_io_post_release(drmachid_t id)
216225cf1a30Sjl {
216325cf1a30Sjl 	return (NULL);
216425cf1a30Sjl }
216525cf1a30Sjl 
216625cf1a30Sjl /*ARGSUSED*/
216725cf1a30Sjl sbd_error_t *
216825cf1a30Sjl drmach_io_post_attach(drmachid_t id)
216925cf1a30Sjl {
217025cf1a30Sjl 	int rv;
217125cf1a30Sjl 
217225cf1a30Sjl 	if (!DRMACH_IS_IO_ID(id))
217325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
217425cf1a30Sjl 
217525cf1a30Sjl 	rv = drmach_console_ops(id, DRMACH_IO_POST_ATTACH);
217625cf1a30Sjl 
217725cf1a30Sjl 	if (rv != 0)
217825cf1a30Sjl 		cmn_err(CE_WARN, "IO callback failed in post-attach\n");
217925cf1a30Sjl 
218025cf1a30Sjl 	return (0);
218125cf1a30Sjl }
218225cf1a30Sjl 
218325cf1a30Sjl static sbd_error_t *
218425cf1a30Sjl drmach_io_status(drmachid_t id, drmach_status_t *stat)
218525cf1a30Sjl {
218625cf1a30Sjl 	drmach_device_t *dp;
218725cf1a30Sjl 	sbd_error_t	*err;
218825cf1a30Sjl 	int		 configured;
218925cf1a30Sjl 
219025cf1a30Sjl 	ASSERT(DRMACH_IS_IO_ID(id));
219125cf1a30Sjl 	dp = id;
219225cf1a30Sjl 
219325cf1a30Sjl 	err = drmach_io_is_attached(id, &configured);
219425cf1a30Sjl 	if (err)
219525cf1a30Sjl 		return (err);
219625cf1a30Sjl 
219725cf1a30Sjl 	stat->assigned = dp->bp->assigned;
219825cf1a30Sjl 	stat->powered = dp->bp->powered;
219925cf1a30Sjl 	stat->configured = (configured != 0);
220025cf1a30Sjl 	stat->busy = dp->busy;
220125cf1a30Sjl 	strncpy(stat->type, dp->type, sizeof (stat->type));
220225cf1a30Sjl 	stat->info[0] = '\0';
220325cf1a30Sjl 
220425cf1a30Sjl 	return (NULL);
220525cf1a30Sjl }
220625cf1a30Sjl 
220725cf1a30Sjl static sbd_error_t *
220825cf1a30Sjl drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
220925cf1a30Sjl {
221025cf1a30Sjl 	static void drmach_mem_dispose(drmachid_t);
221125cf1a30Sjl 	static sbd_error_t *drmach_mem_release(drmachid_t);
221225cf1a30Sjl 	static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
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) ||
2221*e98fafb9Sjl 	    (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 
2236*e98fafb9Sjl 	snprintf(mp->dev.cm.name, sizeof (mp->dev.cm.name), "%s", mp->dev.type);
223725cf1a30Sjl 
223825cf1a30Sjl 	dip = mp->dev.node->n_getdip(mp->dev.node);
223925cf1a30Sjl 	if (drmach_setup_mc_info(dip, mp) != 0) {
22408eafe49bSbm 		return (drerr_new(1, EOPL_MC_SETUP, NULL));
224125cf1a30Sjl 	}
224225cf1a30Sjl 
224368ac2337Sjl 	/* make sure we do not create memoryless nodes */
224468ac2337Sjl 	if (mp->nbytes == 0) {
224568ac2337Sjl 		*idp = (drmachid_t)NULL;
224668ac2337Sjl 		kmem_free(mp, sizeof (drmach_mem_t));
224768ac2337Sjl 	} else
224868ac2337Sjl 		*idp = (drmachid_t)mp;
224968ac2337Sjl 
225025cf1a30Sjl 	return (NULL);
225125cf1a30Sjl }
225225cf1a30Sjl 
225325cf1a30Sjl static void
225425cf1a30Sjl drmach_mem_dispose(drmachid_t id)
225525cf1a30Sjl {
225625cf1a30Sjl 	drmach_mem_t *mp;
225725cf1a30Sjl 
225825cf1a30Sjl 	ASSERT(DRMACH_IS_MEM_ID(id));
225925cf1a30Sjl 
226025cf1a30Sjl 
226125cf1a30Sjl 	mp = id;
226225cf1a30Sjl 
226325cf1a30Sjl 	if (mp->dev.node)
226425cf1a30Sjl 		drmach_node_dispose(mp->dev.node);
226525cf1a30Sjl 
226625cf1a30Sjl 	if (mp->memlist) {
226725cf1a30Sjl 		memlist_delete(mp->memlist);
226825cf1a30Sjl 		mp->memlist = NULL;
226925cf1a30Sjl 	}
227068ac2337Sjl 
227168ac2337Sjl 	kmem_free(mp, sizeof (*mp));
227225cf1a30Sjl }
227325cf1a30Sjl 
227425cf1a30Sjl sbd_error_t *
227525cf1a30Sjl drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
227625cf1a30Sjl {
227725cf1a30Sjl 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
227825cf1a30Sjl 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
227925cf1a30Sjl 	int		rv;
228025cf1a30Sjl 
228125cf1a30Sjl 	ASSERT(size != 0);
228225cf1a30Sjl 
228325cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
228425cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
228525cf1a30Sjl 
228685f58038Sdp 	rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
228725cf1a30Sjl 	if (rv == ENOMEM) {
228825cf1a30Sjl 		cmn_err(CE_WARN, "%ld megabytes not available to kernel cage",
2289*e98fafb9Sjl 		    (size == 0 ? 0 : size / MBYTE));
229025cf1a30Sjl 	} else if (rv != 0) {
229125cf1a30Sjl 		/* catch this in debug kernels */
229225cf1a30Sjl 		ASSERT(0);
229325cf1a30Sjl 
2294*e98fafb9Sjl 		cmn_err(CE_WARN, "unexpected kcage_range_add return value %d",
2295*e98fafb9Sjl 		    rv);
229625cf1a30Sjl 	}
229725cf1a30Sjl 
229825cf1a30Sjl 	if (rv) {
229925cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
230025cf1a30Sjl 	}
230125cf1a30Sjl 	else
230225cf1a30Sjl 		return (NULL);
230325cf1a30Sjl }
230425cf1a30Sjl 
230525cf1a30Sjl sbd_error_t *
230625cf1a30Sjl drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
230725cf1a30Sjl {
230825cf1a30Sjl 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
230925cf1a30Sjl 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
231025cf1a30Sjl 	int		rv;
231125cf1a30Sjl 
231225cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
231325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
231425cf1a30Sjl 
231525cf1a30Sjl 	if (size > 0) {
231625cf1a30Sjl 		rv = kcage_range_delete_post_mem_del(basepfn, npages);
231725cf1a30Sjl 		if (rv != 0) {
231825cf1a30Sjl 			cmn_err(CE_WARN,
231925cf1a30Sjl 			    "unexpected kcage_range_delete_post_mem_del"
232025cf1a30Sjl 			    " return value %d", rv);
232125cf1a30Sjl 			return (DRMACH_INTERNAL_ERROR());
232225cf1a30Sjl 		}
232325cf1a30Sjl 	}
232425cf1a30Sjl 
232525cf1a30Sjl 	return (NULL);
232625cf1a30Sjl }
232725cf1a30Sjl 
232825cf1a30Sjl sbd_error_t *
232925cf1a30Sjl drmach_mem_disable(drmachid_t id)
233025cf1a30Sjl {
233125cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
233225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
233325cf1a30Sjl 	else {
233425cf1a30Sjl 		drmach_flush_all();
233525cf1a30Sjl 		return (NULL);
233625cf1a30Sjl 	}
233725cf1a30Sjl }
233825cf1a30Sjl 
233925cf1a30Sjl sbd_error_t *
234025cf1a30Sjl drmach_mem_enable(drmachid_t id)
234125cf1a30Sjl {
234225cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
234325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
234425cf1a30Sjl 	else
234525cf1a30Sjl 		return (NULL);
234625cf1a30Sjl }
234725cf1a30Sjl 
234825cf1a30Sjl sbd_error_t *
234925cf1a30Sjl drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem)
235025cf1a30Sjl {
235125cf1a30Sjl 	drmach_mem_t *mp;
235225cf1a30Sjl 
235325cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
235425cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
235525cf1a30Sjl 
235625cf1a30Sjl 	mp = (drmach_mem_t *)id;
235725cf1a30Sjl 
235825cf1a30Sjl 	/*
235925cf1a30Sjl 	 * This is only used by dr to round up/down the memory
236025cf1a30Sjl 	 * for copying. Our unit of memory isolation is 64 MB.
236125cf1a30Sjl 	 */
236225cf1a30Sjl 
236325cf1a30Sjl 	mem->mi_alignment_mask = (64 * 1024 * 1024 - 1);
236425cf1a30Sjl 	mem->mi_basepa = mp->base_pa;
236525cf1a30Sjl 	mem->mi_size = mp->nbytes;
236625cf1a30Sjl 	mem->mi_slice_size = mp->slice_size;
236725cf1a30Sjl 
236825cf1a30Sjl 	return (NULL);
236925cf1a30Sjl }
237025cf1a30Sjl 
237125cf1a30Sjl sbd_error_t *
237225cf1a30Sjl drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa)
237325cf1a30Sjl {
237425cf1a30Sjl 	drmach_mem_t *mp;
237525cf1a30Sjl 
237625cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
237725cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
237825cf1a30Sjl 
237925cf1a30Sjl 	mp = (drmach_mem_t *)id;
238025cf1a30Sjl 
238125cf1a30Sjl 	*pa = mp->base_pa;
238225cf1a30Sjl 	return (NULL);
238325cf1a30Sjl }
238425cf1a30Sjl 
238525cf1a30Sjl sbd_error_t *
238625cf1a30Sjl drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
238725cf1a30Sjl {
238825cf1a30Sjl 	drmach_mem_t	*mem;
2389b307f191Sbm #ifdef	DEBUG
239025cf1a30Sjl 	int		rv;
2391b307f191Sbm #endif
239225cf1a30Sjl 	struct memlist	*mlist;
239325cf1a30Sjl 
239425cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
239525cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
239625cf1a30Sjl 
239725cf1a30Sjl 	mem = (drmach_mem_t *)id;
239825cf1a30Sjl 	mlist = memlist_dup(mem->memlist);
239925cf1a30Sjl 
240025cf1a30Sjl #ifdef DEBUG
240125cf1a30Sjl 	/*
240225cf1a30Sjl 	 * Make sure the incoming memlist doesn't already
240325cf1a30Sjl 	 * intersect with what's present in the system (phys_install).
240425cf1a30Sjl 	 */
240525cf1a30Sjl 	memlist_read_lock();
240625cf1a30Sjl 	rv = memlist_intersect(phys_install, mlist);
240725cf1a30Sjl 	memlist_read_unlock();
240825cf1a30Sjl 	if (rv) {
2409*e98fafb9Sjl 		DRMACH_PR("Derived memlist intersects with phys_install\n");
241025cf1a30Sjl 		memlist_dump(mlist);
241125cf1a30Sjl 
241225cf1a30Sjl 		DRMACH_PR("phys_install memlist:\n");
241325cf1a30Sjl 		memlist_dump(phys_install);
241425cf1a30Sjl 
241525cf1a30Sjl 		memlist_delete(mlist);
241625cf1a30Sjl 		return (DRMACH_INTERNAL_ERROR());
241725cf1a30Sjl 	}
241825cf1a30Sjl 
241925cf1a30Sjl 	DRMACH_PR("Derived memlist:");
242025cf1a30Sjl 	memlist_dump(mlist);
242125cf1a30Sjl #endif
242225cf1a30Sjl 	*ml = mlist;
242325cf1a30Sjl 
242425cf1a30Sjl 	return (NULL);
242525cf1a30Sjl }
242625cf1a30Sjl 
242725cf1a30Sjl sbd_error_t *
242825cf1a30Sjl drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
242925cf1a30Sjl {
243025cf1a30Sjl 	drmach_mem_t	*mem;
243125cf1a30Sjl 
243225cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
243325cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
243425cf1a30Sjl 
243525cf1a30Sjl 	mem = (drmach_mem_t *)id;
243625cf1a30Sjl 
243725cf1a30Sjl 	*bytes = mem->slice_size;
243825cf1a30Sjl 
243925cf1a30Sjl 	return (NULL);
244025cf1a30Sjl }
244125cf1a30Sjl 
244225cf1a30Sjl 
244325cf1a30Sjl /* ARGSUSED */
244425cf1a30Sjl processorid_t
244525cf1a30Sjl drmach_mem_cpu_affinity(drmachid_t id)
244625cf1a30Sjl {
244725cf1a30Sjl 	return (CPU_CURRENT);
244825cf1a30Sjl }
244925cf1a30Sjl 
245025cf1a30Sjl static sbd_error_t *
245125cf1a30Sjl drmach_mem_release(drmachid_t id)
245225cf1a30Sjl {
245325cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(id))
245425cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
245525cf1a30Sjl 	return (NULL);
245625cf1a30Sjl }
245725cf1a30Sjl 
245825cf1a30Sjl static sbd_error_t *
245925cf1a30Sjl drmach_mem_status(drmachid_t id, drmach_status_t *stat)
246025cf1a30Sjl {
246125cf1a30Sjl 	drmach_mem_t *dp;
246225cf1a30Sjl 	uint64_t	 pa, slice_size;
246325cf1a30Sjl 	struct memlist	*ml;
246425cf1a30Sjl 
246525cf1a30Sjl 	ASSERT(DRMACH_IS_MEM_ID(id));
246625cf1a30Sjl 	dp = id;
246725cf1a30Sjl 
246825cf1a30Sjl 	/* get starting physical address of target memory */
246925cf1a30Sjl 	pa = dp->base_pa;
247025cf1a30Sjl 
247125cf1a30Sjl 	/* round down to slice boundary */
247225cf1a30Sjl 	slice_size = dp->slice_size;
247325cf1a30Sjl 	pa &= ~(slice_size - 1);
247425cf1a30Sjl 
247525cf1a30Sjl 	/* stop at first span that is in slice */
247625cf1a30Sjl 	memlist_read_lock();
247725cf1a30Sjl 	for (ml = phys_install; ml; ml = ml->next)
247825cf1a30Sjl 		if (ml->address >= pa && ml->address < pa + slice_size)
247925cf1a30Sjl 			break;
248025cf1a30Sjl 	memlist_read_unlock();
248125cf1a30Sjl 
248225cf1a30Sjl 	stat->assigned = dp->dev.bp->assigned;
248325cf1a30Sjl 	stat->powered = dp->dev.bp->powered;
248425cf1a30Sjl 	stat->configured = (ml != NULL);
248525cf1a30Sjl 	stat->busy = dp->dev.busy;
248625cf1a30Sjl 	strncpy(stat->type, dp->dev.type, sizeof (stat->type));
248725cf1a30Sjl 	stat->info[0] = '\0';
248825cf1a30Sjl 
248925cf1a30Sjl 	return (NULL);
249025cf1a30Sjl }
249125cf1a30Sjl 
249225cf1a30Sjl 
249325cf1a30Sjl sbd_error_t *
249425cf1a30Sjl drmach_board_deprobe(drmachid_t id)
249525cf1a30Sjl {
249625cf1a30Sjl 	drmach_board_t	*bp;
249725cf1a30Sjl 
249825cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
249925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
250025cf1a30Sjl 
250125cf1a30Sjl 	bp = id;
250225cf1a30Sjl 
250368ac2337Sjl 	cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum);
250425cf1a30Sjl 
250525cf1a30Sjl 	if (bp->tree) {
250625cf1a30Sjl 		drmach_node_dispose(bp->tree);
250725cf1a30Sjl 		bp->tree = NULL;
250825cf1a30Sjl 	}
250925cf1a30Sjl 	if (bp->devices) {
251025cf1a30Sjl 		drmach_array_dispose(bp->devices, drmach_device_dispose);
251125cf1a30Sjl 		bp->devices = NULL;
251225cf1a30Sjl 	}
251325cf1a30Sjl 
251425cf1a30Sjl 	bp->boot_board = 0;
251525cf1a30Sjl 
251625cf1a30Sjl 	return (NULL);
251725cf1a30Sjl }
251825cf1a30Sjl 
251925cf1a30Sjl /*ARGSUSED*/
252025cf1a30Sjl static sbd_error_t *
252125cf1a30Sjl drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
252225cf1a30Sjl {
252325cf1a30Sjl 	drmach_board_t		*bp = (drmach_board_t *)id;
252425cf1a30Sjl 	sbd_error_t		*err = NULL;
252525cf1a30Sjl 	int	rv;
2526*e98fafb9Sjl 	unsigned cpu_impl;
252725cf1a30Sjl 
252825cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
252925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
253025cf1a30Sjl 
253125cf1a30Sjl 	DRMACH_PR("calling opl_probe_board for bnum=%d\n", bp->bnum);
2532*e98fafb9Sjl 	rv = opl_probe_sb(bp->bnum, &cpu_impl);
253325cf1a30Sjl 	if (rv != 0) {
25348eafe49bSbm 		err = drerr_new(1, EOPL_PROBE, bp->cm.name);
253525cf1a30Sjl 		return (err);
253625cf1a30Sjl 	}
253725cf1a30Sjl 	return (err);
253825cf1a30Sjl }
253925cf1a30Sjl 
254025cf1a30Sjl /*ARGSUSED*/
254125cf1a30Sjl static sbd_error_t *
254225cf1a30Sjl drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
254325cf1a30Sjl {
254425cf1a30Sjl 	drmach_board_t	*bp;
254525cf1a30Sjl 	sbd_error_t	*err = NULL;
254625cf1a30Sjl 	int	rv;
254725cf1a30Sjl 
254825cf1a30Sjl 	if (!DRMACH_IS_BOARD_ID(id))
254925cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
255025cf1a30Sjl 	bp = (drmach_board_t *)id;
255125cf1a30Sjl 
255225cf1a30Sjl 	cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
255325cf1a30Sjl 
255425cf1a30Sjl 	rv = opl_unprobe_sb(bp->bnum);
255525cf1a30Sjl 	if (rv != 0) {
25568eafe49bSbm 		err = drerr_new(1, EOPL_DEPROBE, bp->cm.name);
255725cf1a30Sjl 	}
255825cf1a30Sjl 
255925cf1a30Sjl 	return (err);
256025cf1a30Sjl }
256125cf1a30Sjl 
256225cf1a30Sjl 
256325cf1a30Sjl /*ARGSUSED*/
256425cf1a30Sjl sbd_error_t *
256525cf1a30Sjl drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
256625cf1a30Sjl {
256725cf1a30Sjl 	struct memlist	*ml;
256825cf1a30Sjl 	uint64_t	src_pa;
256925cf1a30Sjl 	uint64_t	dst_pa;
257025cf1a30Sjl 	uint64_t	dst;
257125cf1a30Sjl 
257225cf1a30Sjl 	dst_pa = va_to_pa(&dst);
257325cf1a30Sjl 
257425cf1a30Sjl 	memlist_read_lock();
257525cf1a30Sjl 	for (ml = phys_install; ml; ml = ml->next) {
257625cf1a30Sjl 		uint64_t	nbytes;
257725cf1a30Sjl 
257825cf1a30Sjl 		src_pa = ml->address;
257925cf1a30Sjl 		nbytes = ml->size;
258025cf1a30Sjl 
258125cf1a30Sjl 		while (nbytes != 0ull) {
258225cf1a30Sjl 
258325cf1a30Sjl 			/* copy 32 bytes at arc_pa to dst_pa */
258425cf1a30Sjl 			bcopy32_il(src_pa, dst_pa);
258525cf1a30Sjl 
258625cf1a30Sjl 			/* increment by 32 bytes */
258725cf1a30Sjl 			src_pa += (4 * sizeof (uint64_t));
258825cf1a30Sjl 
258925cf1a30Sjl 			/* decrement by 32 bytes */
259025cf1a30Sjl 			nbytes -= (4 * sizeof (uint64_t));
259125cf1a30Sjl 		}
259225cf1a30Sjl 	}
259325cf1a30Sjl 	memlist_read_unlock();
259425cf1a30Sjl 
259525cf1a30Sjl 	return (NULL);
259625cf1a30Sjl }
259725cf1a30Sjl 
259825cf1a30Sjl static struct {
259925cf1a30Sjl 	const char	*name;
260025cf1a30Sjl 	sbd_error_t	*(*handler)(drmachid_t id, drmach_opts_t *opts);
260125cf1a30Sjl } drmach_pt_arr[] = {
260225cf1a30Sjl 	{ "readmem",		drmach_pt_readmem		},
260325cf1a30Sjl 	{ "ikprobe",	drmach_pt_ikprobe	},
260425cf1a30Sjl 	{ "ikdeprobe",	drmach_pt_ikdeprobe	},
260525cf1a30Sjl 
260625cf1a30Sjl 	/* the following line must always be last */
260725cf1a30Sjl 	{ NULL,			NULL				}
260825cf1a30Sjl };
260925cf1a30Sjl 
261025cf1a30Sjl /*ARGSUSED*/
261125cf1a30Sjl sbd_error_t *
261225cf1a30Sjl drmach_passthru(drmachid_t id, drmach_opts_t *opts)
261325cf1a30Sjl {
261425cf1a30Sjl 	int		i;
261525cf1a30Sjl 	sbd_error_t	*err;
261625cf1a30Sjl 
261725cf1a30Sjl 	i = 0;
261825cf1a30Sjl 	while (drmach_pt_arr[i].name != NULL) {
261925cf1a30Sjl 		int len = strlen(drmach_pt_arr[i].name);
262025cf1a30Sjl 
262125cf1a30Sjl 		if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
262225cf1a30Sjl 			break;
262325cf1a30Sjl 
262425cf1a30Sjl 		i += 1;
262525cf1a30Sjl 	}
262625cf1a30Sjl 
262725cf1a30Sjl 	if (drmach_pt_arr[i].name == NULL)
262825cf1a30Sjl 		err = drerr_new(0, EOPL_UNKPTCMD, opts->copts);
262925cf1a30Sjl 	else
263025cf1a30Sjl 		err = (*drmach_pt_arr[i].handler)(id, opts);
263125cf1a30Sjl 
263225cf1a30Sjl 	return (err);
263325cf1a30Sjl }
263425cf1a30Sjl 
263525cf1a30Sjl sbd_error_t *
263625cf1a30Sjl drmach_release(drmachid_t id)
263725cf1a30Sjl {
263825cf1a30Sjl 	drmach_common_t *cp;
263925cf1a30Sjl 
264025cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
264125cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
264225cf1a30Sjl 	cp = id;
264325cf1a30Sjl 
264425cf1a30Sjl 	return (cp->release(id));
264525cf1a30Sjl }
264625cf1a30Sjl 
264725cf1a30Sjl sbd_error_t *
264825cf1a30Sjl drmach_status(drmachid_t id, drmach_status_t *stat)
264925cf1a30Sjl {
265025cf1a30Sjl 	drmach_common_t *cp;
265125cf1a30Sjl 	sbd_error_t	*err;
265225cf1a30Sjl 
265325cf1a30Sjl 	rw_enter(&drmach_boards_rwlock, RW_READER);
265425cf1a30Sjl 
265525cf1a30Sjl 	if (!DRMACH_IS_ID(id)) {
265625cf1a30Sjl 		rw_exit(&drmach_boards_rwlock);
265725cf1a30Sjl 		return (drerr_new(0, EOPL_NOTID, NULL));
265825cf1a30Sjl 	}
265925cf1a30Sjl 	cp = (drmach_common_t *)id;
266025cf1a30Sjl 	err = cp->status(id, stat);
266125cf1a30Sjl 
266225cf1a30Sjl 	rw_exit(&drmach_boards_rwlock);
266325cf1a30Sjl 
266425cf1a30Sjl 	return (err);
266525cf1a30Sjl }
266625cf1a30Sjl 
266725cf1a30Sjl static sbd_error_t *
266825cf1a30Sjl drmach_i_status(drmachid_t id, drmach_status_t *stat)
266925cf1a30Sjl {
267025cf1a30Sjl 	drmach_common_t *cp;
267125cf1a30Sjl 
267225cf1a30Sjl 	if (!DRMACH_IS_ID(id))
267325cf1a30Sjl 		return (drerr_new(0, EOPL_NOTID, NULL));
267425cf1a30Sjl 	cp = id;
267525cf1a30Sjl 
267625cf1a30Sjl 	return (cp->status(id, stat));
267725cf1a30Sjl }
267825cf1a30Sjl 
267925cf1a30Sjl /*ARGSUSED*/
268025cf1a30Sjl sbd_error_t *
268125cf1a30Sjl drmach_unconfigure(drmachid_t id, int flags)
268225cf1a30Sjl {
268325cf1a30Sjl 	drmach_device_t *dp;
268425cf1a30Sjl 	dev_info_t	*rdip, *fdip = NULL;
268525cf1a30Sjl 	char name[OBP_MAXDRVNAME];
268625cf1a30Sjl 	int rv;
268725cf1a30Sjl 
268825cf1a30Sjl 	if (DRMACH_IS_CPU_ID(id))
268925cf1a30Sjl 		return (NULL);
269025cf1a30Sjl 
269125cf1a30Sjl 	if (!DRMACH_IS_DEVICE_ID(id))
269225cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
269325cf1a30Sjl 
269425cf1a30Sjl 	dp = id;
269525cf1a30Sjl 
269625cf1a30Sjl 	rdip = dp->node->n_getdip(dp->node);
269725cf1a30Sjl 
269825cf1a30Sjl 	ASSERT(rdip);
269925cf1a30Sjl 
270025cf1a30Sjl 	rv = dp->node->n_getprop(dp->node, "name", name, OBP_MAXDRVNAME);
270125cf1a30Sjl 
270225cf1a30Sjl 	if (rv)
270325cf1a30Sjl 		return (NULL);
270425cf1a30Sjl 
270525cf1a30Sjl 	/*
270625cf1a30Sjl 	 * Note: FORCE flag is no longer necessary under devfs
270725cf1a30Sjl 	 */
270825cf1a30Sjl 
270925cf1a30Sjl 	ASSERT(e_ddi_branch_held(rdip));
271025cf1a30Sjl 	if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) {
271125cf1a30Sjl 		sbd_error_t	*err;
271225cf1a30Sjl 		char		*path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
271325cf1a30Sjl 
271425cf1a30Sjl 		/*
271525cf1a30Sjl 		 * If non-NULL, fdip is returned held and must be released.
271625cf1a30Sjl 		 */
271725cf1a30Sjl 		if (fdip != NULL) {
271825cf1a30Sjl 			(void) ddi_pathname(fdip, path);
271925cf1a30Sjl 			ndi_rele_devi(fdip);
272025cf1a30Sjl 		} else {
272125cf1a30Sjl 			(void) ddi_pathname(rdip, path);
272225cf1a30Sjl 		}
272325cf1a30Sjl 
272425cf1a30Sjl 		err = drerr_new(1, EOPL_DRVFAIL, path);
272525cf1a30Sjl 
272625cf1a30Sjl 		kmem_free(path, MAXPATHLEN);
272725cf1a30Sjl 
272825cf1a30Sjl 		return (err);
272925cf1a30Sjl 	}
273025cf1a30Sjl 
273125cf1a30Sjl 	return (NULL);
273225cf1a30Sjl }
273325cf1a30Sjl 
273425cf1a30Sjl 
273525cf1a30Sjl int
273625cf1a30Sjl drmach_cpu_poweron(struct cpu *cp)
273725cf1a30Sjl {
273825cf1a30Sjl 	int bnum, cpuid, onb_core_num, strand_id;
273925cf1a30Sjl 	drmach_board_t *bp;
274025cf1a30Sjl 
274125cf1a30Sjl 	DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
274225cf1a30Sjl 
274325cf1a30Sjl 	cpuid = cp->cpu_id;
274425cf1a30Sjl 	bnum = LSB_ID(cpuid);
274525cf1a30Sjl 	onb_core_num = ON_BOARD_CORE_NUM(cpuid);
274625cf1a30Sjl 	strand_id = STRAND_ID(cpuid);
274725cf1a30Sjl 	bp = drmach_get_board_by_bnum(bnum);
274825cf1a30Sjl 
274925cf1a30Sjl 	ASSERT(bp);
275025cf1a30Sjl 	if (bp->cores[onb_core_num].core_hotadded == 0) {
275125cf1a30Sjl 		if (drmach_add_remove_cpu(bnum, onb_core_num,
2752*e98fafb9Sjl 		    HOTADD_CPU) != 0) {
275325cf1a30Sjl 			cmn_err(CE_WARN, "Failed to add CMP %d on board %d\n",
2754*e98fafb9Sjl 			    onb_core_num, bnum);
275525cf1a30Sjl 			return (EIO);
275625cf1a30Sjl 		}
275725cf1a30Sjl 	}
275825cf1a30Sjl 
275925cf1a30Sjl 	ASSERT(MUTEX_HELD(&cpu_lock));
276025cf1a30Sjl 
276125cf1a30Sjl 	if (drmach_cpu_start(cp) != 0) {
276225cf1a30Sjl 		if (bp->cores[onb_core_num].core_started == 0) {
276325cf1a30Sjl 			/*
276425cf1a30Sjl 			 * we must undo the hotadd or no one will do that
276525cf1a30Sjl 			 * If this fails, we will do this again in
276625cf1a30Sjl 			 * drmach_board_disconnect.
276725cf1a30Sjl 			 */
276825cf1a30Sjl 			if (drmach_add_remove_cpu(bnum, onb_core_num,
2769*e98fafb9Sjl 			    HOTREMOVE_CPU) != 0) {
277025cf1a30Sjl 				cmn_err(CE_WARN, "Failed to remove CMP %d "
2771*e98fafb9Sjl 				    "on board %d\n", onb_core_num, bnum);
277225cf1a30Sjl 			}
277325cf1a30Sjl 		}
277425cf1a30Sjl 		return (EBUSY);
277525cf1a30Sjl 	} else {
277625cf1a30Sjl 		bp->cores[onb_core_num].core_started |= (1 << strand_id);
277725cf1a30Sjl 		return (0);
277825cf1a30Sjl 	}
277925cf1a30Sjl }
278025cf1a30Sjl 
278125cf1a30Sjl int
278225cf1a30Sjl drmach_cpu_poweroff(struct cpu *cp)
278325cf1a30Sjl {
278425cf1a30Sjl 	int 		rv = 0;
278525cf1a30Sjl 	processorid_t	cpuid = cp->cpu_id;
278625cf1a30Sjl 
278725cf1a30Sjl 	DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
278825cf1a30Sjl 
278925cf1a30Sjl 	ASSERT(MUTEX_HELD(&cpu_lock));
279025cf1a30Sjl 
279125cf1a30Sjl 	/*
279225cf1a30Sjl 	 * Capture all CPUs (except for detaching proc) to prevent
279325cf1a30Sjl 	 * crosscalls to the detaching proc until it has cleared its
279425cf1a30Sjl 	 * bit in cpu_ready_set.
279525cf1a30Sjl 	 *
279625cf1a30Sjl 	 * The CPU's remain paused and the prom_mutex is known to be free.
279725cf1a30Sjl 	 * This prevents the x-trap victim from blocking when doing prom
279825cf1a30Sjl 	 * IEEE-1275 calls at a high PIL level.
279925cf1a30Sjl 	 */
280025cf1a30Sjl 
280125cf1a30Sjl 	promsafe_pause_cpus();
280225cf1a30Sjl 
280325cf1a30Sjl 	/*
280425cf1a30Sjl 	 * Quiesce interrupts on the target CPU. We do this by setting
280525cf1a30Sjl 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
280625cf1a30Sjl 	 * prevent it from receiving cross calls and cross traps.
280725cf1a30Sjl 	 * This prevents the processor from receiving any new soft interrupts.
280825cf1a30Sjl 	 */
280925cf1a30Sjl 	mp_cpu_quiesce(cp);
281025cf1a30Sjl 
281125cf1a30Sjl 	rv = prom_stopcpu_bycpuid(cpuid);
281225cf1a30Sjl 	if (rv == 0)
281325cf1a30Sjl 		cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
281425cf1a30Sjl 
281525cf1a30Sjl 	start_cpus();
281625cf1a30Sjl 
281725cf1a30Sjl 	if (rv == 0) {
281825cf1a30Sjl 		int bnum, onb_core_num, strand_id;
281925cf1a30Sjl 		drmach_board_t *bp;
282025cf1a30Sjl 
282125cf1a30Sjl 		CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
282225cf1a30Sjl 
282325cf1a30Sjl 		bnum = LSB_ID(cpuid);
282425cf1a30Sjl 		onb_core_num = ON_BOARD_CORE_NUM(cpuid);
282525cf1a30Sjl 		strand_id = STRAND_ID(cpuid);
282625cf1a30Sjl 		bp = drmach_get_board_by_bnum(bnum);
282725cf1a30Sjl 		ASSERT(bp);
282825cf1a30Sjl 
282925cf1a30Sjl 		bp->cores[onb_core_num].core_started &= ~(1 << strand_id);
283025cf1a30Sjl 		if (bp->cores[onb_core_num].core_started == 0) {
283125cf1a30Sjl 			if (drmach_add_remove_cpu(bnum, onb_core_num,
2832*e98fafb9Sjl 			    HOTREMOVE_CPU) != 0) {
2833*e98fafb9Sjl 				cmn_err(CE_WARN, "Failed to remove CMP %d LSB "
2834*e98fafb9Sjl 				    "%d\n", onb_core_num, bnum);
283525cf1a30Sjl 				return (EIO);
283625cf1a30Sjl 			}
283725cf1a30Sjl 		}
283825cf1a30Sjl 	}
283925cf1a30Sjl 
284025cf1a30Sjl 	return (rv);
284125cf1a30Sjl }
284225cf1a30Sjl 
284325cf1a30Sjl /*ARGSUSED*/
284425cf1a30Sjl int
284525cf1a30Sjl drmach_verify_sr(dev_info_t *dip, int sflag)
284625cf1a30Sjl {
284725cf1a30Sjl 	return (0);
284825cf1a30Sjl }
284925cf1a30Sjl 
285025cf1a30Sjl void
285125cf1a30Sjl drmach_suspend_last(void)
285225cf1a30Sjl {
285325cf1a30Sjl }
285425cf1a30Sjl 
285525cf1a30Sjl void
285625cf1a30Sjl drmach_resume_first(void)
285725cf1a30Sjl {
285825cf1a30Sjl }
285925cf1a30Sjl 
286025cf1a30Sjl /*
286125cf1a30Sjl  * Log a DR sysevent.
286225cf1a30Sjl  * Return value: 0 success, non-zero failure.
286325cf1a30Sjl  */
286425cf1a30Sjl int
286525cf1a30Sjl drmach_log_sysevent(int board, char *hint, int flag, int verbose)
286625cf1a30Sjl {
286725cf1a30Sjl 	sysevent_t			*ev;
286825cf1a30Sjl 	sysevent_id_t			eid;
286925cf1a30Sjl 	int				rv, km_flag;
287025cf1a30Sjl 	sysevent_value_t		evnt_val;
287125cf1a30Sjl 	sysevent_attr_list_t		*evnt_attr_list = NULL;
287225cf1a30Sjl 	char				attach_pnt[MAXNAMELEN];
287325cf1a30Sjl 
287425cf1a30Sjl 	km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
287525cf1a30Sjl 	attach_pnt[0] = '\0';
287625cf1a30Sjl 	if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
287725cf1a30Sjl 		rv = -1;
287825cf1a30Sjl 		goto logexit;
287925cf1a30Sjl 	}
2880b307f191Sbm 	if (verbose) {
288125cf1a30Sjl 		DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
2882*e98fafb9Sjl 		    attach_pnt, hint, flag, verbose);
2883b307f191Sbm 	}
288425cf1a30Sjl 
288525cf1a30Sjl 	if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
2886*e98fafb9Sjl 	    SUNW_KERN_PUB"dr", km_flag)) == NULL) {
288725cf1a30Sjl 		rv = -2;
288825cf1a30Sjl 		goto logexit;
288925cf1a30Sjl 	}
289025cf1a30Sjl 	evnt_val.value_type = SE_DATA_TYPE_STRING;
289125cf1a30Sjl 	evnt_val.value.sv_string = attach_pnt;
2892*e98fafb9Sjl 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
2893*e98fafb9Sjl 	    km_flag)) != 0)
289425cf1a30Sjl 		goto logexit;
289525cf1a30Sjl 
289625cf1a30Sjl 	evnt_val.value_type = SE_DATA_TYPE_STRING;
289725cf1a30Sjl 	evnt_val.value.sv_string = hint;
2898*e98fafb9Sjl 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
2899*e98fafb9Sjl 	    km_flag)) != 0) {
290025cf1a30Sjl 		sysevent_free_attr(evnt_attr_list);
290125cf1a30Sjl 		goto logexit;
290225cf1a30Sjl 	}
290325cf1a30Sjl 
290425cf1a30Sjl 	(void) sysevent_attach_attributes(ev, evnt_attr_list);
290525cf1a30Sjl 
290625cf1a30Sjl 	/*
290725cf1a30Sjl 	 * Log the event but do not sleep waiting for its
290825cf1a30Sjl 	 * delivery. This provides insulation from syseventd.
290925cf1a30Sjl 	 */
291025cf1a30Sjl 	rv = log_sysevent(ev, SE_NOSLEEP, &eid);
291125cf1a30Sjl 
291225cf1a30Sjl logexit:
291325cf1a30Sjl 	if (ev)
291425cf1a30Sjl 		sysevent_free(ev);
291525cf1a30Sjl 	if ((rv != 0) && verbose)
2916*e98fafb9Sjl 		cmn_err(CE_WARN, "drmach_log_sysevent failed (rv %d) for %s "
2917*e98fafb9Sjl 		    " %s\n", rv, attach_pnt, hint);
291825cf1a30Sjl 
291925cf1a30Sjl 	return (rv);
292025cf1a30Sjl }
292125cf1a30Sjl 
292225cf1a30Sjl #define	OPL_DR_STATUS_PROP "dr-status"
292325cf1a30Sjl 
292425cf1a30Sjl static int
292525cf1a30Sjl opl_check_dr_status()
292625cf1a30Sjl {
292725cf1a30Sjl 	pnode_t	node;
292825cf1a30Sjl 	int	rtn, len;
292925cf1a30Sjl 	char	*str;
293025cf1a30Sjl 
293125cf1a30Sjl 	node = prom_rootnode();
293225cf1a30Sjl 	if (node == OBP_BADNODE) {
293325cf1a30Sjl 		return (1);
293425cf1a30Sjl 	}
293525cf1a30Sjl 
293625cf1a30Sjl 	len = prom_getproplen(node, OPL_DR_STATUS_PROP);
293725cf1a30Sjl 	if (len == -1) {
293825cf1a30Sjl 		/*
293925cf1a30Sjl 		 * dr-status doesn't exist when DR is activated and
294025cf1a30Sjl 		 * any warning messages aren't needed.
294125cf1a30Sjl 		 */
294225cf1a30Sjl 		return (1);
294325cf1a30Sjl 	}
294425cf1a30Sjl 
294525cf1a30Sjl 	str = (char *)kmem_zalloc(len+1, KM_SLEEP);
294625cf1a30Sjl 	rtn = prom_getprop(node, OPL_DR_STATUS_PROP, str);
294725cf1a30Sjl 	kmem_free(str, len + 1);
294825cf1a30Sjl 	if (rtn == -1) {
294925cf1a30Sjl 		return (1);
295025cf1a30Sjl 	} else {
295125cf1a30Sjl 		return (0);
295225cf1a30Sjl 	}
295325cf1a30Sjl }
295425cf1a30Sjl 
295525cf1a30Sjl /* we are allocating memlist from TLB locked pages to avoid tlbmisses */
295625cf1a30Sjl 
295725cf1a30Sjl static struct memlist *
295825cf1a30Sjl drmach_memlist_add_span(drmach_copy_rename_program_t *p,
295925cf1a30Sjl 	struct memlist *mlist, uint64_t base, uint64_t len)
296025cf1a30Sjl {
296125cf1a30Sjl 	struct memlist	*ml, *tl, *nl;
296225cf1a30Sjl 
296325cf1a30Sjl 	if (len == 0ull)
296425cf1a30Sjl 		return (NULL);
296525cf1a30Sjl 
296625cf1a30Sjl 	if (mlist == NULL) {
296725cf1a30Sjl 		mlist = p->free_mlist;
296825cf1a30Sjl 		if (mlist == NULL)
296925cf1a30Sjl 			return (NULL);
297025cf1a30Sjl 		p->free_mlist = mlist->next;
297125cf1a30Sjl 		mlist->address = base;
297225cf1a30Sjl 		mlist->size = len;
297325cf1a30Sjl 		mlist->next = mlist->prev = NULL;
297425cf1a30Sjl 
297525cf1a30Sjl 		return (mlist);
297625cf1a30Sjl 	}
297725cf1a30Sjl 
297825cf1a30Sjl 	for (tl = ml = mlist; ml; tl = ml, ml = ml->next) {
297925cf1a30Sjl 		if (base < ml->address) {
298025cf1a30Sjl 			if ((base + len) < ml->address) {
298125cf1a30Sjl 				nl = p->free_mlist;
298225cf1a30Sjl 				if (nl == NULL)
298325cf1a30Sjl 					return (NULL);
298425cf1a30Sjl 				p->free_mlist = nl->next;
298525cf1a30Sjl 				nl->address = base;
298625cf1a30Sjl 				nl->size = len;
298725cf1a30Sjl 				nl->next = ml;
298825cf1a30Sjl 				if ((nl->prev = ml->prev) != NULL)
298925cf1a30Sjl 					nl->prev->next = nl;
299025cf1a30Sjl 				ml->prev = nl;
299125cf1a30Sjl 				if (mlist == ml)
299225cf1a30Sjl 					mlist = nl;
299325cf1a30Sjl 			} else {
2994*e98fafb9Sjl 				ml->size = MAX((base + len), (ml->address +
2995*e98fafb9Sjl 				    ml->size)) - base;
299625cf1a30Sjl 				ml->address = base;
299725cf1a30Sjl 			}
299825cf1a30Sjl 			break;
299925cf1a30Sjl 
300025cf1a30Sjl 		} else if (base <= (ml->address + ml->size)) {
3001*e98fafb9Sjl 			ml->size = MAX((base + len), (ml->address + ml->size)) -
3002*e98fafb9Sjl 			    MIN(ml->address, base);
300325cf1a30Sjl 			ml->address = MIN(ml->address, base);
300425cf1a30Sjl 			break;
300525cf1a30Sjl 		}
300625cf1a30Sjl 	}
300725cf1a30Sjl 	if (ml == NULL) {
300825cf1a30Sjl 		nl = p->free_mlist;
300925cf1a30Sjl 		if (nl == NULL)
301025cf1a30Sjl 			return (NULL);
301125cf1a30Sjl 		p->free_mlist = nl->next;
301225cf1a30Sjl 		nl->address = base;
301325cf1a30Sjl 		nl->size = len;
301425cf1a30Sjl 		nl->next = NULL;
301525cf1a30Sjl 		nl->prev = tl;
301625cf1a30Sjl 		tl->next = nl;
301725cf1a30Sjl 	}
301825cf1a30Sjl 
301925cf1a30Sjl 	return (mlist);
302025cf1a30Sjl }
302125cf1a30Sjl 
302225cf1a30Sjl /*
302325cf1a30Sjl  * The routine performs the necessary memory COPY and MC adr SWITCH.
302425cf1a30Sjl  * Both operations MUST be at the same "level" so that the stack is
302525cf1a30Sjl  * maintained correctly between the copy and switch.  The switch
302625cf1a30Sjl  * portion implements a caching mechanism to guarantee the code text
302725cf1a30Sjl  * is cached prior to execution.  This is to guard against possible
302825cf1a30Sjl  * memory access while the MC adr's are being modified.
302925cf1a30Sjl  *
303025cf1a30Sjl  * IMPORTANT: The _drmach_copy_rename_end() function must immediately
303125cf1a30Sjl  * follow drmach_copy_rename_prog__relocatable() so that the correct
303225cf1a30Sjl  * "length" of the drmach_copy_rename_prog__relocatable can be
303325cf1a30Sjl  * calculated.  This routine MUST be a LEAF function, i.e. it can
303425cf1a30Sjl  * make NO function calls, primarily for two reasons:
303525cf1a30Sjl  *
303625cf1a30Sjl  *	1. We must keep the stack consistent across the "switch".
303725cf1a30Sjl  *	2. Function calls are compiled to relative offsets, and
303825cf1a30Sjl  *	   we execute this function we'll be executing it from
303925cf1a30Sjl  *	   a copied version in a different area of memory, thus
304025cf1a30Sjl  *	   the relative offsets will be bogus.
304125cf1a30Sjl  *
304225cf1a30Sjl  * Moreover, it must have the "__relocatable" suffix to inform DTrace
304325cf1a30Sjl  * providers (and anything else, for that matter) that this
304425cf1a30Sjl  * function's text is manually relocated elsewhere before it is
304525cf1a30Sjl  * executed.  That is, it cannot be safely instrumented with any
304625cf1a30Sjl  * methodology that is PC-relative.
304725cf1a30Sjl  */
304825cf1a30Sjl 
304925cf1a30Sjl /*
305025cf1a30Sjl  * We multiply this to system_clock_frequency so we
305125cf1a30Sjl  * are setting a delay of fmem_timeout second for
3052b307f191Sbm  * the rename command.
3053b307f191Sbm  *
3054b307f191Sbm  * FMEM command itself should complete within 15 sec.
3055b307f191Sbm  * We add 2 more sec to be conservative.
3056b307f191Sbm  *
3057b307f191Sbm  * Note that there is also a SCF BUSY bit checking
3058b307f191Sbm  * in drmach_asm.s right before FMEM command is
3059b307f191Sbm  * issued.  XSCF sets the SCF BUSY bit when the
3060b307f191Sbm  * other domain on the same PSB reboots and it
3061b307f191Sbm  * will not be able to service the FMEM command
3062b307f191Sbm  * within 15 sec.   After setting the SCF BUSY
3063b307f191Sbm  * bit, XSCF will wait a while before servicing
3064b307f191Sbm  * other reboot command so there is no race
3065b307f191Sbm  * condition.
306625cf1a30Sjl  */
3067b307f191Sbm 
306825cf1a30Sjl static int	fmem_timeout = 17;
3069b307f191Sbm 
3070b307f191Sbm /*
3071b307f191Sbm  *	The empirical data on some OPL system shows that
3072b307f191Sbm  *	we can copy 250 MB per second.  We set it to
3073b307f191Sbm  * 	80 MB to be conservative.  In normal case,
3074b307f191Sbm  *	this timeout does not affect anything.
3075b307f191Sbm  */
3076b307f191Sbm 
3077b307f191Sbm static int	min_copy_size_per_sec = 80 * 1024 * 1024;
3078b307f191Sbm 
3079b307f191Sbm /*
3080b307f191Sbm  *	This is the timeout value for the xcall synchronization
3081b307f191Sbm  *	to get all the CPU ready to do the parallel copying.
3082b307f191Sbm  *	Even on a fully loaded system, 10 sec. should be long
3083b307f191Sbm  *	enough.
3084b307f191Sbm  */
3085b307f191Sbm 
3086b307f191Sbm static int	cpu_xcall_delay = 10;
308725cf1a30Sjl int drmach_disable_mcopy = 0;
308825cf1a30Sjl 
308968ac2337Sjl /*
309068ac2337Sjl  * The following delay loop executes sleep instruction to yield the
309168ac2337Sjl  * CPU to other strands.  If this is not done, some strand will tie
309268ac2337Sjl  * up the CPU in busy loops while the other strand cannot do useful
309368ac2337Sjl  * work.  The copy procedure will take a much longer time without this.
309468ac2337Sjl  */
309525cf1a30Sjl #define	DR_DELAY_IL(ms, freq)					\
309625cf1a30Sjl 	{							\
309725cf1a30Sjl 		uint64_t start;					\
309825cf1a30Sjl 		uint64_t nstick;				\
309925cf1a30Sjl 		volatile uint64_t now;				\
310025cf1a30Sjl 		nstick = ((uint64_t)ms * freq)/1000;		\
310125cf1a30Sjl 		start = drmach_get_stick_il();			\
310225cf1a30Sjl 		now = start;					\
310325cf1a30Sjl 		while ((now - start) <= nstick) {		\
310425cf1a30Sjl 			drmach_sleep_il();			\
310525cf1a30Sjl 			now = drmach_get_stick_il();		\
310625cf1a30Sjl 		}						\
310725cf1a30Sjl 	}
310825cf1a30Sjl 
310925cf1a30Sjl static int
311025cf1a30Sjl drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog,
311125cf1a30Sjl 	int cpuid)
311225cf1a30Sjl {
311325cf1a30Sjl 	struct memlist		*ml;
311425cf1a30Sjl 	register int		rtn;
311525cf1a30Sjl 	int			i;
311625cf1a30Sjl 	register uint64_t	curr, limit;
311725cf1a30Sjl 	extern uint64_t		drmach_get_stick_il();
311825cf1a30Sjl 	extern void		membar_sync_il();
311925cf1a30Sjl 	extern void		flush_instr_mem_il(void*);
312068ac2337Sjl 	extern void		flush_windows_il(void);
312125cf1a30Sjl 	uint64_t		copy_start;
312225cf1a30Sjl 
312368ac2337Sjl 	/*
312468ac2337Sjl 	 * flush_windows is moved here to make sure all
312568ac2337Sjl 	 * registers used in the callers are flushed to
312668ac2337Sjl 	 * memory before the copy.
312768ac2337Sjl 	 *
312868ac2337Sjl 	 * If flush_windows() is called too early in the
312968ac2337Sjl 	 * calling function, the compiler might put some
313068ac2337Sjl 	 * data in the local registers after flush_windows().
313168ac2337Sjl 	 * After FMA, if there is any fill trap, the registers
313268ac2337Sjl 	 * will contain stale data.
313368ac2337Sjl 	 */
313468ac2337Sjl 
313568ac2337Sjl 	flush_windows_il();
313668ac2337Sjl 
313725cf1a30Sjl 	prog->critical->stat[cpuid] = FMEM_LOOP_COPY_READY;
313825cf1a30Sjl 	membar_sync_il();
313925cf1a30Sjl 
314025cf1a30Sjl 	if (prog->data->cpuid == cpuid) {
314125cf1a30Sjl 		limit = drmach_get_stick_il();
3142b307f191Sbm 		limit += cpu_xcall_delay * system_clock_freq;
314325cf1a30Sjl 		for (i = 0; i < NCPU; i++) {
314425cf1a30Sjl 			if (CPU_IN_SET(prog->data->cpu_slave_set, i)) {
3145*e98fafb9Sjl 				/* wait for all CPU's to be ready */
3146*e98fafb9Sjl 				for (;;) {
3147*e98fafb9Sjl 					if (prog->critical->stat[i] ==
3148*e98fafb9Sjl 					    FMEM_LOOP_COPY_READY) {
3149*e98fafb9Sjl 						break;
3150*e98fafb9Sjl 					}
3151*e98fafb9Sjl 					DR_DELAY_IL(1, prog->data->stick_freq);
3152*e98fafb9Sjl 				}
3153*e98fafb9Sjl 				curr = drmach_get_stick_il();
3154*e98fafb9Sjl 				if (curr > limit) {
3155*e98fafb9Sjl 					prog->data->fmem_status.error =
3156*e98fafb9Sjl 					    EOPL_FMEM_XC_TIMEOUT;
3157*e98fafb9Sjl 					return (EOPL_FMEM_XC_TIMEOUT);
315825cf1a30Sjl 				}
315925cf1a30Sjl 			}
316025cf1a30Sjl 		}
316125cf1a30Sjl 		prog->data->fmem_status.stat = FMEM_LOOP_COPY_READY;
316225cf1a30Sjl 		membar_sync_il();
316325cf1a30Sjl 		copy_start = drmach_get_stick_il();
316425cf1a30Sjl 	} else {
316525cf1a30Sjl 		for (;;) {
316625cf1a30Sjl 			if (prog->data->fmem_status.stat ==
3167*e98fafb9Sjl 			    FMEM_LOOP_COPY_READY) {
316825cf1a30Sjl 				break;
316925cf1a30Sjl 			}
317025cf1a30Sjl 			if (prog->data->fmem_status.error) {
3171*e98fafb9Sjl 				prog->data->error[cpuid] = EOPL_FMEM_TERMINATE;
3172b307f191Sbm 				return (EOPL_FMEM_TERMINATE);
317325cf1a30Sjl 			}
317468ac2337Sjl 			DR_DELAY_IL(1, prog->data->stick_freq);
317525cf1a30Sjl 		}
317625cf1a30Sjl 	}
317725cf1a30Sjl 
317825cf1a30Sjl 	/*
317925cf1a30Sjl 	 * DO COPY.
318025cf1a30Sjl 	 */
318125cf1a30Sjl 	if (CPU_IN_SET(prog->data->cpu_copy_set, cpuid)) {
3182*e98fafb9Sjl 		for (ml = prog->data->cpu_ml[cpuid]; ml; ml = ml->next) {
3183*e98fafb9Sjl 			uint64_t	s_pa, t_pa;
3184*e98fafb9Sjl 			uint64_t	nbytes;
3185*e98fafb9Sjl 
3186*e98fafb9Sjl 			s_pa = prog->data->s_copybasepa + ml->address;
3187*e98fafb9Sjl 			t_pa = prog->data->t_copybasepa + ml->address;
3188*e98fafb9Sjl 			nbytes = ml->size;
3189*e98fafb9Sjl 
3190*e98fafb9Sjl 			while (nbytes != 0ull) {
3191*e98fafb9Sjl 				/*
3192*e98fafb9Sjl 				 * If the master has detected error, we just
3193*e98fafb9Sjl 				 * bail out
3194*e98fafb9Sjl 				 */
3195*e98fafb9Sjl 				if (prog->data->fmem_status.error !=
3196*e98fafb9Sjl 				    ESBD_NOERROR) {
3197*e98fafb9Sjl 					prog->data->error[cpuid] =
3198*e98fafb9Sjl 					    EOPL_FMEM_TERMINATE;
3199*e98fafb9Sjl 					return (EOPL_FMEM_TERMINATE);
3200*e98fafb9Sjl 				}
3201*e98fafb9Sjl 				/*
3202*e98fafb9Sjl 				 * This copy does NOT use an ASI
3203*e98fafb9Sjl 				 * that avoids the Ecache, therefore
3204*e98fafb9Sjl 				 * the dst_pa addresses may remain
3205*e98fafb9Sjl 				 * in our Ecache after the dst_pa
3206*e98fafb9Sjl 				 * has been removed from the system.
3207*e98fafb9Sjl 				 * A subsequent write-back to memory
3208*e98fafb9Sjl 				 * will cause an ARB-stop because the
3209*e98fafb9Sjl 				 * physical address no longer exists
3210*e98fafb9Sjl 				 * in the system. Therefore we must
3211*e98fafb9Sjl 				 * flush out local Ecache after we
3212*e98fafb9Sjl 				 * finish the copy.
3213*e98fafb9Sjl 				 */
3214*e98fafb9Sjl 
3215*e98fafb9Sjl 				/* copy 32 bytes at src_pa to dst_pa */
3216*e98fafb9Sjl 				bcopy32_il(s_pa, t_pa);
3217*e98fafb9Sjl 
3218*e98fafb9Sjl 				/*
3219*e98fafb9Sjl 				 * increment the counter to signal that we are
3220*e98fafb9Sjl 				 * alive
3221*e98fafb9Sjl 				 */
3222*e98fafb9Sjl 				prog->stat->nbytes[cpuid] += 32;
3223*e98fafb9Sjl 
3224*e98fafb9Sjl 				/* increment by 32 bytes */
3225*e98fafb9Sjl 				s_pa += (4 * sizeof (uint64_t));
3226*e98fafb9Sjl 				t_pa += (4 * sizeof (uint64_t));
3227*e98fafb9Sjl 
3228*e98fafb9Sjl 				/* decrement by 32 bytes */
3229*e98fafb9Sjl 				nbytes -= (4 * sizeof (uint64_t));
323025cf1a30Sjl 			}
323125cf1a30Sjl 		}
3232*e98fafb9Sjl 		prog->critical->stat[cpuid] = FMEM_LOOP_COPY_DONE;
3233*e98fafb9Sjl 		membar_sync_il();
323425cf1a30Sjl 	}
323525cf1a30Sjl 
323625cf1a30Sjl 	/*
323725cf1a30Sjl 	 * Since bcopy32_il() does NOT use an ASI to bypass
323825cf1a30Sjl 	 * the Ecache, we need to flush our Ecache after
323925cf1a30Sjl 	 * the copy is complete.
324025cf1a30Sjl 	 */
324125cf1a30Sjl 	flush_cache_il();
324225cf1a30Sjl 
324325cf1a30Sjl 	/*
324425cf1a30Sjl 	 * drmach_fmem_exec_script()
324525cf1a30Sjl 	 */
324625cf1a30Sjl 	if (prog->data->cpuid == cpuid) {
324725cf1a30Sjl 		uint64_t	last, now;
324825cf1a30Sjl 
324925cf1a30Sjl 		limit = copy_start + prog->data->copy_delay;
325025cf1a30Sjl 		for (i = 0; i < NCPU; i++) {
3251*e98fafb9Sjl 			if (!CPU_IN_SET(prog->data->cpu_slave_set, i))
3252*e98fafb9Sjl 				continue;
3253*e98fafb9Sjl 
3254*e98fafb9Sjl 			for (;;) {
3255*e98fafb9Sjl 				/*
3256*e98fafb9Sjl 				 * we get FMEM_LOOP_FMEM_READY in
3257*e98fafb9Sjl 				 * normal case
3258*e98fafb9Sjl 				 */
325925cf1a30Sjl 				if (prog->critical->stat[i] ==
3260*e98fafb9Sjl 				    FMEM_LOOP_FMEM_READY) {
326125cf1a30Sjl 					break;
326225cf1a30Sjl 				}
326325cf1a30Sjl 				/* got error traps */
3264b307f191Sbm 				if (prog->data->error[i] ==
3265*e98fafb9Sjl 				    EOPL_FMEM_COPY_ERROR) {
326625cf1a30Sjl 					prog->data->fmem_status.error =
3267*e98fafb9Sjl 					    EOPL_FMEM_COPY_ERROR;
3268b307f191Sbm 					return (EOPL_FMEM_COPY_ERROR);
326925cf1a30Sjl 				}
3270*e98fafb9Sjl 				/*
3271*e98fafb9Sjl 				 * if we have not reached limit, wait
3272*e98fafb9Sjl 				 * more
3273*e98fafb9Sjl 				 */
327425cf1a30Sjl 				curr = drmach_get_stick_il();
327525cf1a30Sjl 				if (curr <= limit)
327625cf1a30Sjl 					continue;
327725cf1a30Sjl 
327825cf1a30Sjl 				prog->data->slowest_cpuid = i;
3279*e98fafb9Sjl 				prog->data->copy_wait_time = curr - copy_start;
328025cf1a30Sjl 
328125cf1a30Sjl 				/* now check if slave is alive */
328225cf1a30Sjl 				last = prog->stat->nbytes[i];
328325cf1a30Sjl 
328425cf1a30Sjl 				DR_DELAY_IL(1, prog->data->stick_freq);
328525cf1a30Sjl 
328625cf1a30Sjl 				now = prog->stat->nbytes[i];
328725cf1a30Sjl 				if (now <= last) {
3288*e98fafb9Sjl 					/*
3289*e98fafb9Sjl 					 * no progress, perhaps just
3290*e98fafb9Sjl 					 * finished
3291*e98fafb9Sjl 					 */
329225cf1a30Sjl 					DR_DELAY_IL(1, prog->data->stick_freq);
329325cf1a30Sjl 					if (prog->critical->stat[i] ==
3294*e98fafb9Sjl 					    FMEM_LOOP_FMEM_READY)
329525cf1a30Sjl 						break;
329625cf1a30Sjl 					/* copy error */
3297b307f191Sbm 					if (prog->data->error[i] ==
3298*e98fafb9Sjl 					    EOPL_FMEM_COPY_ERROR) {
3299*e98fafb9Sjl 						prog->data-> fmem_status.error =
3300*e98fafb9Sjl 						    EOPL_FMEM_COPY_ERROR;
3301b307f191Sbm 						return (EOPL_FMEM_COPY_ERROR);
330225cf1a30Sjl 					}
330325cf1a30Sjl 					prog->data->fmem_status.error =
3304b307f191Sbm 					    EOPL_FMEM_COPY_TIMEOUT;
3305b307f191Sbm 					return (EOPL_FMEM_COPY_TIMEOUT);
330625cf1a30Sjl 				}
330725cf1a30Sjl 			}
330825cf1a30Sjl 		}
3309b307f191Sbm 
331025cf1a30Sjl 		prog->critical->stat[cpuid] = FMEM_LOOP_FMEM_READY;
331125cf1a30Sjl 		prog->data->fmem_status.stat  = FMEM_LOOP_FMEM_READY;
331225cf1a30Sjl 
331325cf1a30Sjl 		membar_sync_il();
331425cf1a30Sjl 		flush_instr_mem_il((void*) (prog->critical));
331525cf1a30Sjl 		/*
331625cf1a30Sjl 		 * drmach_fmem_exec_script()
331725cf1a30Sjl 		 */
331825cf1a30Sjl 		rtn = prog->critical->fmem((void *)prog->critical, PAGESIZE);
331925cf1a30Sjl 		return (rtn);
332025cf1a30Sjl 	} else {
332125cf1a30Sjl 		flush_instr_mem_il((void*) (prog->critical));
332225cf1a30Sjl 		/*
332325cf1a30Sjl 		 * drmach_fmem_loop_script()
332425cf1a30Sjl 		 */
3325*e98fafb9Sjl 		rtn = prog->critical->loop((void *)(prog->critical), PAGESIZE,
3326*e98fafb9Sjl 		    (void *)&(prog->critical->stat[cpuid]));
332725cf1a30Sjl 		prog->data->error[cpuid] = rtn;
332825cf1a30Sjl 		/* slave thread does not care the rv */
332925cf1a30Sjl 		return (0);
333025cf1a30Sjl 	}
333125cf1a30Sjl }
333225cf1a30Sjl 
333325cf1a30Sjl static void
333425cf1a30Sjl drmach_copy_rename_end(void)
333525cf1a30Sjl {
333625cf1a30Sjl 	/*
333725cf1a30Sjl 	 * IMPORTANT:	This function's location MUST be located immediately
333825cf1a30Sjl 	 *		following drmach_copy_rename_prog__relocatable to
333925cf1a30Sjl 	 *		accurately estimate its size.  Note that this assumes
334025cf1a30Sjl 	 *		the compiler keeps these functions in the order in
334125cf1a30Sjl 	 *		which they appear :-o
334225cf1a30Sjl 	 */
334325cf1a30Sjl }
334425cf1a30Sjl 
334525cf1a30Sjl 
334625cf1a30Sjl static void
334725cf1a30Sjl drmach_setup_memlist(drmach_copy_rename_program_t *p)
334825cf1a30Sjl {
334925cf1a30Sjl 	struct memlist *ml;
335025cf1a30Sjl 	caddr_t buf;
335125cf1a30Sjl 	int nbytes, s;
335225cf1a30Sjl 
335325cf1a30Sjl 	nbytes = PAGESIZE;
335425cf1a30Sjl 	s = roundup(sizeof (struct memlist), sizeof (void *));
335525cf1a30Sjl 	p->free_mlist = NULL;
335625cf1a30Sjl 	buf = p->memlist_buffer;
335725cf1a30Sjl 	while (nbytes >= sizeof (struct memlist)) {
335825cf1a30Sjl 		ml = (struct memlist *)buf;
335925cf1a30Sjl 		ml->next = p->free_mlist;
336025cf1a30Sjl 		p->free_mlist = ml;
336125cf1a30Sjl 		buf += s;
336225cf1a30Sjl 		nbytes -= s;
336325cf1a30Sjl 	}
336425cf1a30Sjl }
336525cf1a30Sjl 
336668ac2337Sjl static void
336768ac2337Sjl drmach_lock_critical(caddr_t va, caddr_t new_va)
336868ac2337Sjl {
336968ac2337Sjl 	tte_t tte;
337068ac2337Sjl 	int i;
337168ac2337Sjl 
337268ac2337Sjl 	kpreempt_disable();
337368ac2337Sjl 
337468ac2337Sjl 	for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
337568ac2337Sjl 		vtag_flushpage(new_va, (uint64_t)ksfmmup);
3376*e98fafb9Sjl 		sfmmu_memtte(&tte, va_to_pfn(va), PROC_DATA|HAT_NOSYNC, TTE8K);
337768ac2337Sjl 		tte.tte_intlo |= TTE_LCK_INT;
337868ac2337Sjl 		sfmmu_dtlb_ld_kva(new_va, &tte);
337968ac2337Sjl 		sfmmu_itlb_ld_kva(new_va, &tte);
338068ac2337Sjl 		va += PAGESIZE;
338168ac2337Sjl 		new_va += PAGESIZE;
338268ac2337Sjl 	}
338368ac2337Sjl }
338468ac2337Sjl 
338568ac2337Sjl static void
338668ac2337Sjl drmach_unlock_critical(caddr_t va)
338768ac2337Sjl {
338868ac2337Sjl 	int i;
338968ac2337Sjl 
339068ac2337Sjl 	for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
339168ac2337Sjl 		vtag_flushpage(va, (uint64_t)ksfmmup);
339268ac2337Sjl 		va += PAGESIZE;
339368ac2337Sjl 	}
339468ac2337Sjl 
339568ac2337Sjl 	kpreempt_enable();
339668ac2337Sjl }
339768ac2337Sjl 
339825cf1a30Sjl sbd_error_t *
339925cf1a30Sjl drmach_copy_rename_init(drmachid_t t_id, drmachid_t s_id,
340025cf1a30Sjl 	struct memlist *c_ml, drmachid_t *pgm_id)
340125cf1a30Sjl {
340225cf1a30Sjl 	drmach_mem_t	*s_mem;
340325cf1a30Sjl 	drmach_mem_t	*t_mem;
340425cf1a30Sjl 	struct memlist	*x_ml;
340525cf1a30Sjl 	uint64_t	s_copybasepa, t_copybasepa;
340625cf1a30Sjl 	uint_t		len;
340725cf1a30Sjl 	caddr_t		bp, wp;
340825cf1a30Sjl 	int			s_bd, t_bd, cpuid, active_cpus, i;
340925cf1a30Sjl 	uint64_t		c_addr;
341025cf1a30Sjl 	size_t			c_size, copy_sz, sz;
341125cf1a30Sjl 	extern void		drmach_fmem_loop_script();
341225cf1a30Sjl 	extern void		drmach_fmem_loop_script_rtn();
341325cf1a30Sjl 	extern int		drmach_fmem_exec_script();
341425cf1a30Sjl 	extern void		drmach_fmem_exec_script_end();
341525cf1a30Sjl 	sbd_error_t	*err;
341668ac2337Sjl 	drmach_copy_rename_program_t *prog = NULL;
341768ac2337Sjl 	drmach_copy_rename_program_t *prog_kmem = NULL;
341825cf1a30Sjl 	void		(*mc_suspend)(void);
341925cf1a30Sjl 	void		(*mc_resume)(void);
342025cf1a30Sjl 	int		(*scf_fmem_start)(int, int);
342125cf1a30Sjl 	int		(*scf_fmem_end)(void);
342225cf1a30Sjl 	int		(*scf_fmem_cancel)(void);
342368ac2337Sjl 	uint64_t	(*scf_get_base_addr)(void);
342425cf1a30Sjl 
342525cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(s_id))
342625cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
342725cf1a30Sjl 	if (!DRMACH_IS_MEM_ID(t_id))
342825cf1a30Sjl 		return (drerr_new(0, EOPL_INAPPROP, NULL));
342925cf1a30Sjl 
343025cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
343125cf1a30Sjl 		int lsb_id, onb_core_num, strand_id;
343225cf1a30Sjl 		drmach_board_t *bp;
343325cf1a30Sjl 
343425cf1a30Sjl 		/*
343525cf1a30Sjl 		 * this kind of CPU will spin in cache
343625cf1a30Sjl 		 */
343725cf1a30Sjl 		if (CPU_IN_SET(cpu_ready_set, i))
343825cf1a30Sjl 			continue;
343925cf1a30Sjl 
344025cf1a30Sjl 		/*
344125cf1a30Sjl 		 * Now check for any inactive CPU's that
344225cf1a30Sjl 		 * have been hotadded.  This can only occur in
344325cf1a30Sjl 		 * error condition in drmach_cpu_poweron().
344425cf1a30Sjl 		 */
344525cf1a30Sjl 		lsb_id = LSB_ID(i);
344625cf1a30Sjl 		onb_core_num = ON_BOARD_CORE_NUM(i);
344725cf1a30Sjl 		strand_id = STRAND_ID(i);
344825cf1a30Sjl 		bp = drmach_get_board_by_bnum(lsb_id);
344925cf1a30Sjl 		if (bp == NULL)
345025cf1a30Sjl 			continue;
345125cf1a30Sjl 		if (bp->cores[onb_core_num].core_hotadded &
345225cf1a30Sjl 		    (1 << strand_id)) {
3453*e98fafb9Sjl 			if (!(bp->cores[onb_core_num].core_started &
3454*e98fafb9Sjl 			    (1 << strand_id))) {
3455*e98fafb9Sjl 				return (drerr_new(1, EOPL_CPU_STATE, NULL));
3456*e98fafb9Sjl 			}
345725cf1a30Sjl 		}
345825cf1a30Sjl 	}
345925cf1a30Sjl 
346025cf1a30Sjl 	mc_suspend = (void (*)(void))
346125cf1a30Sjl 	    modgetsymvalue("opl_mc_suspend", 0);
346225cf1a30Sjl 	mc_resume = (void (*)(void))
346325cf1a30Sjl 	    modgetsymvalue("opl_mc_resume", 0);
346425cf1a30Sjl 
346525cf1a30Sjl 	if (mc_suspend == NULL || mc_resume == NULL) {
34668eafe49bSbm 		return (drerr_new(1, EOPL_MC_OPL, NULL));
346725cf1a30Sjl 	}
346825cf1a30Sjl 
346925cf1a30Sjl 	scf_fmem_start = (int (*)(int, int))
347025cf1a30Sjl 	    modgetsymvalue("scf_fmem_start", 0);
347125cf1a30Sjl 	if (scf_fmem_start == NULL) {
34728eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
347325cf1a30Sjl 	}
347425cf1a30Sjl 	scf_fmem_end = (int (*)(void))
347525cf1a30Sjl 	    modgetsymvalue("scf_fmem_end", 0);
347625cf1a30Sjl 	if (scf_fmem_end == NULL) {
34778eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
347825cf1a30Sjl 	}
347925cf1a30Sjl 	scf_fmem_cancel = (int (*)(void))
348025cf1a30Sjl 	    modgetsymvalue("scf_fmem_cancel", 0);
348125cf1a30Sjl 	if (scf_fmem_cancel == NULL) {
34828eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
348368ac2337Sjl 	}
348468ac2337Sjl 	scf_get_base_addr = (uint64_t (*)(void))
348568ac2337Sjl 	    modgetsymvalue("scf_get_base_addr", 0);
348668ac2337Sjl 	if (scf_get_base_addr == NULL) {
34878eafe49bSbm 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
348825cf1a30Sjl 	}
348925cf1a30Sjl 	s_mem = s_id;
349025cf1a30Sjl 	t_mem = t_id;
349125cf1a30Sjl 
349225cf1a30Sjl 	s_bd = s_mem->dev.bp->bnum;
349325cf1a30Sjl 	t_bd = t_mem->dev.bp->bnum;
349425cf1a30Sjl 
349525cf1a30Sjl 	/* calculate source and target base pa */
349625cf1a30Sjl 
349725cf1a30Sjl 	s_copybasepa = s_mem->slice_base;
349825cf1a30Sjl 	t_copybasepa = t_mem->slice_base;
349925cf1a30Sjl 
350025cf1a30Sjl 	/* adjust copy memlist addresses to be relative to copy base pa */
350125cf1a30Sjl 	x_ml = c_ml;
350225cf1a30Sjl 	while (x_ml != NULL) {
350325cf1a30Sjl 		x_ml->address -= s_copybasepa;
350425cf1a30Sjl 		x_ml = x_ml->next;
350525cf1a30Sjl 	}
350625cf1a30Sjl 
350725cf1a30Sjl 	/*
350825cf1a30Sjl 	 * bp will be page aligned, since we're calling
350925cf1a30Sjl 	 * kmem_zalloc() with an exact multiple of PAGESIZE.
351025cf1a30Sjl 	 */
351125cf1a30Sjl 
351268ac2337Sjl 	prog_kmem = (drmach_copy_rename_program_t *)kmem_zalloc(
3513*e98fafb9Sjl 	    DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, KM_SLEEP);
351468ac2337Sjl 
351568ac2337Sjl 	prog_kmem->prog = prog_kmem;
351668ac2337Sjl 
351768ac2337Sjl 	/*
351868ac2337Sjl 	 * To avoid MTLB hit, we allocate a new VM space and remap
351968ac2337Sjl 	 * the kmem_alloc buffer to that address.  This solves
352068ac2337Sjl 	 * 2 problems we found:
352168ac2337Sjl 	 * - the kmem_alloc buffer can be just a chunk inside
352268ac2337Sjl 	 *   a much larger, e.g. 4MB buffer and MTLB will occur
352368ac2337Sjl 	 *   if there are both a 4MB and a 8K TLB mapping to
352468ac2337Sjl 	 *   the same VA range.
352568ac2337Sjl 	 * - the kmem mapping got dropped into the TLB by other
352668ac2337Sjl 	 *   strands, unintentionally.
352768ac2337Sjl 	 * Note that the pointers like data, critical, memlist_buffer,
352868ac2337Sjl 	 * and stat inside the copy rename structure are mapped to this
352968ac2337Sjl 	 * alternate VM space so we must make sure we lock the TLB mapping
353068ac2337Sjl 	 * whenever we access data pointed to by these pointers.
353168ac2337Sjl 	 */
353268ac2337Sjl 
353368ac2337Sjl 	prog = prog_kmem->locked_prog = vmem_alloc(heap_arena,
3534*e98fafb9Sjl 	    DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, VM_SLEEP);
353568ac2337Sjl 	wp = bp = (caddr_t)prog;
353668ac2337Sjl 
353768ac2337Sjl 	/* Now remap prog_kmem to prog */
353868ac2337Sjl 	drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
353968ac2337Sjl 
354068ac2337Sjl 	/* All pointers in prog are based on the alternate mapping */
354125cf1a30Sjl 	prog->data = (drmach_copy_rename_data_t *)roundup(((uint64_t)prog +
3542*e98fafb9Sjl 	    sizeof (drmach_copy_rename_program_t)), sizeof (void *));
354325cf1a30Sjl 
354425cf1a30Sjl 	ASSERT(((uint64_t)prog->data + sizeof (drmach_copy_rename_data_t))
3545*e98fafb9Sjl 	    <= ((uint64_t)prog + PAGESIZE));
354625cf1a30Sjl 
354725cf1a30Sjl 	prog->critical = (drmach_copy_rename_critical_t *)
3548*e98fafb9Sjl 	    (wp + DRMACH_FMEM_CRITICAL_PAGE * PAGESIZE);
354925cf1a30Sjl 
3550*e98fafb9Sjl 	prog->memlist_buffer = (caddr_t)(wp + DRMACH_FMEM_MLIST_PAGE *
3551*e98fafb9Sjl 	    PAGESIZE);
355225cf1a30Sjl 
3553*e98fafb9Sjl 	prog->stat = (drmach_cr_stat_t *)(wp + DRMACH_FMEM_STAT_PAGE *
3554*e98fafb9Sjl 	    PAGESIZE);
355525cf1a30Sjl 
355625cf1a30Sjl 	/* LINTED */
3557*e98fafb9Sjl 	ASSERT(sizeof (drmach_cr_stat_t) <= ((DRMACH_FMEM_LOCKED_PAGES -
3558*e98fafb9Sjl 	    DRMACH_FMEM_STAT_PAGE) * PAGESIZE));
355925cf1a30Sjl 
356025cf1a30Sjl 	prog->critical->scf_reg_base = (uint64_t)-1;
356125cf1a30Sjl 	prog->critical->scf_td[0] = (s_bd & 0xff);
356225cf1a30Sjl 	prog->critical->scf_td[1] = (t_bd & 0xff);
356325cf1a30Sjl 	for (i = 2; i < 15; i++) {
356425cf1a30Sjl 		prog->critical->scf_td[i]   = 0;
356525cf1a30Sjl 	}
356625cf1a30Sjl 	prog->critical->scf_td[15] = ((0xaa + s_bd + t_bd) & 0xff);
356725cf1a30Sjl 
356825cf1a30Sjl 	bp = (caddr_t)prog->critical;
356925cf1a30Sjl 	len = sizeof (drmach_copy_rename_critical_t);
357025cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
357125cf1a30Sjl 
357225cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_copy_rename_end -
3573*e98fafb9Sjl 	    (ulong_t)drmach_copy_rename_prog__relocatable);
357425cf1a30Sjl 
357525cf1a30Sjl 	/*
357625cf1a30Sjl 	 * We always leave 1K nop's to prevent the processor from
357725cf1a30Sjl 	 * speculative execution that causes memory access
357825cf1a30Sjl 	 */
357925cf1a30Sjl 	wp = wp + len + 1024;
358025cf1a30Sjl 
358125cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_fmem_exec_script_end -
3582*e98fafb9Sjl 	    (ulong_t)drmach_fmem_exec_script);
358325cf1a30Sjl 	/* this is the entry point of the loop script */
358425cf1a30Sjl 	wp = wp + len + 1024;
358525cf1a30Sjl 
358625cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_fmem_exec_script -
3587*e98fafb9Sjl 	    (ulong_t)drmach_fmem_loop_script);
358825cf1a30Sjl 	wp = wp + len + 1024;
358925cf1a30Sjl 
359025cf1a30Sjl 	/* now we make sure there is 1K extra */
359125cf1a30Sjl 
359225cf1a30Sjl 	if ((wp - bp) > PAGESIZE) {
35938eafe49bSbm 		err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
359468ac2337Sjl 		goto out;
359525cf1a30Sjl 	}
359625cf1a30Sjl 
359725cf1a30Sjl 	bp = (caddr_t)prog->critical;
359825cf1a30Sjl 	len = sizeof (drmach_copy_rename_critical_t);
359925cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
360025cf1a30Sjl 
360125cf1a30Sjl 	prog->critical->run = (int (*)())(wp);
360225cf1a30Sjl 	len = (uint_t)((ulong_t)drmach_copy_rename_end -
3603*e98fafb9Sjl 	    (ulong_t)drmach_copy_rename_prog__relocatable);
360425cf1a30Sjl 
360525cf1a30Sjl 	bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len);
360625cf1a30Sjl 
360725cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
360825cf1a30Sjl 
360925cf1a30Sjl 	prog->critical->fmem = (int (*)())(wp);
361025cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_exec_script_end -
3611*e98fafb9Sjl 	    (ulong_t)drmach_fmem_exec_script);
361225cf1a30Sjl 	bcopy((caddr_t)drmach_fmem_exec_script, wp, len);
361325cf1a30Sjl 
361425cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_exec_script_end -
3615*e98fafb9Sjl 	    (ulong_t)drmach_fmem_exec_script);
361625cf1a30Sjl 	wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
361725cf1a30Sjl 
361825cf1a30Sjl 	prog->critical->loop = (int (*)())(wp);
361925cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_exec_script -
3620*e98fafb9Sjl 	    (ulong_t)drmach_fmem_loop_script);
362125cf1a30Sjl 	bcopy((caddr_t)drmach_fmem_loop_script, (void *)wp, len);
362225cf1a30Sjl 	len = (int)((ulong_t)drmach_fmem_loop_script_rtn-
3623*e98fafb9Sjl 	    (ulong_t)drmach_fmem_loop_script);
362425cf1a30Sjl 	prog->critical->loop_rtn = (void (*)()) (wp+len);
362525cf1a30Sjl 
3626b307f191Sbm 	prog->data->fmem_status.error = ESBD_NOERROR;
3627b307f191Sbm 
362825cf1a30Sjl 	/* now we are committed, call SCF, soft suspend mac patrol */
362925cf1a30Sjl 	if ((*scf_fmem_start)(s_bd, t_bd)) {
36308eafe49bSbm 		err = drerr_new(1, EOPL_SCF_FMEM_START, NULL);
363168ac2337Sjl 		goto out;
363225cf1a30Sjl 	}
363325cf1a30Sjl 	prog->data->scf_fmem_end = scf_fmem_end;
363425cf1a30Sjl 	prog->data->scf_fmem_cancel = scf_fmem_cancel;
363568ac2337Sjl 	prog->data->scf_get_base_addr = scf_get_base_addr;
363625cf1a30Sjl 	prog->data->fmem_status.op |= OPL_FMEM_SCF_START;
3637b307f191Sbm 
363825cf1a30Sjl 	/* soft suspend mac patrol */
363925cf1a30Sjl 	(*mc_suspend)();
364025cf1a30Sjl 	prog->data->fmem_status.op |= OPL_FMEM_MC_SUSPEND;
364125cf1a30Sjl 	prog->data->mc_resume = mc_resume;
364225cf1a30Sjl 
364325cf1a30Sjl 	prog->critical->inst_loop_ret  =
3644*e98fafb9Sjl 	    *(uint64_t *)(prog->critical->loop_rtn);
364525cf1a30Sjl 
364625cf1a30Sjl 	/*
364725cf1a30Sjl 	 * 0x30800000 is op code "ba,a	+0"
364825cf1a30Sjl 	 */
364925cf1a30Sjl 
365025cf1a30Sjl 	*(uint_t *)(prog->critical->loop_rtn) = (uint_t)(0x30800000);
365125cf1a30Sjl 
365225cf1a30Sjl 	/*
365325cf1a30Sjl 	 * set the value of SCF FMEM TIMEOUT
365425cf1a30Sjl 	 */
365525cf1a30Sjl 	prog->critical->delay = fmem_timeout * system_clock_freq;
365625cf1a30Sjl 
365725cf1a30Sjl 	prog->data->s_mem = (drmachid_t)s_mem;
365825cf1a30Sjl 	prog->data->t_mem = (drmachid_t)t_mem;
365925cf1a30Sjl 
366025cf1a30Sjl 	cpuid = CPU->cpu_id;
366125cf1a30Sjl 	prog->data->cpuid = cpuid;
366225cf1a30Sjl 	prog->data->cpu_ready_set = cpu_ready_set;
366325cf1a30Sjl 	prog->data->cpu_slave_set = cpu_ready_set;
366425cf1a30Sjl 	prog->data->slowest_cpuid = (processorid_t)-1;
366525cf1a30Sjl 	prog->data->copy_wait_time = 0;
366625cf1a30Sjl 	CPUSET_DEL(prog->data->cpu_slave_set, cpuid);
366725cf1a30Sjl 
366825cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
366925cf1a30Sjl 		prog->data->cpu_ml[i] = NULL;
367025cf1a30Sjl 	}
367125cf1a30Sjl 
367225cf1a30Sjl 	active_cpus = 0;
367325cf1a30Sjl 	if (drmach_disable_mcopy) {
367425cf1a30Sjl 		active_cpus = 1;
367525cf1a30Sjl 		CPUSET_ADD(prog->data->cpu_copy_set, cpuid);
367625cf1a30Sjl 	} else {
367725cf1a30Sjl 		for (i = 0; i < NCPU; i++) {
367825cf1a30Sjl 			if (CPU_IN_SET(cpu_ready_set, i) &&
3679*e98fafb9Sjl 			    CPU_ACTIVE(cpu[i])) {
368025cf1a30Sjl 				CPUSET_ADD(prog->data->cpu_copy_set, i);
368125cf1a30Sjl 				active_cpus++;
368225cf1a30Sjl 			}
368325cf1a30Sjl 		}
368425cf1a30Sjl 	}
368525cf1a30Sjl 
368625cf1a30Sjl 	drmach_setup_memlist(prog);
368725cf1a30Sjl 
368825cf1a30Sjl 	x_ml = c_ml;
368925cf1a30Sjl 	sz = 0;
369025cf1a30Sjl 	while (x_ml != NULL) {
369125cf1a30Sjl 		sz += x_ml->size;
369225cf1a30Sjl 		x_ml = x_ml->next;
369325cf1a30Sjl 	}
369425cf1a30Sjl 
369525cf1a30Sjl 	copy_sz = sz/active_cpus;
369625cf1a30Sjl 	copy_sz = roundup(copy_sz, MMU_PAGESIZE4M);
369725cf1a30Sjl 
369825cf1a30Sjl 	while (sz > copy_sz*active_cpus) {
369925cf1a30Sjl 		copy_sz += MMU_PAGESIZE4M;
370025cf1a30Sjl 	}
370125cf1a30Sjl 
370225cf1a30Sjl 	prog->data->stick_freq = system_clock_freq;
370325cf1a30Sjl 	prog->data->copy_delay = ((copy_sz / min_copy_size_per_sec) + 2) *
3704*e98fafb9Sjl 	    system_clock_freq;
370525cf1a30Sjl 
370625cf1a30Sjl 	x_ml = c_ml;
370725cf1a30Sjl 	c_addr = x_ml->address;
370825cf1a30Sjl 	c_size = x_ml->size;
370925cf1a30Sjl 
371025cf1a30Sjl 	for (i = 0; i < NCPU; i++) {
371125cf1a30Sjl 		prog->stat->nbytes[i] = 0;
371225cf1a30Sjl 		if (!CPU_IN_SET(prog->data->cpu_copy_set, i)) {
371325cf1a30Sjl 			continue;
371425cf1a30Sjl 		}
371525cf1a30Sjl 		sz = copy_sz;
371625cf1a30Sjl 
371725cf1a30Sjl 		while (sz) {
371825cf1a30Sjl 			if (c_size > sz) {
371925cf1a30Sjl 				prog->data->cpu_ml[i] =
3720*e98fafb9Sjl 				    drmach_memlist_add_span(prog,
3721*e98fafb9Sjl 				    prog->data->cpu_ml[i], c_addr, sz);
372225cf1a30Sjl 				c_addr += sz;
372325cf1a30Sjl 				c_size -= sz;
372425cf1a30Sjl 				break;
372525cf1a30Sjl 			} else {
372625cf1a30Sjl 				sz -= c_size;
3727*e98fafb9Sjl 				prog->data->cpu_ml[i] =
3728*e98fafb9Sjl 				    drmach_memlist_add_span(prog,
3729*e98fafb9Sjl 				    prog->data->cpu_ml[i], c_addr, c_size);
373025cf1a30Sjl 				x_ml = x_ml->next;
373125cf1a30Sjl 				if (x_ml != NULL) {
373225cf1a30Sjl 					c_addr = x_ml->address;
373325cf1a30Sjl 					c_size = x_ml->size;
373425cf1a30Sjl 				} else {
373525cf1a30Sjl 					goto end;
373625cf1a30Sjl 				}
373725cf1a30Sjl 			}
373825cf1a30Sjl 		}
373925cf1a30Sjl 	}
374025cf1a30Sjl end:
374125cf1a30Sjl 	prog->data->s_copybasepa = s_copybasepa;
374225cf1a30Sjl 	prog->data->t_copybasepa = t_copybasepa;
374325cf1a30Sjl 	prog->data->c_ml = c_ml;
374468ac2337Sjl 	*pgm_id = prog_kmem;
374525cf1a30Sjl 
374668ac2337Sjl 	/* Unmap the alternate space.  It will have to be remapped again */
374768ac2337Sjl 	drmach_unlock_critical((caddr_t)prog);
374825cf1a30Sjl 	return (NULL);
374968ac2337Sjl out:
375068ac2337Sjl 	if (prog != NULL) {
375168ac2337Sjl 		drmach_unlock_critical((caddr_t)prog);
3752*e98fafb9Sjl 		vmem_free(heap_arena, prog, DRMACH_FMEM_LOCKED_PAGES *
3753*e98fafb9Sjl 		    PAGESIZE);
375468ac2337Sjl 	}
375568ac2337Sjl 	if (prog_kmem != NULL) {
375668ac2337Sjl 		kmem_free(prog_kmem, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
375768ac2337Sjl 	}
375868ac2337Sjl 	return (err);
375925cf1a30Sjl }
376025cf1a30Sjl 
376125cf1a30Sjl sbd_error_t *
376225cf1a30Sjl drmach_copy_rename_fini(drmachid_t id)
376325cf1a30Sjl {
376425cf1a30Sjl 	drmach_copy_rename_program_t	*prog = id;
376525cf1a30Sjl 	sbd_error_t			*err = NULL;
376625cf1a30Sjl 	int				rv;
3767b307f191Sbm 	uint_t				fmem_error;
376825cf1a30Sjl 
376925cf1a30Sjl 	/*
377025cf1a30Sjl 	 * Note that we have to delay calling SCF to find out the
377125cf1a30Sjl 	 * status of the FMEM operation here because SCF cannot
377225cf1a30Sjl 	 * respond while it is suspended.
377325cf1a30Sjl 	 * This create a small window when we are sure about the
377425cf1a30Sjl 	 * base address of the system board.
377525cf1a30Sjl 	 * If there is any call to mc-opl to get memory unum,
377625cf1a30Sjl 	 * mc-opl will return UNKNOWN as the unum.
377725cf1a30Sjl 	 */
377825cf1a30Sjl 
377968ac2337Sjl 	/*
378068ac2337Sjl 	 * we have to remap again because all the pointer like data,
378168ac2337Sjl 	 * critical in prog are based on the alternate vmem space.
378268ac2337Sjl 	 */
378368ac2337Sjl 	(void) drmach_lock_critical((caddr_t)prog, (caddr_t)prog->locked_prog);
378468ac2337Sjl 
378525cf1a30Sjl 	if (prog->data->c_ml != NULL)
378625cf1a30Sjl 		memlist_delete(prog->data->c_ml);
378725cf1a30Sjl 
378825cf1a30Sjl 	if ((prog->data->fmem_status.op &
3789*e98fafb9Sjl 	    (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) !=
3790*e98fafb9Sjl 	    (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) {
3791*e98fafb9Sjl 		cmn_err(CE_PANIC, "drmach_copy_rename_fini: invalid op "
3792*e98fafb9Sjl 		    "code %x\n", prog->data->fmem_status.op);
379325cf1a30Sjl 	}
379425cf1a30Sjl 
3795b307f191Sbm 	fmem_error = prog->data->fmem_status.error;
3796b307f191Sbm 	if (fmem_error != ESBD_NOERROR) {
3797b307f191Sbm 		err = drerr_new(1, fmem_error, NULL);
3798b307f191Sbm 	}
3799b307f191Sbm 
380025cf1a30Sjl 	/* possible ops are SCF_START, MC_SUSPEND */
380125cf1a30Sjl 	if (prog->critical->fmem_issued) {
3802b307f191Sbm 		if (fmem_error != ESBD_NOERROR) {
3803*e98fafb9Sjl 			cmn_err(CE_PANIC, "Irrecoverable FMEM error %d\n",
3804*e98fafb9Sjl 			    fmem_error);
3805b307f191Sbm 		}
380625cf1a30Sjl 		rv = (*prog->data->scf_fmem_end)();
380725cf1a30Sjl 		if (rv) {
380868ac2337Sjl 			cmn_err(CE_PANIC, "scf_fmem_end() failed rv=%d", rv);
380925cf1a30Sjl 		}
381025cf1a30Sjl 		/*
381125cf1a30Sjl 		 * If we get here, rename is successful.
381225cf1a30Sjl 		 * Do all the copy rename post processing.
381325cf1a30Sjl 		 */
381425cf1a30Sjl 		drmach_swap_pa((drmach_mem_t *)prog->data->s_mem,
3815*e98fafb9Sjl 		    (drmach_mem_t *)prog->data->t_mem);
381625cf1a30Sjl 	} else {
381725cf1a30Sjl 		rv = (*prog->data->scf_fmem_cancel)();
381825cf1a30Sjl 		if (rv) {
3819*e98fafb9Sjl 			cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x",
3820*e98fafb9Sjl 			    rv);
3821*e98fafb9Sjl 			if (!err) {
3822*e98fafb9Sjl 				err = drerr_new(1, EOPL_SCF_FMEM_CANCEL,
3823*e98fafb9Sjl 				    "scf_fmem_cancel() failed. rv = 0x%x", rv);
3824*e98fafb9Sjl 			}
382525cf1a30Sjl 		}
382625cf1a30Sjl 	}
382725cf1a30Sjl 	/* soft resume mac patrol */
382825cf1a30Sjl 	(*prog->data->mc_resume)();
382925cf1a30Sjl 
383068ac2337Sjl 	drmach_unlock_critical((caddr_t)prog->locked_prog);
383168ac2337Sjl 
383268ac2337Sjl 	vmem_free(heap_arena, prog->locked_prog,
3833*e98fafb9Sjl 	    DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
383425cf1a30Sjl 	kmem_free(prog, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
383525cf1a30Sjl 	return (err);
383625cf1a30Sjl }
383725cf1a30Sjl 
383825cf1a30Sjl /*ARGSUSED*/
383925cf1a30Sjl static void
384025cf1a30Sjl drmach_copy_rename_slave(struct regs *rp, drmachid_t id)
384125cf1a30Sjl {
384268ac2337Sjl 	drmach_copy_rename_program_t	*prog =
3843*e98fafb9Sjl 	    (drmach_copy_rename_program_t *)id;
384425cf1a30Sjl 	register int			cpuid;
384525cf1a30Sjl 	extern void			drmach_flush();
384625cf1a30Sjl 	extern void			membar_sync_il();
384725cf1a30Sjl 	extern void			drmach_flush_icache();
384825cf1a30Sjl 	on_trap_data_t			otd;
384925cf1a30Sjl 
385025cf1a30Sjl 	cpuid = CPU->cpu_id;
385125cf1a30Sjl 
385225cf1a30Sjl 	if (on_trap(&otd, OT_DATA_EC)) {
385325cf1a30Sjl 		no_trap();
3854b307f191Sbm 		prog->data->error[cpuid] = EOPL_FMEM_COPY_ERROR;
385525cf1a30Sjl 		prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
385668ac2337Sjl 		drmach_flush_icache();
385768ac2337Sjl 		membar_sync_il();
385825cf1a30Sjl 		return;
385925cf1a30Sjl 	}
386025cf1a30Sjl 
386125cf1a30Sjl 
386225cf1a30Sjl 	/*
386325cf1a30Sjl 	 * jmp drmach_copy_rename_prog().
386425cf1a30Sjl 	 */
386525cf1a30Sjl 
386625cf1a30Sjl 	drmach_flush(prog->critical, PAGESIZE);
386725cf1a30Sjl 	(void) prog->critical->run(prog, cpuid);
386825cf1a30Sjl 	drmach_flush_icache();
386925cf1a30Sjl 
387025cf1a30Sjl 	no_trap();
387125cf1a30Sjl 
387225cf1a30Sjl 	prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
387368ac2337Sjl 
387425cf1a30Sjl 	membar_sync_il();
387525cf1a30Sjl }
387625cf1a30Sjl 
387725cf1a30Sjl static void
387825cf1a30Sjl drmach_swap_pa(drmach_mem_t *s_mem, drmach_mem_t *t_mem)
387925cf1a30Sjl {
388025cf1a30Sjl 	uint64_t s_base, t_base;
388125cf1a30Sjl 	drmach_board_t *s_board, *t_board;
388225cf1a30Sjl 	struct memlist *ml;
388325cf1a30Sjl 
388425cf1a30Sjl 	s_board = s_mem->dev.bp;
388525cf1a30Sjl 	t_board = t_mem->dev.bp;
388625cf1a30Sjl 	if (s_board == NULL || t_board == NULL) {
388725cf1a30Sjl 		cmn_err(CE_PANIC, "Cannot locate source or target board\n");
388825cf1a30Sjl 		return;
388925cf1a30Sjl 	}
389025cf1a30Sjl 	s_base = s_mem->slice_base;
389125cf1a30Sjl 	t_base = t_mem->slice_base;
389225cf1a30Sjl 
389325cf1a30Sjl 	s_mem->slice_base = t_base;
389425cf1a30Sjl 	s_mem->base_pa = (s_mem->base_pa - s_base) + t_base;
389525cf1a30Sjl 
389625cf1a30Sjl 	for (ml = s_mem->memlist; ml; ml = ml->next) {
389725cf1a30Sjl 		ml->address = ml->address - s_base + t_base;
389825cf1a30Sjl 	}
389925cf1a30Sjl 
390025cf1a30Sjl 	t_mem->slice_base = s_base;
390125cf1a30Sjl 	t_mem->base_pa = (t_mem->base_pa - t_base) + s_base;
390225cf1a30Sjl 
390325cf1a30Sjl 	for (ml = t_mem->memlist; ml; ml = ml->next) {
390425cf1a30Sjl 		ml->address = ml->address - t_base + s_base;
390525cf1a30Sjl 	}
390625cf1a30Sjl 
390725cf1a30Sjl 	/*
390825cf1a30Sjl 	 * IKP has to update the sb-mem-ranges for mac patrol driver
390925cf1a30Sjl 	 * when it resumes, it will re-read the sb-mem-range property
391025cf1a30Sjl 	 * to get the new base address
391125cf1a30Sjl 	 */
391225cf1a30Sjl 	if (oplcfg_pa_swap(s_board->bnum, t_board->bnum) != 0)
391325cf1a30Sjl 		cmn_err(CE_PANIC, "Could not update device nodes\n");
391425cf1a30Sjl }
391525cf1a30Sjl 
391625cf1a30Sjl void
391725cf1a30Sjl drmach_copy_rename(drmachid_t id)
391825cf1a30Sjl {
391968ac2337Sjl 	drmach_copy_rename_program_t	*prog_kmem = id;
392068ac2337Sjl 	drmach_copy_rename_program_t	*prog;
392125cf1a30Sjl 	cpuset_t	cpuset;
392225cf1a30Sjl 	int		cpuid;
392325cf1a30Sjl 	uint64_t	inst;
392425cf1a30Sjl 	register int	rtn;
392525cf1a30Sjl 	extern int	in_sync;
392625cf1a30Sjl 	int		old_in_sync;
392725cf1a30Sjl 	extern void	drmach_sys_trap();
392825cf1a30Sjl 	extern void	drmach_flush();
392925cf1a30Sjl 	extern void	drmach_flush_icache();
393025cf1a30Sjl 	extern uint64_t	patch_inst(uint64_t *, uint64_t);
393125cf1a30Sjl 	on_trap_data_t	otd;
393225cf1a30Sjl 
39338eafe49bSbm 
393468ac2337Sjl 	prog = prog_kmem->locked_prog;
393568ac2337Sjl 
39368eafe49bSbm 
393768ac2337Sjl 	/*
393868ac2337Sjl 	 * We must immediately drop in the TLB because all pointers
393968ac2337Sjl 	 * are based on the alternate vmem space.
394068ac2337Sjl 	 */
394168ac2337Sjl 
394268ac2337Sjl 	(void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
394368ac2337Sjl 
394468ac2337Sjl 	/*
394568ac2337Sjl 	 * we call scf to get the base address here becuase if scf
394668ac2337Sjl 	 * has not been suspended yet, the active path can be changing and
394768ac2337Sjl 	 * sometimes it is not even mapped.  We call the interface when
394868ac2337Sjl 	 * the OS has been quiesced.
394968ac2337Sjl 	 */
395068ac2337Sjl 	prog->critical->scf_reg_base = (*prog->data->scf_get_base_addr)();
395168ac2337Sjl 
395268ac2337Sjl 	if (prog->critical->scf_reg_base == (uint64_t)-1 ||
3953*e98fafb9Sjl 	    prog->critical->scf_reg_base == NULL) {
3954b307f191Sbm 		prog->data->fmem_status.error = EOPL_FMEM_SCF_ERR;
395568ac2337Sjl 		drmach_unlock_critical((caddr_t)prog);
395625cf1a30Sjl 		return;
395725cf1a30Sjl 	}
395825cf1a30Sjl 
395925cf1a30Sjl 	cpuset = prog->data->cpu_ready_set;
396025cf1a30Sjl 
396125cf1a30Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
396225cf1a30Sjl 		if (CPU_IN_SET(cpuset, cpuid)) {
396325cf1a30Sjl 			prog->critical->stat[cpuid] = FMEM_LOOP_START;
3964b307f191Sbm 			prog->data->error[cpuid] = ESBD_NOERROR;
396525cf1a30Sjl 		}
396625cf1a30Sjl 	}
396725cf1a30Sjl 
396825cf1a30Sjl 	old_in_sync = in_sync;
396925cf1a30Sjl 	in_sync = 1;
397025cf1a30Sjl 	cpuid = CPU->cpu_id;
397125cf1a30Sjl 
397225cf1a30Sjl 	CPUSET_DEL(cpuset, cpuid);
397325cf1a30Sjl 
397468ac2337Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
397568ac2337Sjl 		if (CPU_IN_SET(cpuset, cpuid)) {
397668ac2337Sjl 			xc_one(cpuid, (xcfunc_t *)drmach_lock_critical,
3977*e98fafb9Sjl 			    (uint64_t)prog_kmem, (uint64_t)prog);
397868ac2337Sjl 		}
397968ac2337Sjl 	}
398068ac2337Sjl 
398168ac2337Sjl 	cpuid = CPU->cpu_id;
398225cf1a30Sjl 
398325cf1a30Sjl 	xt_some(cpuset, (xcfunc_t *)drmach_sys_trap,
3984*e98fafb9Sjl 	    (uint64_t)drmach_copy_rename_slave, (uint64_t)prog);
398525cf1a30Sjl 	xt_sync(cpuset);
398625cf1a30Sjl 
398725cf1a30Sjl 	if (on_trap(&otd, OT_DATA_EC)) {
3988b307f191Sbm 		rtn = EOPL_FMEM_COPY_ERROR;
398968ac2337Sjl 		drmach_flush_icache();
399025cf1a30Sjl 		goto done;
399125cf1a30Sjl 	}
399225cf1a30Sjl 
399325cf1a30Sjl 	/*
399425cf1a30Sjl 	 * jmp drmach_copy_rename_prog().
399525cf1a30Sjl 	 */
39968eafe49bSbm 
399725cf1a30Sjl 	drmach_flush(prog->critical, PAGESIZE);
399825cf1a30Sjl 	rtn = prog->critical->run(prog, cpuid);
3999*e98fafb9Sjl 
400025cf1a30Sjl 	drmach_flush_icache();
400125cf1a30Sjl 
400225cf1a30Sjl 
400325cf1a30Sjl done:
400425cf1a30Sjl 	no_trap();
4005b307f191Sbm 	if (rtn == EOPL_FMEM_HW_ERROR) {
400625cf1a30Sjl 		kpreempt_enable();
4007*e98fafb9Sjl 		prom_panic("URGENT_ERROR_TRAP is detected during FMEM.\n");
400825cf1a30Sjl 	}
400925cf1a30Sjl 
401025cf1a30Sjl 	/*
401125cf1a30Sjl 	 * In normal case, all slave CPU's are still spinning in
401225cf1a30Sjl 	 * the assembly code.  The master has to patch the instruction
401325cf1a30Sjl 	 * to get them out.
401425cf1a30Sjl 	 * In error case, e.g. COPY_ERROR, some slave CPU's might
401525cf1a30Sjl 	 * have aborted and already returned and sset LOOP_EXIT status.
401625cf1a30Sjl 	 * Some CPU might still be copying.
401725cf1a30Sjl 	 * In any case, some delay is necessary to give them
401825cf1a30Sjl 	 * enough time to set the LOOP_EXIT status.
401925cf1a30Sjl 	 */
402025cf1a30Sjl 
402125cf1a30Sjl 	for (;;) {
402225cf1a30Sjl 		inst = patch_inst((uint64_t *)prog->critical->loop_rtn,
4023*e98fafb9Sjl 		    prog->critical->inst_loop_ret);
402425cf1a30Sjl 		if (prog->critical->inst_loop_ret == inst) {
402525cf1a30Sjl 			break;
402625cf1a30Sjl 		}
402725cf1a30Sjl 	}
402825cf1a30Sjl 
402925cf1a30Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
403025cf1a30Sjl 		uint64_t	last, now;
403125cf1a30Sjl 		if (!CPU_IN_SET(cpuset, cpuid)) {
403225cf1a30Sjl 			continue;
403325cf1a30Sjl 		}
403425cf1a30Sjl 		last = prog->stat->nbytes[cpuid];
403525cf1a30Sjl 		/*
403625cf1a30Sjl 		 * Wait for all CPU to exit.
403725cf1a30Sjl 		 * However we do not want an infinite loop
403825cf1a30Sjl 		 * so we detect hangup situation here.
403925cf1a30Sjl 		 * If the slave CPU is still copying data,
404025cf1a30Sjl 		 * we will continue to wait.
404125cf1a30Sjl 		 * In error cases, the master has already set
404225cf1a30Sjl 		 * fmem_status.error to abort the copying.
404325cf1a30Sjl 		 * 1 m.s delay for them to abort copying and
404425cf1a30Sjl 		 * return to drmach_copy_rename_slave to set
404525cf1a30Sjl 		 * FMEM_LOOP_EXIT status should be enough.
404625cf1a30Sjl 		 */
404725cf1a30Sjl 		for (;;) {
404825cf1a30Sjl 			if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT)
404925cf1a30Sjl 				break;
405025cf1a30Sjl 			drmach_sleep_il();
405125cf1a30Sjl 			drv_usecwait(1000);
405225cf1a30Sjl 			now = prog->stat->nbytes[cpuid];
405325cf1a30Sjl 			if (now <= last) {
4054*e98fafb9Sjl 				drv_usecwait(1000);
4055*e98fafb9Sjl 				if (prog->critical->stat[cpuid] ==
4056*e98fafb9Sjl 				    FMEM_LOOP_EXIT)
4057*e98fafb9Sjl 					break;
4058*e98fafb9Sjl 				cmn_err(CE_PANIC, "CPU %d hang during Copy "
4059*e98fafb9Sjl 				    "Rename", cpuid);
406025cf1a30Sjl 			}
406125cf1a30Sjl 			last = now;
406225cf1a30Sjl 		}
4063b307f191Sbm 		if (prog->data->error[cpuid] == EOPL_FMEM_HW_ERROR) {
4064*e98fafb9Sjl 			prom_panic("URGENT_ERROR_TRAP is detected during "
4065*e98fafb9Sjl 			    "FMEM.\n");
406625cf1a30Sjl 		}
406725cf1a30Sjl 	}
406868ac2337Sjl 
406968ac2337Sjl 	/*
407068ac2337Sjl 	 * This must be done after all strands have exit.
407168ac2337Sjl 	 * Removing the TLB entry will affect both strands
407268ac2337Sjl 	 * in the same core.
407368ac2337Sjl 	 */
407468ac2337Sjl 
407568ac2337Sjl 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
407668ac2337Sjl 		if (CPU_IN_SET(cpuset, cpuid)) {
407768ac2337Sjl 			xc_one(cpuid, (xcfunc_t *)drmach_unlock_critical,
4078*e98fafb9Sjl 			    (uint64_t)prog, 0);
407968ac2337Sjl 		}
408068ac2337Sjl 	}
408125cf1a30Sjl 
408225cf1a30Sjl 	in_sync = old_in_sync;
408325cf1a30Sjl 
408468ac2337Sjl 	/*
408568ac2337Sjl 	 * we should unlock before the following lock to keep the kpreempt
408668ac2337Sjl 	 * count correct.
408768ac2337Sjl 	 */
408868ac2337Sjl 	(void) drmach_unlock_critical((caddr_t)prog);
408968ac2337Sjl 
409068ac2337Sjl 	/*
409168ac2337Sjl 	 * we must remap again.  TLB might have been removed in above xcall.
409268ac2337Sjl 	 */
409368ac2337Sjl 
409468ac2337Sjl 	(void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
409568ac2337Sjl 
4096b307f191Sbm 	if (prog->data->fmem_status.error == ESBD_NOERROR)
409725cf1a30Sjl 		prog->data->fmem_status.error = rtn;
409825cf1a30Sjl 
409925cf1a30Sjl 	if (prog->data->copy_wait_time > 0) {
410025cf1a30Sjl 		DRMACH_PR("Unexpected long wait time %ld seconds "
4101*e98fafb9Sjl 		    "during copy rename on CPU %d\n",
4102*e98fafb9Sjl 		    prog->data->copy_wait_time/prog->data->stick_freq,
4103*e98fafb9Sjl 		    prog->data->slowest_cpuid);
410425cf1a30Sjl 	}
410568ac2337Sjl 	drmach_unlock_critical((caddr_t)prog);
410625cf1a30Sjl }
4107