xref: /titanic_50/usr/src/uts/sun4u/starcat/io/drmach.c (revision ac7f5757903d7806e03e59f71c10eec36e0deade)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/note.h>
28 #include <sys/debug.h>
29 #include <sys/types.h>
30 #include <sys/varargs.h>
31 #include <sys/errno.h>
32 #include <sys/cred.h>
33 #include <sys/dditypes.h>
34 #include <sys/devops.h>
35 #include <sys/modctl.h>
36 #include <sys/poll.h>
37 #include <sys/conf.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/sunndi.h>
41 #include <sys/ndi_impldefs.h>
42 #include <sys/stat.h>
43 #include <sys/kmem.h>
44 #include <sys/vmem.h>
45 #include <sys/disp.h>
46 #include <sys/processor.h>
47 #include <sys/cheetahregs.h>
48 #include <sys/cpuvar.h>
49 #include <sys/mem_config.h>
50 #include <sys/ddi_impldefs.h>
51 #include <sys/systm.h>
52 #include <sys/machsystm.h>
53 #include <sys/autoconf.h>
54 #include <sys/cmn_err.h>
55 #include <sys/sysmacros.h>
56 #include <sys/x_call.h>
57 #include <sys/promif.h>
58 #include <sys/prom_plat.h>
59 #include <sys/membar.h>
60 #include <vm/seg_kmem.h>
61 #include <sys/mem_cage.h>
62 #include <sys/stack.h>
63 #include <sys/archsystm.h>
64 #include <vm/hat_sfmmu.h>
65 #include <sys/pte.h>
66 #include <sys/mmu.h>
67 #include <sys/cpu_module.h>
68 #include <sys/obpdefs.h>
69 #include <sys/mboxsc.h>
70 #include <sys/plat_ecc_dimm.h>
71 
72 #include <sys/hotplug/hpctrl.h>		/* XXX should be included by schpc.h */
73 #include <sys/schpc.h>
74 #include <sys/pci.h>
75 
76 #include <sys/starcat.h>
77 #include <sys/cpu_sgnblk_defs.h>
78 #include <sys/drmach.h>
79 #include <sys/dr_util.h>
80 #include <sys/dr_mbx.h>
81 #include <sys/sc_gptwocfg.h>
82 #include <sys/iosramreg.h>
83 #include <sys/iosramio.h>
84 #include <sys/iosramvar.h>
85 #include <sys/axq.h>
86 #include <sys/post/scat_dcd.h>
87 #include <sys/kobj.h>
88 #include <sys/taskq.h>
89 #include <sys/cmp.h>
90 #include <sys/sbd_ioctl.h>
91 
92 #include <sys/sysevent.h>
93 #include <sys/sysevent/dr.h>
94 #include <sys/sysevent/eventdefs.h>
95 
96 #include <sys/pci/pcisch.h>
97 #include <sys/pci/pci_regs.h>
98 
99 #include <sys/ontrap.h>
100 
101 /* defined in ../ml/drmach.il.cpp */
102 extern void		bcopy32_il(uint64_t, uint64_t);
103 extern void		flush_ecache_il(int64_t physaddr, int size, int linesz);
104 extern void		flush_dcache_il(void);
105 extern void		flush_icache_il(void);
106 extern void		flush_pcache_il(void);
107 
108 /* defined in ../ml/drmach_asm.s */
109 extern uint64_t		lddmcdecode(uint64_t physaddr);
110 extern uint64_t		lddsafconfig(void);
111 
112 /* XXX here until provided by sys/dman.h */
113 extern int man_dr_attach(dev_info_t *);
114 extern int man_dr_detach(dev_info_t *);
115 
116 #define	DRMACH_BNUM2EXP(bnum)		((bnum) >> 1)
117 #define	DRMACH_BNUM2SLOT(bnum)		((bnum) & 1)
118 #define	DRMACH_EXPSLOT2BNUM(exp, slot)	(((exp) << 1) + (slot))
119 
120 #define	DRMACH_SLICE_MASK		0x1Full
121 #define	DRMACH_SLICE_TO_PA(s)		(((s) & DRMACH_SLICE_MASK) << 37)
122 #define	DRMACH_PA_TO_SLICE(a)		(((a) >> 37) & DRMACH_SLICE_MASK)
123 
124 /*
125  * DRMACH_MEM_SLICE_SIZE and DRMACH_MEM_USABLE_SLICE_SIZE define the
126  * available address space and the usable address space for every slice.
127  * There must be a distinction between the available and usable do to a
128  * restriction imposed by CDC memory size.
129  */
130 
131 #define	DRMACH_MEM_SLICE_SIZE		(1ull << 37)	/* 128GB */
132 #define	DRMACH_MEM_USABLE_SLICE_SIZE	(1ull << 36)	/* 64GB */
133 
134 #define	DRMACH_MC_NBANKS		4
135 
136 #define	DRMACH_MC_ADDR(mp, bank)	((mp)->madr_pa + 16 + 8 * (bank))
137 #define	DRMACH_MC_ASI_ADDR(mp, bank)	(DRMACH_MC_ADDR(mp, bank) & 0xFF)
138 
139 #define	DRMACH_EMU_ACT_STATUS_OFFSET	0x50
140 #define	DRMACH_EMU_ACT_STATUS_ADDR(mp)	\
141 	((mp)->madr_pa + DRMACH_EMU_ACT_STATUS_OFFSET)
142 
143 /*
144  * The Cheetah's Safari Configuration Register and the Schizo's
145  * Safari Control/Status Register place the LPA base and bound fields in
146  * same bit locations with in their register word. This source code takes
147  * advantage of this by defining only one set of LPA encoding/decoding macros
148  * which are shared by various Cheetah and Schizo drmach routines.
149  */
150 #define	DRMACH_LPA_BASE_MASK		(0x3Full	<< 3)
151 #define	DRMACH_LPA_BND_MASK		(0x3Full	<< 9)
152 
153 #define	DRMACH_LPA_BASE_TO_PA(scr)	(((scr) & DRMACH_LPA_BASE_MASK) << 34)
154 #define	DRMACH_LPA_BND_TO_PA(scr)	(((scr) & DRMACH_LPA_BND_MASK) << 28)
155 #define	DRMACH_PA_TO_LPA_BASE(pa)	(((pa) >> 34) & DRMACH_LPA_BASE_MASK)
156 #define	DRMACH_PA_TO_LPA_BND(pa)	(((pa) >> 28) & DRMACH_LPA_BND_MASK)
157 
158 #define	DRMACH_L1_SET_LPA(b)		\
159 	(((b)->flags & DRMACH_NULL_PROC_LPA) == 0)
160 
161 #define	DRMACH_CPU_SRAM_ADDR    	0x7fff0900000ull
162 #define	DRMACH_CPU_SRAM_SIZE    	0x20000ull
163 
164 /*
165  * Name properties for frequently accessed device nodes.
166  */
167 #define	DRMACH_CPU_NAMEPROP		"cpu"
168 #define	DRMACH_CMP_NAMEPROP		"cmp"
169 #define	DRMACH_AXQ_NAMEPROP		"address-extender-queue"
170 #define	DRMACH_PCI_NAMEPROP		"pci"
171 
172 /*
173  * Maximum value of processor Safari Timeout Log (TOL) field of
174  * Safari Config reg (7 secs).
175  */
176 #define	DRMACH_SAF_TOL_MAX		7 * 1000000
177 
178 /*
179  * drmach_board_t flag definitions
180  */
181 #define	DRMACH_NULL_PROC_LPA		0x1
182 
183 typedef struct {
184 	uint32_t	reg_addr_hi;
185 	uint32_t	reg_addr_lo;
186 	uint32_t	reg_size_hi;
187 	uint32_t	reg_size_lo;
188 } drmach_reg_t;
189 
190 typedef struct {
191 	struct drmach_node	*node;
192 	void			*data;
193 } drmach_node_walk_args_t;
194 
195 typedef struct drmach_node {
196 	void		*here;
197 
198 	pnode_t		 (*get_dnode)(struct drmach_node *node);
199 	int		 (*walk)(struct drmach_node *node, void *data,
200 				int (*cb)(drmach_node_walk_args_t *args));
201 	dev_info_t	*(*n_getdip)(struct drmach_node *node);
202 	int		 (*n_getproplen)(struct drmach_node *node, char *name,
203 				int *len);
204 	int		 (*n_getprop)(struct drmach_node *node, char *name,
205 				void *buf, int len);
206 	int		 (*get_parent)(struct drmach_node *node,
207 				struct drmach_node *pnode);
208 } drmach_node_t;
209 
210 typedef struct {
211 	int		 min_index;
212 	int		 max_index;
213 	int		 arr_sz;
214 	drmachid_t	*arr;
215 } drmach_array_t;
216 
217 typedef struct {
218 	void		*isa;
219 
220 	void		 (*dispose)(drmachid_t);
221 	sbd_error_t	*(*release)(drmachid_t);
222 	sbd_error_t	*(*status)(drmachid_t, drmach_status_t *);
223 
224 	char		 name[MAXNAMELEN];
225 } drmach_common_t;
226 
227 struct drmach_board;
228 typedef struct drmach_board drmach_board_t;
229 
230 typedef struct {
231 	drmach_common_t	 cm;
232 	const char	*type;
233 	drmach_board_t	*bp;
234 	drmach_node_t	*node;
235 	int		 portid;
236 	int		 unum;
237 	int		 busy;
238 	int		 powered;
239 } drmach_device_t;
240 
241 typedef struct drmach_cpu {
242 	drmach_device_t	 dev;
243 	uint64_t	 scr_pa;
244 	processorid_t	 cpuid;
245 	int		 coreid;
246 } drmach_cpu_t;
247 
248 typedef struct drmach_mem {
249 	drmach_device_t	 dev;
250 	struct drmach_mem *next;
251 	uint64_t	 nbytes;
252 	uint64_t	 madr_pa;
253 } drmach_mem_t;
254 
255 typedef struct drmach_io {
256 	drmach_device_t	 dev;
257 	uint64_t	 scsr_pa; /* PA of Schizo Control/Status Register */
258 } drmach_io_t;
259 
260 struct drmach_board {
261 	drmach_common_t	 cm;
262 	int		 bnum;
263 	int		 assigned;
264 	int		 powered;
265 	int		 connected;
266 	int		 empty;
267 	int		 cond;
268 	uint_t		 cpu_impl;
269 	uint_t		 flags;
270 	drmach_node_t	*tree;
271 	drmach_array_t	*devices;
272 	drmach_mem_t	*mem;
273 	uint64_t	 stardrb_offset;
274 	char		 type[BD_TYPELEN];
275 };
276 
277 typedef struct {
278 	int		 flags;
279 	drmach_device_t	*dp;
280 	sbd_error_t	*err;
281 	dev_info_t	*fdip;
282 } drmach_config_args_t;
283 
284 typedef struct {
285 	drmach_board_t	*obj;
286 	int		 ndevs;
287 	void		*a;
288 	sbd_error_t	*(*found)(void *a, const char *, int, drmachid_t);
289 	sbd_error_t	*err;
290 } drmach_board_cb_data_t;
291 
292 typedef struct drmach_casmslot {
293 	int	valid;
294 	int	slice;
295 } drmach_casmslot_t;
296 
297 typedef enum {
298 	DRMACH_CR_OK,
299 	DRMACH_CR_MC_IDLE_ERR,
300 	DRMACH_CR_IOPAUSE_ERR,
301 	DRMACH_CR_ONTRAP_ERR
302 } drmach_cr_err_t;
303 
304 typedef struct {
305 	void		*isa;
306 	caddr_t		 data;
307 	drmach_mem_t	*s_mp;
308 	drmach_mem_t	*t_mp;
309 	struct memlist	*c_ml;
310 	uint64_t	 s_copybasepa;
311 	uint64_t	 t_copybasepa;
312 	drmach_cr_err_t	 ecode;
313 	void		*earg;
314 } drmach_copy_rename_t;
315 
316 /*
317  * The following global is read as a boolean value, non-zero is true.
318  * If zero, DR copy-rename and cpu poweron will not set the processor
319  * LPA settings (CBASE, CBND of Safari config register) to correspond
320  * to the current memory slice map. LPAs of processors present at boot
321  * will remain as programmed by POST. LPAs of processors on boards added
322  * by DR will remain NULL, as programmed by POST. This can be used to
323  * to override the per-board L1SSFLG_THIS_L1_NULL_PROC_LPA flag set by
324  * POST in the LDCD (and copied to the GDCD by SMS).
325  *
326  * drmach_reprogram_lpa and L1SSFLG_THIS_L1_NULL_PROC_LPA do not apply
327  * to Schizo device LPAs. These are always set by DR.
328  */
329 static int		 drmach_reprogram_lpa = 1;
330 
331 /*
332  * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
333  * can fail to receive an XIR. To workaround this issue until a hardware
334  * fix is implemented, we will exclude the selection of these CPUs.
335  * Setting this to 0 will allow their selection again.
336  */
337 static int		 drmach_iocage_exclude_jaguar_port_zero = 1;
338 
339 static int		 drmach_initialized;
340 static drmach_array_t	*drmach_boards;
341 
342 static int		 drmach_cpu_delay = 1000;
343 static int		 drmach_cpu_ntries = 50000;
344 
345 static uint32_t		 drmach_slice_table[AXQ_MAX_EXP];
346 static kmutex_t		 drmach_slice_table_lock;
347 
348 tte_t			 drmach_cpu_sram_tte[NCPU];
349 caddr_t			 drmach_cpu_sram_va;
350 
351 /*
352  * Setting to non-zero will enable delay before all disconnect ops.
353  */
354 static int		 drmach_unclaim_delay_all;
355 /*
356  * Default delay is slightly greater than the max processor Safari timeout.
357  * This delay is intended to ensure the outstanding Safari activity has
358  * retired on this board prior to a board disconnect.
359  */
360 static clock_t		 drmach_unclaim_usec_delay = DRMACH_SAF_TOL_MAX + 10;
361 
362 /*
363  * By default, DR of non-Panther procs is not allowed into a Panther
364  * domain with large page sizes enabled.  Setting this to 0 will remove
365  * the restriction.
366  */
367 static int		 drmach_large_page_restriction = 1;
368 
369 /*
370  * Used to pass updated LPA values to procs.
371  * Protocol is to clear the array before use.
372  */
373 volatile uchar_t	*drmach_xt_mb;
374 volatile uint64_t	 drmach_xt_ready;
375 static kmutex_t		 drmach_xt_mb_lock;
376 static int		 drmach_xt_mb_size;
377 
378 uint64_t		 drmach_bus_sync_list[18 * 4 * 4 + 1];
379 static kmutex_t		 drmach_bus_sync_lock;
380 
381 static sbd_error_t	*drmach_device_new(drmach_node_t *,
382 				drmach_board_t *, int, drmachid_t *);
383 static sbd_error_t	*drmach_cpu_new(drmach_device_t *, drmachid_t *);
384 static sbd_error_t	*drmach_mem_new(drmach_device_t *, drmachid_t *);
385 static sbd_error_t	*drmach_pci_new(drmach_device_t *, drmachid_t *);
386 static sbd_error_t	*drmach_io_new(drmach_device_t *, drmachid_t *);
387 
388 static dev_info_t	*drmach_node_ddi_get_dip(drmach_node_t *np);
389 static int		 drmach_node_ddi_get_prop(drmach_node_t *np,
390 				char *name, void *buf, int len);
391 static int		 drmach_node_ddi_get_proplen(drmach_node_t *np,
392 				char *name, int *len);
393 
394 static dev_info_t	*drmach_node_obp_get_dip(drmach_node_t *np);
395 static int		 drmach_node_obp_get_prop(drmach_node_t *np,
396 				char *name, void *buf, int len);
397 static int		 drmach_node_obp_get_proplen(drmach_node_t *np,
398 				char *name, int *len);
399 
400 static sbd_error_t	*drmach_mbox_trans(uint8_t msgtype, int bnum,
401 				caddr_t obufp, int olen,
402 				caddr_t ibufp, int ilen);
403 
404 sbd_error_t		*drmach_io_post_attach(drmachid_t id);
405 sbd_error_t		*drmach_io_post_release(drmachid_t id);
406 
407 static sbd_error_t	*drmach_iocage_setup(dr_testboard_req_t *,
408 				drmach_device_t **dpp, cpu_flag_t *oflags);
409 static int		drmach_iocage_cpu_return(drmach_device_t *dp,
410 				cpu_flag_t oflags);
411 static sbd_error_t	*drmach_iocage_mem_return(dr_testboard_reply_t *tbr);
412 void			drmach_iocage_mem_scrub(uint64_t nbytes);
413 
414 static sbd_error_t 	*drmach_i_status(drmachid_t id, drmach_status_t *stat);
415 
416 static void		drmach_slot1_lpa_set(drmach_board_t *bp);
417 
418 static void		drmach_cpu_read(uint64_t arg1, uint64_t arg2);
419 static int		drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr);
420 
421 static void		 drmach_bus_sync_list_update(void);
422 static void		 drmach_slice_table_update(drmach_board_t *, int);
423 static int		 drmach_portid2bnum(int);
424 
425 static void		drmach_msg_memslice_init(dr_memslice_t slice_arr[]);
426 static void		drmach_msg_memregs_init(dr_memregs_t regs_arr[]);
427 
428 static int		drmach_panther_boards(void);
429 
430 static int		drmach_name2type_idx(char *);
431 
432 #ifdef DEBUG
433 
434 #define	DRMACH_PR		if (drmach_debug) printf
435 #define	DRMACH_MEMLIST_DUMP	if (drmach_debug) MEMLIST_DUMP
436 int drmach_debug = 0;		 /* set to non-zero to enable debug messages */
437 #else
438 
439 #define	DRMACH_PR		_NOTE(CONSTANTCONDITION) if (0) printf
440 #define	DRMACH_MEMLIST_DUMP	_NOTE(CONSTANTCONDITION) if (0) MEMLIST_DUMP
441 #endif /* DEBUG */
442 
443 #define	DRMACH_OBJ(id)		((drmach_common_t *)id)
444 
445 #define	DRMACH_IS_BOARD_ID(id)	\
446 	((id != 0) &&		\
447 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
448 
449 #define	DRMACH_IS_CPU_ID(id)	\
450 	((id != 0) &&		\
451 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
452 
453 #define	DRMACH_IS_MEM_ID(id)	\
454 	((id != 0) &&		\
455 	(DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
456 
457 #define	DRMACH_IS_IO_ID(id)	\
458 	((id != 0) &&		\
459 	(DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
460 
461 #define	DRMACH_IS_DEVICE_ID(id)					\
462 	((id != 0) &&						\
463 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
464 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
465 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
466 
467 #define	DRMACH_IS_ID(id)					\
468 	((id != 0) &&						\
469 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new ||	\
470 	    DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
471 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
472 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
473 
474 #define	DRMACH_INTERNAL_ERROR() \
475 	drerr_new(1, ESTC_INTERNAL, drmach_ie_fmt, __LINE__)
476 static char		*drmach_ie_fmt = "drmach.c %d";
477 
478 static struct {
479 	const char	 *name;
480 	const char	 *type;
481 	sbd_error_t	 *(*new)(drmach_device_t *, drmachid_t *);
482 } drmach_name2type[] = {
483 	{"cmp",			    DRMACH_DEVTYPE_CMP,    NULL },
484 	{"cpu",			    DRMACH_DEVTYPE_CPU,    drmach_cpu_new },
485 	{"SUNW,UltraSPARC-III",	    DRMACH_DEVTYPE_CPU,    drmach_cpu_new },
486 	{"SUNW,UltraSPARC-III+",    DRMACH_DEVTYPE_CPU,    drmach_cpu_new },
487 	{"memory-controller",	    DRMACH_DEVTYPE_MEM,    drmach_mem_new },
488 	{"pci",			    DRMACH_DEVTYPE_PCI,    drmach_pci_new },
489 	{"SUNW,wci",		    DRMACH_DEVTYPE_WCI,    drmach_io_new  },
490 };
491 
492 /*
493  * drmach autoconfiguration data structures and interfaces
494  */
495 
496 extern struct mod_ops mod_miscops;
497 
498 static struct modlmisc modlmisc = {
499 	&mod_miscops,
500 	"Sun Fire 15000 DR"
501 };
502 
503 static struct modlinkage modlinkage = {
504 	MODREV_1,
505 	(void *)&modlmisc,
506 	NULL
507 };
508 
509 /*
510  * drmach_boards_rwlock is used to synchronize read/write
511  * access to drmach_boards array between status and board lookup
512  * as READERS, and assign, and unassign threads as WRITERS.
513  */
514 static krwlock_t	drmach_boards_rwlock;
515 
516 static kmutex_t		drmach_i_lock;
517 static kmutex_t		drmach_iocage_lock;
518 static kcondvar_t 	drmach_iocage_cv;
519 static int		drmach_iocage_is_busy = 0;
520 uint64_t		drmach_iocage_paddr;
521 static caddr_t		drmach_iocage_vaddr;
522 static int		drmach_iocage_size = 0;
523 static int		drmach_is_cheetah = -1;
524 
525 int
526 _init(void)
527 {
528 	int	err;
529 
530 	mutex_init(&drmach_i_lock, NULL, MUTEX_DRIVER, NULL);
531 	rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
532 	drmach_xt_mb_size = NCPU * sizeof (uchar_t);
533 	drmach_xt_mb = (uchar_t *)vmem_alloc(static_alloc_arena,
534 	    drmach_xt_mb_size, VM_SLEEP);
535 	bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
536 	if ((err = mod_install(&modlinkage)) != 0) {
537 		mutex_destroy(&drmach_i_lock);
538 		rw_destroy(&drmach_boards_rwlock);
539 		vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
540 		    drmach_xt_mb_size);
541 	}
542 
543 	return (err);
544 }
545 
546 int
547 _fini(void)
548 {
549 	static void	drmach_fini(void);
550 	int		err;
551 
552 	if ((err = mod_remove(&modlinkage)) == 0)
553 		drmach_fini();
554 
555 	return (err);
556 }
557 
558 int
559 _info(struct modinfo *modinfop)
560 {
561 	return (mod_info(&modlinkage, modinfop));
562 }
563 
564 /*
565  * drmach_node_* routines serve the purpose of separating the
566  * rest of the code from the device tree and OBP.  This is necessary
567  * because of In-Kernel-Probing.  Devices probed after stod, are probed
568  * by the in-kernel-prober, not OBP.  These devices, therefore, do not
569  * have dnode ids.
570  */
571 
572 static int
573 drmach_node_obp_get_parent(drmach_node_t *np, drmach_node_t *pp)
574 {
575 	pnode_t		nodeid;
576 	static char	*fn = "drmach_node_obp_get_parent";
577 
578 	nodeid = np->get_dnode(np);
579 	if (nodeid == OBP_NONODE) {
580 		cmn_err(CE_WARN, "%s: invalid dnode", fn);
581 		return (-1);
582 	}
583 
584 	bcopy(np, pp, sizeof (drmach_node_t));
585 
586 	pp->here = (void *)(uintptr_t)prom_parentnode(nodeid);
587 	if (pp->here == OBP_NONODE) {
588 		cmn_err(CE_WARN, "%s: invalid parent dnode", fn);
589 		return (-1);
590 	}
591 
592 	return (0);
593 }
594 
595 static pnode_t
596 drmach_node_obp_get_dnode(drmach_node_t *np)
597 {
598 	return ((pnode_t)(uintptr_t)np->here);
599 }
600 
601 typedef struct {
602 	drmach_node_walk_args_t	*nwargs;
603 	int 			(*cb)(drmach_node_walk_args_t *args);
604 	int			err;
605 } drmach_node_ddi_walk_args_t;
606 
607 int
608 drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
609 {
610 	drmach_node_ddi_walk_args_t	*nargs;
611 
612 	nargs = (drmach_node_ddi_walk_args_t *)arg;
613 
614 	/*
615 	 * dip doesn't have to be held here as we are called
616 	 * from ddi_walk_devs() which holds the dip.
617 	 */
618 	nargs->nwargs->node->here = (void *)dip;
619 
620 	nargs->err = nargs->cb(nargs->nwargs);
621 
622 	/*
623 	 * Set "here" to NULL so that unheld dip is not accessible
624 	 * outside ddi_walk_devs()
625 	 */
626 	nargs->nwargs->node->here = NULL;
627 
628 	if (nargs->err)
629 		return (DDI_WALK_TERMINATE);
630 	else
631 		return (DDI_WALK_CONTINUE);
632 }
633 
634 static int
635 drmach_node_ddi_walk(drmach_node_t *np, void *data,
636 		int (*cb)(drmach_node_walk_args_t *args))
637 {
638 	drmach_node_walk_args_t		args;
639 	drmach_node_ddi_walk_args_t	nargs;
640 
641 	/* initialized args structure for callback */
642 	args.node = np;
643 	args.data = data;
644 
645 	nargs.nwargs = &args;
646 	nargs.cb = cb;
647 	nargs.err = 0;
648 
649 	/*
650 	 * Root node doesn't have to be held in any way.
651 	 */
652 	ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
653 
654 	return (nargs.err);
655 }
656 
657 static int
658 drmach_node_obp_walk(drmach_node_t *np, void *data,
659 		int (*cb)(drmach_node_walk_args_t *args))
660 {
661 	pnode_t			nodeid;
662 	int			rv;
663 	drmach_node_walk_args_t	args;
664 
665 	/* initialized args structure for callback */
666 	args.node = np;
667 	args.data = data;
668 
669 	nodeid = prom_childnode(prom_rootnode());
670 
671 	/* save our new position within the tree */
672 	np->here = (void *)(uintptr_t)nodeid;
673 
674 	rv = 0;
675 	while (nodeid != OBP_NONODE) {
676 
677 		pnode_t child;
678 
679 		rv = (*cb)(&args);
680 		if (rv)
681 			break;
682 
683 		child = prom_childnode(nodeid);
684 		np->here = (void *)(uintptr_t)child;
685 
686 		while (child != OBP_NONODE) {
687 			rv = (*cb)(&args);
688 			if (rv)
689 				break;
690 
691 			child = prom_nextnode(child);
692 			np->here = (void *)(uintptr_t)child;
693 		}
694 
695 		nodeid = prom_nextnode(nodeid);
696 
697 		/* save our new position within the tree */
698 		np->here = (void *)(uintptr_t)nodeid;
699 	}
700 
701 	return (rv);
702 }
703 
704 static int
705 drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
706 {
707 	dev_info_t	*ndip;
708 	static char	*fn = "drmach_node_ddi_get_parent";
709 
710 	ndip = np->n_getdip(np);
711 	if (ndip == NULL) {
712 		cmn_err(CE_WARN, "%s: NULL dip", fn);
713 		return (-1);
714 	}
715 
716 	bcopy(np, pp, sizeof (drmach_node_t));
717 
718 	pp->here = (void *)ddi_get_parent(ndip);
719 	if (pp->here == NULL) {
720 		cmn_err(CE_WARN, "%s: NULL parent dip", fn);
721 		return (-1);
722 	}
723 
724 	return (0);
725 }
726 
727 /*ARGSUSED*/
728 static pnode_t
729 drmach_node_ddi_get_dnode(drmach_node_t *np)
730 {
731 	return ((pnode_t)NULL);
732 }
733 
734 static drmach_node_t *
735 drmach_node_new(void)
736 {
737 	drmach_node_t *np;
738 
739 	np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
740 
741 	if (drmach_initialized) {
742 		np->get_dnode = drmach_node_ddi_get_dnode;
743 		np->walk = drmach_node_ddi_walk;
744 		np->n_getdip = drmach_node_ddi_get_dip;
745 		np->n_getproplen = drmach_node_ddi_get_proplen;
746 		np->n_getprop = drmach_node_ddi_get_prop;
747 		np->get_parent = drmach_node_ddi_get_parent;
748 	} else {
749 		np->get_dnode = drmach_node_obp_get_dnode;
750 		np->walk = drmach_node_obp_walk;
751 		np->n_getdip = drmach_node_obp_get_dip;
752 		np->n_getproplen = drmach_node_obp_get_proplen;
753 		np->n_getprop = drmach_node_obp_get_prop;
754 		np->get_parent = drmach_node_obp_get_parent;
755 	}
756 
757 	return (np);
758 }
759 
760 static void
761 drmach_node_dispose(drmach_node_t *np)
762 {
763 	kmem_free(np, sizeof (*np));
764 }
765 
766 /*
767  * Check if a CPU node is part of a CMP.
768  */
769 static int
770 drmach_is_cmp_child(dev_info_t *dip)
771 {
772 	dev_info_t *pdip;
773 
774 	if (strcmp(ddi_node_name(dip), DRMACH_CPU_NAMEPROP) != 0) {
775 		return (0);
776 	}
777 
778 	pdip = ddi_get_parent(dip);
779 
780 	ASSERT(pdip);
781 
782 	if (strcmp(ddi_node_name(pdip), DRMACH_CMP_NAMEPROP) == 0) {
783 		return (1);
784 	}
785 
786 	return (0);
787 }
788 
789 static dev_info_t *
790 drmach_node_obp_get_dip(drmach_node_t *np)
791 {
792 	pnode_t		nodeid;
793 	dev_info_t	*dip;
794 
795 	nodeid = np->get_dnode(np);
796 	if (nodeid == OBP_NONODE)
797 		return (NULL);
798 
799 	dip = e_ddi_nodeid_to_dip(nodeid);
800 	if (dip) {
801 		/*
802 		 * The branch rooted at dip will have been previously
803 		 * held, or it will be the child of a CMP. In either
804 		 * case, the hold acquired in e_ddi_nodeid_to_dip()
805 		 * is not needed.
806 		 */
807 		ddi_release_devi(dip);
808 		ASSERT(drmach_is_cmp_child(dip) || e_ddi_branch_held(dip));
809 	}
810 
811 	return (dip);
812 }
813 
814 static dev_info_t *
815 drmach_node_ddi_get_dip(drmach_node_t *np)
816 {
817 	return ((dev_info_t *)np->here);
818 }
819 
820 static int
821 drmach_node_walk(drmach_node_t *np, void *param,
822 		int (*cb)(drmach_node_walk_args_t *args))
823 {
824 	return (np->walk(np, param, cb));
825 }
826 
827 static int
828 drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
829 {
830 	int		rv = 0;
831 	dev_info_t	*ndip;
832 	static char	*fn = "drmach_node_ddi_get_prop";
833 
834 	ndip = np->n_getdip(np);
835 	if (ndip == NULL) {
836 		cmn_err(CE_WARN, "%s: NULL dip", fn);
837 		rv = -1;
838 	} else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
839 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, name,
840 	    (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
841 		rv = -1;
842 	}
843 
844 	return (rv);
845 }
846 
847 /* ARGSUSED */
848 static int
849 drmach_node_obp_get_prop(drmach_node_t *np, char *name, void *buf, int len)
850 {
851 	int		rv = 0;
852 	pnode_t		nodeid;
853 	static char	*fn = "drmach_node_obp_get_prop";
854 
855 	nodeid = np->get_dnode(np);
856 	if (nodeid == OBP_NONODE) {
857 		cmn_err(CE_WARN, "%s: invalid dnode", fn);
858 		rv = -1;
859 	} else if (prom_getproplen(nodeid, (caddr_t)name) < 0) {
860 		rv = -1;
861 	} else {
862 		(void) prom_getprop(nodeid, (caddr_t)name, (caddr_t)buf);
863 	}
864 
865 	return (rv);
866 }
867 
868 static int
869 drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
870 {
871 	int		rv = 0;
872 	dev_info_t	*ndip;
873 
874 	ndip = np->n_getdip(np);
875 	if (ndip == NULL) {
876 		rv = -1;
877 	} else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS,
878 	    name, len) != DDI_PROP_SUCCESS) {
879 		rv = -1;
880 	}
881 
882 	return (rv);
883 }
884 
885 static int
886 drmach_node_obp_get_proplen(drmach_node_t *np, char *name, int *len)
887 {
888 	pnode_t	 nodeid;
889 	int	 rv;
890 
891 	nodeid = np->get_dnode(np);
892 	if (nodeid == OBP_NONODE)
893 		rv = -1;
894 	else {
895 		*len = prom_getproplen(nodeid, (caddr_t)name);
896 		rv = (*len < 0 ? -1 : 0);
897 	}
898 
899 	return (rv);
900 }
901 
902 static drmachid_t
903 drmach_node_dup(drmach_node_t *np)
904 {
905 	drmach_node_t *dup;
906 
907 	dup = drmach_node_new();
908 	dup->here = np->here;
909 	dup->get_dnode = np->get_dnode;
910 	dup->walk = np->walk;
911 	dup->n_getdip = np->n_getdip;
912 	dup->n_getproplen = np->n_getproplen;
913 	dup->n_getprop = np->n_getprop;
914 	dup->get_parent = np->get_parent;
915 
916 	return (dup);
917 }
918 
919 /*
920  * drmach_array provides convenient array construction, access,
921  * bounds checking and array destruction logic.
922  */
923 
924 static drmach_array_t *
925 drmach_array_new(int min_index, int max_index)
926 {
927 	drmach_array_t *arr;
928 
929 	arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
930 
931 	arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
932 	if (arr->arr_sz > 0) {
933 		arr->min_index = min_index;
934 		arr->max_index = max_index;
935 
936 		arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
937 		return (arr);
938 	} else {
939 		kmem_free(arr, sizeof (*arr));
940 		return (0);
941 	}
942 }
943 
944 static int
945 drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
946 {
947 	if (idx < arr->min_index || idx > arr->max_index)
948 		return (-1);
949 	else {
950 		arr->arr[idx - arr->min_index] = val;
951 		return (0);
952 	}
953 	/*NOTREACHED*/
954 }
955 
956 static int
957 drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
958 {
959 	if (idx < arr->min_index || idx > arr->max_index)
960 		return (-1);
961 	else {
962 		*val = arr->arr[idx - arr->min_index];
963 		return (0);
964 	}
965 	/*NOTREACHED*/
966 }
967 
968 static int
969 drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
970 {
971 	int rv;
972 
973 	*idx = arr->min_index;
974 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
975 		*idx += 1;
976 
977 	return (rv);
978 }
979 
980 static int
981 drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
982 {
983 	int rv;
984 
985 	*idx += 1;
986 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
987 		*idx += 1;
988 
989 	return (rv);
990 }
991 
992 static void
993 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
994 {
995 	drmachid_t	val;
996 	int		idx;
997 	int		rv;
998 
999 	rv = drmach_array_first(arr, &idx, &val);
1000 	while (rv == 0) {
1001 		(*disposer)(val);
1002 
1003 		/* clear the array entry */
1004 		rv = drmach_array_set(arr, idx, NULL);
1005 		ASSERT(rv == 0);
1006 
1007 		rv = drmach_array_next(arr, &idx, &val);
1008 	}
1009 
1010 	kmem_free(arr->arr, arr->arr_sz);
1011 	kmem_free(arr, sizeof (*arr));
1012 }
1013 
1014 
1015 static gdcd_t *
1016 drmach_gdcd_new()
1017 {
1018 	gdcd_t *gdcd;
1019 
1020 	gdcd = kmem_zalloc(sizeof (gdcd_t), KM_SLEEP);
1021 
1022 	/* read the gdcd, bail if magic or ver #s are not what is expected */
1023 	if (iosram_rd(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd)) {
1024 bail:
1025 		kmem_free(gdcd, sizeof (gdcd_t));
1026 		return (NULL);
1027 	} else if (gdcd->h.dcd_magic != GDCD_MAGIC) {
1028 		goto bail;
1029 	} else if (gdcd->h.dcd_version != DCD_VERSION) {
1030 		goto bail;
1031 	}
1032 
1033 	return (gdcd);
1034 }
1035 
1036 static void
1037 drmach_gdcd_dispose(gdcd_t *gdcd)
1038 {
1039 	kmem_free(gdcd, sizeof (gdcd_t));
1040 }
1041 
1042 /*ARGSUSED*/
1043 sbd_error_t *
1044 drmach_configure(drmachid_t id, int flags)
1045 {
1046 	drmach_device_t	*dp;
1047 	dev_info_t	*rdip;
1048 	sbd_error_t	*err = NULL;
1049 
1050 	/*
1051 	 * On Starcat, there is no CPU driver, so it is
1052 	 * not necessary to configure any CPU nodes.
1053 	 */
1054 	if (DRMACH_IS_CPU_ID(id)) {
1055 		return (NULL);
1056 	}
1057 
1058 	for (; id; ) {
1059 		dev_info_t	*fdip = NULL;
1060 
1061 		if (!DRMACH_IS_DEVICE_ID(id))
1062 			return (drerr_new(0, ESTC_INAPPROP, NULL));
1063 		dp = id;
1064 
1065 		rdip = dp->node->n_getdip(dp->node);
1066 
1067 		/*
1068 		 * We held this branch earlier, so at a minimum its
1069 		 * root should still be present in the device tree.
1070 		 */
1071 		ASSERT(rdip);
1072 
1073 		DRMACH_PR("drmach_configure: configuring DDI branch");
1074 
1075 		ASSERT(e_ddi_branch_held(rdip));
1076 		if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
1077 			if (err == NULL) {
1078 				/*
1079 				 * Record first failure but don't stop
1080 				 */
1081 				char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1082 				dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
1083 
1084 				(void) ddi_pathname(dip, path);
1085 				err = drerr_new(1, ESTC_DRVFAIL, path);
1086 
1087 				kmem_free(path, MAXPATHLEN);
1088 			}
1089 
1090 			/*
1091 			 * If non-NULL, fdip is returned held and must be
1092 			 * released.
1093 			 */
1094 			if (fdip != NULL) {
1095 				ddi_release_devi(fdip);
1096 			}
1097 		}
1098 
1099 		if (DRMACH_IS_MEM_ID(id)) {
1100 			drmach_mem_t	*mp = id;
1101 			id = mp->next;
1102 		} else {
1103 			id = NULL;
1104 		}
1105 	}
1106 
1107 	return (err);
1108 }
1109 
1110 static sbd_error_t *
1111 drmach_device_new(drmach_node_t *node,
1112 	drmach_board_t *bp, int portid, drmachid_t *idp)
1113 {
1114 	int		i, rv, device_id, unum;
1115 	char		name[OBP_MAXDRVNAME];
1116 	drmach_device_t	proto;
1117 
1118 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
1119 	if (rv) {
1120 		sbd_error_t *err;
1121 
1122 		/* every node is expected to have a name */
1123 		err = drerr_new(1, ESTC_GETPROP,
1124 		    "dip: 0x%p: property %s",
1125 		    node->n_getdip(node), OBP_NAME);
1126 
1127 		return (err);
1128 	}
1129 
1130 	i = drmach_name2type_idx(name);
1131 
1132 	if (i < 0 || strcmp(name, "cmp") == 0) {
1133 		/*
1134 		 * Not a node of interest to dr - including "cmp",
1135 		 * but it is in drmach_name2type[], which lets gptwocfg
1136 		 * driver to check if node is OBP created.
1137 		 */
1138 		*idp = (drmachid_t)0;
1139 		return (NULL);
1140 	}
1141 
1142 	/*
1143 	 * Derive a best-guess unit number from the portid value.
1144 	 * Some drmach_*_new constructors (drmach_pci_new, for example)
1145 	 * will overwrite the prototype unum value with one that is more
1146 	 * appropriate for the device.
1147 	 */
1148 	device_id = portid & 0x1f;
1149 	if (device_id < 4)
1150 		unum = device_id;
1151 	else if (device_id == 8) {
1152 		unum = 0;
1153 	} else if (device_id == 9) {
1154 		unum = 1;
1155 	} else if (device_id == 0x1c) {
1156 		unum = 0;
1157 	} else if (device_id == 0x1d) {
1158 		unum = 1;
1159 	} else {
1160 		return (DRMACH_INTERNAL_ERROR());
1161 	}
1162 
1163 	bzero(&proto, sizeof (proto));
1164 	proto.type = drmach_name2type[i].type;
1165 	proto.bp = bp;
1166 	proto.node = node;
1167 	proto.portid = portid;
1168 	proto.unum = unum;
1169 
1170 	return (drmach_name2type[i].new(&proto, idp));
1171 }
1172 
1173 static void
1174 drmach_device_dispose(drmachid_t id)
1175 {
1176 	drmach_device_t *self = id;
1177 
1178 	self->cm.dispose(id);
1179 }
1180 
1181 static drmach_board_t *
1182 drmach_board_new(int bnum)
1183 {
1184 	static sbd_error_t *drmach_board_release(drmachid_t);
1185 	static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
1186 
1187 	drmach_board_t	*bp;
1188 
1189 	bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
1190 
1191 	bp->cm.isa = (void *)drmach_board_new;
1192 	bp->cm.release = drmach_board_release;
1193 	bp->cm.status = drmach_board_status;
1194 
1195 	(void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
1196 
1197 	bp->bnum = bnum;
1198 	bp->devices = NULL;
1199 	bp->tree = drmach_node_new();
1200 
1201 	(void) drmach_array_set(drmach_boards, bnum, bp);
1202 	return (bp);
1203 }
1204 
1205 static void
1206 drmach_board_dispose(drmachid_t id)
1207 {
1208 	drmach_board_t *bp;
1209 
1210 	ASSERT(DRMACH_IS_BOARD_ID(id));
1211 	bp = id;
1212 
1213 	if (bp->tree)
1214 		drmach_node_dispose(bp->tree);
1215 
1216 	if (bp->devices)
1217 		drmach_array_dispose(bp->devices, drmach_device_dispose);
1218 
1219 	kmem_free(bp, sizeof (*bp));
1220 }
1221 
1222 static sbd_error_t *
1223 drmach_board_status(drmachid_t id, drmach_status_t *stat)
1224 {
1225 	sbd_error_t	*err = NULL;
1226 	drmach_board_t	*bp;
1227 	caddr_t		obufp;
1228 	dr_showboard_t	shb;
1229 
1230 	if (!DRMACH_IS_BOARD_ID(id))
1231 		return (drerr_new(0, ESTC_INAPPROP, NULL));
1232 
1233 	bp = id;
1234 
1235 	/*
1236 	 * we need to know if the board's connected before
1237 	 * issuing a showboard message.  If it's connected, we just
1238 	 * reply with status composed of cached info
1239 	 */
1240 
1241 	if (!bp->connected) {
1242 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
1243 		err = drmach_mbox_trans(DRMSG_SHOWBOARD, bp->bnum, obufp,
1244 		    sizeof (dr_proto_hdr_t), (caddr_t)&shb,
1245 		    sizeof (dr_showboard_t));
1246 
1247 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
1248 		if (err)
1249 			return (err);
1250 
1251 		bp->connected = (shb.bd_assigned && shb.bd_active);
1252 		(void) strncpy(bp->type, shb.board_type, sizeof (bp->type));
1253 		stat->assigned = bp->assigned = shb.bd_assigned;
1254 		stat->powered = bp->powered = shb.power_on;
1255 		stat->empty = bp->empty = shb.slot_empty;
1256 
1257 		switch (shb.test_status) {
1258 			case DR_TEST_STATUS_UNKNOWN:
1259 			case DR_TEST_STATUS_IPOST:
1260 			case DR_TEST_STATUS_ABORTED:
1261 				stat->cond = bp->cond = SBD_COND_UNKNOWN;
1262 				break;
1263 			case DR_TEST_STATUS_PASSED:
1264 				stat->cond = bp->cond = SBD_COND_OK;
1265 				break;
1266 			case DR_TEST_STATUS_FAILED:
1267 				stat->cond = bp->cond = SBD_COND_FAILED;
1268 				break;
1269 			default:
1270 				stat->cond = bp->cond = SBD_COND_UNKNOWN;
1271 				DRMACH_PR("Unknown test status=0x%x from SC\n",
1272 				    shb.test_status);
1273 				break;
1274 
1275 		}
1276 
1277 		(void) strncpy(stat->type, shb.board_type, sizeof (stat->type));
1278 		(void) snprintf(stat->info, sizeof (stat->info),
1279 		    "Test Level=%d", shb.test_level);
1280 	} else {
1281 		stat->assigned = bp->assigned;
1282 		stat->powered = bp->powered;
1283 		stat->empty = bp->empty;
1284 		stat->cond = bp->cond;
1285 		(void) strncpy(stat->type, bp->type, sizeof (stat->type));
1286 	}
1287 
1288 	stat->busy = 0;			/* assume not busy */
1289 	stat->configured = 0;		/* assume not configured */
1290 	if (bp->devices) {
1291 		int		 rv;
1292 		int		 d_idx;
1293 		drmachid_t	 d_id;
1294 
1295 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
1296 		while (rv == 0) {
1297 			drmach_status_t	d_stat;
1298 
1299 			err = drmach_i_status(d_id, &d_stat);
1300 			if (err)
1301 				break;
1302 
1303 			stat->busy |= d_stat.busy;
1304 			stat->configured |= d_stat.configured;
1305 
1306 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
1307 		}
1308 	}
1309 
1310 	return (err);
1311 }
1312 
1313 typedef struct drmach_msglist {
1314 	kcondvar_t		s_cv; 		/* condvar for sending msg */
1315 	kmutex_t		s_lock;		/* mutex for sending */
1316 	kcondvar_t		g_cv;		/* condvar for getting reply */
1317 	kmutex_t		g_lock;		/* mutex for getting reply */
1318 	struct drmach_msglist	*prev;		/* link to previous entry */
1319 	struct drmach_msglist	*next;		/* link to next entry */
1320 	struct drmach_msglist	*link;		/* link to related entry */
1321 	caddr_t			o_buf;		/* address of output buffer */
1322 	caddr_t			i_buf; 		/* address of input buffer */
1323 	uint32_t		o_buflen;	/* output buffer length */
1324 	uint32_t		i_buflen;	/* input buffer length */
1325 	uint32_t		msgid;		/* message identifier */
1326 	int			o_nretry;	/* number of sending retries */
1327 	int			f_error;	/* mailbox framework error */
1328 	uint8_t			e_code;		/* error code returned by SC */
1329 	uint8_t			p_flag	:1,	/* successfully putmsg */
1330 				m_reply	:1,	/* msg reply received */
1331 				unused	:6;
1332 } drmach_msglist_t;
1333 
1334 kmutex_t		drmach_g_mbox_mutex;	/* mutex for mailbox globals */
1335 kmutex_t		drmach_ri_mbox_mutex;	/* mutex for mailbox reinit */
1336 kmutex_t		drmach_msglist_mutex;	/* mutex for message list */
1337 drmach_msglist_t	*drmach_msglist_first;	/* first entry in msg list */
1338 drmach_msglist_t	*drmach_msglist_last;	/* last entry in msg list */
1339 uint32_t		drmach_msgid;		/* current message id */
1340 kthread_t		*drmach_getmsg_thread;	/* ptr to getmsg thread */
1341 volatile int		drmach_getmsg_thread_run; /* run flag for getmsg thr */
1342 kmutex_t		drmach_sendmsg_mutex;	/* mutex for sendmsg cv */
1343 kcondvar_t		drmach_sendmsg_cv;	/* signaled to send new msg */
1344 kthread_t		*drmach_sendmsg_thread; /* ptr to sendmsg thread */
1345 volatile int		drmach_sendmsg_thread_run; /* run flag for sendmsg */
1346 int			drmach_mbox_istate;	/* mailbox init state */
1347 int			drmach_mbox_iflag;	/* set if init'd with SC */
1348 int			drmach_mbox_ipending;	/* set if reinit scheduled */
1349 
1350 /*
1351  * Timeout values (in seconds) used when waiting for replies (from the SC) to
1352  * requests that we sent.  Since we only receive boardevent messages, and they
1353  * are events rather than replies, there is no boardevent timeout.
1354  */
1355 int	drmach_to_mbxinit	= 60;		/* 1 minute */
1356 int	drmach_to_assign	= 60;		/* 1 minute */
1357 int	drmach_to_unassign	= 60;		/* 1 minute */
1358 int	drmach_to_claim		= 3600;		/* 1 hour */
1359 int	drmach_to_unclaim	= 3600;		/* 1 hour */
1360 int	drmach_to_poweron	= 480;		/* 8 minutes */
1361 int	drmach_to_poweroff	= 480;		/* 8 minutes */
1362 int	drmach_to_testboard	= 43200;	/* 12 hours */
1363 int	drmach_to_aborttest	= 180;		/* 3 minutes */
1364 int	drmach_to_showboard	= 180;		/* 3 minutes */
1365 int	drmach_to_unconfig	= 180;		/* 3 minutes */
1366 
1367 /*
1368  * Delay (in seconds) used after receiving a non-transient error indication from
1369  * an mboxsc_getmsg call in the thread that loops waiting for incoming messages.
1370  */
1371 int	drmach_mbxerr_delay	= 15;		/* 15 seconds */
1372 
1373 /*
1374  * Timeout values (in milliseconds) for mboxsc_putmsg and mboxsc_getmsg calls.
1375  */
1376 clock_t	drmach_to_putmsg;			/* set in drmach_mbox_init */
1377 clock_t	drmach_to_getmsg	= 31000;	/* 31 seconds */
1378 
1379 /*
1380  * Normally, drmach_to_putmsg is set dynamically during initialization in
1381  * drmach_mbox_init.  This has the potentially undesirable side effect of
1382  * clobbering any value that might have been set in /etc/system.  To prevent
1383  * dynamic setting of drmach_to_putmsg (thereby allowing it to be tuned in
1384  * /etc/system), set drmach_use_tuned_putmsg_to to 1.
1385  */
1386 int	drmach_use_tuned_putmsg_to	= 0;
1387 
1388 
1389 /* maximum conceivable message size for future mailbox protocol versions */
1390 #define	DRMACH_MAX_MBOX_MSG_SIZE	4096
1391 
1392 /*ARGSUSED*/
1393 void
1394 drmach_mbox_prmsg(dr_mbox_msg_t *mbp, int dir)
1395 {
1396 	int		i, j;
1397 	dr_memregs_t	*memregs;
1398 	dr_proto_hdr_t	*php = &mbp->p_hdr;
1399 	dr_msg_t	*mp = &mbp->msgdata;
1400 
1401 #ifdef DEBUG
1402 	switch (php->command) {
1403 		case DRMSG_BOARDEVENT:
1404 			if (dir) {
1405 				DRMACH_PR("ERROR!! outgoing BOARDEVENT\n");
1406 			} else {
1407 				DRMACH_PR("BOARDEVENT received:\n");
1408 				DRMACH_PR("init=%d ins=%d rem=%d asgn=%d\n",
1409 				    mp->dm_be.initialized,
1410 				    mp->dm_be.board_insertion,
1411 				    mp->dm_be.board_removal,
1412 				    mp->dm_be.slot_assign);
1413 				DRMACH_PR("unasgn=%d avail=%d unavail=%d\n",
1414 				    mp->dm_be.slot_unassign,
1415 				    mp->dm_be.slot_avail,
1416 				    mp->dm_be.slot_unavail);
1417 			}
1418 			break;
1419 		case DRMSG_MBOX_INIT:
1420 			if (dir) {
1421 				DRMACH_PR("MBOX_INIT Request:\n");
1422 			} else {
1423 				DRMACH_PR("MBOX_INIT Reply:\n");
1424 			}
1425 			break;
1426 		case DRMSG_ASSIGN:
1427 			if (dir) {
1428 				DRMACH_PR("ASSIGN Request:\n");
1429 			} else {
1430 				DRMACH_PR("ASSIGN Reply:\n");
1431 			}
1432 			break;
1433 		case DRMSG_UNASSIGN:
1434 			if (dir) {
1435 				DRMACH_PR("UNASSIGN Request:\n");
1436 			} else {
1437 				DRMACH_PR("UNASSIGN Reply:\n");
1438 			}
1439 			break;
1440 		case DRMSG_CLAIM:
1441 			if (!dir) {
1442 				DRMACH_PR("CLAIM Reply:\n");
1443 				break;
1444 			}
1445 
1446 			DRMACH_PR("CLAIM Request:\n");
1447 			for (i = 0; i < 18; ++i) {
1448 				DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
1449 				    mp->dm_cr.mem_slice[i].valid,
1450 				    mp->dm_cr.mem_slice[i].slice);
1451 				memregs = &(mp->dm_cr.mem_regs[i]);
1452 				for (j = 0; j < S0_LPORT_COUNT; j++) {
1453 					DRMACH_PR("  MC %2d: "
1454 					    "MADR[%d] = 0x%lx, "
1455 					    "MADR[%d] = 0x%lx\n", j,
1456 					    0, DRMACH_MCREG_TO_U64(
1457 					    memregs->madr[j][0]),
1458 					    1, DRMACH_MCREG_TO_U64(
1459 					    memregs->madr[j][1]));
1460 					DRMACH_PR("       : "
1461 					    "MADR[%d] = 0x%lx, "
1462 					    "MADR[%d] = 0x%lx\n",
1463 					    2, DRMACH_MCREG_TO_U64(
1464 					    memregs->madr[j][2]),
1465 					    3, DRMACH_MCREG_TO_U64(
1466 					    memregs->madr[j][3]));
1467 				}
1468 			}
1469 			break;
1470 		case DRMSG_UNCLAIM:
1471 			if (!dir) {
1472 				DRMACH_PR("UNCLAIM Reply:\n");
1473 				break;
1474 			}
1475 
1476 			DRMACH_PR("UNCLAIM Request:\n");
1477 			for (i = 0; i < 18; ++i) {
1478 				DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
1479 				    mp->dm_ur.mem_slice[i].valid,
1480 				    mp->dm_ur.mem_slice[i].slice);
1481 				memregs = &(mp->dm_ur.mem_regs[i]);
1482 				for (j = 0; j < S0_LPORT_COUNT; j++) {
1483 					DRMACH_PR("  MC %2d: "
1484 					    "MADR[%d] = 0x%lx, "
1485 					    "MADR[%d] = 0x%lx\n", j,
1486 					    0, DRMACH_MCREG_TO_U64(
1487 					    memregs->madr[j][0]),
1488 					    1, DRMACH_MCREG_TO_U64(
1489 					    memregs->madr[j][1]));
1490 					DRMACH_PR("       : "
1491 					    "MADR[%d] = 0x%lx, "
1492 					    "MADR[%d] = 0x%lx\n",
1493 					    2, DRMACH_MCREG_TO_U64(
1494 					    memregs->madr[j][2]),
1495 					    3, DRMACH_MCREG_TO_U64(
1496 					    memregs->madr[j][3]));
1497 				}
1498 			}
1499 			DRMACH_PR(" mem_clear=%d\n", mp->dm_ur.mem_clear);
1500 			break;
1501 		case DRMSG_UNCONFIG:
1502 			if (!dir) {
1503 				DRMACH_PR("UNCONFIG Reply:\n");
1504 				break;
1505 			}
1506 
1507 			DRMACH_PR("UNCONFIG Request:\n");
1508 			for (i = 0; i < 18; ++i) {
1509 				DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
1510 				    mp->dm_uc.mem_slice[i].valid,
1511 				    mp->dm_uc.mem_slice[i].slice);
1512 				memregs = &(mp->dm_uc.mem_regs[i]);
1513 				for (j = 0; j < S0_LPORT_COUNT; j++) {
1514 					DRMACH_PR("  MC %2d: "
1515 					    "MADR[%d] = 0x%lx, "
1516 					    "MADR[%d] = 0x%lx\n", j,
1517 					    0, DRMACH_MCREG_TO_U64(
1518 					    memregs->madr[j][0]),
1519 					    1, DRMACH_MCREG_TO_U64(
1520 					    memregs->madr[j][1]));
1521 					DRMACH_PR("       : "
1522 					    "MADR[%d] = 0x%lx, "
1523 					    "MADR[%d] = 0x%lx\n",
1524 					    2, DRMACH_MCREG_TO_U64(
1525 					    memregs->madr[j][2]),
1526 					    3, DRMACH_MCREG_TO_U64(
1527 					    memregs->madr[j][3]));
1528 				}
1529 			}
1530 			break;
1531 		case DRMSG_POWERON:
1532 			if (dir) {
1533 				DRMACH_PR("POWERON Request:\n");
1534 			} else {
1535 				DRMACH_PR("POWERON Reply:\n");
1536 			}
1537 			break;
1538 		case DRMSG_POWEROFF:
1539 			if (dir) {
1540 				DRMACH_PR("POWEROFF Request:\n");
1541 			} else {
1542 				DRMACH_PR("POWEROFF Reply:\n");
1543 			}
1544 			break;
1545 		case DRMSG_TESTBOARD:
1546 			if (dir) {
1547 				DRMACH_PR("TESTBOARD Request:\n");
1548 				DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
1549 				    mp->dm_tb.memaddrhi,
1550 				    mp->dm_tb.memaddrlo);
1551 				DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
1552 				    mp->dm_tb.memlen, mp->dm_tb.cpu_portid);
1553 				DRMACH_PR("\tforce=0x%x imm=0x%x\n",
1554 				    mp->dm_tb.force, mp->dm_tb.immediate);
1555 			} else {
1556 				DRMACH_PR("TESTBOARD Reply:\n");
1557 				DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
1558 				    mp->dm_tr.memaddrhi,
1559 				    mp->dm_tr.memaddrlo);
1560 				DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
1561 				    mp->dm_tr.memlen, mp->dm_tr.cpu_portid);
1562 				DRMACH_PR("\trecovered=0x%x test status=0x%x\n",
1563 				    mp->dm_tr.cpu_recovered,
1564 				    mp->dm_tr.test_status);
1565 
1566 			}
1567 			break;
1568 		case DRMSG_ABORT_TEST:
1569 			if (dir) {
1570 				DRMACH_PR("ABORT_TEST Request:\n");
1571 			} else {
1572 				DRMACH_PR("ABORT_TEST Reply:\n");
1573 			}
1574 
1575 			DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
1576 			    mp->dm_ta.memaddrhi,
1577 			    mp->dm_ta.memaddrlo);
1578 			DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
1579 			    mp->dm_ta.memlen, mp->dm_ta.cpu_portid);
1580 			break;
1581 		case DRMSG_SHOWBOARD:
1582 			if (dir) {
1583 				DRMACH_PR("SHOWBOARD Request:\n");
1584 			} else {
1585 				DRMACH_PR("SHOWBOARD Reply:\n");
1586 
1587 				DRMACH_PR(": empty=%d power=%d assigned=%d",
1588 				    mp->dm_sb.slot_empty,
1589 				    mp->dm_sb.power_on,
1590 				    mp->dm_sb.bd_assigned);
1591 				DRMACH_PR(": active=%d t_status=%d t_level=%d ",
1592 				    mp->dm_sb.bd_active,
1593 				    mp->dm_sb.test_status,
1594 				    mp->dm_sb.test_level);
1595 				DRMACH_PR(": type=%s ", mp->dm_sb.board_type);
1596 			}
1597 			break;
1598 		default:
1599 			DRMACH_PR("Unknown message type\n");
1600 			break;
1601 	}
1602 
1603 	DRMACH_PR("dr hdr:\n\tid=0x%x vers=0x%x cmd=0x%x exp=0x%x slot=0x%x\n",
1604 	    php->message_id, php->drproto_version, php->command,
1605 	    php->expbrd, php->slot);
1606 #endif
1607 	DRMACH_PR("\treply_status=0x%x error_code=0x%x\n", php->reply_status,
1608 	    php->error_code);
1609 }
1610 
1611 /*
1612  * Callback function passed to taskq_dispatch when a mailbox reinitialization
1613  * handshake needs to be scheduled.  The handshake can't be performed by the
1614  * thread that determines it is needed, in most cases, so this function is
1615  * dispatched on the system-wide taskq pool of threads.  Failure is reported but
1616  * otherwise ignored, since any situation that requires a mailbox initialization
1617  * handshake will continue to request the handshake until it succeeds.
1618  */
1619 static void
1620 drmach_mbox_reinit(void *unused)
1621 {
1622 	_NOTE(ARGUNUSED(unused))
1623 
1624 	caddr_t		obufp = NULL;
1625 	sbd_error_t	*serr = NULL;
1626 
1627 	DRMACH_PR("scheduled mailbox reinit running\n");
1628 
1629 	mutex_enter(&drmach_ri_mbox_mutex);
1630 	mutex_enter(&drmach_g_mbox_mutex);
1631 	if (drmach_mbox_iflag == 0) {
1632 		/* need to initialize the mailbox */
1633 		mutex_exit(&drmach_g_mbox_mutex);
1634 
1635 		cmn_err(CE_NOTE, "!reinitializing DR mailbox");
1636 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
1637 		serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
1638 		    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
1639 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
1640 
1641 		if (serr) {
1642 			cmn_err(CE_WARN,
1643 			    "mbox_init: MBOX_INIT failed ecode=0x%x",
1644 			    serr->e_code);
1645 			sbd_err_clear(&serr);
1646 		}
1647 		mutex_enter(&drmach_g_mbox_mutex);
1648 		if (!serr) {
1649 			drmach_mbox_iflag = 1;
1650 		}
1651 	}
1652 	drmach_mbox_ipending = 0;
1653 	mutex_exit(&drmach_g_mbox_mutex);
1654 	mutex_exit(&drmach_ri_mbox_mutex);
1655 }
1656 
1657 /*
1658  * To ensure sufficient compatibility with future versions of the DR mailbox
1659  * protocol, we use a buffer that is large enough to receive the largest message
1660  * that could possibly be sent to us.  However, since that ends up being fairly
1661  * large, allocating it on the stack is a bad idea.  Fortunately, this function
1662  * does not need to be MT-safe since it is only invoked by the mailbox
1663  * framework, which will never invoke it multiple times concurrently.  Since
1664  * that is the case, we can use a static buffer.
1665  */
1666 void
1667 drmach_mbox_event(void)
1668 {
1669 	static uint8_t	buf[DRMACH_MAX_MBOX_MSG_SIZE];
1670 	dr_mbox_msg_t	*msg = (dr_mbox_msg_t *)buf;
1671 	int		err;
1672 	uint32_t	type = MBOXSC_MSG_EVENT;
1673 	uint32_t	command = DRMSG_BOARDEVENT;
1674 	uint64_t	transid = 0;
1675 	uint32_t	length = DRMACH_MAX_MBOX_MSG_SIZE;
1676 	char		*hint = "";
1677 	int		logsys = 0;
1678 
1679 	do {
1680 		err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
1681 		    &length, (void *)msg, 0);
1682 	} while (err == EAGAIN);
1683 
1684 	/* don't try to interpret anything with the wrong version number */
1685 	if ((err == 0) && (msg->p_hdr.drproto_version != DRMBX_VERSION)) {
1686 		cmn_err(CE_WARN, "mailbox version mismatch 0x%x vs 0x%x",
1687 		    msg->p_hdr.drproto_version, DRMBX_VERSION);
1688 		mutex_enter(&drmach_g_mbox_mutex);
1689 		drmach_mbox_iflag = 0;
1690 		/* schedule a reinit handshake if one isn't pending */
1691 		if (!drmach_mbox_ipending) {
1692 			if (taskq_dispatch(system_taskq, drmach_mbox_reinit,
1693 			    NULL, TQ_NOSLEEP) != NULL) {
1694 				drmach_mbox_ipending = 1;
1695 			} else {
1696 				cmn_err(CE_WARN,
1697 				    "failed to schedule mailbox reinit");
1698 			}
1699 		}
1700 		mutex_exit(&drmach_g_mbox_mutex);
1701 		return;
1702 	}
1703 
1704 	if ((err != 0) || (msg->p_hdr.reply_status != DRMSG_REPLY_OK)) {
1705 		cmn_err(CE_WARN,
1706 		    "Unsolicited mboxsc_getmsg failed: err=0x%x code=0x%x",
1707 		    err, msg->p_hdr.error_code);
1708 	} else {
1709 		dr_boardevent_t	*be;
1710 		be = (dr_boardevent_t *)&msg->msgdata;
1711 
1712 		/* check for initialization event */
1713 		if (be->initialized) {
1714 			mutex_enter(&drmach_g_mbox_mutex);
1715 			drmach_mbox_iflag = 0;
1716 			/* schedule a reinit handshake if one isn't pending */
1717 			if (!drmach_mbox_ipending) {
1718 				if (taskq_dispatch(system_taskq,
1719 				    drmach_mbox_reinit, NULL, TQ_NOSLEEP)
1720 				    != NULL) {
1721 					drmach_mbox_ipending = 1;
1722 				} else {
1723 					cmn_err(CE_WARN, "failed to schedule "
1724 					    "mailbox reinit");
1725 				}
1726 			}
1727 			mutex_exit(&drmach_g_mbox_mutex);
1728 			cmn_err(CE_NOTE, "!Mailbox Init event received");
1729 		}
1730 
1731 		/* anything else will be a log_sysevent call */
1732 
1733 		if (be->board_insertion) {
1734 			DRMACH_PR("Board Insertion event received");
1735 			hint = DR_HINT_INSERT;
1736 			logsys++;
1737 	}
1738 		if (be->board_removal) {
1739 			DRMACH_PR("Board Removal event received");
1740 			hint = DR_HINT_REMOVE;
1741 			logsys++;
1742 		}
1743 		if (be->slot_assign) {
1744 			DRMACH_PR("Slot Assign event received");
1745 			logsys++;
1746 		}
1747 		if (be->slot_unassign) {
1748 			DRMACH_PR("Slot Unassign event received");
1749 			logsys++;
1750 		}
1751 		if (be->slot_avail) {
1752 			DRMACH_PR("Slot Available event received");
1753 			logsys++;
1754 		}
1755 		if (be->slot_unavail) {
1756 			DRMACH_PR("Slot Unavailable event received");
1757 			logsys++;
1758 		}
1759 		if (be->power_on) {
1760 			DRMACH_PR("Power ON event received");
1761 			logsys++;
1762 		}
1763 		if (be->power_off) {
1764 			DRMACH_PR("Power OFF event received");
1765 			logsys++;
1766 		}
1767 
1768 		if (logsys)
1769 			(void) drmach_log_sysevent(
1770 			    DRMACH_EXPSLOT2BNUM(msg->p_hdr.expbrd,
1771 			    msg->p_hdr.slot), hint, SE_NOSLEEP, 1);
1772 	}
1773 }
1774 
1775 static uint32_t
1776 drmach_get_msgid()
1777 {
1778 	uint32_t	rv;
1779 	mutex_enter(&drmach_msglist_mutex);
1780 	if (!(++drmach_msgid))
1781 		++drmach_msgid;
1782 	rv = drmach_msgid;
1783 	mutex_exit(&drmach_msglist_mutex);
1784 	return (rv);
1785 }
1786 
1787 /*
1788  *	unlink an entry from the message transaction list
1789  *
1790  *	caller must hold drmach_msglist_mutex
1791  */
1792 void
1793 drmach_msglist_unlink(drmach_msglist_t *entry)
1794 {
1795 	ASSERT(mutex_owned(&drmach_msglist_mutex));
1796 	if (entry->prev) {
1797 		entry->prev->next = entry->next;
1798 		if (entry->next)
1799 			entry->next->prev = entry->prev;
1800 	} else {
1801 		drmach_msglist_first = entry->next;
1802 		if (entry->next)
1803 			entry->next->prev = NULL;
1804 	}
1805 	if (entry == drmach_msglist_last) {
1806 		drmach_msglist_last = entry->prev;
1807 	}
1808 }
1809 
1810 void
1811 drmach_msglist_link(drmach_msglist_t *entry)
1812 {
1813 	mutex_enter(&drmach_msglist_mutex);
1814 	if (drmach_msglist_last) {
1815 		entry->prev = drmach_msglist_last;
1816 		drmach_msglist_last->next = entry;
1817 		drmach_msglist_last = entry;
1818 	} else {
1819 		drmach_msglist_last = drmach_msglist_first = entry;
1820 	}
1821 	mutex_exit(&drmach_msglist_mutex);
1822 }
1823 
1824 void
1825 drmach_mbox_getmsg()
1826 {
1827 	int			err;
1828 	register int		msgid;
1829 	static uint8_t		buf[DRMACH_MAX_MBOX_MSG_SIZE];
1830 	dr_mbox_msg_t		*msg = (dr_mbox_msg_t *)buf;
1831 	dr_proto_hdr_t		*php;
1832 	drmach_msglist_t	*found, *entry;
1833 	uint32_t		type = MBOXSC_MSG_REPLY;
1834 	uint32_t		command;
1835 	uint64_t		transid;
1836 	uint32_t		length;
1837 
1838 	php = &msg->p_hdr;
1839 
1840 	while (drmach_getmsg_thread_run != 0) {
1841 		/* get a reply message */
1842 		command = 0;
1843 		transid = 0;
1844 		length = DRMACH_MAX_MBOX_MSG_SIZE;
1845 		err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
1846 		    &length, (void *)msg, drmach_to_getmsg);
1847 
1848 		if (err) {
1849 			/*
1850 			 * If mboxsc_getmsg returns ETIMEDOUT or EAGAIN, then
1851 			 * the "error" is really just a normal, transient
1852 			 * condition and we can retry the operation right away.
1853 			 * Any other error suggests a more serious problem,
1854 			 * ranging from a message being too big for our buffer
1855 			 * (EMSGSIZE) to total failure of the mailbox layer.
1856 			 * This second class of errors is much less "transient",
1857 			 * so rather than retrying over and over (and getting
1858 			 * the same error over and over) as fast as we can,
1859 			 * we'll sleep for a while before retrying.
1860 			 */
1861 			if ((err != ETIMEDOUT) && (err != EAGAIN)) {
1862 				cmn_err(CE_WARN,
1863 				    "mboxsc_getmsg failed, err=0x%x", err);
1864 				delay(drmach_mbxerr_delay * hz);
1865 			}
1866 			continue;
1867 		}
1868 
1869 		drmach_mbox_prmsg(msg, 0);
1870 
1871 		if (php->drproto_version != DRMBX_VERSION) {
1872 			cmn_err(CE_WARN,
1873 			    "mailbox version mismatch 0x%x vs 0x%x",
1874 			    php->drproto_version, DRMBX_VERSION);
1875 
1876 			mutex_enter(&drmach_g_mbox_mutex);
1877 			drmach_mbox_iflag = 0;
1878 			/* schedule a reinit handshake if one isn't pending */
1879 			if (!drmach_mbox_ipending) {
1880 				if (taskq_dispatch(system_taskq,
1881 				    drmach_mbox_reinit, NULL, TQ_NOSLEEP)
1882 				    != NULL) {
1883 					drmach_mbox_ipending = 1;
1884 				} else {
1885 					cmn_err(CE_WARN, "failed to schedule "
1886 					    "mailbox reinit");
1887 				}
1888 			}
1889 			mutex_exit(&drmach_g_mbox_mutex);
1890 
1891 			continue;
1892 		}
1893 
1894 		msgid = php->message_id;
1895 		found = NULL;
1896 		mutex_enter(&drmach_msglist_mutex);
1897 		entry = drmach_msglist_first;
1898 		while (entry != NULL) {
1899 			if (entry->msgid == msgid) {
1900 				found = entry;
1901 				drmach_msglist_unlink(entry);
1902 				entry = NULL;
1903 			} else
1904 				entry = entry->next;
1905 		}
1906 
1907 		if (found) {
1908 			mutex_enter(&found->g_lock);
1909 
1910 			found->e_code = php->error_code;
1911 			if (found->i_buflen > 0)
1912 				bcopy((caddr_t)&msg->msgdata, found->i_buf,
1913 				    found->i_buflen);
1914 			found->m_reply = 1;
1915 
1916 			cv_signal(&found->g_cv);
1917 			mutex_exit(&found->g_lock);
1918 		} else {
1919 			cmn_err(CE_WARN, "!mbox_getmsg: no match for id 0x%x",
1920 			    msgid);
1921 			cmn_err(CE_WARN, "!    cmd = 0x%x, exb = %d, slot = %d",
1922 			    php->command, php->expbrd, php->slot);
1923 		}
1924 
1925 		mutex_exit(&drmach_msglist_mutex);
1926 	}
1927 	cmn_err(CE_WARN, "mbox_getmsg: exiting");
1928 	mutex_enter(&drmach_msglist_mutex);
1929 	entry = drmach_msglist_first;
1930 	while (entry != NULL) {
1931 		if (entry->p_flag == 1) {
1932 			entry->f_error = -1;
1933 			mutex_enter(&entry->g_lock);
1934 			cv_signal(&entry->g_cv);
1935 			mutex_exit(&entry->g_lock);
1936 			drmach_msglist_unlink(entry);
1937 		}
1938 		entry = entry->next;
1939 	}
1940 	mutex_exit(&drmach_msglist_mutex);
1941 	drmach_getmsg_thread_run = -1;
1942 	thread_exit();
1943 }
1944 
1945 void
1946 drmach_mbox_sendmsg()
1947 {
1948 	int		err, retry;
1949 	drmach_msglist_t *entry;
1950 	dr_mbox_msg_t   *mp;
1951 	dr_proto_hdr_t  *php;
1952 
1953 	while (drmach_sendmsg_thread_run != 0) {
1954 		/*
1955 		 * Search through the list to find entries awaiting
1956 		 * transmission to the SC
1957 		 */
1958 		mutex_enter(&drmach_msglist_mutex);
1959 		entry = drmach_msglist_first;
1960 		retry = 0;
1961 		while (entry != NULL) {
1962 			if (entry->p_flag == 1) {
1963 				entry = entry->next;
1964 				continue;
1965 			}
1966 
1967 			mutex_exit(&drmach_msglist_mutex);
1968 
1969 			if (!retry)
1970 				mutex_enter(&entry->s_lock);
1971 			mp = (dr_mbox_msg_t *)entry->o_buf;
1972 			php = &mp->p_hdr;
1973 
1974 			drmach_mbox_prmsg(mp, 1);
1975 
1976 			err = mboxsc_putmsg(KEY_DRSC, MBOXSC_MSG_REQUEST,
1977 			    php->command, NULL, entry->o_buflen, (void *)mp,
1978 			    drmach_to_putmsg);
1979 
1980 			if (err) {
1981 				switch (err) {
1982 
1983 				case EAGAIN:
1984 				case EBUSY:
1985 					++retry;
1986 					mutex_enter(&drmach_msglist_mutex);
1987 					continue;
1988 
1989 				case ETIMEDOUT:
1990 					if (--entry->o_nretry <= 0) {
1991 						mutex_enter(
1992 						    &drmach_msglist_mutex);
1993 						drmach_msglist_unlink(entry);
1994 						mutex_exit(
1995 						    &drmach_msglist_mutex);
1996 						entry->f_error = err;
1997 						entry->p_flag = 1;
1998 						cv_signal(&entry->s_cv);
1999 					} else {
2000 						++retry;
2001 						mutex_enter(
2002 						    &drmach_msglist_mutex);
2003 						continue;
2004 					}
2005 					break;
2006 				default:
2007 					mutex_enter(&drmach_msglist_mutex);
2008 					drmach_msglist_unlink(entry);
2009 					mutex_exit(&drmach_msglist_mutex);
2010 					entry->f_error = err;
2011 					entry->p_flag = 1;
2012 					cv_signal(&entry->s_cv);
2013 					break;
2014 				}
2015 			} else {
2016 				entry->p_flag = 1;
2017 				cv_signal(&entry->s_cv);
2018 			}
2019 
2020 			mutex_exit(&entry->s_lock);
2021 			retry = 0;
2022 			mutex_enter(&drmach_msglist_mutex);
2023 			entry = drmach_msglist_first;
2024 		}
2025 		mutex_exit(&drmach_msglist_mutex);
2026 
2027 		mutex_enter(&drmach_sendmsg_mutex);
2028 		(void) cv_reltimedwait(&drmach_sendmsg_cv,
2029 		    &drmach_sendmsg_mutex, (5 * hz), TR_CLOCK_TICK);
2030 		mutex_exit(&drmach_sendmsg_mutex);
2031 	}
2032 	cmn_err(CE_WARN, "mbox_sendmsg: exiting");
2033 	mutex_enter(&drmach_msglist_mutex);
2034 	entry = drmach_msglist_first;
2035 	while (entry != NULL) {
2036 		if (entry->p_flag == 0) {
2037 			entry->f_error = -1;
2038 			mutex_enter(&entry->s_lock);
2039 			cv_signal(&entry->s_cv);
2040 			mutex_exit(&entry->s_lock);
2041 			drmach_msglist_unlink(entry);
2042 		}
2043 		entry = entry->next;
2044 	}
2045 	mutex_exit(&drmach_msglist_mutex);
2046 	cv_destroy(&drmach_sendmsg_cv);
2047 	mutex_destroy(&drmach_sendmsg_mutex);
2048 
2049 	drmach_sendmsg_thread_run = -1;
2050 	thread_exit();
2051 }
2052 
2053 void
2054 drmach_msglist_destroy(drmach_msglist_t *listp)
2055 {
2056 	if (listp != NULL) {
2057 		drmach_msglist_t	*entry;
2058 
2059 		mutex_enter(&drmach_msglist_mutex);
2060 		entry = drmach_msglist_first;
2061 		while (entry) {
2062 			if (listp == entry) {
2063 				drmach_msglist_unlink(listp);
2064 				entry = NULL;
2065 			} else
2066 				entry = entry->next;
2067 		}
2068 
2069 		mutex_destroy(&listp->s_lock);
2070 		cv_destroy(&listp->s_cv);
2071 		mutex_destroy(&listp->g_lock);
2072 		cv_destroy(&listp->g_cv);
2073 		kmem_free(listp, sizeof (drmach_msglist_t));
2074 
2075 		mutex_exit(&drmach_msglist_mutex);
2076 	}
2077 }
2078 
2079 static drmach_msglist_t	*
2080 drmach_msglist_new(caddr_t ibufp, uint32_t ilen, dr_proto_hdr_t *hdrp,
2081 	uint32_t olen, int nrtry)
2082 {
2083 	drmach_msglist_t	*listp;
2084 
2085 	listp = kmem_zalloc(sizeof (drmach_msglist_t), KM_SLEEP);
2086 	mutex_init(&listp->s_lock, NULL, MUTEX_DRIVER, NULL);
2087 	cv_init(&listp->s_cv, NULL, CV_DRIVER, NULL);
2088 	mutex_init(&listp->g_lock, NULL, MUTEX_DRIVER, NULL);
2089 	cv_init(&listp->g_cv, NULL, CV_DRIVER, NULL);
2090 	listp->o_buf = (caddr_t)hdrp;
2091 	listp->o_buflen = olen;
2092 	listp->i_buf = ibufp;
2093 	listp->i_buflen = ilen;
2094 	listp->o_nretry = nrtry;
2095 	listp->msgid = hdrp->message_id;
2096 
2097 	return (listp);
2098 }
2099 
2100 static drmach_msglist_t *
2101 drmach_mbox_req_rply(dr_proto_hdr_t *hdrp, uint32_t olen, caddr_t ibufp,
2102 	uint32_t ilen, int timeout, int nrtry, int nosig,
2103 	drmach_msglist_t *link)
2104 {
2105 	int		crv;
2106 	drmach_msglist_t *listp;
2107 	clock_t		to_val;
2108 	dr_proto_hdr_t	*php;
2109 
2110 	/* setup transaction list entry */
2111 	listp = drmach_msglist_new(ibufp, ilen, hdrp, olen, nrtry);
2112 
2113 	/* send mailbox message, await reply */
2114 	mutex_enter(&listp->s_lock);
2115 	mutex_enter(&listp->g_lock);
2116 
2117 	listp->link = link;
2118 	drmach_msglist_link(listp);
2119 
2120 	mutex_enter(&drmach_sendmsg_mutex);
2121 	cv_signal(&drmach_sendmsg_cv);
2122 	mutex_exit(&drmach_sendmsg_mutex);
2123 
2124 	while (listp->p_flag == 0) {
2125 		cv_wait(&listp->s_cv, &listp->s_lock);
2126 	}
2127 
2128 	to_val = ddi_get_lbolt() + (timeout * hz);
2129 
2130 	if (listp->f_error) {
2131 		listp->p_flag = 0;
2132 		cmn_err(CE_WARN, "!mboxsc_putmsg failed: 0x%x", listp->f_error);
2133 		php = (dr_proto_hdr_t *)listp->o_buf;
2134 		cmn_err(CE_WARN, "!    cmd = 0x%x, exb = %d, slot = %d",
2135 		    php->command, php->expbrd, php->slot);
2136 	} else {
2137 		while (listp->m_reply == 0 && listp->f_error == 0) {
2138 			if (nosig)
2139 				crv = cv_timedwait(&listp->g_cv, &listp->g_lock,
2140 				    to_val);
2141 			else
2142 				crv = cv_timedwait_sig(&listp->g_cv,
2143 				    &listp->g_lock, to_val);
2144 			switch (crv) {
2145 				case -1: /* timed out */
2146 					cmn_err(CE_WARN,
2147 					    "!msgid=0x%x reply timed out",
2148 					    hdrp->message_id);
2149 					php = (dr_proto_hdr_t *)listp->o_buf;
2150 					cmn_err(CE_WARN, "!    cmd = 0x%x, "
2151 					    "exb = %d, slot = %d", php->command,
2152 					    php->expbrd, php->slot);
2153 					listp->f_error = ETIMEDOUT;
2154 					break;
2155 				case 0: /* signal received */
2156 					cmn_err(CE_WARN,
2157 					    "operation interrupted by signal");
2158 					listp->f_error = EINTR;
2159 					break;
2160 				default:
2161 					break;
2162 				}
2163 		}
2164 
2165 		/*
2166 		 * If link is set for this entry, check to see if
2167 		 * the linked entry has been replied to.  If not,
2168 		 * wait for the response.
2169 		 * Currently, this is only used for ABORT_TEST functionality,
2170 		 * wherein a check is made for the TESTBOARD reply when
2171 		 * the ABORT_TEST reply is received.
2172 		 */
2173 
2174 		if (link) {
2175 			mutex_enter(&link->g_lock);
2176 			/*
2177 			 * If the reply to the linked entry hasn't been
2178 			 * received, clear the existing link->f_error,
2179 			 * and await the reply.
2180 			 */
2181 			if (link->m_reply == 0) {
2182 				link->f_error = 0;
2183 			}
2184 			to_val =  ddi_get_lbolt() + (timeout * hz);
2185 			while (link->m_reply == 0 && link->f_error == 0) {
2186 				crv = cv_timedwait(&link->g_cv, &link->g_lock,
2187 				    to_val);
2188 				switch (crv) {
2189 				case -1: /* timed out */
2190 					cmn_err(CE_NOTE,
2191 					    "!link msgid=0x%x reply timed out",
2192 					    link->msgid);
2193 					link->f_error = ETIMEDOUT;
2194 					break;
2195 				default:
2196 					break;
2197 				}
2198 			}
2199 			mutex_exit(&link->g_lock);
2200 		}
2201 	}
2202 	mutex_exit(&listp->g_lock);
2203 	mutex_exit(&listp->s_lock);
2204 	return (listp);
2205 }
2206 
2207 static sbd_error_t *
2208 drmach_mbx2sbderr(drmach_msglist_t *mlp)
2209 {
2210 	char		a_pnt[MAXNAMELEN];
2211 	dr_proto_hdr_t	*php;
2212 	int		bnum;
2213 
2214 	if (mlp->f_error) {
2215 		/*
2216 		 * If framework failure is due to signal, return "no error"
2217 		 * error.
2218 		 */
2219 		if (mlp->f_error == EINTR)
2220 			return (drerr_new(0, ESTC_NONE, NULL));
2221 
2222 		mutex_enter(&drmach_g_mbox_mutex);
2223 		drmach_mbox_iflag = 0;
2224 		mutex_exit(&drmach_g_mbox_mutex);
2225 		if (!mlp->p_flag)
2226 			return (drerr_new(1, ESTC_MBXRQST, NULL));
2227 		else
2228 			return (drerr_new(1, ESTC_MBXRPLY, NULL));
2229 	}
2230 	php = (dr_proto_hdr_t *)mlp->o_buf;
2231 	bnum = 2 * php->expbrd + php->slot;
2232 	a_pnt[0] = '\0';
2233 	(void) drmach_board_name(bnum, a_pnt, MAXNAMELEN);
2234 
2235 	switch (mlp->e_code) {
2236 		case 0:
2237 			return (NULL);
2238 		case DRERR_NOACL:
2239 			return (drerr_new(0, ESTC_NOACL, "%s", a_pnt));
2240 		case DRERR_NOT_ASSIGNED:
2241 			return (drerr_new(0, ESTC_NOT_ASSIGNED, "%s", a_pnt));
2242 		case DRERR_NOT_ACTIVE:
2243 			return (drerr_new(0, ESTC_NOT_ACTIVE, "%s", a_pnt));
2244 		case DRERR_EMPTY_SLOT:
2245 			return (drerr_new(0, ESTC_EMPTY_SLOT, "%s", a_pnt));
2246 		case DRERR_POWER_OFF:
2247 			return (drerr_new(0, ESTC_POWER_OFF, "%s", a_pnt));
2248 		case DRERR_TEST_IN_PROGRESS:
2249 			return (drerr_new(0, ESTC_TEST_IN_PROGRESS, "%s",
2250 			    a_pnt));
2251 		case DRERR_TESTING_BUSY:
2252 			return (drerr_new(0, ESTC_TESTING_BUSY, "%s", a_pnt));
2253 		case DRERR_TEST_REQUIRED:
2254 			return (drerr_new(0, ESTC_TEST_REQUIRED, "%s", a_pnt));
2255 		case DRERR_UNAVAILABLE:
2256 			return (drerr_new(0, ESTC_UNAVAILABLE, "%s", a_pnt));
2257 		case DRERR_RECOVERABLE:
2258 			return (drerr_new(0, ESTC_SMS_ERR_RECOVERABLE, "%s",
2259 			    a_pnt));
2260 		case DRERR_UNRECOVERABLE:
2261 			return (drerr_new(1, ESTC_SMS_ERR_UNRECOVERABLE, "%s",
2262 			    a_pnt));
2263 		default:
2264 			return (drerr_new(1, ESTC_MBOX_UNKNOWN, NULL));
2265 	}
2266 }
2267 
2268 static sbd_error_t *
2269 drmach_mbox_trans(uint8_t msgtype, int bnum, caddr_t obufp, int olen,
2270 	caddr_t ibufp, int ilen)
2271 {
2272 	int			timeout = 0;
2273 	int			ntries = 0;
2274 	int			nosignals = 0;
2275 	dr_proto_hdr_t 		*hdrp;
2276 	drmach_msglist_t 	*mlp;
2277 	sbd_error_t		*err = NULL;
2278 
2279 	if (msgtype != DRMSG_MBOX_INIT) {
2280 		mutex_enter(&drmach_ri_mbox_mutex);
2281 		mutex_enter(&drmach_g_mbox_mutex);
2282 		if (drmach_mbox_iflag == 0) {
2283 			/* need to initialize the mailbox */
2284 			dr_proto_hdr_t	imsg;
2285 
2286 			mutex_exit(&drmach_g_mbox_mutex);
2287 
2288 			imsg.command = DRMSG_MBOX_INIT;
2289 
2290 			imsg.message_id = drmach_get_msgid();
2291 			imsg.drproto_version = DRMBX_VERSION;
2292 			imsg.expbrd = 0;
2293 			imsg.slot = 0;
2294 
2295 			cmn_err(CE_WARN, "!reinitializing DR mailbox");
2296 			mlp = drmach_mbox_req_rply(&imsg, sizeof (imsg), 0, 0,
2297 			    10, 5, 0, NULL);
2298 			err = drmach_mbx2sbderr(mlp);
2299 			/*
2300 			 * If framework failure incoming is encountered on
2301 			 * the MBOX_INIT [timeout on SMS reply], the error
2302 			 * type must be changed before returning to caller.
2303 			 * This is to prevent drmach_board_connect() and
2304 			 * drmach_board_disconnect() from marking boards
2305 			 * UNUSABLE based on MBOX_INIT failures.
2306 			 */
2307 			if ((err != NULL) && (err->e_code == ESTC_MBXRPLY)) {
2308 				cmn_err(CE_WARN,
2309 				    "!Changed mbox incoming to outgoing"
2310 				    " failure on reinit");
2311 				sbd_err_clear(&err);
2312 				err = drerr_new(0, ESTC_MBXRQST, NULL);
2313 			}
2314 			drmach_msglist_destroy(mlp);
2315 			if (err) {
2316 				mutex_exit(&drmach_ri_mbox_mutex);
2317 				return (err);
2318 			}
2319 			mutex_enter(&drmach_g_mbox_mutex);
2320 			drmach_mbox_iflag = 1;
2321 		}
2322 		mutex_exit(&drmach_g_mbox_mutex);
2323 		mutex_exit(&drmach_ri_mbox_mutex);
2324 	}
2325 
2326 	hdrp = (dr_proto_hdr_t *)obufp;
2327 
2328 	/* setup outgoing mailbox header */
2329 	hdrp->command = msgtype;
2330 	hdrp->message_id = drmach_get_msgid();
2331 	hdrp->drproto_version = DRMBX_VERSION;
2332 	hdrp->expbrd = DRMACH_BNUM2EXP(bnum);
2333 	hdrp->slot = DRMACH_BNUM2SLOT(bnum);
2334 
2335 	switch (msgtype) {
2336 
2337 		case DRMSG_MBOX_INIT:
2338 			timeout = drmach_to_mbxinit;
2339 			ntries = 1;
2340 			nosignals = 0;
2341 			break;
2342 
2343 		case DRMSG_ASSIGN:
2344 			timeout = drmach_to_assign;
2345 			ntries = 1;
2346 			nosignals = 0;
2347 			break;
2348 
2349 		case DRMSG_UNASSIGN:
2350 			timeout = drmach_to_unassign;
2351 			ntries = 1;
2352 			nosignals = 0;
2353 			break;
2354 
2355 		case DRMSG_POWERON:
2356 			timeout = drmach_to_poweron;
2357 			ntries = 1;
2358 			nosignals = 0;
2359 			break;
2360 
2361 		case DRMSG_POWEROFF:
2362 			timeout = drmach_to_poweroff;
2363 			ntries = 1;
2364 			nosignals = 0;
2365 			break;
2366 
2367 		case DRMSG_SHOWBOARD:
2368 			timeout = drmach_to_showboard;
2369 			ntries = 1;
2370 			nosignals = 0;
2371 			break;
2372 
2373 		case DRMSG_CLAIM:
2374 			timeout = drmach_to_claim;
2375 			ntries = 1;
2376 			nosignals = 1;
2377 			break;
2378 
2379 		case DRMSG_UNCLAIM:
2380 			timeout = drmach_to_unclaim;
2381 			ntries = 1;
2382 			nosignals = 1;
2383 			break;
2384 
2385 		case DRMSG_UNCONFIG:
2386 			timeout = drmach_to_unconfig;
2387 			ntries = 1;
2388 			nosignals = 0;
2389 			break;
2390 
2391 		case DRMSG_TESTBOARD:
2392 			timeout = drmach_to_testboard;
2393 			ntries = 1;
2394 			nosignals = 0;
2395 			break;
2396 
2397 		default:
2398 			cmn_err(CE_WARN, "Unknown outgoing message type 0x%x",
2399 			    msgtype);
2400 			err = DRMACH_INTERNAL_ERROR();
2401 			break;
2402 	}
2403 
2404 	if (err == NULL) {
2405 		mlp = drmach_mbox_req_rply(hdrp, olen, ibufp, ilen, timeout,
2406 		    ntries, nosignals, NULL);
2407 		err = drmach_mbx2sbderr(mlp);
2408 
2409 		/*
2410 		 * For DRMSG_TESTBOARD attempts which have timed out, or
2411 		 * been aborted due to a signal received after mboxsc_putmsg()
2412 		 * has succeeded in sending the message, a DRMSG_ABORT_TEST
2413 		 * must be sent.
2414 		 */
2415 		if ((msgtype == DRMSG_TESTBOARD) && (err != NULL) &&
2416 		    ((mlp->f_error == EINTR) || ((mlp->f_error == ETIMEDOUT) &&
2417 		    (mlp->p_flag != 0)))) {
2418 			drmach_msglist_t	*abmlp;
2419 			dr_abort_test_t		abibuf;
2420 
2421 			hdrp->command = DRMSG_ABORT_TEST;
2422 			hdrp->message_id = drmach_get_msgid();
2423 			abmlp = drmach_mbox_req_rply(hdrp,
2424 			    sizeof (dr_abort_test_t), (caddr_t)&abibuf,
2425 			    sizeof (abibuf), drmach_to_aborttest, 5, 1, mlp);
2426 			cmn_err(CE_WARN, "test aborted");
2427 			drmach_msglist_destroy(abmlp);
2428 		}
2429 
2430 		drmach_msglist_destroy(mlp);
2431 	}
2432 
2433 	return (err);
2434 }
2435 
2436 static int
2437 drmach_mbox_init()
2438 {
2439 	int			err;
2440 	caddr_t			obufp;
2441 	sbd_error_t		*serr = NULL;
2442 	mboxsc_timeout_range_t	mbxtoz;
2443 
2444 	drmach_mbox_istate = 0;
2445 	/* register the outgoing mailbox */
2446 	if ((err = mboxsc_init(KEY_DRSC, MBOXSC_MBOX_OUT,
2447 	    NULL)) != 0) {
2448 		cmn_err(CE_WARN, "DR - SC mboxsc_init failed: 0x%x", err);
2449 		return (-1);
2450 	}
2451 	drmach_mbox_istate = 1;
2452 
2453 	/* setup the mboxsc_putmsg timeout value */
2454 	if (drmach_use_tuned_putmsg_to) {
2455 		cmn_err(CE_NOTE, "!using tuned drmach_to_putmsg = 0x%lx\n",
2456 		    drmach_to_putmsg);
2457 	} else {
2458 		if ((err = mboxsc_ctrl(KEY_DRSC,
2459 		    MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE, &mbxtoz)) != 0) {
2460 			cmn_err(CE_WARN, "mboxsc_ctrl failed: 0x%x", err);
2461 			drmach_to_putmsg = 60000;
2462 		} else {
2463 			drmach_to_putmsg = mboxsc_putmsg_def_timeout() * 6;
2464 			DRMACH_PR("putmsg range is 0x%lx - 0x%lx value"
2465 			    " is 0x%lx\n", mbxtoz.min_timeout,
2466 			    mbxtoz.max_timeout, drmach_to_putmsg);
2467 		}
2468 	}
2469 
2470 	/* register the incoming mailbox */
2471 	if ((err = mboxsc_init(KEY_SCDR, MBOXSC_MBOX_IN,
2472 	    drmach_mbox_event)) != 0) {
2473 		cmn_err(CE_WARN, "SC - DR mboxsc_init failed: 0x%x", err);
2474 		return (-1);
2475 	}
2476 	drmach_mbox_istate = 2;
2477 
2478 	/* initialize mutex for mailbox globals */
2479 	mutex_init(&drmach_g_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
2480 
2481 	/* initialize mutex for mailbox re-init */
2482 	mutex_init(&drmach_ri_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
2483 
2484 	/* initialize mailbox message list elements */
2485 	drmach_msglist_first = drmach_msglist_last = NULL;
2486 	mutex_init(&drmach_msglist_mutex, NULL, MUTEX_DRIVER, NULL);
2487 
2488 	mutex_init(&drmach_sendmsg_mutex, NULL, MUTEX_DRIVER, NULL);
2489 	cv_init(&drmach_sendmsg_cv, NULL, CV_DRIVER, NULL);
2490 
2491 	drmach_mbox_istate = 3;
2492 
2493 	/* start mailbox sendmsg thread */
2494 	drmach_sendmsg_thread_run = 1;
2495 	if (drmach_sendmsg_thread == NULL)
2496 		drmach_sendmsg_thread = thread_create(NULL, 0,
2497 		    (void (*)())drmach_mbox_sendmsg, NULL, 0, &p0,
2498 		    TS_RUN, minclsyspri);
2499 
2500 	/* start mailbox getmsg thread */
2501 	drmach_getmsg_thread_run = 1;
2502 	if (drmach_getmsg_thread == NULL)
2503 		drmach_getmsg_thread = thread_create(NULL, 0,
2504 		    (void (*)())drmach_mbox_getmsg, NULL, 0, &p0,
2505 		    TS_RUN, minclsyspri);
2506 
2507 	obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
2508 	serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
2509 	    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
2510 	kmem_free(obufp, sizeof (dr_proto_hdr_t));
2511 	if (serr) {
2512 		cmn_err(CE_WARN, "mbox_init: MBOX_INIT failed ecode=0x%x",
2513 		    serr->e_code);
2514 		sbd_err_clear(&serr);
2515 		return (-1);
2516 	}
2517 	mutex_enter(&drmach_g_mbox_mutex);
2518 	drmach_mbox_iflag = 1;
2519 	drmach_mbox_ipending = 0;
2520 	mutex_exit(&drmach_g_mbox_mutex);
2521 
2522 	return (0);
2523 }
2524 
2525 static int
2526 drmach_mbox_fini()
2527 {
2528 	int err, rv = 0;
2529 
2530 	if (drmach_mbox_istate > 2) {
2531 		drmach_getmsg_thread_run = 0;
2532 		drmach_sendmsg_thread_run = 0;
2533 		cmn_err(CE_WARN,
2534 		    "drmach_mbox_fini: waiting for mbox threads...");
2535 		while ((drmach_getmsg_thread_run == 0) ||
2536 		    (drmach_sendmsg_thread_run == 0)) {
2537 			continue;
2538 		}
2539 		cmn_err(CE_WARN, "drmach_mbox_fini: mbox threads done.");
2540 		mutex_destroy(&drmach_msglist_mutex);
2541 
2542 	}
2543 	if (drmach_mbox_istate) {
2544 		/* de-register the outgoing mailbox */
2545 		if ((err = mboxsc_fini(KEY_DRSC)) != 0) {
2546 			cmn_err(CE_WARN, "DR - SC mboxsc_fini failed: 0x%x",
2547 			    err);
2548 			rv = -1;
2549 		}
2550 	}
2551 	if (drmach_mbox_istate > 1) {
2552 		/* de-register the incoming mailbox */
2553 		if ((err = mboxsc_fini(KEY_SCDR)) != 0) {
2554 			cmn_err(CE_WARN, "SC - DR mboxsc_fini failed: 0x%x",
2555 			    err);
2556 			rv = -1;
2557 		}
2558 	}
2559 	mutex_destroy(&drmach_g_mbox_mutex);
2560 	mutex_destroy(&drmach_ri_mbox_mutex);
2561 	return (rv);
2562 }
2563 
2564 static int
2565 drmach_portid2bnum(int portid)
2566 {
2567 	int slot;
2568 
2569 	switch (portid & 0x1f) {
2570 	case 0: case 1: case 2: case 3:	/* cpu/wci devices */
2571 	case 0x1e:			/* slot 0 axq registers */
2572 		slot = 0;
2573 		break;
2574 
2575 	case 8: case 9:			/* cpu devices */
2576 	case 0x1c: case 0x1d:		/* schizo/wci devices */
2577 	case 0x1f:			/* slot 1 axq registers */
2578 		slot = 1;
2579 		break;
2580 
2581 	default:
2582 		ASSERT(0);		/* catch in debug kernels */
2583 	}
2584 
2585 	return (((portid >> 4) & 0x7e) | slot);
2586 }
2587 
2588 extern int axq_suspend_iopause;
2589 
2590 static int
2591 hold_rele_branch(dev_info_t *rdip, void *arg)
2592 {
2593 	int	i;
2594 	int	*holdp	= (int *)arg;
2595 	char	*name = ddi_node_name(rdip);
2596 
2597 	/*
2598 	 * For Starcat, we must be children of the root devinfo node
2599 	 */
2600 	ASSERT(ddi_get_parent(rdip) == ddi_root_node());
2601 
2602 	i = drmach_name2type_idx(name);
2603 
2604 	/*
2605 	 * Only children of the root devinfo node need to be
2606 	 * held/released since they are the only valid targets
2607 	 * of tree operations. This corresponds to the node types
2608 	 * listed in the drmach_name2type array.
2609 	 */
2610 	if (i < 0) {
2611 		/* Not of interest to us */
2612 		return (DDI_WALK_PRUNECHILD);
2613 	}
2614 
2615 	if (*holdp) {
2616 		ASSERT(!e_ddi_branch_held(rdip));
2617 		e_ddi_branch_hold(rdip);
2618 	} else {
2619 		ASSERT(e_ddi_branch_held(rdip));
2620 		e_ddi_branch_rele(rdip);
2621 	}
2622 
2623 	return (DDI_WALK_PRUNECHILD);
2624 }
2625 
2626 static int
2627 drmach_init(void)
2628 {
2629 	pnode_t 	nodeid;
2630 	gdcd_t		*gdcd;
2631 	int		bnum;
2632 	dev_info_t	*rdip;
2633 	int		hold, circ;
2634 
2635 	mutex_enter(&drmach_i_lock);
2636 	if (drmach_initialized) {
2637 		mutex_exit(&drmach_i_lock);
2638 		return (0);
2639 	}
2640 
2641 	gdcd = drmach_gdcd_new();
2642 	if (gdcd == NULL) {
2643 		mutex_exit(&drmach_i_lock);
2644 		cmn_err(CE_WARN, "drmach_init: failed to access GDCD\n");
2645 		return (-1);
2646 	}
2647 
2648 	drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
2649 
2650 	nodeid = prom_childnode(prom_rootnode());
2651 	do {
2652 		int		 len;
2653 		int		 portid;
2654 		drmachid_t	 id;
2655 
2656 		len = prom_getproplen(nodeid, "portid");
2657 		if (len != sizeof (portid))
2658 			continue;
2659 
2660 		portid = -1;
2661 		(void) prom_getprop(nodeid, "portid", (caddr_t)&portid);
2662 		if (portid == -1)
2663 			continue;
2664 
2665 		bnum = drmach_portid2bnum(portid);
2666 
2667 		if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
2668 			/* portid translated to an invalid board number */
2669 			cmn_err(CE_WARN, "OBP node 0x%x has"
2670 			    " invalid property value, %s=%u",
2671 			    nodeid, "portid", portid);
2672 
2673 			/* clean up */
2674 			drmach_array_dispose(drmach_boards,
2675 			    drmach_board_dispose);
2676 			drmach_gdcd_dispose(gdcd);
2677 			mutex_exit(&drmach_i_lock);
2678 			return (-1);
2679 		} else if (id == NULL) {
2680 			drmach_board_t	*bp;
2681 			l1_slot_stat_t	*dcd;
2682 			int		exp, slot;
2683 
2684 			bp = drmach_board_new(bnum);
2685 			bp->assigned = !drmach_initialized;
2686 			bp->powered = !drmach_initialized;
2687 
2688 			exp = DRMACH_BNUM2EXP(bnum);
2689 			slot = DRMACH_BNUM2SLOT(bnum);
2690 			dcd = &gdcd->dcd_slot[exp][slot];
2691 			bp->stardrb_offset =
2692 			    dcd->l1ss_cpu_drblock_xwd_offset << 3;
2693 			DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
2694 			    bp->stardrb_offset);
2695 
2696 			if (gdcd->dcd_slot[exp][slot].l1ss_flags &
2697 			    L1SSFLG_THIS_L1_NULL_PROC_LPA) {
2698 				bp->flags |= DRMACH_NULL_PROC_LPA;
2699 				DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
2700 			}
2701 		}
2702 	} while ((nodeid = prom_nextnode(nodeid)) != OBP_NONODE);
2703 
2704 	drmach_cpu_sram_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
2705 
2706 	if (gdcd->dcd_testcage_log2_mbytes_size != DCD_DR_TESTCAGE_DISABLED) {
2707 		ASSERT(gdcd->dcd_testcage_log2_mbytes_size ==
2708 		    gdcd->dcd_testcage_log2_mbytes_align);
2709 		drmach_iocage_paddr =
2710 		    (uint64_t)gdcd->dcd_testcage_mbyte_PA << 20;
2711 		drmach_iocage_size =
2712 		    1 << (gdcd->dcd_testcage_log2_mbytes_size + 20);
2713 
2714 		drmach_iocage_vaddr = (caddr_t)vmem_alloc(heap_arena,
2715 		    drmach_iocage_size, VM_SLEEP);
2716 		hat_devload(kas.a_hat, drmach_iocage_vaddr, drmach_iocage_size,
2717 		    mmu_btop(drmach_iocage_paddr),
2718 		    PROT_READ | PROT_WRITE,
2719 		    HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
2720 
2721 		DRMACH_PR("gdcd size=0x%x align=0x%x PA=0x%x\n",
2722 		    gdcd->dcd_testcage_log2_mbytes_size,
2723 		    gdcd->dcd_testcage_log2_mbytes_align,
2724 		    gdcd->dcd_testcage_mbyte_PA);
2725 		DRMACH_PR("drmach size=0x%x PA=0x%lx VA=0x%p\n",
2726 		    drmach_iocage_size, drmach_iocage_paddr,
2727 		    (void *)drmach_iocage_vaddr);
2728 	}
2729 
2730 	if (drmach_iocage_size == 0) {
2731 		drmach_array_dispose(drmach_boards, drmach_board_dispose);
2732 		drmach_boards = NULL;
2733 		vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
2734 		drmach_gdcd_dispose(gdcd);
2735 		mutex_exit(&drmach_i_lock);
2736 		cmn_err(CE_WARN, "drmach_init: iocage not available\n");
2737 		return (-1);
2738 	}
2739 
2740 	drmach_gdcd_dispose(gdcd);
2741 
2742 	mutex_init(&drmach_iocage_lock, NULL, MUTEX_DRIVER, NULL);
2743 	cv_init(&drmach_iocage_cv, NULL, CV_DRIVER, NULL);
2744 	mutex_init(&drmach_xt_mb_lock, NULL, MUTEX_DRIVER, NULL);
2745 	mutex_init(&drmach_bus_sync_lock, NULL, MUTEX_DRIVER, NULL);
2746 	mutex_init(&drmach_slice_table_lock, NULL, MUTEX_DRIVER, NULL);
2747 
2748 	mutex_enter(&cpu_lock);
2749 	mutex_enter(&drmach_iocage_lock);
2750 	ASSERT(drmach_iocage_is_busy == 0);
2751 	drmach_iocage_is_busy = 1;
2752 	drmach_iocage_mem_scrub(drmach_iocage_size);
2753 	drmach_iocage_is_busy = 0;
2754 	cv_signal(&drmach_iocage_cv);
2755 	mutex_exit(&drmach_iocage_lock);
2756 	mutex_exit(&cpu_lock);
2757 
2758 
2759 	if (drmach_mbox_init() == -1) {
2760 		cmn_err(CE_WARN, "DR - SC mailbox initialization Failed");
2761 	}
2762 
2763 	/*
2764 	 * Walk immediate children of devinfo root node and hold
2765 	 * all devinfo branches of interest.
2766 	 */
2767 	hold = 1;
2768 	rdip = ddi_root_node();
2769 
2770 	ndi_devi_enter(rdip, &circ);
2771 	ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
2772 	ndi_devi_exit(rdip, circ);
2773 
2774 	drmach_initialized = 1;
2775 
2776 	/*
2777 	 * To avoid a circular patch dependency between DR and AXQ, the AXQ
2778 	 * rev introducing the axq_iopause_*_all interfaces should not regress
2779 	 * when installed without the DR rev using those interfaces. The default
2780 	 * is for iopause to be enabled/disabled during axq suspend/resume. By
2781 	 * setting the following axq flag to zero, axq will not enable iopause
2782 	 * during suspend/resume, instead DR will call the axq_iopause_*_all
2783 	 * interfaces during drmach_copy_rename.
2784 	 */
2785 	axq_suspend_iopause = 0;
2786 
2787 	mutex_exit(&drmach_i_lock);
2788 
2789 	return (0);
2790 }
2791 
2792 static void
2793 drmach_fini(void)
2794 {
2795 	dev_info_t	*rdip;
2796 	int		hold, circ;
2797 
2798 	if (drmach_initialized) {
2799 		rw_enter(&drmach_boards_rwlock, RW_WRITER);
2800 		drmach_array_dispose(drmach_boards, drmach_board_dispose);
2801 		drmach_boards = NULL;
2802 		rw_exit(&drmach_boards_rwlock);
2803 
2804 		mutex_destroy(&drmach_slice_table_lock);
2805 		mutex_destroy(&drmach_xt_mb_lock);
2806 		mutex_destroy(&drmach_bus_sync_lock);
2807 		cv_destroy(&drmach_iocage_cv);
2808 		mutex_destroy(&drmach_iocage_lock);
2809 
2810 		vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
2811 
2812 		/*
2813 		 * Walk immediate children of the root devinfo node
2814 		 * releasing holds acquired on branches in drmach_init()
2815 		 */
2816 		hold = 0;
2817 		rdip = ddi_root_node();
2818 
2819 		ndi_devi_enter(rdip, &circ);
2820 		ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
2821 		ndi_devi_exit(rdip, circ);
2822 
2823 		drmach_initialized = 0;
2824 	}
2825 
2826 	(void) drmach_mbox_fini();
2827 	if (drmach_xt_mb != NULL) {
2828 		vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
2829 		    drmach_xt_mb_size);
2830 	}
2831 	rw_destroy(&drmach_boards_rwlock);
2832 	mutex_destroy(&drmach_i_lock);
2833 }
2834 
2835 static void
2836 drmach_mem_read_madr(drmach_mem_t *mp, int bank, uint64_t *madr)
2837 {
2838 	kpreempt_disable();
2839 
2840 	/* get register address, read madr value */
2841 	if (STARCAT_CPUID_TO_PORTID(CPU->cpu_id) == mp->dev.portid) {
2842 		*madr = lddmcdecode(DRMACH_MC_ASI_ADDR(mp, bank));
2843 	} else {
2844 		*madr = lddphysio(DRMACH_MC_ADDR(mp, bank));
2845 	}
2846 
2847 	kpreempt_enable();
2848 }
2849 
2850 
2851 static uint64_t *
2852 drmach_prep_mc_rename(uint64_t *p, int local,
2853 	drmach_mem_t *mp, uint64_t current_basepa, uint64_t new_basepa)
2854 {
2855 	int bank;
2856 
2857 	for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
2858 		uint64_t madr, bank_offset;
2859 
2860 		/* fetch mc's bank madr register value */
2861 		drmach_mem_read_madr(mp, bank, &madr);
2862 		if (madr & DRMACH_MC_VALID_MASK) {
2863 			uint64_t bankpa;
2864 
2865 			bank_offset = (DRMACH_MC_UM_TO_PA(madr) |
2866 			    DRMACH_MC_LM_TO_PA(madr)) - current_basepa;
2867 			bankpa = new_basepa + bank_offset;
2868 
2869 			/* encode new base pa into madr */
2870 			madr &= ~DRMACH_MC_UM_MASK;
2871 			madr |= DRMACH_MC_PA_TO_UM(bankpa);
2872 			madr &= ~DRMACH_MC_LM_MASK;
2873 			madr |= DRMACH_MC_PA_TO_LM(bankpa);
2874 
2875 			if (local)
2876 				*p++ = DRMACH_MC_ASI_ADDR(mp, bank);
2877 			else
2878 				*p++ = DRMACH_MC_ADDR(mp, bank);
2879 
2880 			*p++ = madr;
2881 		}
2882 	}
2883 
2884 	return (p);
2885 }
2886 
2887 static uint64_t *
2888 drmach_prep_schizo_script(uint64_t *p, drmach_mem_t *mp, uint64_t new_basepa)
2889 {
2890 	drmach_board_t	*bp;
2891 	int		 rv;
2892 	int		 idx;
2893 	drmachid_t	 id;
2894 	uint64_t	 last_scsr_pa = 0;
2895 
2896 	/* memory is always in slot 0 */
2897 	ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
2898 
2899 	/* look up slot 1 board on same expander */
2900 	idx = DRMACH_EXPSLOT2BNUM(DRMACH_BNUM2EXP(mp->dev.bp->bnum), 1);
2901 	rv = drmach_array_get(drmach_boards, idx, &id);
2902 	bp = id; /* bp will be NULL if board not found */
2903 
2904 	/* look up should never be out of bounds */
2905 	ASSERT(rv == 0);
2906 
2907 	/* nothing to do when board is not found or has no devices */
2908 	if (rv == -1 || bp == NULL || bp->devices == NULL)
2909 		return (p);
2910 
2911 	rv = drmach_array_first(bp->devices, &idx, &id);
2912 	while (rv == 0) {
2913 		if (DRMACH_IS_IO_ID(id)) {
2914 			drmach_io_t *io = id;
2915 
2916 			/*
2917 			 * Skip all non-Schizo IO devices (only IO nodes
2918 			 * that are Schizo devices have non-zero scsr_pa).
2919 			 * Filter out "other" leaf to avoid writing to the
2920 			 * same Schizo Control/Status Register twice.
2921 			 */
2922 			if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
2923 				uint64_t scsr;
2924 
2925 				scsr  = lddphysio(io->scsr_pa);
2926 				scsr &= ~(DRMACH_LPA_BASE_MASK |
2927 				    DRMACH_LPA_BND_MASK);
2928 				scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
2929 				scsr |= DRMACH_PA_TO_LPA_BND(
2930 				    new_basepa + DRMACH_MEM_SLICE_SIZE);
2931 
2932 				*p++ = io->scsr_pa;
2933 				*p++ = scsr;
2934 
2935 				last_scsr_pa = io->scsr_pa;
2936 			}
2937 		}
2938 		rv = drmach_array_next(bp->devices, &idx, &id);
2939 	}
2940 
2941 	return (p);
2942 }
2943 
2944 /*
2945  * For Panther MCs, append the MC idle reg address and drmach_mem_t pointer.
2946  * The latter is returned when drmach_rename fails to idle a Panther MC and
2947  * is used to identify the MC for error reporting.
2948  */
2949 static uint64_t *
2950 drmach_prep_pn_mc_idle(uint64_t *p, drmach_mem_t *mp, int local)
2951 {
2952 	/* only slot 0 has memory */
2953 	ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
2954 	ASSERT(IS_PANTHER(mp->dev.bp->cpu_impl));
2955 
2956 	for (mp = mp->dev.bp->mem; mp != NULL; mp = mp->next) {
2957 		ASSERT(DRMACH_IS_MEM_ID(mp));
2958 
2959 		if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
2960 			if (local) {
2961 				*p++ = ASI_EMU_ACT_STATUS_VA;	/* local ASI */
2962 				*p++ = (uintptr_t)mp;
2963 			}
2964 		} else if (!local) {
2965 			*p++ = DRMACH_EMU_ACT_STATUS_ADDR(mp);	/* PIO */
2966 			*p++ = (uintptr_t)mp;
2967 		}
2968 	}
2969 
2970 	return (p);
2971 }
2972 
2973 static sbd_error_t *
2974 drmach_prep_rename_script(drmach_mem_t *s_mp, drmach_mem_t *t_mp,
2975 	uint64_t t_slice_offset, caddr_t buf, int buflen)
2976 {
2977 	_NOTE(ARGUNUSED(buflen))
2978 
2979 	uint64_t		*p = (uint64_t *)buf, *q;
2980 	sbd_error_t		*err;
2981 	int			 rv;
2982 	drmach_mem_t		*mp, *skip_mp;
2983 	uint64_t		 s_basepa, t_basepa;
2984 	uint64_t		 s_new_basepa, t_new_basepa;
2985 
2986 	/* verify supplied buffer space is adequate */
2987 	ASSERT(buflen >=
2988 	    /* addr for all possible MC banks */
2989 	    (sizeof (uint64_t) * 4 * 4 * 18) +
2990 	    /* list section terminator */
2991 	    (sizeof (uint64_t) * 1) +
2992 	    /* addr/id tuple for local Panther MC idle reg */
2993 	    (sizeof (uint64_t) * 2) +
2994 	    /* list section terminator */
2995 	    (sizeof (uint64_t) * 1) +
2996 	    /* addr/id tuple for 2 boards with 4 Panther MC idle regs */
2997 	    (sizeof (uint64_t) * 2 * 2 * 4) +
2998 	    /* list section terminator */
2999 	    (sizeof (uint64_t) * 1) +
3000 	    /* addr/val tuple for 1 proc with 4 MC banks */
3001 	    (sizeof (uint64_t) * 2 * 4) +
3002 	    /* list section terminator */
3003 	    (sizeof (uint64_t) * 1) +
3004 	    /* addr/val tuple for 2 boards w/ 2 schizos each */
3005 	    (sizeof (uint64_t) * 2 * 2 * 2) +
3006 	    /* addr/val tuple for 2 boards w/ 16 MC banks each */
3007 	    (sizeof (uint64_t) * 2 * 2 * 16) +
3008 	    /* list section terminator */
3009 	    (sizeof (uint64_t) * 1) +
3010 	    /* addr/val tuple for 18 AXQs w/ two slots each */
3011 	    (sizeof (uint64_t) * 2 * 2 * 18) +
3012 	    /* list section terminator */
3013 	    (sizeof (uint64_t) * 1) +
3014 	    /* list terminator */
3015 	    (sizeof (uint64_t) * 1));
3016 
3017 	/* copy bank list to rename script */
3018 	mutex_enter(&drmach_bus_sync_lock);
3019 	for (q = drmach_bus_sync_list; *q; q++, p++)
3020 		*p = *q;
3021 	mutex_exit(&drmach_bus_sync_lock);
3022 
3023 	/* list section terminator */
3024 	*p++ = 0;
3025 
3026 	/*
3027 	 * Write idle script for MC on this processor.  A script will be
3028 	 * produced only if this is a Panther processor on the source or
3029 	 * target board.
3030 	 */
3031 	if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
3032 		p = drmach_prep_pn_mc_idle(p, s_mp, 1);
3033 
3034 	if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
3035 		p = drmach_prep_pn_mc_idle(p, t_mp, 1);
3036 
3037 	/* list section terminator */
3038 	*p++ = 0;
3039 
3040 	/*
3041 	 * Write idle script for all other MCs on source and target
3042 	 * Panther boards.
3043 	 */
3044 	if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
3045 		p = drmach_prep_pn_mc_idle(p, s_mp, 0);
3046 
3047 	if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
3048 		p = drmach_prep_pn_mc_idle(p, t_mp, 0);
3049 
3050 	/* list section terminator */
3051 	*p++ = 0;
3052 
3053 	/*
3054 	 * Step 1:	Write source base address to target MC
3055 	 *		with present bit off.
3056 	 * Step 2:	Now rewrite target reg with present bit on.
3057 	 */
3058 	err = drmach_mem_get_base_physaddr(s_mp, &s_basepa);
3059 	ASSERT(err == NULL);
3060 	err = drmach_mem_get_base_physaddr(t_mp, &t_basepa);
3061 	ASSERT(err == NULL);
3062 
3063 	/* exchange base pa. include slice offset in new target base pa */
3064 	s_new_basepa = t_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1);
3065 	t_new_basepa = (s_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1)) +
3066 	    t_slice_offset;
3067 
3068 	DRMACH_PR("s_new_basepa 0x%lx\n", s_new_basepa);
3069 	DRMACH_PR("t_new_basepa 0x%lx\n", t_new_basepa);
3070 
3071 	DRMACH_PR("preparing MC MADR rename script (master is CPU%d):\n",
3072 	    CPU->cpu_id);
3073 
3074 	/*
3075 	 * Write rename script for MC on this processor.  A script will
3076 	 * be produced only if this processor is on the source or target
3077 	 * board.
3078 	 */
3079 
3080 	skip_mp = NULL;
3081 	mp = s_mp->dev.bp->mem;
3082 	while (mp != NULL && skip_mp == NULL) {
3083 		if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
3084 			skip_mp = mp;
3085 			p = drmach_prep_mc_rename(p, 1, mp, s_basepa,
3086 			    s_new_basepa);
3087 		}
3088 
3089 		mp = mp->next;
3090 	}
3091 
3092 	mp = t_mp->dev.bp->mem;
3093 	while (mp != NULL && skip_mp == NULL) {
3094 		if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
3095 			skip_mp = mp;
3096 			p = drmach_prep_mc_rename(p, 1, mp, t_basepa,
3097 			    t_new_basepa);
3098 		}
3099 
3100 		mp = mp->next;
3101 	}
3102 
3103 	/* list section terminator */
3104 	*p++ = 0;
3105 
3106 	/*
3107 	 * Write rename script for all other MCs on source and target
3108 	 * boards.
3109 	 */
3110 
3111 	for (mp = s_mp->dev.bp->mem; mp; mp = mp->next) {
3112 		if (mp == skip_mp)
3113 			continue;
3114 		p = drmach_prep_mc_rename(p, 0, mp, s_basepa, s_new_basepa);
3115 	}
3116 
3117 	for (mp = t_mp->dev.bp->mem; mp; mp = mp->next) {
3118 		if (mp == skip_mp)
3119 			continue;
3120 		p = drmach_prep_mc_rename(p, 0, mp, t_basepa, t_new_basepa);
3121 	}
3122 
3123 	/* Write rename script for Schizo LPA_BASE/LPA_BND */
3124 	p = drmach_prep_schizo_script(p, s_mp, s_new_basepa);
3125 	p = drmach_prep_schizo_script(p, t_mp, t_new_basepa);
3126 
3127 	/* list section terminator */
3128 	*p++ = 0;
3129 
3130 	DRMACH_PR("preparing AXQ CASM rename script (EXP%d <> EXP%d):\n",
3131 	    DRMACH_BNUM2EXP(s_mp->dev.bp->bnum),
3132 	    DRMACH_BNUM2EXP(t_mp->dev.bp->bnum));
3133 
3134 	rv = axq_do_casm_rename_script(&p,
3135 	    DRMACH_PA_TO_SLICE(s_new_basepa),
3136 	    DRMACH_PA_TO_SLICE(t_new_basepa));
3137 	if (rv == DDI_FAILURE)
3138 		return (DRMACH_INTERNAL_ERROR());
3139 
3140 	/* list section & final terminator */
3141 	*p++ = 0;
3142 	*p++ = 0;
3143 
3144 #ifdef DEBUG
3145 	{
3146 		uint64_t *q = (uint64_t *)buf;
3147 
3148 		/* paranoia */
3149 		ASSERT((caddr_t)p <= buf + buflen);
3150 
3151 		DRMACH_PR("MC bank base pa list:\n");
3152 		while (*q) {
3153 			uint64_t a = *q++;
3154 
3155 			DRMACH_PR("0x%lx\n", a);
3156 		}
3157 
3158 		/* skip terminator */
3159 		q += 1;
3160 
3161 		DRMACH_PR("local Panther MC idle reg (via ASI 0x4a):\n");
3162 		while (*q) {
3163 			DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
3164 			q += 2;
3165 		}
3166 
3167 		/* skip terminator */
3168 		q += 1;
3169 
3170 		DRMACH_PR("non-local Panther MC idle reg (via ASI 0x15):\n");
3171 		while (*q) {
3172 			DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
3173 			q += 2;
3174 		}
3175 
3176 		/* skip terminator */
3177 		q += 1;
3178 
3179 		DRMACH_PR("MC reprogramming script (via ASI 0x72):\n");
3180 		while (*q) {
3181 			uint64_t r = *q++;	/* register address */
3182 			uint64_t v = *q++;	/* new register value */
3183 
3184 			DRMACH_PR("0x%lx = 0x%lx, basepa 0x%lx\n",
3185 			    r, v, (long)(DRMACH_MC_UM_TO_PA(v)|
3186 			    DRMACH_MC_LM_TO_PA(v)));
3187 		}
3188 
3189 		/* skip terminator */
3190 		q += 1;
3191 
3192 		DRMACH_PR("MC/SCHIZO reprogramming script:\n");
3193 		while (*q) {
3194 			DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
3195 			q += 2;
3196 		}
3197 
3198 		/* skip terminator */
3199 		q += 1;
3200 
3201 		DRMACH_PR("AXQ reprogramming script:\n");
3202 		while (*q) {
3203 			DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
3204 			q += 2;
3205 		}
3206 
3207 		/* verify final terminator is present */
3208 		ASSERT(*(q + 1) == 0);
3209 
3210 		DRMACH_PR("copy-rename script 0x%p, len %d\n",
3211 		    (void *)buf, (int)((intptr_t)p - (intptr_t)buf));
3212 
3213 		if (drmach_debug)
3214 			DELAY(10000000);
3215 	}
3216 #endif
3217 
3218 	return (NULL);
3219 }
3220 
3221 static void
3222 drmach_prep_xt_mb_for_slice_update(drmach_board_t *bp, uchar_t slice)
3223 {
3224 	int		 rv;
3225 
3226 	ASSERT(MUTEX_HELD(&drmach_xt_mb_lock));
3227 
3228 	if (bp->devices) {
3229 		int		 d_idx;
3230 		drmachid_t	 d_id;
3231 
3232 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
3233 		while (rv == 0) {
3234 			if (DRMACH_IS_CPU_ID(d_id)) {
3235 				drmach_cpu_t	*cp = d_id;
3236 				processorid_t	 cpuid = cp->cpuid;
3237 
3238 				mutex_enter(&cpu_lock);
3239 				if (cpu[cpuid] && cpu[cpuid]->cpu_flags)
3240 					drmach_xt_mb[cpuid] = 0x80 | slice;
3241 				mutex_exit(&cpu_lock);
3242 			}
3243 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
3244 		}
3245 	}
3246 	if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
3247 		drmach_board_t	*s1bp = NULL;
3248 
3249 		rv = drmach_array_get(drmach_boards, bp->bnum + 1,
3250 		    (void *) &s1bp);
3251 		if (rv == 0 && s1bp != NULL) {
3252 			ASSERT(DRMACH_IS_BOARD_ID(s1bp));
3253 			ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
3254 			drmach_prep_xt_mb_for_slice_update(s1bp, slice);
3255 		}
3256 	}
3257 }
3258 
3259 sbd_error_t *
3260 drmach_copy_rename_init(drmachid_t t_id, uint64_t t_slice_offset,
3261 	drmachid_t s_id, struct memlist *c_ml, drmachid_t *cr_id)
3262 {
3263 	extern void drmach_rename(uint64_t *, uint_t *, uint64_t *);
3264 	extern void drmach_rename_end(void);
3265 
3266 	drmach_mem_t	*s_mp, *t_mp;
3267 	struct memlist	*x_ml;
3268 	uint64_t	 off_mask, s_copybasepa, t_copybasepa, t_basepa;
3269 	int		 len;
3270 	caddr_t		 bp, wp;
3271 	uint_t		*p, *q;
3272 	sbd_error_t	*err;
3273 	tte_t		*tte;
3274 	drmach_copy_rename_t *cr;
3275 
3276 	if (!DRMACH_IS_MEM_ID(s_id))
3277 		return (drerr_new(0, ESTC_INAPPROP, NULL));
3278 	if (!DRMACH_IS_MEM_ID(t_id))
3279 		return (drerr_new(0, ESTC_INAPPROP, NULL));
3280 	s_mp = s_id;
3281 	t_mp = t_id;
3282 
3283 	/* get starting physical address of target memory */
3284 	err = drmach_mem_get_base_physaddr(t_id, &t_basepa);
3285 	if (err)
3286 		return (err);
3287 
3288 	/* calculate slice offset mask from slice size */
3289 	off_mask = DRMACH_MEM_SLICE_SIZE - 1;
3290 
3291 	/* calculate source and target base pa */
3292 	s_copybasepa = c_ml->ml_address;
3293 	t_copybasepa =
3294 	    t_basepa + ((c_ml->ml_address & off_mask) - t_slice_offset);
3295 
3296 	/* paranoia */
3297 	ASSERT((c_ml->ml_address & off_mask) >= t_slice_offset);
3298 
3299 	/* adjust copy memlist addresses to be relative to copy base pa */
3300 	x_ml = c_ml;
3301 	while (x_ml != NULL) {
3302 		x_ml->ml_address -= s_copybasepa;
3303 		x_ml = x_ml->ml_next;
3304 	}
3305 
3306 #ifdef DEBUG
3307 	{
3308 	uint64_t s_basepa, s_size, t_size;
3309 
3310 	x_ml = c_ml;
3311 	while (x_ml->ml_next != NULL)
3312 		x_ml = x_ml->ml_next;
3313 
3314 	DRMACH_PR("source copy span: base pa 0x%lx, end pa 0x%lx\n",
3315 	    s_copybasepa,
3316 	    s_copybasepa + x_ml->ml_address + x_ml->ml_size);
3317 
3318 	DRMACH_PR("target copy span: base pa 0x%lx, end pa 0x%lx\n",
3319 	    t_copybasepa,
3320 	    t_copybasepa + x_ml->ml_address + x_ml->ml_size);
3321 
3322 	DRMACH_PR("copy memlist (relative to copy base pa):\n");
3323 	DRMACH_MEMLIST_DUMP(c_ml);
3324 
3325 	err = drmach_mem_get_base_physaddr(s_id, &s_basepa);
3326 	ASSERT(err == NULL);
3327 
3328 	err = drmach_mem_get_size(s_id, &s_size);
3329 	ASSERT(err == NULL);
3330 
3331 	err = drmach_mem_get_size(t_id, &t_size);
3332 	ASSERT(err == NULL);
3333 
3334 	DRMACH_PR("current source base pa 0x%lx, size 0x%lx\n",
3335 	    s_basepa, s_size);
3336 	DRMACH_PR("current target base pa 0x%lx, size 0x%lx\n",
3337 	    t_basepa, t_size);
3338 	}
3339 #endif /* DEBUG */
3340 
3341 	/* Map in appropriate cpu sram page */
3342 	tte = &drmach_cpu_sram_tte[CPU->cpu_id];
3343 	ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) &&
3344 	    TTE_IS_PRIVILEGED(tte) && TTE_IS_LOCKED(tte));
3345 	sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
3346 	sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
3347 
3348 	bp = wp = drmach_cpu_sram_va;
3349 
3350 	/* Make sure the rename routine will fit */
3351 	len = (ptrdiff_t)drmach_rename_end - (ptrdiff_t)drmach_rename;
3352 	ASSERT(wp + len < bp + PAGESIZE);
3353 
3354 	/* copy text. standard bcopy not designed to work in nc space */
3355 	p = (uint_t *)wp;
3356 	q = (uint_t *)drmach_rename;
3357 	while (q < (uint_t *)drmach_rename_end)
3358 		*p++ = *q++;
3359 
3360 	/* zero remainder. standard bzero not designed to work in nc space */
3361 	while (p < (uint_t *)(bp + PAGESIZE))
3362 		*p++ = 0;
3363 
3364 	DRMACH_PR("drmach_rename function 0x%p, len %d\n", (void *)wp, len);
3365 	wp += (len + 15) & ~15;
3366 
3367 	err = drmach_prep_rename_script(s_mp, t_mp, t_slice_offset, wp,
3368 	    PAGESIZE - (wp - bp));
3369 	if (err) {
3370 cleanup:
3371 		xt_one(CPU->cpu_id, vtag_flushpage_tl1,
3372 		    (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
3373 		return (err);
3374 	}
3375 
3376 	/* disable and flush CDC */
3377 	if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
3378 		axq_cdc_enable_all();	/* paranoia */
3379 		err = DRMACH_INTERNAL_ERROR();
3380 		goto cleanup;
3381 	}
3382 
3383 	/* mark both memory units busy */
3384 	t_mp->dev.busy++;
3385 	s_mp->dev.busy++;
3386 
3387 	cr = vmem_alloc(static_alloc_arena, sizeof (drmach_copy_rename_t),
3388 	    VM_SLEEP);
3389 	cr->isa = (void *)drmach_copy_rename_init;
3390 	cr->data = wp;
3391 	cr->c_ml = c_ml;
3392 	cr->s_mp = s_mp;
3393 	cr->t_mp = t_mp;
3394 	cr->s_copybasepa = s_copybasepa;
3395 	cr->t_copybasepa = t_copybasepa;
3396 	cr->ecode = DRMACH_CR_OK;
3397 
3398 	mutex_enter(&drmach_slice_table_lock);
3399 
3400 	mutex_enter(&drmach_xt_mb_lock);
3401 	bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
3402 
3403 	if (DRMACH_L1_SET_LPA(s_mp->dev.bp) && drmach_reprogram_lpa) {
3404 		drmach_prep_xt_mb_for_slice_update(s_mp->dev.bp,
3405 		    DRMACH_PA_TO_SLICE(t_copybasepa));
3406 	}
3407 	if (DRMACH_L1_SET_LPA(t_mp->dev.bp) && drmach_reprogram_lpa) {
3408 		drmach_prep_xt_mb_for_slice_update(t_mp->dev.bp,
3409 		    DRMACH_PA_TO_SLICE(s_copybasepa));
3410 	}
3411 
3412 	*cr_id = cr;
3413 	return (NULL);
3414 }
3415 
3416 int drmach_rename_count;
3417 int drmach_rename_ntries;
3418 
3419 sbd_error_t *
3420 drmach_copy_rename_fini(drmachid_t id)
3421 {
3422 	drmach_copy_rename_t	*cr = id;
3423 	sbd_error_t		*err = NULL;
3424 	dr_mbox_msg_t		*obufp;
3425 
3426 	ASSERT(cr->isa == (void *)drmach_copy_rename_init);
3427 
3428 	axq_cdc_enable_all();
3429 
3430 	xt_one(CPU->cpu_id, vtag_flushpage_tl1,
3431 	    (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
3432 
3433 	switch (cr->ecode) {
3434 	case DRMACH_CR_OK:
3435 		break;
3436 	case DRMACH_CR_MC_IDLE_ERR: {
3437 		dev_info_t	*dip = NULL;
3438 		drmach_mem_t	*mp = (drmach_mem_t *)cr->earg;
3439 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
3440 
3441 		ASSERT(DRMACH_IS_MEM_ID(mp));
3442 
3443 		err = drmach_get_dip(mp, &dip);
3444 
3445 		ASSERT(err == NULL);
3446 		ASSERT(dip != NULL);
3447 
3448 		err = drerr_new(0, ESBD_MEMFAIL, NULL);
3449 		(void) ddi_pathname(dip, path);
3450 		cmn_err(CE_WARN, "failed to idle memory controller %s on %s: "
3451 		    "copy-rename aborted", path, mp->dev.bp->cm.name);
3452 		kmem_free(path, MAXPATHLEN);
3453 		break;
3454 	}
3455 	case DRMACH_CR_IOPAUSE_ERR:
3456 		ASSERT((uintptr_t)cr->earg >= 0 &&
3457 		    (uintptr_t)cr->earg < AXQ_MAX_EXP);
3458 
3459 		err = drerr_new(0,  ESBD_SUSPEND, "EX%d", (uintptr_t)cr->earg);
3460 		cmn_err(CE_WARN, "failed to idle EX%ld AXQ slot1 activity prior"
3461 		    " to copy-rename", (uintptr_t)cr->earg);
3462 		break;
3463 	case DRMACH_CR_ONTRAP_ERR:
3464 		err = drerr_new(0, ESBD_MEMFAIL, NULL);
3465 		cmn_err(CE_WARN, "copy-rename aborted due to uncorrectable "
3466 		    "memory error");
3467 		break;
3468 	default:
3469 		err = DRMACH_INTERNAL_ERROR();
3470 		cmn_err(CE_WARN, "unknown copy-rename error code (%d)\n",
3471 		    cr->ecode);
3472 		break;
3473 	}
3474 
3475 #ifdef DEBUG
3476 	if ((DRMACH_L1_SET_LPA(cr->s_mp->dev.bp) ||
3477 	    DRMACH_L1_SET_LPA(cr->t_mp->dev.bp)) && drmach_reprogram_lpa) {
3478 		int	i;
3479 		for (i = 0; i < NCPU; i++) {
3480 			if (drmach_xt_mb[i])
3481 				DRMACH_PR("cpu%d ignored drmach_xt_mb", i);
3482 		}
3483 	}
3484 #endif
3485 	mutex_exit(&drmach_xt_mb_lock);
3486 
3487 	if (cr->c_ml != NULL)
3488 		memlist_delete(cr->c_ml);
3489 
3490 	cr->t_mp->dev.busy--;
3491 	cr->s_mp->dev.busy--;
3492 
3493 	if (err) {
3494 		mutex_exit(&drmach_slice_table_lock);
3495 		goto done;
3496 	}
3497 
3498 	/* update casm shadow for target and source board */
3499 	drmach_slice_table_update(cr->t_mp->dev.bp, 0);
3500 	drmach_slice_table_update(cr->s_mp->dev.bp, 0);
3501 	mutex_exit(&drmach_slice_table_lock);
3502 
3503 	mutex_enter(&drmach_bus_sync_lock);
3504 	drmach_bus_sync_list_update();
3505 	mutex_exit(&drmach_bus_sync_lock);
3506 
3507 	/*
3508 	 * Make a good-faith effort to notify the SC about the copy-rename, but
3509 	 * don't worry if it fails, since a subsequent claim/unconfig/unclaim
3510 	 * will duplicate the update.
3511 	 */
3512 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
3513 	mutex_enter(&drmach_slice_table_lock);
3514 	drmach_msg_memslice_init(obufp->msgdata.dm_uc.mem_slice);
3515 	drmach_msg_memregs_init(obufp->msgdata.dm_uc.mem_regs);
3516 	mutex_exit(&drmach_slice_table_lock);
3517 	(void) drmach_mbox_trans(DRMSG_UNCONFIG, cr->s_mp->dev.bp->bnum,
3518 	    (caddr_t)obufp, sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
3519 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
3520 
3521 done:
3522 	vmem_free(static_alloc_arena, cr, sizeof (drmach_copy_rename_t));
3523 
3524 	DRMACH_PR("waited %d out of %d tries for drmach_rename_wait on %d cpus",
3525 	    drmach_rename_ntries, drmach_cpu_ntries, drmach_rename_count);
3526 
3527 	return (err);
3528 }
3529 
3530 int drmach_slow_copy = 0;
3531 
3532 void
3533 drmach_copy_rename(drmachid_t id)
3534 {
3535 	extern uint_t		 getpstate(void);
3536 	extern void		 setpstate(uint_t);
3537 
3538 	extern xcfunc_t		 drmach_rename_wait;
3539 	extern xcfunc_t		 drmach_rename_done;
3540 	extern xcfunc_t		 drmach_rename_abort;
3541 
3542 	drmach_copy_rename_t	*cr = id;
3543 	uint64_t		 neer;
3544 	struct memlist		*ml;
3545 	int			 i, count;
3546 	int			 csize, lnsize;
3547 	uint64_t		 caddr;
3548 	cpuset_t		 cpuset;
3549 	uint_t			 pstate;
3550 	uint32_t		 exp = 0;
3551 	on_trap_data_t		 otd;
3552 	xcfunc_t		*drmach_end_wait_xcall = drmach_rename_done;
3553 
3554 	ASSERT(cr->isa == (void *)drmach_copy_rename_init);
3555 	ASSERT(MUTEX_HELD(&cpu_lock));
3556 	ASSERT(cr->ecode == DRMACH_CR_OK);
3557 
3558 	/*
3559 	 * Prevent slot1 IO from accessing Safari memory bus.
3560 	 */
3561 	if (axq_iopause_enable_all(&exp) != DDI_SUCCESS) {
3562 		ASSERT(exp >= 0 && exp < AXQ_MAX_EXP);
3563 		cr->ecode = DRMACH_CR_IOPAUSE_ERR;
3564 		cr->earg = (void *)(uintptr_t)exp;
3565 		return;
3566 	}
3567 
3568 	cpuset = cpu_ready_set;
3569 	CPUSET_DEL(cpuset, CPU->cpu_id);
3570 	count = ncpus - 1;
3571 	drmach_rename_count = count;	/* for debug */
3572 
3573 	drmach_xt_ready = 0;
3574 	xt_some(cpuset, drmach_rename_wait, NULL, NULL);
3575 
3576 	for (i = 0; i < drmach_cpu_ntries; i++) {
3577 		if (drmach_xt_ready == count)
3578 			break;
3579 		DELAY(drmach_cpu_delay);
3580 	}
3581 
3582 	drmach_rename_ntries = i;	/* for debug */
3583 
3584 	drmach_xt_ready = 0;		/* steal the line back */
3585 	for (i = 0; i < NCPU; i++)	/* steal the line back, preserve data */
3586 		drmach_xt_mb[i] = drmach_xt_mb[i];
3587 
3588 	caddr = drmach_iocage_paddr;
3589 	csize = cpunodes[CPU->cpu_id].ecache_size;
3590 	lnsize = cpunodes[CPU->cpu_id].ecache_linesize;
3591 
3592 	/* disable CE reporting */
3593 	neer = get_error_enable();
3594 	set_error_enable(neer & ~EN_REG_CEEN);
3595 
3596 	/* disable interrupts (paranoia) */
3597 	pstate = getpstate();
3598 	setpstate(pstate & ~PSTATE_IE);
3599 
3600 	/*
3601 	 * Execute copy-rename under on_trap to protect against a panic due
3602 	 * to an uncorrectable error. Instead, DR will abort the copy-rename
3603 	 * operation and rely on the OS to do the error reporting.
3604 	 *
3605 	 * In general, trap handling on any cpu once the copy begins
3606 	 * can result in an inconsistent memory image on the target.
3607 	 */
3608 	if (on_trap(&otd, OT_DATA_EC)) {
3609 		cr->ecode = DRMACH_CR_ONTRAP_ERR;
3610 		goto copy_rename_end;
3611 	}
3612 
3613 	/*
3614 	 * DO COPY.
3615 	 */
3616 	for (ml = cr->c_ml; ml; ml = ml->ml_next) {
3617 		uint64_t	s_pa, t_pa;
3618 		uint64_t	nbytes;
3619 
3620 		s_pa = cr->s_copybasepa + ml->ml_address;
3621 		t_pa = cr->t_copybasepa + ml->ml_address;
3622 		nbytes = ml->ml_size;
3623 
3624 		while (nbytes != 0ull) {
3625 			/* copy 32 bytes at src_pa to dst_pa */
3626 			bcopy32_il(s_pa, t_pa);
3627 
3628 			/* increment by 32 bytes */
3629 			s_pa += (4 * sizeof (uint64_t));
3630 			t_pa += (4 * sizeof (uint64_t));
3631 
3632 			/* decrement by 32 bytes */
3633 			nbytes -= (4 * sizeof (uint64_t));
3634 
3635 			if (drmach_slow_copy) {	/* for debug */
3636 				uint64_t i = 13 * 50;
3637 				while (i--)
3638 					;
3639 			}
3640 		}
3641 	}
3642 
3643 	/*
3644 	 * XXX CHEETAH SUPPORT
3645 	 * For cheetah, we need to grab the iocage lock since iocage
3646 	 * memory is used for e$ flush.
3647 	 *
3648 	 * NOTE: This code block is dangerous at this point in the
3649 	 * copy-rename operation. It modifies memory after the copy
3650 	 * has taken place which means that any persistent state will
3651 	 * be abandoned after the rename operation. The code is also
3652 	 * performing thread synchronization at a time when all but
3653 	 * one processors are paused. This is a potential deadlock
3654 	 * situation.
3655 	 *
3656 	 * This code block must be moved to drmach_copy_rename_init.
3657 	 */
3658 	if (drmach_is_cheetah) {
3659 		mutex_enter(&drmach_iocage_lock);
3660 		while (drmach_iocage_is_busy)
3661 			cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
3662 		drmach_iocage_is_busy = 1;
3663 		drmach_iocage_mem_scrub(ecache_size * 2);
3664 		mutex_exit(&drmach_iocage_lock);
3665 	}
3666 
3667 	/*
3668 	 * bcopy32_il is implemented as a series of ldxa/stxa via
3669 	 * ASI_MEM instructions. Following the copy loop, the E$
3670 	 * of the master (this) processor will have lines in state
3671 	 * O that correspond to lines of home memory in state gI.
3672 	 * An E$ flush is necessary to commit these lines before
3673 	 * proceeding with the rename operation.
3674 	 *
3675 	 * Flushing the E$ will automatically flush the W$, but
3676 	 * the D$ and I$ must be flushed separately and explicitly.
3677 	 */
3678 	flush_ecache_il(caddr, csize, lnsize);	/* inline version */
3679 
3680 	/*
3681 	 * Each line of home memory is now in state gM, except in
3682 	 * the case of a cheetah processor when the E$ flush area
3683 	 * is included within the copied region. In such a case,
3684 	 * the lines of home memory for the upper half of the
3685 	 * flush area are in state gS.
3686 	 *
3687 	 * Each line of target memory is in state gM.
3688 	 *
3689 	 * Each line of this processor's E$ is in state I, except
3690 	 * those of a cheetah processor. All lines of a cheetah
3691 	 * processor's E$ are in state S and correspond to the lines
3692 	 * in upper half of the E$ flush area.
3693 	 *
3694 	 * It is vital at this point that none of the lines in the
3695 	 * home or target memories are in state gI and that none
3696 	 * of the lines in this processor's E$ are in state O or Os.
3697 	 * A single instance of such a condition will cause loss of
3698 	 * coherency following the rename operation.
3699 	 */
3700 
3701 	/*
3702 	 * Rename
3703 	 */
3704 	(*(void(*)())drmach_cpu_sram_va)(cr->data, &cr->ecode, &cr->earg);
3705 
3706 	/*
3707 	 * Rename operation complete. The physical address space
3708 	 * of the home and target memories have been swapped, the
3709 	 * routing data in the respective CASM entries have been
3710 	 * swapped, and LPA settings in the processor and schizo
3711 	 * devices have been reprogrammed accordingly.
3712 	 *
3713 	 * In the case of a cheetah processor, the E$ remains
3714 	 * populated with lines in state S that correspond to the
3715 	 * lines in the former home memory. Now that the physical
3716 	 * addresses have been swapped, these E$ lines correspond
3717 	 * to lines in the new home memory which are in state gM.
3718 	 * This combination is invalid. An additional E$ flush is
3719 	 * necessary to restore coherency. The E$ flush will cause
3720 	 * the lines of the new home memory for the flush region
3721 	 * to transition from state gM to gS. The former home memory
3722 	 * remains unmodified. This additional E$ flush has no effect
3723 	 * on a cheetah+ processor.
3724 	 */
3725 	flush_ecache_il(caddr, csize, lnsize);	/* inline version */
3726 
3727 	/*
3728 	 * The D$ and I$ must be flushed to ensure that coherency is
3729 	 * maintained. Any line in a cache that is in the valid
3730 	 * state has its corresponding line of the new home memory
3731 	 * in the gM state. This is an invalid condition. When the
3732 	 * flushes are complete the cache line states will be
3733 	 * resynchronized with those in the new home memory.
3734 	 */
3735 	flush_icache_il();			/* inline version */
3736 	flush_dcache_il();			/* inline version */
3737 	flush_pcache_il();			/* inline version */
3738 
3739 copy_rename_end:
3740 
3741 	no_trap();
3742 
3743 	/* enable interrupts */
3744 	setpstate(pstate);
3745 
3746 	/* enable CE reporting */
3747 	set_error_enable(neer);
3748 
3749 	if (cr->ecode != DRMACH_CR_OK)
3750 		drmach_end_wait_xcall = drmach_rename_abort;
3751 
3752 	/*
3753 	 * XXX CHEETAH SUPPORT
3754 	 */
3755 	if (drmach_is_cheetah) {
3756 		mutex_enter(&drmach_iocage_lock);
3757 		drmach_iocage_mem_scrub(ecache_size * 2);
3758 		drmach_iocage_is_busy = 0;
3759 		cv_signal(&drmach_iocage_cv);
3760 		mutex_exit(&drmach_iocage_lock);
3761 	}
3762 
3763 	axq_iopause_disable_all();
3764 
3765 	xt_some(cpuset, drmach_end_wait_xcall, NULL, NULL);
3766 }
3767 
3768 static void drmach_io_dispose(drmachid_t);
3769 static sbd_error_t *drmach_io_release(drmachid_t);
3770 static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
3771 
3772 static sbd_error_t *
3773 drmach_pci_new(drmach_device_t *proto, drmachid_t *idp)
3774 {
3775 	drmach_node_t	*node = proto->node;
3776 	sbd_error_t	*err;
3777 	drmach_reg_t	 regs[3];
3778 	int		 rv;
3779 	int		 len = 0;
3780 
3781 	rv = node->n_getproplen(node, "reg", &len);
3782 	if (rv != 0 || len != sizeof (regs)) {
3783 		sbd_error_t *err;
3784 
3785 		/* pci nodes are expected to have regs */
3786 		err = drerr_new(1, ESTC_GETPROP,
3787 		    "Device Node 0x%x: property %s",
3788 		    (uint_t)node->get_dnode(node), "reg");
3789 		return (err);
3790 	}
3791 
3792 	rv = node->n_getprop(node, "reg", (void *)regs, sizeof (regs));
3793 	if (rv) {
3794 		sbd_error_t *err;
3795 
3796 		err = drerr_new(1, ESTC_GETPROP,
3797 		    "Device Node 0x%x: property %s",
3798 		    (uint_t)node->get_dnode(node), "reg");
3799 
3800 		return (err);
3801 	}
3802 
3803 	/*
3804 	 * Fix up unit number so that Leaf A has a lower unit number
3805 	 * than Leaf B.
3806 	 */
3807 	if ((proto->portid % 2) != 0) {
3808 		if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
3809 			proto->unum = 0;
3810 		else
3811 			proto->unum = 1;
3812 	} else {
3813 		if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
3814 			proto->unum = 2;
3815 		else
3816 			proto->unum = 3;
3817 	}
3818 
3819 	err = drmach_io_new(proto, idp);
3820 	if (err == NULL) {
3821 		drmach_io_t *self = *idp;
3822 
3823 		/* reassemble 64-bit base address */
3824 		self->scsr_pa  = (uint64_t)regs[1].reg_addr_hi << 32;
3825 		self->scsr_pa |= (uint64_t)regs[1].reg_addr_lo;
3826 	}
3827 
3828 	return (err);
3829 }
3830 
3831 static sbd_error_t *
3832 drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
3833 {
3834 	drmach_io_t	*ip;
3835 
3836 	ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
3837 	bcopy(proto, &ip->dev, sizeof (ip->dev));
3838 	ip->dev.node = drmach_node_dup(proto->node);
3839 	ip->dev.cm.isa = (void *)drmach_io_new;
3840 	ip->dev.cm.dispose = drmach_io_dispose;
3841 	ip->dev.cm.release = drmach_io_release;
3842 	ip->dev.cm.status = drmach_io_status;
3843 
3844 	(void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
3845 	    ip->dev.type, ip->dev.unum);
3846 
3847 	*idp = (drmachid_t)ip;
3848 	return (NULL);
3849 }
3850 
3851 static void
3852 drmach_io_dispose(drmachid_t id)
3853 {
3854 	drmach_io_t *self;
3855 
3856 	ASSERT(DRMACH_IS_IO_ID(id));
3857 
3858 	self = id;
3859 	if (self->dev.node)
3860 		drmach_node_dispose(self->dev.node);
3861 
3862 	kmem_free(self, sizeof (*self));
3863 }
3864 
3865 /*ARGSUSED*/
3866 sbd_error_t *
3867 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
3868 {
3869 	drmach_board_t	*bp = (drmach_board_t *)id;
3870 	sbd_error_t	*err = NULL;
3871 
3872 	if (id && DRMACH_IS_BOARD_ID(id)) {
3873 		switch (cmd) {
3874 			case SBD_CMD_TEST:
3875 			case SBD_CMD_STATUS:
3876 			case SBD_CMD_GETNCM:
3877 				break;
3878 			case SBD_CMD_CONNECT:
3879 				if (bp->connected)
3880 					err = drerr_new(0, ESBD_STATE, NULL);
3881 
3882 				if (bp->cond == SBD_COND_UNUSABLE)
3883 					err = drerr_new(0,
3884 					    ESBD_FATAL_STATE, NULL);
3885 				break;
3886 			case SBD_CMD_DISCONNECT:
3887 				if (!bp->connected)
3888 					err = drerr_new(0, ESBD_STATE, NULL);
3889 
3890 				if (bp->cond == SBD_COND_UNUSABLE)
3891 					err = drerr_new(0,
3892 					    ESBD_FATAL_STATE, NULL);
3893 				break;
3894 			default:
3895 				if (bp->cond == SBD_COND_UNUSABLE)
3896 					err = drerr_new(0,
3897 					    ESBD_FATAL_STATE, NULL);
3898 				break;
3899 
3900 		}
3901 	}
3902 
3903 	return (err);
3904 }
3905 
3906 /*ARGSUSED*/
3907 sbd_error_t *
3908 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
3909 {
3910 	return (NULL);
3911 }
3912 
3913 sbd_error_t *
3914 drmach_board_assign(int bnum, drmachid_t *id)
3915 {
3916 	sbd_error_t	*err = NULL;
3917 	caddr_t		obufp;
3918 
3919 	if (!drmach_initialized && drmach_init() == -1) {
3920 		err = DRMACH_INTERNAL_ERROR();
3921 	}
3922 
3923 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
3924 
3925 	if (!err) {
3926 		if (drmach_array_get(drmach_boards, bnum, id) == -1) {
3927 			err = drerr_new(0, ESTC_BNUM, "%d", bnum);
3928 		} else {
3929 			drmach_board_t	*bp;
3930 
3931 			if (*id)
3932 				rw_downgrade(&drmach_boards_rwlock);
3933 
3934 			obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
3935 			err = drmach_mbox_trans(DRMSG_ASSIGN, bnum, obufp,
3936 			    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
3937 			kmem_free(obufp, sizeof (dr_proto_hdr_t));
3938 
3939 			if (!err) {
3940 				bp = *id;
3941 				if (!*id)
3942 					bp = *id  =
3943 					    (drmachid_t)drmach_board_new(bnum);
3944 				bp->assigned = 1;
3945 			}
3946 		}
3947 	}
3948 	rw_exit(&drmach_boards_rwlock);
3949 	return (err);
3950 }
3951 
3952 static uint_t
3953 drmach_board_non_panther_cpus(gdcd_t *gdcd, uint_t exp, uint_t slot)
3954 {
3955 	uint_t	port, port_start, port_end;
3956 	uint_t	non_panther_cpus = 0;
3957 	uint_t	impl;
3958 
3959 	ASSERT(gdcd != NULL);
3960 
3961 	/*
3962 	 * Determine PRD port indices based on slot location.
3963 	 */
3964 	switch (slot) {
3965 	case 0:
3966 		port_start = 0;
3967 		port_end = 3;
3968 		break;
3969 	case 1:
3970 		port_start = 4;
3971 		port_end = 5;
3972 		break;
3973 	default:
3974 		ASSERT(0);
3975 		/* check all */
3976 		port_start = 0;
3977 		port_end = 5;
3978 		break;
3979 	}
3980 
3981 	for (port = port_start; port <= port_end; port++) {
3982 		if (gdcd->dcd_prd[exp][port].prd_ptype == SAFPTYPE_CPU &&
3983 		    RSV_GOOD(gdcd->dcd_prd[exp][port].prd_prsv)) {
3984 			/*
3985 			 * This Safari port passed POST and represents a
3986 			 * cpu, so check the implementation.
3987 			 */
3988 			impl = (gdcd->dcd_prd[exp][port].prd_ver_reg >> 32)
3989 			    & 0xffff;
3990 
3991 			switch (impl) {
3992 			case CHEETAH_IMPL:
3993 			case CHEETAH_PLUS_IMPL:
3994 			case JAGUAR_IMPL:
3995 				non_panther_cpus++;
3996 				break;
3997 			case PANTHER_IMPL:
3998 				break;
3999 			default:
4000 				ASSERT(0);
4001 				non_panther_cpus++;
4002 				break;
4003 			}
4004 		}
4005 	}
4006 
4007 	DRMACH_PR("drmach_board_non_panther_cpus: exp=%d, slot=%d, "
4008 	    "non_panther_cpus=%d", exp, slot, non_panther_cpus);
4009 
4010 	return (non_panther_cpus);
4011 }
4012 
4013 sbd_error_t *
4014 drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
4015 {
4016 	_NOTE(ARGUNUSED(opts))
4017 
4018 	drmach_board_t		*bp = (drmach_board_t *)id;
4019 	sbd_error_t		*err;
4020 	dr_mbox_msg_t		*obufp;
4021 	gdcd_t			*gdcd = NULL;
4022 	uint_t			exp, slot;
4023 	sc_gptwocfg_cookie_t	scc;
4024 	int			panther_pages_enabled;
4025 
4026 	if (!DRMACH_IS_BOARD_ID(id))
4027 		return (drerr_new(0, ESTC_INAPPROP, NULL));
4028 
4029 	/*
4030 	 * Build the casm info portion of the CLAIM message.
4031 	 */
4032 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
4033 	mutex_enter(&drmach_slice_table_lock);
4034 	drmach_msg_memslice_init(obufp->msgdata.dm_cr.mem_slice);
4035 	drmach_msg_memregs_init(obufp->msgdata.dm_cr.mem_regs);
4036 	mutex_exit(&drmach_slice_table_lock);
4037 	err = drmach_mbox_trans(DRMSG_CLAIM, bp->bnum, (caddr_t)obufp,
4038 	    sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
4039 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
4040 
4041 	if (err) {
4042 		/*
4043 		 * if mailbox timeout or unrecoverable error from SC,
4044 		 * board cannot be touched.  Mark the status as
4045 		 * unusable.
4046 		 */
4047 		if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
4048 		    (err->e_code == ESTC_MBXRPLY))
4049 			bp->cond = SBD_COND_UNUSABLE;
4050 		return (err);
4051 	}
4052 
4053 	gdcd = drmach_gdcd_new();
4054 	if (gdcd == NULL) {
4055 		cmn_err(CE_WARN, "failed to read GDCD info for %s\n",
4056 		    bp->cm.name);
4057 		return (DRMACH_INTERNAL_ERROR());
4058 	}
4059 
4060 	/*
4061 	 * Read CPU SRAM DR buffer offset from GDCD.
4062 	 */
4063 	exp = DRMACH_BNUM2EXP(bp->bnum);
4064 	slot = DRMACH_BNUM2SLOT(bp->bnum);
4065 	bp->stardrb_offset =
4066 	    gdcd->dcd_slot[exp][slot].l1ss_cpu_drblock_xwd_offset << 3;
4067 	DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
4068 	    bp->stardrb_offset);
4069 
4070 	/*
4071 	 * Read board LPA setting from GDCD.
4072 	 */
4073 	bp->flags &= ~DRMACH_NULL_PROC_LPA;
4074 	if (gdcd->dcd_slot[exp][slot].l1ss_flags &
4075 	    L1SSFLG_THIS_L1_NULL_PROC_LPA) {
4076 		bp->flags |= DRMACH_NULL_PROC_LPA;
4077 		DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
4078 	}
4079 
4080 	/*
4081 	 * XXX Until the Solaris large pages support heterogeneous cpu
4082 	 * domains, DR needs to prevent the addition of non-Panther cpus
4083 	 * to an all-Panther domain with large pages enabled.
4084 	 */
4085 	panther_pages_enabled = (page_num_pagesizes() > DEFAULT_MMU_PAGE_SIZES);
4086 	if (drmach_board_non_panther_cpus(gdcd, exp, slot) > 0 &&
4087 	    panther_pages_enabled && drmach_large_page_restriction) {
4088 		cmn_err(CE_WARN, "Domain shutdown is required to add a non-"
4089 		    "UltraSPARC-IV+ board into an all UltraSPARC-IV+ domain");
4090 		err = drerr_new(0, ESTC_SUPPORT, NULL);
4091 	}
4092 
4093 	if (err == NULL) {
4094 		/* do saf configurator stuff */
4095 		DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
4096 		scc = sc_probe_board(bp->bnum);
4097 		if (scc == NULL)
4098 			err = drerr_new(0, ESTC_PROBE, bp->cm.name);
4099 	}
4100 
4101 	if (err) {
4102 		/* flush CDC srams */
4103 		if (axq_cdc_flush_all() != DDI_SUCCESS) {
4104 			goto out;
4105 		}
4106 
4107 		/*
4108 		 * Build the casm info portion of the UNCLAIM message.
4109 		 */
4110 		obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
4111 		mutex_enter(&drmach_slice_table_lock);
4112 		drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
4113 		drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
4114 		mutex_exit(&drmach_slice_table_lock);
4115 		(void) drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum,
4116 		    (caddr_t)obufp, sizeof (dr_mbox_msg_t),
4117 		    (caddr_t)NULL, 0);
4118 
4119 		kmem_free(obufp, sizeof (dr_mbox_msg_t));
4120 
4121 		/*
4122 		 * we clear the connected flag just in case it would have
4123 		 * been set by a concurrent drmach_board_status() thread
4124 		 * before the UNCLAIM completed.
4125 		 */
4126 		bp->connected = 0;
4127 		goto out;
4128 	}
4129 
4130 	/*
4131 	 * Now that the board has been successfully attached, obtain
4132 	 * platform-specific DIMM serial id information for the board.
4133 	 */
4134 	if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
4135 	    plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE)) {
4136 		(void) plat_request_mem_sids(DRMACH_BNUM2EXP(bp->bnum));
4137 	}
4138 
4139 out:
4140 	if (gdcd != NULL)
4141 		drmach_gdcd_dispose(gdcd);
4142 
4143 	return (err);
4144 }
4145 
4146 static void
4147 drmach_slice_table_update(drmach_board_t *bp, int invalidate)
4148 {
4149 	static char		*axq_name = "address-extender-queue";
4150 	static dev_info_t	*axq_dip = NULL;
4151 	static int		 axq_exp = -1;
4152 	static int		 axq_slot;
4153 	int			 e, s, slice;
4154 
4155 	ASSERT(MUTEX_HELD(&drmach_slice_table_lock));
4156 
4157 	e = DRMACH_BNUM2EXP(bp->bnum);
4158 	if (invalidate) {
4159 		ASSERT(DRMACH_BNUM2SLOT(bp->bnum) == 0);
4160 
4161 		/* invalidate cached casm value */
4162 		drmach_slice_table[e] = 0;
4163 
4164 		/* invalidate cached axq info if for same exp */
4165 		if (e == axq_exp && axq_dip) {
4166 			ndi_rele_devi(axq_dip);
4167 			axq_dip = NULL;
4168 		}
4169 	}
4170 
4171 	if (axq_dip == NULL || !i_ddi_devi_attached(axq_dip)) {
4172 		int i, portid;
4173 
4174 		/* search for an attached slot0 axq instance */
4175 		for (i = 0; i < AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP; i++) {
4176 			if (axq_dip)
4177 				ndi_rele_devi(axq_dip);
4178 			axq_dip = ddi_find_devinfo(axq_name, i, 0);
4179 			if (axq_dip && DDI_CF2(axq_dip)) {
4180 				portid = ddi_getprop(DDI_DEV_T_ANY, axq_dip,
4181 				    DDI_PROP_DONTPASS, "portid", -1);
4182 				if (portid == -1) {
4183 					DRMACH_PR("cant get portid of axq "
4184 					    "instance %d\n", i);
4185 					continue;
4186 				}
4187 
4188 				axq_exp = (portid >> 5) & 0x1f;
4189 				axq_slot = portid & 1;
4190 
4191 				if (invalidate && axq_exp == e)
4192 					continue;
4193 
4194 				if (axq_slot == 0)
4195 					break;	/* found */
4196 			}
4197 		}
4198 
4199 		if (i == AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP) {
4200 			if (axq_dip) {
4201 				ndi_rele_devi(axq_dip);
4202 				axq_dip = NULL;
4203 			}
4204 			DRMACH_PR("drmach_slice_table_update: failed to "
4205 			    "update axq dip\n");
4206 			return;
4207 		}
4208 
4209 	}
4210 
4211 	ASSERT(axq_dip);
4212 	ASSERT(axq_slot == 0);
4213 
4214 	if (invalidate)
4215 		return;
4216 
4217 	s = DRMACH_BNUM2SLOT(bp->bnum);
4218 	DRMACH_PR("using AXQ casm %d.%d for slot%d.%d\n", axq_exp, axq_slot,
4219 	    e, s);
4220 
4221 	/* invalidate entry */
4222 	drmach_slice_table[e] &= ~0x20;
4223 
4224 	/*
4225 	 * find a slice that routes to expander e. If no match
4226 	 * is found, drmach_slice_table[e] will remain invalid.
4227 	 *
4228 	 * The CASM is a routing table indexed by slice number.
4229 	 * Each element in the table contains permission bits,
4230 	 * a destination expander number and a valid bit. The
4231 	 * valid bit must true for the element to be meaningful.
4232 	 *
4233 	 * CASM entry structure
4234 	 *   Bits 15..6 ignored
4235 	 *   Bit  5	valid
4236 	 *   Bits 0..4	expander number
4237 	 *
4238 	 * NOTE: the for loop is really enumerating the range of slices,
4239 	 * which is ALWAYS equal to the range of expanders. Hence,
4240 	 * AXQ_MAX_EXP is okay to use in this loop.
4241 	 */
4242 	for (slice = 0; slice < AXQ_MAX_EXP; slice++) {
4243 		uint32_t casm = axq_casm_read(axq_exp, axq_slot, slice);
4244 
4245 		if ((casm & 0x20) && (casm & 0x1f) == e)
4246 			drmach_slice_table[e] = 0x20 | slice;
4247 	}
4248 }
4249 
4250 /*
4251  * Get base and bound PAs for slot 1 board lpa programming
4252  * If a cpu/mem board is present in the same expander, use slice
4253  * information corresponding to the CASM.  Otherwise, set base and
4254  * bound PAs to 0.
4255  */
4256 static void
4257 drmach_lpa_bb_get(drmach_board_t *s1bp, uint64_t *basep, uint64_t *boundp)
4258 {
4259 	drmachid_t s0id;
4260 
4261 	ASSERT(mutex_owned(&drmach_slice_table_lock));
4262 	ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
4263 
4264 	*basep = *boundp = 0;
4265 	if (drmach_array_get(drmach_boards, s1bp->bnum - 1, &s0id) == 0 &&
4266 	    s0id != 0) {
4267 
4268 		uint32_t slice;
4269 		if ((slice = drmach_slice_table[DRMACH_BNUM2EXP(s1bp->bnum)])
4270 		    & 0x20) {
4271 			*basep = DRMACH_SLICE_TO_PA(slice & DRMACH_SLICE_MASK);
4272 			*boundp = *basep + DRMACH_MEM_SLICE_SIZE;
4273 		}
4274 	}
4275 }
4276 
4277 
4278 /*
4279  * Reprogram slot 1 lpa's as required.
4280  * The purpose of this routine is maintain the LPA settings of the devices
4281  * in slot 1. To date we know Schizo and Cheetah are the only devices that
4282  * require this attention. The LPA setting must match the slice field in the
4283  * CASM element for the local expander. This field is guaranteed to be
4284  * programmed in accordance with the cacheable address space on the slot 0
4285  * board of the local expander. If no memory is present on the slot 0 board,
4286  * there is no cacheable address space and, hence, the CASM slice field will
4287  * be zero or its valid bit will be false (or both).
4288  */
4289 
4290 static void
4291 drmach_slot1_lpa_set(drmach_board_t *bp)
4292 {
4293 	drmachid_t	id;
4294 	drmach_board_t	*s1bp = NULL;
4295 	int		rv, idx, is_maxcat = 1;
4296 	uint64_t	last_scsr_pa = 0;
4297 	uint64_t	new_basepa, new_boundpa;
4298 
4299 	if (DRMACH_BNUM2SLOT(bp->bnum)) {
4300 		s1bp = bp;
4301 		if (s1bp->devices == NULL) {
4302 			DRMACH_PR("drmach...lpa_set: slot1=%d not present",
4303 			    bp->bnum);
4304 			return;
4305 		}
4306 	} else {
4307 		rv = drmach_array_get(drmach_boards, bp->bnum + 1, &id);
4308 		/* nothing to do when board is not found or has no devices */
4309 		s1bp = id;
4310 		if (rv == -1 || s1bp == NULL || s1bp->devices == NULL) {
4311 			DRMACH_PR("drmach...lpa_set: slot1=%d not present",
4312 			    bp->bnum + 1);
4313 			return;
4314 		}
4315 		ASSERT(DRMACH_IS_BOARD_ID(id));
4316 	}
4317 	mutex_enter(&drmach_slice_table_lock);
4318 	drmach_lpa_bb_get(s1bp, &new_basepa, &new_boundpa);
4319 	DRMACH_PR("drmach_...lpa_set: bnum=%d base=0x%lx bound=0x%lx\n",
4320 	    s1bp->bnum, new_basepa, new_boundpa);
4321 
4322 	rv = drmach_array_first(s1bp->devices, &idx, &id);
4323 	while (rv == 0) {
4324 		if (DRMACH_IS_IO_ID(id)) {
4325 			drmach_io_t *io = id;
4326 
4327 			is_maxcat = 0;
4328 
4329 			/*
4330 			 * Skip all non-Schizo IO devices (only IO nodes
4331 			 * that are Schizo devices have non-zero scsr_pa).
4332 			 * Filter out "other" leaf to avoid writing to the
4333 			 * same Schizo Control/Status Register twice.
4334 			 */
4335 			if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
4336 				uint64_t scsr;
4337 
4338 				scsr  = lddphysio(io->scsr_pa);
4339 				DRMACH_PR("drmach...lpa_set: old scsr=0x%lx\n",
4340 				    scsr);
4341 				scsr &= ~(DRMACH_LPA_BASE_MASK |
4342 				    DRMACH_LPA_BND_MASK);
4343 				scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
4344 				scsr |= DRMACH_PA_TO_LPA_BND(new_boundpa);
4345 
4346 				stdphysio(io->scsr_pa, scsr);
4347 				DRMACH_PR("drmach...lpa_set: new scsr=0x%lx\n",
4348 				    scsr);
4349 
4350 				last_scsr_pa = io->scsr_pa;
4351 			}
4352 		}
4353 		rv = drmach_array_next(s1bp->devices, &idx, &id);
4354 	}
4355 
4356 	if (is_maxcat && DRMACH_L1_SET_LPA(s1bp) && drmach_reprogram_lpa) {
4357 		extern xcfunc_t	drmach_set_lpa;
4358 
4359 		DRMACH_PR("reprogramming maxcat lpa's");
4360 
4361 		mutex_enter(&cpu_lock);
4362 		rv = drmach_array_first(s1bp->devices, &idx, &id);
4363 		while (rv == 0 && id != NULL) {
4364 			if (DRMACH_IS_CPU_ID(id)) {
4365 				int ntries;
4366 				processorid_t cpuid;
4367 
4368 				cpuid = ((drmach_cpu_t *)id)->cpuid;
4369 
4370 				/*
4371 				 * Check for unconfigured or powered-off
4372 				 * MCPUs.  If CPU_READY flag is clear, the
4373 				 * MCPU cannot be xcalled.
4374 				 */
4375 				if ((cpu[cpuid] == NULL) ||
4376 				    (cpu[cpuid]->cpu_flags &
4377 				    CPU_READY) == 0) {
4378 
4379 					rv = drmach_array_next(s1bp->devices,
4380 					    &idx, &id);
4381 					continue;
4382 				}
4383 
4384 				/*
4385 				 * XXX CHEETAH SUPPORT
4386 				 * for cheetah, we need to clear iocage
4387 				 * memory since it will be used for e$ flush
4388 				 * in drmach_set_lpa.
4389 				 */
4390 				if (drmach_is_cheetah) {
4391 					mutex_enter(&drmach_iocage_lock);
4392 					while (drmach_iocage_is_busy)
4393 						cv_wait(&drmach_iocage_cv,
4394 						    &drmach_iocage_lock);
4395 					drmach_iocage_is_busy = 1;
4396 					drmach_iocage_mem_scrub(ecache_size *
4397 					    2);
4398 					mutex_exit(&drmach_iocage_lock);
4399 				}
4400 
4401 				/*
4402 				 * drmach_slice_table[*]
4403 				 *	bit 5	valid
4404 				 *	bit 0:4	slice number
4405 				 *
4406 				 * drmach_xt_mb[*] format for drmach_set_lpa
4407 				 *	bit 7	valid
4408 				 *	bit 6	set null LPA
4409 				 *			(overrides bits 0:4)
4410 				 *	bit 0:4	slice number
4411 				 *
4412 				 * drmach_set_lpa derives processor CBASE and
4413 				 * CBND from bits 6 and 0:4 of drmach_xt_mb.
4414 				 * If bit 6 is set, then CBASE = CBND = 0.
4415 				 * Otherwise, CBASE = slice number;
4416 				 * CBND = slice number + 1.
4417 				 * No action is taken if bit 7 is zero.
4418 				 */
4419 
4420 				mutex_enter(&drmach_xt_mb_lock);
4421 				bzero((void *)drmach_xt_mb,
4422 				    drmach_xt_mb_size);
4423 
4424 				if (new_basepa == 0 && new_boundpa == 0)
4425 					drmach_xt_mb[cpuid] = 0x80 | 0x40;
4426 				else
4427 					drmach_xt_mb[cpuid] = 0x80 |
4428 					    DRMACH_PA_TO_SLICE(new_basepa);
4429 
4430 				drmach_xt_ready = 0;
4431 
4432 				xt_one(cpuid, drmach_set_lpa, NULL, NULL);
4433 
4434 				ntries = drmach_cpu_ntries;
4435 				while (!drmach_xt_ready && ntries) {
4436 					DELAY(drmach_cpu_delay);
4437 					ntries--;
4438 				}
4439 				mutex_exit(&drmach_xt_mb_lock);
4440 				drmach_xt_ready = 0;
4441 
4442 				/*
4443 				 * XXX CHEETAH SUPPORT
4444 				 * for cheetah, we need to clear iocage
4445 				 * memory since it was used for e$ flush
4446 				 * in performed drmach_set_lpa.
4447 				 */
4448 				if (drmach_is_cheetah) {
4449 					mutex_enter(&drmach_iocage_lock);
4450 					drmach_iocage_mem_scrub(ecache_size *
4451 					    2);
4452 					drmach_iocage_is_busy = 0;
4453 					cv_signal(&drmach_iocage_cv);
4454 					mutex_exit(&drmach_iocage_lock);
4455 				}
4456 			}
4457 			rv = drmach_array_next(s1bp->devices, &idx, &id);
4458 		}
4459 		mutex_exit(&cpu_lock);
4460 	}
4461 	mutex_exit(&drmach_slice_table_lock);
4462 }
4463 
4464 /*
4465  * Return the number of connected Panther boards in the domain.
4466  */
4467 static int
4468 drmach_panther_boards(void)
4469 {
4470 	int		rv;
4471 	int		b_idx;
4472 	drmachid_t	b_id;
4473 	drmach_board_t	*bp;
4474 	int		npanther = 0;
4475 
4476 	rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
4477 	while (rv == 0) {
4478 		ASSERT(DRMACH_IS_BOARD_ID(b_id));
4479 		bp = b_id;
4480 
4481 		if (IS_PANTHER(bp->cpu_impl))
4482 			npanther++;
4483 
4484 		rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
4485 	}
4486 
4487 	return (npanther);
4488 }
4489 
4490 /*ARGSUSED*/
4491 sbd_error_t *
4492 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
4493 {
4494 	drmach_board_t	*bp;
4495 	dr_mbox_msg_t	*obufp;
4496 	sbd_error_t	*err = NULL;
4497 
4498 	sc_gptwocfg_cookie_t	scc;
4499 
4500 	if (!DRMACH_IS_BOARD_ID(id))
4501 		return (drerr_new(0, ESTC_INAPPROP, NULL));
4502 	bp = id;
4503 
4504 	/*
4505 	 * Build the casm info portion of the UNCLAIM message.
4506 	 * This must be done prior to calling for saf configurator
4507 	 * deprobe, to ensure that the associated axq instance
4508 	 * is not detached.
4509 	 */
4510 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
4511 	mutex_enter(&drmach_slice_table_lock);
4512 	drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
4513 
4514 	/*
4515 	 * If disconnecting slot 0 board, update the casm slice table
4516 	 * info now, for use by drmach_slot1_lpa_set()
4517 	 */
4518 	if (DRMACH_BNUM2SLOT(bp->bnum) == 0)
4519 		drmach_slice_table_update(bp, 1);
4520 
4521 	drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
4522 	mutex_exit(&drmach_slice_table_lock);
4523 
4524 	/*
4525 	 * Update LPA information for slot1 board
4526 	 */
4527 	drmach_slot1_lpa_set(bp);
4528 
4529 	/* disable and flush CDC */
4530 	if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
4531 		axq_cdc_enable_all();	/* paranoia */
4532 		err = DRMACH_INTERNAL_ERROR();
4533 	}
4534 
4535 	/*
4536 	 * call saf configurator for deprobe
4537 	 * It's done now before sending an UNCLAIM message because
4538 	 * IKP will probe boards it doesn't know about <present at boot>
4539 	 * prior to unprobing them.  If this happens after sending the
4540 	 * UNCLAIM, it will cause a dstop for domain transgression error.
4541 	 */
4542 
4543 	if (!err) {
4544 		scc = sc_unprobe_board(bp->bnum);
4545 		axq_cdc_enable_all();
4546 		if (scc != NULL) {
4547 			err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
4548 		}
4549 	}
4550 
4551 	/*
4552 	 * If disconnecting a board from a Panther domain, wait a fixed-
4553 	 * time delay for pending Safari transactions to complete on the
4554 	 * disconnecting board's processors.  The bus sync list read used
4555 	 * in drmach_shutdown_asm to synchronize with outstanding Safari
4556 	 * transactions assumes no read-bypass-write mode for all memory
4557 	 * controllers.  Since Panther supports read-bypass-write, a
4558 	 * delay is used that is slightly larger than the maximum Safari
4559 	 * timeout value in the Safari/Fireplane Config Reg.
4560 	 */
4561 	if (drmach_panther_boards() > 0 || drmach_unclaim_delay_all) {
4562 		clock_t	stime = ddi_get_lbolt();
4563 
4564 		delay(drv_usectohz(drmach_unclaim_usec_delay));
4565 
4566 		stime = ddi_get_lbolt() - stime;
4567 		DRMACH_PR("delayed %ld ticks (%ld secs) before disconnecting "
4568 		    "board %s from domain\n", stime, stime / hz, bp->cm.name);
4569 	}
4570 
4571 	if (!err) {
4572 		obufp->msgdata.dm_ur.mem_clear = 0;
4573 
4574 		err = drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum, (caddr_t)obufp,
4575 		    sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
4576 
4577 		if (err) {
4578 			/*
4579 			 * if mailbox timeout or unrecoverable error from SC,
4580 			 * board cannot be touched.  Mark the status as
4581 			 * unusable.
4582 			 */
4583 			if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
4584 			    (err->e_code == ESTC_MBXRPLY))
4585 				bp->cond = SBD_COND_UNUSABLE;
4586 			else {
4587 				DRMACH_PR("UNCLAIM failed for bnum=%d\n",
4588 				    bp->bnum);
4589 				DRMACH_PR("calling sc_probe_board: bnum=%d\n",
4590 				    bp->bnum);
4591 				scc = sc_probe_board(bp->bnum);
4592 				if (scc == NULL) {
4593 					cmn_err(CE_WARN,
4594 					"sc_probe_board failed for bnum=%d",
4595 					    bp->bnum);
4596 				} else {
4597 					if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
4598 						mutex_enter(
4599 						    &drmach_slice_table_lock);
4600 						drmach_slice_table_update(bp,
4601 						    0);
4602 						mutex_exit(
4603 						    &drmach_slice_table_lock);
4604 					}
4605 					drmach_slot1_lpa_set(bp);
4606 				}
4607 			}
4608 		} else {
4609 			bp->connected = 0;
4610 			/*
4611 			 * Now that the board has been successfully detached,
4612 			 * discard platform-specific DIMM serial id information
4613 			 * for the board.
4614 			 */
4615 			if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
4616 			    plat_ecc_capability_sc_get(
4617 			    PLAT_ECC_DIMM_SID_MESSAGE)) {
4618 				(void) plat_discard_mem_sids(
4619 				    DRMACH_BNUM2EXP(bp->bnum));
4620 			}
4621 		}
4622 	}
4623 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
4624 
4625 	return (err);
4626 }
4627 
4628 static int
4629 drmach_get_portid(drmach_node_t *np)
4630 {
4631 	drmach_node_t	pp;
4632 	int		portid;
4633 	char		type[OBP_MAXPROPNAME];
4634 
4635 	if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
4636 		return (portid);
4637 
4638 	/*
4639 	 * Get the device_type property to see if we should
4640 	 * continue processing this node.
4641 	 */
4642 	if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
4643 		return (-1);
4644 
4645 	/*
4646 	 * If the device is a CPU without a 'portid' property,
4647 	 * it is a CMP core. For such cases, the parent node
4648 	 * has the portid.
4649 	 */
4650 	if (strcmp(type, DRMACH_CPU_NAMEPROP) == 0) {
4651 		if (np->get_parent(np, &pp) != 0)
4652 			return (-1);
4653 
4654 		if (pp.n_getprop(&pp, "portid", &portid, sizeof (portid)) == 0)
4655 			return (portid);
4656 	}
4657 
4658 	return (-1);
4659 }
4660 
4661 /*
4662  * This is a helper function to determine if a given
4663  * node should be considered for a dr operation according
4664  * to predefined dr type nodes and the node's name.
4665  * Formal Parameter : The name of a device node.
4666  * Return Value: -1, name does not map to a valid dr type.
4667  *		 A value greater or equal to 0, name is a valid dr type.
4668  */
4669 static int
4670 drmach_name2type_idx(char *name)
4671 {
4672 	int 	index, ntypes;
4673 
4674 	if (name == NULL)
4675 		return (-1);
4676 
4677 	/*
4678 	 * Determine how many possible types are currently supported
4679 	 * for dr.
4680 	 */
4681 	ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
4682 
4683 	/* Determine if the node's name correspond to a predefined type. */
4684 	for (index = 0; index < ntypes; index++) {
4685 		if (strcmp(drmach_name2type[index].name, name) == 0)
4686 			/* The node is an allowed type for dr. */
4687 			return (index);
4688 	}
4689 
4690 	/*
4691 	 * If the name of the node does not map to any of the
4692 	 * types in the array drmach_name2type then the node is not of
4693 	 * interest to dr.
4694 	 */
4695 	return (-1);
4696 }
4697 
4698 static int
4699 drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
4700 {
4701 	drmach_node_t			*node = args->node;
4702 	drmach_board_cb_data_t		*data = args->data;
4703 	drmach_board_t			*obj = data->obj;
4704 
4705 	int		rv, portid;
4706 	drmachid_t	id;
4707 	drmach_device_t	*device;
4708 	char	name[OBP_MAXDRVNAME];
4709 
4710 	portid = drmach_get_portid(node);
4711 	if (portid == -1) {
4712 		/*
4713 		 * if the node does not have a portid property, then
4714 		 * by that information alone it is known that drmach
4715 		 * is not interested in it.
4716 		 */
4717 		return (0);
4718 	}
4719 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
4720 
4721 	/* The node must have a name */
4722 	if (rv)
4723 		return (0);
4724 
4725 	/*
4726 	 * Ignore devices whose portid do not map to this board,
4727 	 * or that their name property is not mapped to a valid
4728 	 * dr device name.
4729 	 */
4730 	if ((drmach_portid2bnum(portid) != obj->bnum) ||
4731 	    (drmach_name2type_idx(name) < 0))
4732 		return (0);
4733 
4734 	/*
4735 	 * Create a device data structure from this node data.
4736 	 * The call may yield nothing if the node is not of interest
4737 	 * to drmach.
4738 	 */
4739 	data->err = drmach_device_new(node, obj, portid, &id);
4740 	if (data->err)
4741 		return (-1);
4742 	else if (!id) {
4743 		/*
4744 		 * drmach_device_new examined the node we passed in
4745 		 * and determined that it was either one not of
4746 		 * interest to drmach or the PIM dr layer.
4747 		 * So, it is skipped.
4748 		 */
4749 		return (0);
4750 	}
4751 
4752 	rv = drmach_array_set(obj->devices, data->ndevs++, id);
4753 	if (rv) {
4754 		data->err = DRMACH_INTERNAL_ERROR();
4755 		return (-1);
4756 	}
4757 
4758 	device = id;
4759 
4760 #ifdef DEBUG
4761 	DRMACH_PR("%d %s %d %p\n", portid, device->type, device->unum, id);
4762 	if (DRMACH_IS_IO_ID(id))
4763 		DRMACH_PR("ndevs = %d dip/node = %p", data->ndevs, node->here);
4764 #endif
4765 
4766 	data->err = (*data->found)(data->a, device->type, device->unum, id);
4767 	return (data->err == NULL ? 0 : -1);
4768 }
4769 
4770 sbd_error_t *
4771 drmach_board_find_devices(drmachid_t id, void *a,
4772 	sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
4773 {
4774 	drmach_board_t		*bp = (drmach_board_t *)id;
4775 	sbd_error_t		*err;
4776 	int			 max_devices;
4777 	int			 rv;
4778 	drmach_board_cb_data_t	data;
4779 
4780 	if (!DRMACH_IS_BOARD_ID(id))
4781 		return (drerr_new(0, ESTC_INAPPROP, NULL));
4782 
4783 	max_devices  = plat_max_cpu_units_per_board();
4784 	max_devices += plat_max_mem_units_per_board();
4785 	max_devices += plat_max_io_units_per_board();
4786 
4787 	bp->devices = drmach_array_new(0, max_devices);
4788 
4789 	if (bp->tree == NULL)
4790 		bp->tree = drmach_node_new();
4791 
4792 	data.obj = bp;
4793 	data.ndevs = 0;
4794 	data.found = found;
4795 	data.a = a;
4796 	data.err = NULL;
4797 
4798 	mutex_enter(&drmach_slice_table_lock);
4799 	mutex_enter(&drmach_bus_sync_lock);
4800 
4801 	rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
4802 
4803 	drmach_slice_table_update(bp, 0);
4804 	drmach_bus_sync_list_update();
4805 
4806 	mutex_exit(&drmach_bus_sync_lock);
4807 	mutex_exit(&drmach_slice_table_lock);
4808 
4809 	if (rv == 0) {
4810 		err = NULL;
4811 		drmach_slot1_lpa_set(bp);
4812 	} else {
4813 		drmach_array_dispose(bp->devices, drmach_device_dispose);
4814 		bp->devices = NULL;
4815 
4816 		if (data.err)
4817 			err = data.err;
4818 		else
4819 			err = DRMACH_INTERNAL_ERROR();
4820 	}
4821 
4822 	return (err);
4823 }
4824 
4825 int
4826 drmach_board_lookup(int bnum, drmachid_t *id)
4827 {
4828 	int	rv = 0;
4829 
4830 	if (!drmach_initialized && drmach_init() == -1) {
4831 		*id = 0;
4832 		return (-1);
4833 	}
4834 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
4835 	if (drmach_array_get(drmach_boards, bnum, id)) {
4836 		*id = 0;
4837 		rv = -1;
4838 	} else {
4839 		caddr_t		obufp;
4840 		dr_showboard_t	shb;
4841 		sbd_error_t	*err = NULL;
4842 		drmach_board_t	*bp;
4843 
4844 		bp = *id;
4845 
4846 		if (bp)
4847 			rw_downgrade(&drmach_boards_rwlock);
4848 
4849 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
4850 		err = drmach_mbox_trans(DRMSG_SHOWBOARD, bnum, obufp,
4851 		    sizeof (dr_proto_hdr_t), (caddr_t)&shb,
4852 		    sizeof (dr_showboard_t));
4853 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
4854 
4855 		if (err) {
4856 			if (err->e_code == ESTC_UNAVAILABLE) {
4857 				*id = 0;
4858 				rv = -1;
4859 			}
4860 			sbd_err_clear(&err);
4861 		} else {
4862 			if (!bp)
4863 				bp = *id  = (drmachid_t)drmach_board_new(bnum);
4864 			bp->connected = (shb.bd_assigned && shb.bd_active);
4865 			bp->empty = shb.slot_empty;
4866 
4867 			switch (shb.test_status) {
4868 				case DR_TEST_STATUS_UNKNOWN:
4869 				case DR_TEST_STATUS_IPOST:
4870 				case DR_TEST_STATUS_ABORTED:
4871 					bp->cond = SBD_COND_UNKNOWN;
4872 					break;
4873 				case DR_TEST_STATUS_PASSED:
4874 					bp->cond = SBD_COND_OK;
4875 					break;
4876 				case DR_TEST_STATUS_FAILED:
4877 					bp->cond = SBD_COND_FAILED;
4878 					break;
4879 				default:
4880 					bp->cond = SBD_COND_UNKNOWN;
4881 				DRMACH_PR("Unknown test status=0x%x from SC\n",
4882 				    shb.test_status);
4883 					break;
4884 			}
4885 			(void) strncpy(bp->type, shb.board_type,
4886 			    sizeof (bp->type));
4887 			bp->assigned = shb.bd_assigned;
4888 			bp->powered = shb.power_on;
4889 		}
4890 	}
4891 	rw_exit(&drmach_boards_rwlock);
4892 	return (rv);
4893 }
4894 
4895 sbd_error_t *
4896 drmach_board_name(int bnum, char *buf, int buflen)
4897 {
4898 	(void) snprintf(buf, buflen, "%s%d", DRMACH_BNUM2SLOT(bnum) ?
4899 	    "IO" : "SB", DRMACH_BNUM2EXP(bnum));
4900 
4901 	return (NULL);
4902 }
4903 
4904 sbd_error_t *
4905 drmach_board_poweroff(drmachid_t id)
4906 {
4907 	drmach_board_t	*bp;
4908 	sbd_error_t	*err;
4909 	drmach_status_t	 stat;
4910 
4911 	if (!DRMACH_IS_BOARD_ID(id))
4912 		return (drerr_new(0, ESTC_INAPPROP, NULL));
4913 	bp = id;
4914 
4915 	err = drmach_board_status(id, &stat);
4916 	if (!err) {
4917 		if (stat.configured || stat.busy)
4918 			err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
4919 		else {
4920 			caddr_t	obufp;
4921 
4922 			obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
4923 			err = drmach_mbox_trans(DRMSG_POWEROFF, bp->bnum, obufp,
4924 			    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
4925 			kmem_free(obufp, sizeof (dr_proto_hdr_t));
4926 			if (!err)
4927 				bp->powered = 0;
4928 		}
4929 	}
4930 	return (err);
4931 }
4932 
4933 sbd_error_t *
4934 drmach_board_poweron(drmachid_t id)
4935 {
4936 	drmach_board_t	*bp;
4937 	caddr_t		obufp;
4938 	sbd_error_t	*err;
4939 
4940 	if (!DRMACH_IS_BOARD_ID(id))
4941 		return (drerr_new(0, ESTC_INAPPROP, NULL));
4942 	bp = id;
4943 
4944 	obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
4945 	err = drmach_mbox_trans(DRMSG_POWERON, bp->bnum, obufp,
4946 	    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
4947 	if (!err)
4948 		bp->powered = 1;
4949 
4950 	kmem_free(obufp, sizeof (dr_proto_hdr_t));
4951 
4952 	return (err);
4953 }
4954 
4955 static sbd_error_t *
4956 drmach_board_release(drmachid_t id)
4957 {
4958 	if (!DRMACH_IS_BOARD_ID(id))
4959 		return (drerr_new(0, ESTC_INAPPROP, NULL));
4960 	return (NULL);
4961 }
4962 
4963 sbd_error_t *
4964 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
4965 {
4966 	drmach_board_t		*bp;
4967 	drmach_device_t		*dp[MAX_CORES_PER_CMP];
4968 	dr_mbox_msg_t		*obufp;
4969 	sbd_error_t		*err;
4970 	dr_testboard_reply_t	tbr;
4971 	int			cpylen;
4972 	char			*copts;
4973 	int			is_io;
4974 	cpu_flag_t		oflags[MAX_CORES_PER_CMP];
4975 
4976 	if (!DRMACH_IS_BOARD_ID(id))
4977 		return (drerr_new(0, ESTC_INAPPROP, NULL));
4978 	bp = id;
4979 
4980 	/*
4981 	 * If the board is an I/O or MAXCAT board, setup I/O cage for
4982 	 * testing. Slot 1 indicates I/O or MAXCAT board.
4983 	 */
4984 
4985 	is_io = DRMACH_BNUM2SLOT(bp->bnum);
4986 
4987 	obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
4988 
4989 	if (force)
4990 		obufp->msgdata.dm_tb.force = 1;
4991 
4992 	obufp->msgdata.dm_tb.immediate = 1;
4993 
4994 	if ((opts->size > 0) && ((copts = opts->copts) != NULL)) {
4995 		cpylen = (opts->size > DR_HPOPTLEN ? DR_HPOPTLEN : opts->size);
4996 		bcopy(copts, obufp->msgdata.dm_tb.hpost_opts, cpylen);
4997 	}
4998 
4999 	if (is_io) {
5000 		err = drmach_iocage_setup(&obufp->msgdata.dm_tb, dp, oflags);
5001 
5002 		if (err) {
5003 			kmem_free(obufp, sizeof (dr_mbox_msg_t));
5004 			return (err);
5005 		}
5006 	}
5007 
5008 	err = drmach_mbox_trans(DRMSG_TESTBOARD, bp->bnum, (caddr_t)obufp,
5009 	    sizeof (dr_mbox_msg_t), (caddr_t)&tbr, sizeof (tbr));
5010 
5011 	if (!err)
5012 		bp->cond = SBD_COND_OK;
5013 	else
5014 		bp->cond = SBD_COND_UNKNOWN;
5015 
5016 	if ((!err) && (tbr.test_status != DR_TEST_STATUS_PASSED)) {
5017 		/* examine test status */
5018 		switch (tbr.test_status) {
5019 			case DR_TEST_STATUS_IPOST:
5020 				bp->cond = SBD_COND_UNKNOWN;
5021 				err = drerr_new(0, ESTC_TEST_IN_PROGRESS, NULL);
5022 				break;
5023 			case DR_TEST_STATUS_UNKNOWN:
5024 				bp->cond = SBD_COND_UNKNOWN;
5025 				err = drerr_new(1,
5026 				    ESTC_TEST_STATUS_UNKNOWN, NULL);
5027 				break;
5028 			case DR_TEST_STATUS_FAILED:
5029 				bp->cond = SBD_COND_FAILED;
5030 				err = drerr_new(1, ESTC_TEST_FAILED, NULL);
5031 				break;
5032 			case DR_TEST_STATUS_ABORTED:
5033 				bp->cond = SBD_COND_UNKNOWN;
5034 				err = drerr_new(1, ESTC_TEST_ABORTED, NULL);
5035 				break;
5036 			default:
5037 				bp->cond = SBD_COND_UNKNOWN;
5038 				err = drerr_new(1, ESTC_TEST_RESULT_UNKNOWN,
5039 				    NULL);
5040 				break;
5041 		}
5042 	}
5043 
5044 	/*
5045 	 * If I/O cage test was performed, check for availability of the
5046 	 * cpu used.  If cpu has been returned, it's OK to proceed with
5047 	 * reconfiguring it for use.
5048 	 */
5049 	if (is_io) {
5050 		DRMACH_PR("drmach_board_test: tbr.cpu_recovered: %d",
5051 		    tbr.cpu_recovered);
5052 		DRMACH_PR("drmach_board_test: port id: %d",
5053 		    tbr.cpu_portid);
5054 
5055 		/*
5056 		 * Check the cpu_recovered flag in the testboard reply, or
5057 		 * if the testboard request message was not sent to SMS due
5058 		 * to an mboxsc_putmsg() failure, it's OK to recover the
5059 		 * cpu since hpost hasn't touched it.
5060 		 */
5061 		if ((tbr.cpu_recovered && tbr.cpu_portid ==
5062 		    obufp->msgdata.dm_tb.cpu_portid) ||
5063 		    ((err) && (err->e_code == ESTC_MBXRQST))) {
5064 
5065 			int i;
5066 
5067 			mutex_enter(&cpu_lock);
5068 			for (i = 0; i < MAX_CORES_PER_CMP; i++) {
5069 				if (dp[i] != NULL) {
5070 					(void) drmach_iocage_cpu_return(dp[i],
5071 					    oflags[i]);
5072 				}
5073 			}
5074 			mutex_exit(&cpu_lock);
5075 		} else {
5076 			cmn_err(CE_WARN, "Unable to recover port id %d "
5077 			    "after I/O cage test: cpu_recovered=%d, "
5078 			    "returned portid=%d",
5079 			    obufp->msgdata.dm_tb.cpu_portid,
5080 			    tbr.cpu_recovered, tbr.cpu_portid);
5081 		}
5082 		(void) drmach_iocage_mem_return(&tbr);
5083 	}
5084 	kmem_free(obufp, sizeof (dr_mbox_msg_t));
5085 
5086 	return (err);
5087 }
5088 
5089 sbd_error_t *
5090 drmach_board_unassign(drmachid_t id)
5091 {
5092 	drmach_board_t	*bp;
5093 	sbd_error_t	*err;
5094 	drmach_status_t	 stat;
5095 	caddr_t		obufp;
5096 
5097 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
5098 
5099 	if (!DRMACH_IS_BOARD_ID(id)) {
5100 		rw_exit(&drmach_boards_rwlock);
5101 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5102 	}
5103 	bp = id;
5104 
5105 	err = drmach_board_status(id, &stat);
5106 	if (err) {
5107 		rw_exit(&drmach_boards_rwlock);
5108 		return (err);
5109 	}
5110 
5111 	if (stat.configured || stat.busy) {
5112 		err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
5113 	} else {
5114 
5115 		obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
5116 		err = drmach_mbox_trans(DRMSG_UNASSIGN, bp->bnum, obufp,
5117 		    sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
5118 		kmem_free(obufp, sizeof (dr_proto_hdr_t));
5119 		if (!err) {
5120 			if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
5121 				err = DRMACH_INTERNAL_ERROR();
5122 			else
5123 				drmach_board_dispose(bp);
5124 		}
5125 	}
5126 	rw_exit(&drmach_boards_rwlock);
5127 	return (err);
5128 }
5129 
5130 static sbd_error_t *
5131 drmach_read_reg_addr(drmach_device_t *dp, uint64_t *p)
5132 {
5133 	int		len;
5134 	drmach_reg_t	reg;
5135 	drmach_node_t	pp;
5136 	drmach_node_t	*np = dp->node;
5137 
5138 	/*
5139 	 * If the node does not have a portid property,
5140 	 * it represents a CMP device. For a CMP, the reg
5141 	 * property of the parent holds the information of
5142 	 * interest.
5143 	 */
5144 	if (dp->node->n_getproplen(dp->node, "portid", &len) != 0) {
5145 
5146 		if (dp->node->get_parent(dp->node, &pp) != 0) {
5147 			return (DRMACH_INTERNAL_ERROR());
5148 		}
5149 		np = &pp;
5150 	}
5151 
5152 	if (np->n_getproplen(np, "reg", &len) != 0)
5153 		return (DRMACH_INTERNAL_ERROR());
5154 
5155 	if (len != sizeof (reg))
5156 		return (DRMACH_INTERNAL_ERROR());
5157 
5158 	if (np->n_getprop(np, "reg", &reg, sizeof (reg)) != 0)
5159 		return (DRMACH_INTERNAL_ERROR());
5160 
5161 	/* reassemble 64-bit base address */
5162 	*p = ((uint64_t)reg.reg_addr_hi << 32) | reg.reg_addr_lo;
5163 
5164 	return (NULL);
5165 }
5166 
5167 static void
5168 drmach_cpu_read(uint64_t arg1, uint64_t arg2)
5169 {
5170 	uint64_t	*saf_config_reg = (uint64_t *)arg1;
5171 	uint_t		*reg_read = (uint_t *)arg2;
5172 
5173 	*saf_config_reg = lddsafconfig();
5174 	*reg_read = 0x1;
5175 }
5176 
5177 /*
5178  * A return value of 1 indicates success and 0 indicates a failure
5179  */
5180 static int
5181 drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr)
5182 {
5183 
5184 	int 	rv = 0x0;
5185 
5186 	*scr = 0x0;
5187 
5188 	/*
5189 	 * Confirm cpu was in ready set when xc was issued.
5190 	 * This is done by verifying rv which is
5191 	 * set to 0x1 when xc_one is successful.
5192 	 */
5193 	xc_one(cp->dev.portid, (xcfunc_t *)drmach_cpu_read,
5194 	    (uint64_t)scr, (uint64_t)&rv);
5195 
5196 	return (rv);
5197 
5198 }
5199 
5200 static sbd_error_t *
5201 drmach_cpu_read_cpuid(drmach_cpu_t *cp, processorid_t *cpuid)
5202 {
5203 	drmach_node_t	*np;
5204 
5205 	np = cp->dev.node;
5206 
5207 	/*
5208 	 * If a CPU does not have a portid property, it must
5209 	 * be a CMP device with a cpuid property.
5210 	 */
5211 	if (np->n_getprop(np, "portid", cpuid, sizeof (*cpuid)) != 0) {
5212 
5213 		if (np->n_getprop(np, "cpuid", cpuid, sizeof (*cpuid)) != 0) {
5214 			return (DRMACH_INTERNAL_ERROR());
5215 		}
5216 	}
5217 
5218 	return (NULL);
5219 }
5220 
5221 /* Starcat CMP core id is bit 2 of the cpuid */
5222 #define	DRMACH_COREID_MASK	(1u << 2)
5223 #define	DRMACH_CPUID2SRAM_IDX(id) \
5224 		((id & DRMACH_COREID_MASK) >> 1 | (id & 0x1))
5225 
5226 static sbd_error_t *
5227 drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
5228 {
5229 	static void drmach_cpu_dispose(drmachid_t);
5230 	static sbd_error_t *drmach_cpu_release(drmachid_t);
5231 	static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
5232 
5233 	sbd_error_t	*err;
5234 	uint64_t	scr_pa;
5235 	drmach_cpu_t	*cp = NULL;
5236 	pfn_t		pfn;
5237 	uint64_t	cpu_stardrb_offset, cpu_sram_pa;
5238 	int		idx;
5239 	int		impl;
5240 	processorid_t	cpuid;
5241 
5242 	err = drmach_read_reg_addr(proto, &scr_pa);
5243 	if (err) {
5244 		goto fail;
5245 	}
5246 
5247 	cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
5248 	bcopy(proto, &cp->dev, sizeof (cp->dev));
5249 	cp->dev.node = drmach_node_dup(proto->node);
5250 	cp->dev.cm.isa = (void *)drmach_cpu_new;
5251 	cp->dev.cm.dispose = drmach_cpu_dispose;
5252 	cp->dev.cm.release = drmach_cpu_release;
5253 	cp->dev.cm.status = drmach_cpu_status;
5254 	cp->scr_pa = scr_pa;
5255 
5256 	err = drmach_cpu_read_cpuid(cp, &cpuid);
5257 	if (err) {
5258 		goto fail;
5259 	}
5260 
5261 	err = drmach_cpu_get_impl(cp, &impl);
5262 	if (err) {
5263 		goto fail;
5264 	}
5265 
5266 	cp->cpuid = cpuid;
5267 	cp->coreid = STARCAT_CPUID_TO_COREID(cp->cpuid);
5268 	cp->dev.unum = STARCAT_CPUID_TO_AGENT(cp->cpuid);
5269 
5270 	/*
5271 	 * Init the board cpu type.  Assumes all board cpus are the same type.
5272 	 */
5273 	if (cp->dev.bp->cpu_impl == 0) {
5274 		cp->dev.bp->cpu_impl = impl;
5275 	}
5276 	ASSERT(cp->dev.bp->cpu_impl == impl);
5277 
5278 	/*
5279 	 * XXX CHEETAH SUPPORT
5280 	 * determine if the domain uses Cheetah procs
5281 	 */
5282 	if (drmach_is_cheetah < 0) {
5283 		drmach_is_cheetah = IS_CHEETAH(impl);
5284 	}
5285 
5286 	/*
5287 	 * Initialize TTE for mapping CPU SRAM STARDRB buffer.
5288 	 * The STARDRB buffer (16KB on Cheetah+ boards, 32KB on
5289 	 * Jaguar/Panther boards) is shared by all cpus in a Safari port
5290 	 * pair. Each cpu uses 8KB according to the following layout:
5291 	 *
5292 	 * Page 0:	even numbered Cheetah+'s and Panther/Jaguar core 0's
5293 	 * Page 1:	odd numbered Cheetah+'s and Panther/Jaguar core 0's
5294 	 * Page 2:	even numbered Panther/Jaguar core 1's
5295 	 * Page 3:	odd numbered Panther/Jaguar core 1's
5296 	 */
5297 	idx = DRMACH_CPUID2SRAM_IDX(cp->cpuid);
5298 	cpu_stardrb_offset = cp->dev.bp->stardrb_offset + (PAGESIZE * idx);
5299 	cpu_sram_pa = DRMACH_CPU_SRAM_ADDR + cpu_stardrb_offset;
5300 	pfn = cpu_sram_pa >> PAGESHIFT;
5301 
5302 	ASSERT(drmach_cpu_sram_tte[cp->cpuid].tte_inthi == 0 &&
5303 	    drmach_cpu_sram_tte[cp->cpuid].tte_intlo == 0);
5304 	drmach_cpu_sram_tte[cp->cpuid].tte_inthi = TTE_PFN_INTHI(pfn) |
5305 	    TTE_VALID_INT | TTE_SZ_INT(TTE8K);
5306 	drmach_cpu_sram_tte[cp->cpuid].tte_intlo = TTE_PFN_INTLO(pfn) |
5307 	    TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
5308 
5309 	DRMACH_PR("drmach_cpu_new: cpuid=%d, coreid=%d, stardrb_offset=0x%lx, "
5310 	    "cpu_sram_offset=0x%lx, idx=%d\n", cp->cpuid, cp->coreid,
5311 	    cp->dev.bp->stardrb_offset, cpu_stardrb_offset, idx);
5312 
5313 	(void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
5314 	    cp->dev.type, cp->dev.unum);
5315 
5316 	*idp = (drmachid_t)cp;
5317 	return (NULL);
5318 
5319 fail:
5320 	if (cp) {
5321 		drmach_node_dispose(cp->dev.node);
5322 		kmem_free(cp, sizeof (*cp));
5323 	}
5324 
5325 	*idp = (drmachid_t)0;
5326 	return (err);
5327 }
5328 
5329 static void
5330 drmach_cpu_dispose(drmachid_t id)
5331 {
5332 	drmach_cpu_t	*self;
5333 	processorid_t	cpuid;
5334 
5335 	ASSERT(DRMACH_IS_CPU_ID(id));
5336 
5337 	self = id;
5338 	if (self->dev.node)
5339 		drmach_node_dispose(self->dev.node);
5340 
5341 	cpuid = self->cpuid;
5342 	ASSERT(TTE_IS_VALID(&drmach_cpu_sram_tte[cpuid]) &&
5343 	    TTE_IS_8K(&drmach_cpu_sram_tte[cpuid]) &&
5344 	    TTE_IS_PRIVILEGED(&drmach_cpu_sram_tte[cpuid]) &&
5345 	    TTE_IS_LOCKED(&drmach_cpu_sram_tte[cpuid]));
5346 	drmach_cpu_sram_tte[cpuid].tte_inthi = 0;
5347 	drmach_cpu_sram_tte[cpuid].tte_intlo = 0;
5348 
5349 	kmem_free(self, sizeof (*self));
5350 }
5351 
5352 static int
5353 drmach_cpu_start(struct cpu *cp)
5354 {
5355 	extern xcfunc_t	drmach_set_lpa;
5356 	extern void	restart_other_cpu(int);
5357 	int		cpuid = cp->cpu_id;
5358 	int		rv, bnum;
5359 	drmach_board_t	*bp;
5360 
5361 	ASSERT(MUTEX_HELD(&cpu_lock));
5362 	ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
5363 
5364 	cp->cpu_flags &= ~CPU_POWEROFF;
5365 
5366 	/*
5367 	 * NOTE: restart_other_cpu pauses cpus during the
5368 	 *	 slave cpu start.  This helps to quiesce the
5369 	 *	 bus traffic a bit which makes the tick sync
5370 	 *	 routine in the prom more robust.
5371 	 */
5372 	DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
5373 
5374 	if (prom_hotaddcpu(cpuid) != 0) {
5375 		cmn_err(CE_PANIC, "prom_hotaddcpu() for cpuid=%d failed.",
5376 		    cpuid);
5377 	}
5378 
5379 	restart_other_cpu(cpuid);
5380 
5381 	bnum = drmach_portid2bnum(cpunodes[cpuid].portid);
5382 	rv = drmach_array_get(drmach_boards, bnum, (drmachid_t)&bp);
5383 	if (rv == -1 || bp == NULL) {
5384 		DRMACH_PR("drmach_cpu_start: cannot read board info for "
5385 		    "cpuid=%d: rv=%d, bp=%p\n", cpuid, rv, (void *)bp);
5386 	} else if (DRMACH_L1_SET_LPA(bp) && drmach_reprogram_lpa) {
5387 		int exp;
5388 		int ntries;
5389 
5390 		mutex_enter(&drmach_xt_mb_lock);
5391 		mutex_enter(&drmach_slice_table_lock);
5392 		bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
5393 
5394 		/*
5395 		 * drmach_slice_table[*]
5396 		 *	bit 5	valid
5397 		 *	bit 0:4	slice number
5398 		 *
5399 		 * drmach_xt_mb[*] format for drmach_set_lpa
5400 		 *	bit 7	valid
5401 		 *	bit 6	set null LPA (overrides bits 0:4)
5402 		 *	bit 0:4	slice number
5403 		 *
5404 		 * drmach_set_lpa derives processor CBASE and CBND
5405 		 * from bits 6 and 0:4 of drmach_xt_mb.  If bit 6 is
5406 		 * set, then CBASE = CBND = 0. Otherwise, CBASE = slice
5407 		 * number; CBND = slice number + 1.
5408 		 * No action is taken if bit 7 is zero.
5409 		 */
5410 		exp = (cpuid >> 5) & 0x1f;
5411 		if (drmach_slice_table[exp] & 0x20) {
5412 			drmach_xt_mb[cpuid] = 0x80 |
5413 			    (drmach_slice_table[exp] & 0x1f);
5414 		} else {
5415 			drmach_xt_mb[cpuid] = 0x80 | 0x40;
5416 		}
5417 
5418 		drmach_xt_ready = 0;
5419 
5420 		xt_one(cpuid, drmach_set_lpa, NULL, NULL);
5421 
5422 		ntries = drmach_cpu_ntries;
5423 		while (!drmach_xt_ready && ntries) {
5424 			DELAY(drmach_cpu_delay);
5425 			ntries--;
5426 		}
5427 
5428 		mutex_exit(&drmach_slice_table_lock);
5429 		mutex_exit(&drmach_xt_mb_lock);
5430 
5431 		DRMACH_PR(
5432 		    "waited %d out of %d tries for drmach_set_lpa on cpu%d",
5433 		    drmach_cpu_ntries - ntries, drmach_cpu_ntries,
5434 		    cp->cpu_id);
5435 	}
5436 
5437 	xt_one(cpuid, vtag_flushpage_tl1, (uint64_t)drmach_cpu_sram_va,
5438 	    (uint64_t)ksfmmup);
5439 
5440 	return (0);
5441 }
5442 
5443 /*
5444  * A detaching CPU is xcalled with an xtrap to drmach_cpu_stop_self() after
5445  * it has been offlined. The function of this routine is to get the cpu
5446  * spinning in a safe place. The requirement is that the system will not
5447  * reference anything on the detaching board (memory and i/o is detached
5448  * elsewhere) and that the CPU not reference anything on any other board
5449  * in the system.  This isolation is required during and after the writes
5450  * to the domain masks to remove the board from the domain.
5451  *
5452  * To accomplish this isolation the following is done:
5453  *	1) Create a locked mapping to the STARDRB data buffer located
5454  *	   in this cpu's sram. There is one TTE per cpu, initialized in
5455  *	   drmach_cpu_new(). The cpuid is used to select which TTE to use.
5456  *	   Each Safari port pair shares the CPU SRAM on a Serengeti CPU/MEM
5457  *	   board. The STARDRB buffer is 16KB on Cheetah+ boards, 32KB on Jaguar
5458  *	   boards. Each STARDRB buffer is logically divided by DR into one
5459  *	   8KB page per cpu (or Jaguar core).
5460  *	2) Copy the target function (drmach_shutdown_asm) into buffer.
5461  *	3) Jump to function now in the cpu sram.
5462  *	   Function will:
5463  *	   3.1) Flush its Ecache (displacement).
5464  *	   3.2) Flush its Dcache with HW mechanism.
5465  *	   3.3) Flush its Icache with HW mechanism.
5466  *	   3.4) Flush all valid and _unlocked_ D-TLB and I-TLB entries.
5467  *	   3.5) Set LPA to NULL
5468  *	   3.6) Clear xt_mb to signal completion. Note: cache line is
5469  *	        recovered by drmach_cpu_poweroff().
5470  *	4) Jump into an infinite loop.
5471  */
5472 
5473 static void
5474 drmach_cpu_stop_self(void)
5475 {
5476 	extern void drmach_shutdown_asm(uint64_t, uint64_t, int, int, uint64_t);
5477 	extern void drmach_shutdown_asm_end(void);
5478 
5479 	tte_t		*tte;
5480 	uint_t		*p, *q;
5481 	uint64_t	 stack_pointer;
5482 
5483 	ASSERT(((ptrdiff_t)drmach_shutdown_asm_end -
5484 	    (ptrdiff_t)drmach_shutdown_asm) < PAGESIZE);
5485 
5486 	tte = &drmach_cpu_sram_tte[CPU->cpu_id];
5487 	ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) && TTE_IS_PRIVILEGED(tte) &&
5488 	    TTE_IS_LOCKED(tte));
5489 	sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
5490 	sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
5491 
5492 	/* copy text. standard bcopy not designed to work in nc space */
5493 	p = (uint_t *)drmach_cpu_sram_va;
5494 	q = (uint_t *)drmach_shutdown_asm;
5495 	while (q < (uint_t *)drmach_shutdown_asm_end)
5496 		*p++ = *q++;
5497 
5498 	/* zero to assist debug */
5499 	q = (uint_t *)(drmach_cpu_sram_va + PAGESIZE);
5500 	while (p < q)
5501 		*p++ = 0;
5502 
5503 	/* a parking spot for the stack pointer */
5504 	stack_pointer = (uint64_t)q;
5505 
5506 	/* call copy of drmach_shutdown_asm */
5507 	(*(void (*)())drmach_cpu_sram_va)(
5508 	    stack_pointer,
5509 	    drmach_iocage_paddr,
5510 	    cpunodes[CPU->cpu_id].ecache_size,
5511 	    cpunodes[CPU->cpu_id].ecache_linesize,
5512 	    va_to_pa((void *)&drmach_xt_mb[CPU->cpu_id]));
5513 }
5514 
5515 static void
5516 drmach_cpu_shutdown_self(void)
5517 {
5518 	cpu_t		*cp = CPU;
5519 	int		cpuid = cp->cpu_id;
5520 	extern void	flush_windows(void);
5521 
5522 	flush_windows();
5523 
5524 	(void) spl8();
5525 
5526 	ASSERT(cp->cpu_intr_actv == 0);
5527 	ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
5528 	    cp->cpu_thread == cp->cpu_startup_thread);
5529 
5530 	cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
5531 
5532 	drmach_cpu_stop_self();
5533 
5534 	cmn_err(CE_PANIC, "CPU %d FAILED TO SHUTDOWN", cpuid);
5535 }
5536 
5537 static sbd_error_t *
5538 drmach_cpu_release(drmachid_t id)
5539 {
5540 	drmach_cpu_t	*cp;
5541 	struct cpu	*cpu;
5542 	sbd_error_t	*err;
5543 
5544 	if (!DRMACH_IS_CPU_ID(id))
5545 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5546 	cp = id;
5547 
5548 	ASSERT(MUTEX_HELD(&cpu_lock));
5549 
5550 	cpu = cpu_get(cp->cpuid);
5551 	if (cpu == NULL)
5552 		err = DRMACH_INTERNAL_ERROR();
5553 	else
5554 		err = NULL;
5555 
5556 	return (err);
5557 }
5558 
5559 static sbd_error_t *
5560 drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
5561 {
5562 	drmach_cpu_t	*cp;
5563 	drmach_device_t	*dp;
5564 
5565 	ASSERT(DRMACH_IS_CPU_ID(id));
5566 	cp = id;
5567 	dp = &cp->dev;
5568 
5569 	stat->assigned = dp->bp->assigned;
5570 	stat->powered = dp->bp->powered;
5571 	mutex_enter(&cpu_lock);
5572 	stat->configured = (cpu_get(cp->cpuid) != NULL);
5573 	mutex_exit(&cpu_lock);
5574 	stat->busy = dp->busy;
5575 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
5576 	stat->info[0] = '\0';
5577 
5578 	return (NULL);
5579 }
5580 
5581 sbd_error_t *
5582 drmach_cpu_disconnect(drmachid_t id)
5583 {
5584 	if (!DRMACH_IS_CPU_ID(id))
5585 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5586 
5587 	return (NULL);
5588 }
5589 
5590 sbd_error_t *
5591 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
5592 {
5593 	drmach_cpu_t	*cpu;
5594 
5595 	if (!DRMACH_IS_CPU_ID(id))
5596 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5597 	cpu = id;
5598 
5599 	*cpuid = cpu->cpuid;
5600 	return (NULL);
5601 }
5602 
5603 sbd_error_t *
5604 drmach_cpu_get_impl(drmachid_t id, int *ip)
5605 {
5606 	drmach_node_t	*np;
5607 	int		impl;
5608 
5609 	if (!DRMACH_IS_CPU_ID(id))
5610 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5611 
5612 	np = ((drmach_device_t *)id)->node;
5613 
5614 	if (np->n_getprop(np, "implementation#", &impl, sizeof (impl)) == -1) {
5615 		return (DRMACH_INTERNAL_ERROR());
5616 	}
5617 
5618 	*ip = impl;
5619 
5620 	return (NULL);
5621 }
5622 
5623 /*
5624  * Flush this cpu's ecache, then ensure all outstanding safari
5625  * transactions have retired.
5626  */
5627 void
5628 drmach_cpu_flush_ecache_sync(void)
5629 {
5630 	uint64_t *p;
5631 
5632 	ASSERT(curthread->t_bound_cpu == CPU);
5633 
5634 	cpu_flush_ecache();
5635 
5636 	mutex_enter(&drmach_bus_sync_lock);
5637 	for (p = drmach_bus_sync_list; *p; p++)
5638 		(void) ldphys(*p);
5639 	mutex_exit(&drmach_bus_sync_lock);
5640 
5641 	cpu_flush_ecache();
5642 }
5643 
5644 sbd_error_t *
5645 drmach_get_dip(drmachid_t id, dev_info_t **dip)
5646 {
5647 	drmach_device_t	*dp;
5648 
5649 	if (!DRMACH_IS_DEVICE_ID(id))
5650 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5651 	dp = id;
5652 
5653 	*dip = dp->node->n_getdip(dp->node);
5654 	return (NULL);
5655 }
5656 
5657 sbd_error_t *
5658 drmach_io_is_attached(drmachid_t id, int *yes)
5659 {
5660 	drmach_device_t *dp;
5661 	dev_info_t	*dip;
5662 	int state;
5663 
5664 	if (!DRMACH_IS_IO_ID(id))
5665 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5666 	dp = id;
5667 
5668 	dip = dp->node->n_getdip(dp->node);
5669 	if (dip == NULL) {
5670 		*yes = 0;
5671 		return (NULL);
5672 	}
5673 
5674 	state = ddi_get_devstate(dip);
5675 	*yes = i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP);
5676 
5677 	return (NULL);
5678 }
5679 
5680 static int
5681 drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t *dip)
5682 {
5683 	char			dtype[OBP_MAXPROPNAME];
5684 	int			portid;
5685 	uint_t			pci_csr_base;
5686 	struct pci_phys_spec	*regbuf = NULL;
5687 	int			rv, len;
5688 
5689 	ASSERT(dip != NULL);
5690 	rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "device_type", &len);
5691 	if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (dtype)))
5692 		return (0);
5693 
5694 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "device_type",
5695 	    (caddr_t)dtype, &len) == DDI_PROP_SUCCESS) {
5696 
5697 		if (strncmp(dtype, "pci", 3) == 0) {
5698 
5699 			/*
5700 			 * Get safari portid. All schizo/xmits 0
5701 			 * safari IDs end in 0x1C.
5702 			 */
5703 			rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid",
5704 			    &len);
5705 
5706 			if ((rv != DDI_PROP_SUCCESS) ||
5707 			    (len > sizeof (portid)))
5708 				return (0);
5709 
5710 			rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
5711 			    "portid", (caddr_t)&portid, &len);
5712 
5713 			if (rv != DDI_PROP_SUCCESS)
5714 				return (0);
5715 
5716 			if ((portid & 0x1F) != 0x1C)
5717 				return (0);
5718 
5719 			if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
5720 			    DDI_PROP_DONTPASS, "reg", (caddr_t)&regbuf,
5721 			    &len) == DDI_PROP_SUCCESS) {
5722 
5723 				pci_csr_base = regbuf[0].pci_phys_mid &
5724 				    PCI_CONF_ADDR_MASK;
5725 				kmem_free(regbuf, len);
5726 				/*
5727 				 * All PCI B-Leafs are at configspace 0x70.0000.
5728 				 */
5729 				if (pci_csr_base == 0x700000)
5730 					return (1);
5731 			}
5732 		}
5733 	}
5734 	return (0);
5735 }
5736 
5737 #define	SCHIZO_BINDING_NAME		"pci108e,8001"
5738 #define	XMITS_BINDING_NAME		"pci108e,8002"
5739 
5740 /*
5741  * Verify if the dip is an instance of MAN 'eri'.
5742  */
5743 static int
5744 drmach_dip_is_man_eri(dev_info_t *dip)
5745 {
5746 	struct pci_phys_spec	*regbuf = NULL;
5747 	dev_info_t		*parent_dip;
5748 	char			*name;
5749 	uint_t			pci_device;
5750 	uint_t			pci_function;
5751 	int			len;
5752 
5753 	if (dip == NULL)
5754 		return (0);
5755 	/*
5756 	 * Verify if the parent is schizo(xmits)0 and pci B leaf.
5757 	 */
5758 	if (((parent_dip = ddi_get_parent(dip)) == NULL) ||
5759 	    ((name = ddi_binding_name(parent_dip)) == NULL))
5760 		return (0);
5761 	if (strcmp(name, SCHIZO_BINDING_NAME) != 0) {
5762 		/*
5763 		 * This RIO could be on XMITS, so get the dip to
5764 		 * XMITS PCI Leaf.
5765 		 */
5766 		if ((parent_dip = ddi_get_parent(parent_dip)) == NULL)
5767 			return (0);
5768 		if (((name = ddi_binding_name(parent_dip)) == NULL) ||
5769 		    (strcmp(name, XMITS_BINDING_NAME) != 0)) {
5770 			return (0);
5771 		}
5772 	}
5773 	if (!drmach_dip_is_schizo_xmits_0_pci_b(parent_dip))
5774 		return (0);
5775 	/*
5776 	 * Finally make sure it is the MAN eri.
5777 	 */
5778 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
5779 	    "reg", (caddr_t)&regbuf, &len) == DDI_PROP_SUCCESS) {
5780 
5781 		pci_device = PCI_REG_DEV_G(regbuf->pci_phys_hi);
5782 		pci_function = PCI_REG_FUNC_G(regbuf->pci_phys_hi);
5783 		kmem_free(regbuf, len);
5784 
5785 		/*
5786 		 * The network function of the RIO ASIC will always be
5787 		 * device 3 and function 1 ("network@3,1").
5788 		 */
5789 		if ((pci_device == 3) && (pci_function == 1))
5790 			return (1);
5791 	}
5792 	return (0);
5793 }
5794 
5795 typedef struct {
5796 	int		iosram_inst;
5797 	dev_info_t	*eri_dip;
5798 	int		bnum;
5799 } drmach_io_inst_t;
5800 
5801 int
5802 drmach_board_find_io_insts(dev_info_t *dip, void *args)
5803 {
5804 	drmach_io_inst_t	*ios = (drmach_io_inst_t *)args;
5805 
5806 	int	rv;
5807 	int	len;
5808 	int	portid;
5809 	char	name[OBP_MAXDRVNAME];
5810 
5811 	rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid", &len);
5812 
5813 	if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (portid))) {
5814 		return (DDI_WALK_CONTINUE);
5815 	}
5816 
5817 	rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
5818 	    "portid", (caddr_t)&portid, &len);
5819 	if (rv != DDI_PROP_SUCCESS)
5820 		return (DDI_WALK_CONTINUE);
5821 
5822 	/* ignore devices that are not on this board */
5823 	if (drmach_portid2bnum(portid) != ios->bnum)
5824 		return (DDI_WALK_CONTINUE);
5825 
5826 	if ((ios->iosram_inst < 0) || (ios->eri_dip == NULL)) {
5827 		rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "name", &len);
5828 		if (rv == DDI_PROP_SUCCESS) {
5829 
5830 			rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
5831 			    0, "name",
5832 			    (caddr_t)name, &len);
5833 			if (rv != DDI_PROP_SUCCESS)
5834 				return (DDI_WALK_CONTINUE);
5835 
5836 			if (strncmp("iosram", name, 6) == 0) {
5837 				ios->iosram_inst = ddi_get_instance(dip);
5838 				if (ios->eri_dip == NULL)
5839 					return (DDI_WALK_CONTINUE);
5840 				else
5841 					return (DDI_WALK_TERMINATE);
5842 			} else {
5843 				if (drmach_dip_is_man_eri(dip)) {
5844 					ASSERT(ios->eri_dip == NULL);
5845 					ndi_hold_devi(dip);
5846 					ios->eri_dip = dip;
5847 					if (ios->iosram_inst < 0)
5848 						return (DDI_WALK_CONTINUE);
5849 					else
5850 						return (DDI_WALK_TERMINATE);
5851 				}
5852 			}
5853 		}
5854 	}
5855 	return (DDI_WALK_CONTINUE);
5856 }
5857 
5858 sbd_error_t *
5859 drmach_io_pre_release(drmachid_t id)
5860 {
5861 	drmach_io_inst_t	ios;
5862 	drmach_board_t		*bp;
5863 	int			rv = 0;
5864 	sbd_error_t		*err = NULL;
5865 	drmach_device_t		*dp;
5866 	dev_info_t		*rdip;
5867 	int			circ;
5868 
5869 	if (!DRMACH_IS_IO_ID(id))
5870 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5871 	dp = id;
5872 	bp = dp->bp;
5873 
5874 	rdip = dp->node->n_getdip(dp->node);
5875 
5876 	/* walk device tree to find iosram instance for the board */
5877 	ios.iosram_inst = -1;
5878 	ios.eri_dip = NULL;
5879 	ios.bnum = bp->bnum;
5880 
5881 	ndi_devi_enter(rdip, &circ);
5882 	ddi_walk_devs(ddi_get_child(rdip), drmach_board_find_io_insts,
5883 	    (void *)&ios);
5884 
5885 	DRMACH_PR("drmach_io_pre_release: bnum=%d iosram=%d eri=0x%p\n",
5886 	    ios.bnum, ios.iosram_inst, (void *)ios.eri_dip);
5887 	ndi_devi_exit(rdip, circ);
5888 
5889 	if (ios.eri_dip) {
5890 		/*
5891 		 * Release hold acquired in drmach_board_find_io_insts()
5892 		 */
5893 		ndi_rele_devi(ios.eri_dip);
5894 	}
5895 	if (ios.iosram_inst >= 0) {
5896 		/* call for tunnel switch */
5897 		do {
5898 			DRMACH_PR("calling iosram_switchfrom(%d)\n",
5899 			    ios.iosram_inst);
5900 			rv = iosram_switchfrom(ios.iosram_inst);
5901 			if (rv)
5902 				DRMACH_PR("iosram_switchfrom returned %d\n",
5903 				    rv);
5904 		} while (rv == EAGAIN);
5905 
5906 		if (rv)
5907 			err = drerr_new(0, ESTC_IOSWITCH, NULL);
5908 	}
5909 	return (err);
5910 }
5911 
5912 sbd_error_t *
5913 drmach_io_unrelease(drmachid_t id)
5914 {
5915 	dev_info_t	*dip;
5916 	sbd_error_t	*err = NULL;
5917 	drmach_device_t	*dp;
5918 
5919 	if (!DRMACH_IS_IO_ID(id))
5920 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5921 	dp = id;
5922 
5923 	dip = dp->node->n_getdip(dp->node);
5924 
5925 	if (dip == NULL)
5926 		err = DRMACH_INTERNAL_ERROR();
5927 	else {
5928 		int (*func)(dev_info_t *dip);
5929 
5930 		func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach",
5931 		    0);
5932 
5933 		if (func) {
5934 			drmach_io_inst_t ios;
5935 			dev_info_t	*pdip;
5936 			int		circ;
5937 
5938 			/*
5939 			 * Walk device tree to find rio dip for the board
5940 			 * Since we are not interested in iosram instance here,
5941 			 * initialize it to 0, so that the walk terminates as
5942 			 * soon as eri dip is found.
5943 			 */
5944 			ios.iosram_inst = 0;
5945 			ios.eri_dip = NULL;
5946 			ios.bnum = dp->bp->bnum;
5947 
5948 			if (pdip = ddi_get_parent(dip)) {
5949 				ndi_hold_devi(pdip);
5950 				ndi_devi_enter(pdip, &circ);
5951 			}
5952 			/*
5953 			 * Root node doesn't have to be held in any way.
5954 			 */
5955 			ddi_walk_devs(dip, drmach_board_find_io_insts,
5956 			    (void *)&ios);
5957 
5958 			if (pdip) {
5959 				ndi_devi_exit(pdip, circ);
5960 				ndi_rele_devi(pdip);
5961 			}
5962 
5963 			DRMACH_PR("drmach_io_unrelease: bnum=%d eri=0x%p\n",
5964 			    ios.bnum, (void *)ios.eri_dip);
5965 
5966 			if (ios.eri_dip) {
5967 				DRMACH_PR("calling man_dr_attach\n");
5968 				if ((*func)(ios.eri_dip))
5969 					err = drerr_new(0, ESTC_NWSWITCH, NULL);
5970 				/*
5971 				 * Release hold acquired in
5972 				 * drmach_board_find_io_insts()
5973 				 */
5974 				ndi_rele_devi(ios.eri_dip);
5975 			}
5976 		} else
5977 			DRMACH_PR("man_dr_attach NOT present\n");
5978 	}
5979 	return (err);
5980 }
5981 
5982 static sbd_error_t *
5983 drmach_io_release(drmachid_t id)
5984 {
5985 	dev_info_t	*dip;
5986 	sbd_error_t	*err = NULL;
5987 	drmach_device_t	*dp;
5988 
5989 	if (!DRMACH_IS_IO_ID(id))
5990 		return (drerr_new(0, ESTC_INAPPROP, NULL));
5991 	dp = id;
5992 
5993 	dip = dp->node->n_getdip(dp->node);
5994 
5995 	if (dip == NULL)
5996 		err = DRMACH_INTERNAL_ERROR();
5997 	else {
5998 		int (*func)(dev_info_t *dip);
5999 
6000 		func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_detach",
6001 		    0);
6002 
6003 		if (func) {
6004 			drmach_io_inst_t ios;
6005 			dev_info_t	*pdip;
6006 			int		circ;
6007 
6008 			/*
6009 			 * Walk device tree to find rio dip for the board
6010 			 * Since we are not interested in iosram instance here,
6011 			 * initialize it to 0, so that the walk terminates as
6012 			 * soon as eri dip is found.
6013 			 */
6014 			ios.iosram_inst = 0;
6015 			ios.eri_dip = NULL;
6016 			ios.bnum = dp->bp->bnum;
6017 
6018 			if (pdip = ddi_get_parent(dip)) {
6019 				ndi_hold_devi(pdip);
6020 				ndi_devi_enter(pdip, &circ);
6021 			}
6022 			/*
6023 			 * Root node doesn't have to be held in any way.
6024 			 */
6025 			ddi_walk_devs(dip, drmach_board_find_io_insts,
6026 			    (void *)&ios);
6027 
6028 			if (pdip) {
6029 				ndi_devi_exit(pdip, circ);
6030 				ndi_rele_devi(pdip);
6031 			}
6032 
6033 			DRMACH_PR("drmach_io_release: bnum=%d eri=0x%p\n",
6034 			    ios.bnum, (void *)ios.eri_dip);
6035 
6036 			if (ios.eri_dip) {
6037 				DRMACH_PR("calling man_dr_detach\n");
6038 				if ((*func)(ios.eri_dip))
6039 					err = drerr_new(0, ESTC_NWSWITCH, NULL);
6040 				/*
6041 				 * Release hold acquired in
6042 				 * drmach_board_find_io_insts()
6043 				 */
6044 				ndi_rele_devi(ios.eri_dip);
6045 			}
6046 		} else
6047 			DRMACH_PR("man_dr_detach NOT present\n");
6048 	}
6049 	return (err);
6050 }
6051 
6052 sbd_error_t *
6053 drmach_io_post_release(drmachid_t id)
6054 {
6055 	char 		*path;
6056 	dev_info_t	*rdip;
6057 	drmach_device_t	*dp;
6058 
6059 	if (!DRMACH_IS_DEVICE_ID(id))
6060 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6061 	dp = id;
6062 
6063 	rdip = dp->node->n_getdip(dp->node);
6064 
6065 	/*
6066 	 * Always called after drmach_unconfigure() which on Starcat
6067 	 * unconfigures the branch but doesn't remove it so the
6068 	 * dip must always exist.
6069 	 */
6070 	ASSERT(rdip);
6071 
6072 	ASSERT(e_ddi_branch_held(rdip));
6073 #ifdef DEBUG
6074 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
6075 	(void) ddi_pathname(rdip, path);
6076 	DRMACH_PR("post_release dip path is: %s\n", path);
6077 	kmem_free(path, MAXPATHLEN);
6078 #endif
6079 
6080 	if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
6081 		if (schpc_remove_pci(rdip)) {
6082 			DRMACH_PR("schpc_remove_pci failed\n");
6083 			return (drerr_new(0, ESBD_OFFLINE, NULL));
6084 		} else {
6085 			DRMACH_PR("schpc_remove_pci succeeded\n");
6086 		}
6087 	}
6088 
6089 	return (NULL);
6090 }
6091 
6092 sbd_error_t *
6093 drmach_io_post_attach(drmachid_t id)
6094 {
6095 	int		circ;
6096 	dev_info_t	*dip;
6097 	dev_info_t	*pdip;
6098 	drmach_device_t	*dp;
6099 	drmach_io_inst_t ios;
6100 
6101 	if (!DRMACH_IS_DEVICE_ID(id))
6102 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6103 	dp = id;
6104 
6105 	dip = dp->node->n_getdip(dp->node);
6106 
6107 	/*
6108 	 * We held the branch rooted at dip earlier, so at a minimum the
6109 	 * root i.e. dip must be present in the device tree.
6110 	 */
6111 	ASSERT(dip);
6112 
6113 	if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
6114 		if (schpc_add_pci(dip)) {
6115 			DRMACH_PR("schpc_add_pci failed\n");
6116 		} else {
6117 			DRMACH_PR("schpc_add_pci succeeded\n");
6118 		}
6119 	}
6120 
6121 	/*
6122 	 * Walk device tree to find rio dip for the board
6123 	 * Since we are not interested in iosram instance here,
6124 	 * initialize it to 0, so that the walk terminates as
6125 	 * soon as eri dip is found.
6126 	 */
6127 	ios.iosram_inst = 0;
6128 	ios.eri_dip = NULL;
6129 	ios.bnum = dp->bp->bnum;
6130 
6131 	if (pdip = ddi_get_parent(dip)) {
6132 		ndi_hold_devi(pdip);
6133 		ndi_devi_enter(pdip, &circ);
6134 	}
6135 	/*
6136 	 * Root node doesn't have to be held in any way.
6137 	 */
6138 	ddi_walk_devs(dip, drmach_board_find_io_insts, (void *)&ios);
6139 	if (pdip) {
6140 		ndi_devi_exit(pdip, circ);
6141 		ndi_rele_devi(pdip);
6142 	}
6143 
6144 	DRMACH_PR("drmach_io_post_attach: bnum=%d eri=0x%p\n",
6145 	    ios.bnum, (void *)ios.eri_dip);
6146 
6147 	if (ios.eri_dip) {
6148 		int (*func)(dev_info_t *dip);
6149 
6150 		func =
6151 		    (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach", 0);
6152 
6153 		if (func) {
6154 			DRMACH_PR("calling man_dr_attach\n");
6155 			(void) (*func)(ios.eri_dip);
6156 		} else {
6157 			DRMACH_PR("man_dr_attach NOT present\n");
6158 		}
6159 
6160 		/*
6161 		 * Release hold acquired in drmach_board_find_io_insts()
6162 		 */
6163 		ndi_rele_devi(ios.eri_dip);
6164 
6165 	}
6166 
6167 	return (NULL);
6168 }
6169 
6170 static sbd_error_t *
6171 drmach_io_status(drmachid_t id, drmach_status_t *stat)
6172 {
6173 	drmach_device_t *dp;
6174 	sbd_error_t	*err;
6175 	int		 configured;
6176 
6177 	ASSERT(DRMACH_IS_IO_ID(id));
6178 	dp = id;
6179 
6180 	err = drmach_io_is_attached(id, &configured);
6181 	if (err)
6182 		return (err);
6183 
6184 	stat->assigned = dp->bp->assigned;
6185 	stat->powered = dp->bp->powered;
6186 	stat->configured = (configured != 0);
6187 	stat->busy = dp->busy;
6188 	(void) strncpy(stat->type, dp->type, sizeof (stat->type));
6189 	stat->info[0] = '\0';
6190 
6191 	return (NULL);
6192 }
6193 
6194 sbd_error_t *
6195 drmach_mem_init_size(drmachid_t id)
6196 {
6197 	drmach_mem_t	*mp;
6198 	sbd_error_t	*err;
6199 	gdcd_t		*gdcd;
6200 	mem_chunk_t	*chunk;
6201 	uint64_t	 chunks, pa, mask, sz;
6202 
6203 	if (!DRMACH_IS_MEM_ID(id))
6204 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6205 	mp = id;
6206 
6207 	err = drmach_mem_get_base_physaddr(id, &pa);
6208 	if (err)
6209 		return (err);
6210 
6211 	mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
6212 	pa &= mask;
6213 
6214 	gdcd = drmach_gdcd_new();
6215 	if (gdcd == NULL)
6216 		return (DRMACH_INTERNAL_ERROR());
6217 
6218 	sz = 0;
6219 	chunk = gdcd->dcd_chunk_list.dcl_chunk;
6220 	chunks = gdcd->dcd_chunk_list.dcl_chunks;
6221 	while (chunks-- != 0) {
6222 		if ((chunk->mc_base_pa & mask) == pa) {
6223 			sz += chunk->mc_mbytes * 1048576;
6224 		}
6225 
6226 		++chunk;
6227 	}
6228 	mp->nbytes = sz;
6229 
6230 	drmach_gdcd_dispose(gdcd);
6231 	return (NULL);
6232 }
6233 
6234 /*
6235  * Hardware registers are organized into consecutively
6236  * addressed registers.  The reg property's hi and lo fields
6237  * together describe the base address of the register set for
6238  * this memory-controller.  Register descriptions and offsets
6239  * (from the base address) are as follows:
6240  *
6241  * Description				Offset	Size (bytes)
6242  * Memory Timing Control Register I	0x00	8
6243  * Memory Timing Control Register II	0x08	8
6244  * Memory Address Decoding Register I	0x10	8
6245  * Memory Address Decoding Register II	0x18	8
6246  * Memory Address Decoding Register III	0x20	8
6247  * Memory Address Decoding Register IV	0x28	8
6248  * Memory Address Control Register	0x30	8
6249  * Memory Timing Control Register III	0x38	8
6250  * Memory Timing Control Register IV	0x40	8
6251  * Memory Timing Control Register V  	0x48	8 (Jaguar, Panther only)
6252  * EMU Activity Status Register		0x50	8 (Panther only)
6253  *
6254  * Only the Memory Address Decoding Register and EMU Activity Status
6255  * Register addresses are needed for DRMACH.
6256  */
6257 static sbd_error_t *
6258 drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
6259 {
6260 	static void drmach_mem_dispose(drmachid_t);
6261 	static sbd_error_t *drmach_mem_release(drmachid_t);
6262 	static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
6263 
6264 	sbd_error_t	*err;
6265 	uint64_t	 madr_pa;
6266 	drmach_mem_t	*mp;
6267 	int		 bank, count;
6268 
6269 	err = drmach_read_reg_addr(proto, &madr_pa);
6270 	if (err)
6271 		return (err);
6272 
6273 	mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
6274 	bcopy(proto, &mp->dev, sizeof (mp->dev));
6275 	mp->dev.node = drmach_node_dup(proto->node);
6276 	mp->dev.cm.isa = (void *)drmach_mem_new;
6277 	mp->dev.cm.dispose = drmach_mem_dispose;
6278 	mp->dev.cm.release = drmach_mem_release;
6279 	mp->dev.cm.status = drmach_mem_status;
6280 	mp->madr_pa = madr_pa;
6281 
6282 	(void) snprintf(mp->dev.cm.name,
6283 	    sizeof (mp->dev.cm.name), "%s", mp->dev.type);
6284 
6285 	for (count = bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
6286 		uint64_t madr;
6287 
6288 		drmach_mem_read_madr(mp, bank, &madr);
6289 		if (madr & DRMACH_MC_VALID_MASK) {
6290 			count += 1;
6291 			break;
6292 		}
6293 	}
6294 
6295 	/*
6296 	 * If none of the banks had their valid bit set, that means
6297 	 * post did not configure this MC to participate in the
6298 	 * domain.  So, pretend this node does not exist by returning
6299 	 * a drmachid of zero.
6300 	 */
6301 	if (count == 0) {
6302 		/* drmach_mem_dispose frees board mem list */
6303 		drmach_node_dispose(mp->dev.node);
6304 		kmem_free(mp, sizeof (*mp));
6305 		*idp = (drmachid_t)0;
6306 		return (NULL);
6307 	}
6308 
6309 	/*
6310 	 * Only one mem unit per board is exposed to the
6311 	 * PIM layer.  The first mem unit encountered during
6312 	 * tree walk is used to represent all mem units on
6313 	 * the same board.
6314 	 */
6315 	if (mp->dev.bp->mem == NULL) {
6316 		/* start list of mem units on this board */
6317 		mp->dev.bp->mem = mp;
6318 
6319 		/*
6320 		 * force unum to zero since this is the only mem unit
6321 		 * that will be visible to the PIM layer.
6322 		 */
6323 		mp->dev.unum = 0;
6324 
6325 		/*
6326 		 * board memory size kept in this mem unit only
6327 		 */
6328 		err = drmach_mem_init_size(mp);
6329 		if (err) {
6330 			mp->dev.bp->mem = NULL;
6331 			/* drmach_mem_dispose frees board mem list */
6332 			drmach_node_dispose(mp->dev.node);
6333 			kmem_free(mp, sizeof (*mp));
6334 			*idp = (drmachid_t)0;
6335 			return (NULL);
6336 		}
6337 
6338 		/*
6339 		 * allow this instance (the first encountered on this board)
6340 		 * to be visible to the PIM layer.
6341 		 */
6342 		*idp = (drmachid_t)mp;
6343 	} else {
6344 		drmach_mem_t *lp;
6345 
6346 		/* hide this mem instance behind the first. */
6347 		for (lp = mp->dev.bp->mem; lp->next; lp = lp->next)
6348 			;
6349 		lp->next = mp;
6350 
6351 		/*
6352 		 * hide this instance from the caller.
6353 		 * See drmach_board_find_devices_cb() for details.
6354 		 */
6355 		*idp = (drmachid_t)0;
6356 	}
6357 
6358 	return (NULL);
6359 }
6360 
6361 static void
6362 drmach_mem_dispose(drmachid_t id)
6363 {
6364 	drmach_mem_t *mp, *next;
6365 	drmach_board_t *bp;
6366 
6367 	ASSERT(DRMACH_IS_MEM_ID(id));
6368 
6369 	mutex_enter(&drmach_bus_sync_lock);
6370 
6371 	mp = id;
6372 	bp = mp->dev.bp;
6373 
6374 	do {
6375 		if (mp->dev.node)
6376 			drmach_node_dispose(mp->dev.node);
6377 
6378 		next = mp->next;
6379 		kmem_free(mp, sizeof (*mp));
6380 		mp = next;
6381 	} while (mp);
6382 
6383 	bp->mem = NULL;
6384 
6385 	drmach_bus_sync_list_update();
6386 	mutex_exit(&drmach_bus_sync_lock);
6387 }
6388 
6389 sbd_error_t *
6390 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
6391 {
6392 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
6393 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
6394 	int		rv;
6395 
6396 	ASSERT(size != 0);
6397 
6398 	if (!DRMACH_IS_MEM_ID(id))
6399 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6400 
6401 	rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
6402 	if (rv == ENOMEM) {
6403 		cmn_err(CE_WARN, "%lu megabytes not available"
6404 		    " to kernel cage", size >> 20);
6405 	} else if (rv != 0) {
6406 		/* catch this in debug kernels */
6407 		ASSERT(0);
6408 
6409 		cmn_err(CE_WARN, "unexpected kcage_range_add"
6410 		    " return value %d", rv);
6411 	}
6412 
6413 	return (NULL);
6414 }
6415 
6416 sbd_error_t *
6417 drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
6418 {
6419 	pfn_t		 basepfn = (pfn_t)(basepa >> PAGESHIFT);
6420 	pgcnt_t		 npages = (pgcnt_t)(size >> PAGESHIFT);
6421 	int		 rv;
6422 
6423 	if (!DRMACH_IS_MEM_ID(id))
6424 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6425 
6426 	if (size > 0) {
6427 		rv = kcage_range_delete_post_mem_del(basepfn, npages);
6428 		if (rv != 0) {
6429 			cmn_err(CE_WARN,
6430 			    "unexpected kcage_range_delete_post_mem_del"
6431 			    " return value %d", rv);
6432 			return (DRMACH_INTERNAL_ERROR());
6433 		}
6434 	}
6435 
6436 	return (NULL);
6437 }
6438 
6439 sbd_error_t *
6440 drmach_mem_disable(drmachid_t id)
6441 {
6442 	if (!DRMACH_IS_MEM_ID(id))
6443 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6444 	else
6445 		return (NULL);
6446 }
6447 
6448 sbd_error_t *
6449 drmach_mem_enable(drmachid_t id)
6450 {
6451 	if (!DRMACH_IS_MEM_ID(id))
6452 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6453 	else
6454 		return (NULL);
6455 }
6456 
6457 sbd_error_t *
6458 drmach_mem_get_alignment(drmachid_t id, uint64_t *mask)
6459 {
6460 #define	MB(mb) ((mb) * 1048576ull)
6461 
6462 	static struct {
6463 		uint_t		uk;
6464 		uint64_t	segsz;
6465 	}  uk2segsz[] = {
6466 		{ 0x003,	MB(256)	  },
6467 		{ 0x007,	MB(512)	  },
6468 		{ 0x00f,	MB(1024)  },
6469 		{ 0x01f,	MB(2048)  },
6470 		{ 0x03f,	MB(4096)  },
6471 		{ 0x07f,	MB(8192)  },
6472 		{ 0x0ff,	MB(16384) },
6473 		{ 0x1ff,	MB(32768) },
6474 		{ 0x3ff,	MB(65536) },
6475 		{ 0x7ff,	MB(131072) }
6476 	};
6477 	static int len = sizeof (uk2segsz) / sizeof (uk2segsz[0]);
6478 
6479 #undef MB
6480 
6481 	uint64_t	 largest_sz = 0;
6482 	drmach_mem_t	*mp;
6483 
6484 	if (!DRMACH_IS_MEM_ID(id))
6485 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6486 
6487 	/* prime the result with a default value */
6488 	*mask = (DRMACH_MEM_SLICE_SIZE - 1);
6489 
6490 	for (mp = id; mp; mp = mp->next) {
6491 		int bank;
6492 
6493 		for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
6494 			int		i;
6495 			uint_t		uk;
6496 			uint64_t	madr;
6497 
6498 			/* get register value, extract uk and normalize */
6499 			drmach_mem_read_madr(mp, bank, &madr);
6500 
6501 			if (!(madr & DRMACH_MC_VALID_MASK))
6502 				continue;
6503 
6504 			uk = DRMACH_MC_UK(madr);
6505 
6506 			/* match uk value */
6507 			for (i = 0; i < len; i++)
6508 				if (uk == uk2segsz[i].uk)
6509 					break;
6510 
6511 			if (i < len) {
6512 				uint64_t sz = uk2segsz[i].segsz;
6513 
6514 				/*
6515 				 * remember largest segment size,
6516 				 * update mask result
6517 				 */
6518 				if (sz > largest_sz) {
6519 					largest_sz = sz;
6520 					*mask = sz - 1;
6521 				}
6522 			} else {
6523 				/*
6524 				 * uk not in table, punt using
6525 				 * entire slice size. no longer any
6526 				 * reason to check other banks.
6527 				 */
6528 				*mask = (DRMACH_MEM_SLICE_SIZE - 1);
6529 				return (NULL);
6530 			}
6531 		}
6532 	}
6533 
6534 	return (NULL);
6535 }
6536 
6537 sbd_error_t *
6538 drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *base_addr)
6539 {
6540 	drmach_mem_t *mp;
6541 
6542 	if (!DRMACH_IS_MEM_ID(id))
6543 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6544 
6545 	*base_addr = (uint64_t)-1;
6546 	for (mp = id; mp; mp = mp->next) {
6547 		int bank;
6548 
6549 		for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
6550 			uint64_t addr, madr;
6551 
6552 			drmach_mem_read_madr(mp, bank, &madr);
6553 			if (madr & DRMACH_MC_VALID_MASK) {
6554 				addr = DRMACH_MC_UM_TO_PA(madr) |
6555 				    DRMACH_MC_LM_TO_PA(madr);
6556 
6557 				if (addr < *base_addr)
6558 					*base_addr = addr;
6559 			}
6560 		}
6561 	}
6562 
6563 	/* should not happen, but ... */
6564 	if (*base_addr == (uint64_t)-1)
6565 		return (DRMACH_INTERNAL_ERROR());
6566 
6567 	return (NULL);
6568 }
6569 
6570 void
6571 drmach_bus_sync_list_update(void)
6572 {
6573 	int		rv, idx, cnt = 0;
6574 	drmachid_t	id;
6575 
6576 	ASSERT(MUTEX_HELD(&drmach_bus_sync_lock));
6577 
6578 	rv = drmach_array_first(drmach_boards, &idx, &id);
6579 	while (rv == 0) {
6580 		drmach_board_t		*bp = id;
6581 		drmach_mem_t		*mp = bp->mem;
6582 
6583 		while (mp) {
6584 			int bank;
6585 
6586 			for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
6587 				uint64_t madr;
6588 
6589 				drmach_mem_read_madr(mp, bank, &madr);
6590 				if (madr & DRMACH_MC_VALID_MASK) {
6591 					uint64_t pa;
6592 
6593 					pa  = DRMACH_MC_UM_TO_PA(madr);
6594 					pa |= DRMACH_MC_LM_TO_PA(madr);
6595 
6596 					/*
6597 					 * The list is zero terminated.
6598 					 * Offset the pa by a doubleword
6599 					 * to avoid confusing a pa value of
6600 					 * of zero with the terminator.
6601 					 */
6602 					pa += sizeof (uint64_t);
6603 
6604 					drmach_bus_sync_list[cnt++] = pa;
6605 				}
6606 			}
6607 
6608 			mp = mp->next;
6609 		}
6610 
6611 		rv = drmach_array_next(drmach_boards, &idx, &id);
6612 	}
6613 
6614 	drmach_bus_sync_list[cnt] = 0;
6615 }
6616 
6617 sbd_error_t *
6618 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
6619 {
6620 	sbd_error_t	*err;
6621 	struct memlist	*mlist;
6622 	gdcd_t		*gdcd;
6623 	mem_chunk_t	*chunk;
6624 	uint64_t	 chunks, pa, mask;
6625 
6626 	err = drmach_mem_get_base_physaddr(id, &pa);
6627 	if (err)
6628 		return (err);
6629 
6630 	gdcd = drmach_gdcd_new();
6631 	if (gdcd == NULL)
6632 		return (DRMACH_INTERNAL_ERROR());
6633 
6634 	mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
6635 	pa &= mask;
6636 
6637 	mlist = NULL;
6638 	chunk = gdcd->dcd_chunk_list.dcl_chunk;
6639 	chunks = gdcd->dcd_chunk_list.dcl_chunks;
6640 	while (chunks-- != 0) {
6641 		if ((chunk->mc_base_pa & mask) == pa) {
6642 			mlist = memlist_add_span(mlist, chunk->mc_base_pa,
6643 			    chunk->mc_mbytes * 1048576);
6644 		}
6645 
6646 		++chunk;
6647 	}
6648 
6649 	drmach_gdcd_dispose(gdcd);
6650 
6651 #ifdef DEBUG
6652 	DRMACH_PR("GDCD derived memlist:");
6653 	memlist_dump(mlist);
6654 #endif
6655 
6656 	*ml = mlist;
6657 	return (NULL);
6658 }
6659 
6660 sbd_error_t *
6661 drmach_mem_get_size(drmachid_t id, uint64_t *bytes)
6662 {
6663 	drmach_mem_t	*mp;
6664 
6665 	if (!DRMACH_IS_MEM_ID(id))
6666 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6667 	mp = id;
6668 
6669 	ASSERT(mp->nbytes != 0);
6670 	*bytes = mp->nbytes;
6671 
6672 	return (NULL);
6673 }
6674 
6675 sbd_error_t *
6676 drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
6677 {
6678 	sbd_error_t	*err;
6679 	drmach_device_t	*mp;
6680 
6681 	if (!DRMACH_IS_MEM_ID(id))
6682 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6683 	mp = id;
6684 
6685 	switch (DRMACH_BNUM2SLOT(mp->bp->bnum)) {
6686 		case 0:	*bytes = DRMACH_MEM_USABLE_SLICE_SIZE;
6687 			err = NULL;
6688 			break;
6689 
6690 		case 1: *bytes = 0;
6691 			err = NULL;
6692 			break;
6693 
6694 		default:
6695 			err = DRMACH_INTERNAL_ERROR();
6696 			break;
6697 	}
6698 
6699 	return (err);
6700 }
6701 
6702 processorid_t drmach_mem_cpu_affinity_nail;
6703 
6704 processorid_t
6705 drmach_mem_cpu_affinity(drmachid_t id)
6706 {
6707 	drmach_device_t	*mp;
6708 	drmach_board_t	*bp;
6709 	processorid_t	 cpuid;
6710 
6711 	if (!DRMACH_IS_MEM_ID(id))
6712 		return (CPU_CURRENT);
6713 
6714 	if (drmach_mem_cpu_affinity_nail) {
6715 		cpuid = drmach_mem_cpu_affinity_nail;
6716 
6717 		if (cpuid < 0 || cpuid > NCPU)
6718 			return (CPU_CURRENT);
6719 
6720 		mutex_enter(&cpu_lock);
6721 		if (cpu[cpuid] == NULL || !CPU_ACTIVE(cpu[cpuid]))
6722 			cpuid = CPU_CURRENT;
6723 		mutex_exit(&cpu_lock);
6724 
6725 		return (cpuid);
6726 	}
6727 
6728 	/* try to choose a proc on the target board */
6729 	mp = id;
6730 	bp = mp->bp;
6731 	if (bp->devices) {
6732 		int		 rv;
6733 		int		 d_idx;
6734 		drmachid_t	 d_id;
6735 
6736 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
6737 		while (rv == 0) {
6738 			if (DRMACH_IS_CPU_ID(d_id)) {
6739 				drmach_cpu_t	*cp = d_id;
6740 
6741 				mutex_enter(&cpu_lock);
6742 				cpuid = cp->cpuid;
6743 				if (cpu[cpuid] && CPU_ACTIVE(cpu[cpuid])) {
6744 					mutex_exit(&cpu_lock);
6745 					return (cpuid);
6746 				} else {
6747 					mutex_exit(&cpu_lock);
6748 				}
6749 			}
6750 
6751 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
6752 		}
6753 	}
6754 
6755 	/* otherwise, this proc, wherever it is */
6756 	return (CPU_CURRENT);
6757 }
6758 
6759 static sbd_error_t *
6760 drmach_mem_release(drmachid_t id)
6761 {
6762 	if (!DRMACH_IS_MEM_ID(id))
6763 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6764 	return (NULL);
6765 }
6766 
6767 static sbd_error_t *
6768 drmach_mem_status(drmachid_t id, drmach_status_t *stat)
6769 {
6770 	drmach_mem_t	*mp;
6771 	sbd_error_t	*err;
6772 	uint64_t	 pa, slice_size;
6773 	struct memlist	*ml;
6774 
6775 	ASSERT(DRMACH_IS_MEM_ID(id));
6776 	mp = id;
6777 
6778 	/* get starting physical address of target memory */
6779 	err = drmach_mem_get_base_physaddr(id, &pa);
6780 	if (err)
6781 		return (err);
6782 
6783 	/* round down to slice boundary */
6784 	slice_size = DRMACH_MEM_SLICE_SIZE;
6785 	pa &= ~ (slice_size - 1);
6786 
6787 	/* stop at first span that is in slice */
6788 	memlist_read_lock();
6789 	for (ml = phys_install; ml; ml = ml->ml_next)
6790 		if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
6791 			break;
6792 	memlist_read_unlock();
6793 
6794 	stat->assigned = mp->dev.bp->assigned;
6795 	stat->powered = mp->dev.bp->powered;
6796 	stat->configured = (ml != NULL);
6797 	stat->busy = mp->dev.busy;
6798 	(void) strncpy(stat->type, mp->dev.type, sizeof (stat->type));
6799 	stat->info[0] = '\0';
6800 
6801 	return (NULL);
6802 }
6803 
6804 sbd_error_t *
6805 drmach_board_deprobe(drmachid_t id)
6806 {
6807 	drmach_board_t	*bp;
6808 	sbd_error_t	*err = NULL;
6809 
6810 	if (!DRMACH_IS_BOARD_ID(id))
6811 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6812 	bp = id;
6813 
6814 	if (bp->tree) {
6815 		drmach_node_dispose(bp->tree);
6816 		bp->tree = NULL;
6817 	}
6818 	if (bp->devices) {
6819 		drmach_array_dispose(bp->devices, drmach_device_dispose);
6820 		bp->devices = NULL;
6821 		bp->mem = NULL;  /* TODO: still needed? */
6822 	}
6823 	return (err);
6824 }
6825 
6826 /*ARGSUSED1*/
6827 static sbd_error_t *
6828 drmach_pt_showlpa(drmachid_t id, drmach_opts_t *opts)
6829 {
6830 	drmach_device_t	*dp;
6831 	uint64_t	val;
6832 	int		err = 1;
6833 
6834 	if (DRMACH_IS_CPU_ID(id)) {
6835 		drmach_cpu_t *cp = id;
6836 		if (drmach_cpu_read_scr(cp, &val))
6837 			err = 0;
6838 	} else if (DRMACH_IS_IO_ID(id) && ((drmach_io_t *)id)->scsr_pa != 0) {
6839 		drmach_io_t *io = id;
6840 		val = lddphysio(io->scsr_pa);
6841 		err = 0;
6842 	}
6843 	if (err)
6844 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6845 
6846 	dp = id;
6847 	uprintf("showlpa %s::%s portid %d, base pa %lx, bound pa %lx\n",
6848 	    dp->bp->cm.name,
6849 	    dp->cm.name,
6850 	    dp->portid,
6851 	    (long)(DRMACH_LPA_BASE_TO_PA(val)),
6852 	    (long)(DRMACH_LPA_BND_TO_PA(val)));
6853 
6854 	return (NULL);
6855 }
6856 
6857 /*ARGSUSED*/
6858 static sbd_error_t *
6859 drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
6860 {
6861 	drmach_board_t		*bp = (drmach_board_t *)id;
6862 	sbd_error_t		*err;
6863 	sc_gptwocfg_cookie_t	scc;
6864 
6865 	if (!DRMACH_IS_BOARD_ID(id))
6866 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6867 
6868 	/* do saf configurator stuff */
6869 	DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
6870 	scc = sc_probe_board(bp->bnum);
6871 	if (scc == NULL) {
6872 		err = drerr_new(0, ESTC_PROBE, bp->cm.name);
6873 		return (err);
6874 	}
6875 
6876 	return (err);
6877 }
6878 
6879 /*ARGSUSED*/
6880 static sbd_error_t *
6881 drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
6882 {
6883 	drmach_board_t	*bp;
6884 	sbd_error_t	*err = NULL;
6885 	sc_gptwocfg_cookie_t	scc;
6886 
6887 	if (!DRMACH_IS_BOARD_ID(id))
6888 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6889 	bp = id;
6890 
6891 	cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
6892 	scc = sc_unprobe_board(bp->bnum);
6893 	if (scc != NULL) {
6894 		err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
6895 	}
6896 
6897 	if (err == NULL)
6898 		err = drmach_board_deprobe(id);
6899 
6900 	return (err);
6901 }
6902 
6903 static sbd_error_t *
6904 drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
6905 {
6906 	_NOTE(ARGUNUSED(id))
6907 	_NOTE(ARGUNUSED(opts))
6908 
6909 	struct memlist	*ml;
6910 	uint64_t	src_pa;
6911 	uint64_t	dst_pa;
6912 	uint64_t	dst;
6913 
6914 	dst_pa = va_to_pa(&dst);
6915 
6916 	memlist_read_lock();
6917 	for (ml = phys_install; ml; ml = ml->ml_next) {
6918 		uint64_t	nbytes;
6919 
6920 		src_pa = ml->ml_address;
6921 		nbytes = ml->ml_size;
6922 
6923 		while (nbytes != 0ull) {
6924 
6925 			/* copy 32 bytes at src_pa to dst_pa */
6926 			bcopy32_il(src_pa, dst_pa);
6927 
6928 			/* increment by 32 bytes */
6929 			src_pa += (4 * sizeof (uint64_t));
6930 
6931 			/* decrement by 32 bytes */
6932 			nbytes -= (4 * sizeof (uint64_t));
6933 		}
6934 	}
6935 	memlist_read_unlock();
6936 
6937 	return (NULL);
6938 }
6939 
6940 static sbd_error_t *
6941 drmach_pt_recovercpu(drmachid_t id, drmach_opts_t *opts)
6942 {
6943 	_NOTE(ARGUNUSED(opts))
6944 
6945 	drmach_cpu_t	*cp;
6946 
6947 	if (!DRMACH_IS_CPU_ID(id))
6948 		return (drerr_new(0, ESTC_INAPPROP, NULL));
6949 	cp = id;
6950 
6951 	mutex_enter(&cpu_lock);
6952 	(void) drmach_iocage_cpu_return(&(cp->dev),
6953 	    CPU_ENABLE | CPU_EXISTS | CPU_READY | CPU_RUNNING);
6954 	mutex_exit(&cpu_lock);
6955 
6956 	return (NULL);
6957 }
6958 
6959 /*
6960  * Starcat DR passthrus are for debugging purposes only.
6961  */
6962 static struct {
6963 	const char	*name;
6964 	sbd_error_t	*(*handler)(drmachid_t id, drmach_opts_t *opts);
6965 } drmach_pt_arr[] = {
6966 	{ "showlpa",		drmach_pt_showlpa		},
6967 	{ "ikprobe",		drmach_pt_ikprobe		},
6968 	{ "ikdeprobe",		drmach_pt_ikdeprobe		},
6969 	{ "readmem",		drmach_pt_readmem		},
6970 	{ "recovercpu",		drmach_pt_recovercpu		},
6971 
6972 	/* the following line must always be last */
6973 	{ NULL,			NULL				}
6974 };
6975 
6976 /*ARGSUSED*/
6977 sbd_error_t *
6978 drmach_passthru(drmachid_t id, drmach_opts_t *opts)
6979 {
6980 	int		i;
6981 	sbd_error_t	*err;
6982 
6983 	i = 0;
6984 	while (drmach_pt_arr[i].name != NULL) {
6985 		int len = strlen(drmach_pt_arr[i].name);
6986 
6987 		if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
6988 			break;
6989 
6990 		i += 1;
6991 	}
6992 
6993 	if (drmach_pt_arr[i].name == NULL)
6994 		err = drerr_new(0, ESTC_UNKPTCMD, opts->copts);
6995 	else
6996 		err = (*drmach_pt_arr[i].handler)(id, opts);
6997 
6998 	return (err);
6999 }
7000 
7001 sbd_error_t *
7002 drmach_release(drmachid_t id)
7003 {
7004 	drmach_common_t *cp;
7005 
7006 	if (!DRMACH_IS_DEVICE_ID(id))
7007 		return (drerr_new(0, ESTC_INAPPROP, NULL));
7008 	cp = id;
7009 
7010 	return (cp->release(id));
7011 }
7012 
7013 sbd_error_t *
7014 drmach_status(drmachid_t id, drmach_status_t *stat)
7015 {
7016 	drmach_common_t *cp;
7017 	sbd_error_t	*err;
7018 
7019 	rw_enter(&drmach_boards_rwlock, RW_READER);
7020 
7021 	if (!DRMACH_IS_ID(id)) {
7022 		rw_exit(&drmach_boards_rwlock);
7023 		return (drerr_new(0, ESTC_NOTID, NULL));
7024 	}
7025 
7026 	cp = id;
7027 
7028 	err = cp->status(id, stat);
7029 	rw_exit(&drmach_boards_rwlock);
7030 	return (err);
7031 }
7032 
7033 static sbd_error_t *
7034 drmach_i_status(drmachid_t id, drmach_status_t *stat)
7035 {
7036 	drmach_common_t *cp;
7037 
7038 	if (!DRMACH_IS_ID(id))
7039 		return (drerr_new(0, ESTC_NOTID, NULL));
7040 	cp = id;
7041 
7042 	return (cp->status(id, stat));
7043 }
7044 
7045 /*ARGSUSED*/
7046 sbd_error_t *
7047 drmach_unconfigure(drmachid_t id, int flags)
7048 {
7049 	drmach_device_t	*dp;
7050 	dev_info_t 	*rdip;
7051 
7052 	char	name[OBP_MAXDRVNAME];
7053 	int rv;
7054 
7055 	/*
7056 	 * Since CPU nodes are not configured, it is
7057 	 * necessary to skip the unconfigure step as
7058 	 * well.
7059 	 */
7060 	if (DRMACH_IS_CPU_ID(id)) {
7061 		return (NULL);
7062 	}
7063 
7064 	for (; id; ) {
7065 		dev_info_t	*fdip = NULL;
7066 
7067 		if (!DRMACH_IS_DEVICE_ID(id))
7068 			return (drerr_new(0, ESTC_INAPPROP, NULL));
7069 		dp = id;
7070 
7071 		rdip = dp->node->n_getdip(dp->node);
7072 
7073 		/*
7074 		 * drmach_unconfigure() is always called on a configured branch.
7075 		 * So the root of the branch was held earlier and must exist.
7076 		 */
7077 		ASSERT(rdip);
7078 
7079 		DRMACH_PR("drmach_unconfigure: unconfiguring DDI branch");
7080 
7081 		rv = dp->node->n_getprop(dp->node,
7082 		    "name", name, OBP_MAXDRVNAME);
7083 
7084 		/* The node must have a name */
7085 		if (rv)
7086 			return (0);
7087 
7088 		if (drmach_name2type_idx(name) < 0) {
7089 			if (DRMACH_IS_MEM_ID(id)) {
7090 				drmach_mem_t	*mp = id;
7091 				id = mp->next;
7092 			} else {
7093 				id = NULL;
7094 			}
7095 			continue;
7096 		}
7097 
7098 		/*
7099 		 * NOTE: FORCE flag is no longer needed under devfs
7100 		 */
7101 		ASSERT(e_ddi_branch_held(rdip));
7102 		if (e_ddi_branch_unconfigure(rdip, &fdip, 0) != 0) {
7103 			sbd_error_t *err = NULL;
7104 			char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
7105 
7106 			/*
7107 			 * If non-NULL, fdip is returned held and must be
7108 			 * released.
7109 			 */
7110 			if (fdip != NULL) {
7111 				(void) ddi_pathname(fdip, path);
7112 				ddi_release_devi(fdip);
7113 			} else {
7114 				(void) ddi_pathname(rdip, path);
7115 			}
7116 
7117 			err = drerr_new(1, ESTC_DRVFAIL, path);
7118 
7119 			kmem_free(path, MAXPATHLEN);
7120 
7121 			/*
7122 			 * If we were unconfiguring an IO board, a call was
7123 			 * made to man_dr_detach.  We now need to call
7124 			 * man_dr_attach to regain man use of the eri.
7125 			 */
7126 			if (DRMACH_IS_IO_ID(id)) {
7127 				int (*func)(dev_info_t *dip);
7128 
7129 				func = (int (*)(dev_info_t *))kobj_getsymvalue\
7130 				    ("man_dr_attach", 0);
7131 
7132 				if (func) {
7133 					drmach_io_inst_t ios;
7134 					dev_info_t 	*pdip;
7135 					int		circ;
7136 
7137 					/*
7138 					 * Walk device tree to find rio dip for
7139 					 * the board
7140 					 * Since we are not interested in iosram
7141 					 * instance here, initialize it to 0, so
7142 					 * that the walk terminates as soon as
7143 					 * eri dip is found.
7144 					 */
7145 					ios.iosram_inst = 0;
7146 					ios.eri_dip = NULL;
7147 					ios.bnum = dp->bp->bnum;
7148 
7149 					if (pdip = ddi_get_parent(rdip)) {
7150 						ndi_hold_devi(pdip);
7151 						ndi_devi_enter(pdip, &circ);
7152 					}
7153 					/*
7154 					 * Root node doesn't have to be held in
7155 					 * any way.
7156 					 */
7157 					ASSERT(e_ddi_branch_held(rdip));
7158 					ddi_walk_devs(rdip,
7159 					    drmach_board_find_io_insts,
7160 					    (void *)&ios);
7161 
7162 					DRMACH_PR("drmach_unconfigure: bnum=%d"
7163 					    " eri=0x%p\n",
7164 					    ios.bnum, (void *)ios.eri_dip);
7165 
7166 					if (pdip) {
7167 						ndi_devi_exit(pdip, circ);
7168 						ndi_rele_devi(pdip);
7169 					}
7170 
7171 					if (ios.eri_dip) {
7172 						DRMACH_PR("calling"
7173 						    " man_dr_attach\n");
7174 						(void) (*func)(ios.eri_dip);
7175 						/*
7176 						 * Release hold acquired in
7177 						 * drmach_board_find_io_insts()
7178 						 */
7179 						ndi_rele_devi(ios.eri_dip);
7180 					}
7181 				}
7182 			}
7183 			return (err);
7184 		}
7185 
7186 		if (DRMACH_IS_MEM_ID(id)) {
7187 			drmach_mem_t	*mp = id;
7188 			id = mp->next;
7189 		} else {
7190 			id = NULL;
7191 		}
7192 	}
7193 
7194 	return (NULL);
7195 }
7196 
7197 /*
7198  * drmach interfaces to legacy Starfire platmod logic
7199  * linkage via runtime symbol look up, called from plat_cpu_power*
7200  */
7201 
7202 /*
7203  * Start up a cpu.  It is possible that we're attempting to restart
7204  * the cpu after an UNCONFIGURE in which case the cpu will be
7205  * spinning in its cache.  So, all we have to do is wakeup him up.
7206  * Under normal circumstances the cpu will be coming from a previous
7207  * CONNECT and thus will be spinning in OBP.  In both cases, the
7208  * startup sequence is the same.
7209  */
7210 int
7211 drmach_cpu_poweron(struct cpu *cp)
7212 {
7213 	DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
7214 
7215 	ASSERT(MUTEX_HELD(&cpu_lock));
7216 
7217 	if (drmach_cpu_start(cp) != 0)
7218 		return (EBUSY);
7219 	else
7220 		return (0);
7221 }
7222 
7223 int
7224 drmach_cpu_poweroff(struct cpu *cp)
7225 {
7226 	int		ntries;
7227 	processorid_t	cpuid;
7228 	void		drmach_cpu_shutdown_self(void);
7229 
7230 	DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
7231 
7232 	ASSERT(MUTEX_HELD(&cpu_lock));
7233 
7234 	/*
7235 	 * XXX CHEETAH SUPPORT
7236 	 * for cheetah, we need to grab the iocage lock since iocage
7237 	 * memory is used for e$ flush.
7238 	 */
7239 	if (drmach_is_cheetah) {
7240 		mutex_enter(&drmach_iocage_lock);
7241 		while (drmach_iocage_is_busy)
7242 			cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
7243 		drmach_iocage_is_busy = 1;
7244 		drmach_iocage_mem_scrub(ecache_size * 2);
7245 		mutex_exit(&drmach_iocage_lock);
7246 	}
7247 
7248 	cpuid = cp->cpu_id;
7249 
7250 	/*
7251 	 * Set affinity to ensure consistent reading and writing of
7252 	 * drmach_xt_mb[cpuid] by one "master" CPU directing
7253 	 * the shutdown of the target CPU.
7254 	 */
7255 	affinity_set(CPU->cpu_id);
7256 
7257 	/*
7258 	 * Capture all CPUs (except for detaching proc) to prevent
7259 	 * crosscalls to the detaching proc until it has cleared its
7260 	 * bit in cpu_ready_set.
7261 	 *
7262 	 * The CPUs remain paused and the prom_mutex is known to be free.
7263 	 * This prevents blocking when doing prom IEEE-1275 calls at a
7264 	 * high PIL level.
7265 	 */
7266 	promsafe_pause_cpus();
7267 
7268 	/*
7269 	 * Quiesce interrupts on the target CPU. We do this by setting
7270 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
7271 	 * prevent it from receiving cross calls and cross traps.
7272 	 * This prevents the processor from receiving any new soft interrupts.
7273 	 */
7274 	mp_cpu_quiesce(cp);
7275 
7276 	(void) prom_hotremovecpu(cpuid);
7277 
7278 	start_cpus();
7279 
7280 	/* setup xt_mb, will be cleared by drmach_shutdown_asm when ready */
7281 	drmach_xt_mb[cpuid] = 0x80;
7282 
7283 	xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
7284 	    (uint64_t)drmach_cpu_shutdown_self, NULL);
7285 
7286 	ntries = drmach_cpu_ntries;
7287 	while (drmach_xt_mb[cpuid] && ntries) {
7288 		DELAY(drmach_cpu_delay);
7289 		ntries--;
7290 	}
7291 
7292 	drmach_xt_mb[cpuid] = 0;	/* steal the cache line back */
7293 
7294 	membar_sync();			/* make sure copy-back retires */
7295 
7296 	affinity_clear();
7297 
7298 	/*
7299 	 * XXX CHEETAH SUPPORT
7300 	 */
7301 	if (drmach_is_cheetah) {
7302 		mutex_enter(&drmach_iocage_lock);
7303 		drmach_iocage_mem_scrub(ecache_size * 2);
7304 		drmach_iocage_is_busy = 0;
7305 		cv_signal(&drmach_iocage_cv);
7306 		mutex_exit(&drmach_iocage_lock);
7307 	}
7308 
7309 	DRMACH_PR("waited %d out of %d tries for "
7310 	    "drmach_cpu_shutdown_self on cpu%d",
7311 	    drmach_cpu_ntries - ntries, drmach_cpu_ntries, cp->cpu_id);
7312 
7313 	/*
7314 	 * Do this here instead of drmach_cpu_shutdown_self() to
7315 	 * avoid an assertion failure panic in turnstile.c.
7316 	 */
7317 	CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
7318 
7319 	return (0);
7320 }
7321 
7322 void
7323 drmach_iocage_mem_scrub(uint64_t nbytes)
7324 {
7325 	extern uint32_t drmach_bc_bzero(void*, size_t);
7326 	uint32_t	rv;
7327 
7328 	ASSERT(MUTEX_HELD(&cpu_lock));
7329 
7330 	affinity_set(CPU->cpu_id);
7331 
7332 	rv = drmach_bc_bzero(drmach_iocage_vaddr, nbytes);
7333 	if (rv != 0) {
7334 		DRMACH_PR(
7335 		"iocage scrub failed, drmach_bc_bzero returned %d\n", rv);
7336 		rv = drmach_bc_bzero(drmach_iocage_vaddr, drmach_iocage_size);
7337 		if (rv != 0)
7338 			cmn_err(CE_PANIC,
7339 			    "iocage scrub failed, drmach_bc_bzero rv=%d\n",
7340 			    rv);
7341 	}
7342 
7343 	cpu_flush_ecache();
7344 
7345 	affinity_clear();
7346 }
7347 
7348 #define	ALIGN(x, a)	((a) == 0 ? (uintptr_t)(x) : \
7349 	(((uintptr_t)(x) + (uintptr_t)(a) - 1l) & ~((uintptr_t)(a) - 1l)))
7350 
7351 static sbd_error_t *
7352 drmach_iocage_mem_get(dr_testboard_req_t *tbrq)
7353 {
7354 	pfn_t		basepfn;
7355 	pgcnt_t		npages;
7356 	extern int	memscrub_delete_span(pfn_t, pgcnt_t);
7357 	uint64_t	drmach_iocage_paddr_mbytes;
7358 
7359 	ASSERT(drmach_iocage_paddr != -1);
7360 
7361 	basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
7362 	npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
7363 
7364 	(void) memscrub_delete_span(basepfn, npages);
7365 
7366 	mutex_enter(&cpu_lock);
7367 	drmach_iocage_mem_scrub(drmach_iocage_size);
7368 	mutex_exit(&cpu_lock);
7369 
7370 	/*
7371 	 * HPOST wants the address of the cage to be 64 megabyte-aligned
7372 	 * and in megabyte units.
7373 	 * The size of the cage is also in megabyte units.
7374 	 */
7375 	ASSERT(drmach_iocage_paddr == ALIGN(drmach_iocage_paddr, 0x4000000));
7376 
7377 	drmach_iocage_paddr_mbytes = drmach_iocage_paddr / 0x100000;
7378 
7379 	tbrq->memaddrhi = (uint32_t)(drmach_iocage_paddr_mbytes >> 32);
7380 	tbrq->memaddrlo = (uint32_t)drmach_iocage_paddr_mbytes;
7381 	tbrq->memlen = drmach_iocage_size / 0x100000;
7382 
7383 	DRMACH_PR("drmach_iocage_mem_get: hi: 0x%x", tbrq->memaddrhi);
7384 	DRMACH_PR("drmach_iocage_mem_get: lo: 0x%x", tbrq->memaddrlo);
7385 	DRMACH_PR("drmach_iocage_mem_get: size: 0x%x", tbrq->memlen);
7386 
7387 	return (NULL);
7388 }
7389 
7390 static sbd_error_t *
7391 drmach_iocage_mem_return(dr_testboard_reply_t *tbr)
7392 {
7393 	_NOTE(ARGUNUSED(tbr))
7394 
7395 	pfn_t		basepfn;
7396 	pgcnt_t		npages;
7397 	extern int	memscrub_add_span(pfn_t, pgcnt_t);
7398 
7399 	ASSERT(drmach_iocage_paddr != -1);
7400 
7401 	basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
7402 	npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
7403 
7404 	(void) memscrub_add_span(basepfn, npages);
7405 
7406 	mutex_enter(&cpu_lock);
7407 	mutex_enter(&drmach_iocage_lock);
7408 	drmach_iocage_mem_scrub(drmach_iocage_size);
7409 	drmach_iocage_is_busy = 0;
7410 	cv_signal(&drmach_iocage_cv);
7411 	mutex_exit(&drmach_iocage_lock);
7412 	mutex_exit(&cpu_lock);
7413 
7414 	return (NULL);
7415 }
7416 
7417 static int
7418 drmach_cpu_intr_disable(cpu_t *cp)
7419 {
7420 	if (cpu_intr_disable(cp) != 0)
7421 		return (-1);
7422 	return (0);
7423 }
7424 
7425 static int
7426 drmach_iocage_cpu_acquire(drmach_device_t *dp, cpu_flag_t *oflags)
7427 {
7428 	struct cpu	*cp;
7429 	processorid_t	cpuid;
7430 	static char	*fn = "drmach_iocage_cpu_acquire";
7431 	sbd_error_t 	*err;
7432 	int 		impl;
7433 
7434 	ASSERT(DRMACH_IS_CPU_ID(dp));
7435 	ASSERT(MUTEX_HELD(&cpu_lock));
7436 
7437 	cpuid = ((drmach_cpu_t *)dp)->cpuid;
7438 
7439 	DRMACH_PR("%s: attempting to acquire CPU id %d", fn, cpuid);
7440 
7441 	if (dp->busy)
7442 		return (-1);
7443 
7444 	if ((cp = cpu_get(cpuid)) == NULL) {
7445 		DRMACH_PR("%s: cpu_get(%d) returned NULL", fn, cpuid);
7446 		return (-1);
7447 	}
7448 
7449 	if (!CPU_ACTIVE(cp)) {
7450 		DRMACH_PR("%s: skipping offlined CPU id %d", fn, cpuid);
7451 		return (-1);
7452 	}
7453 
7454 	/*
7455 	 * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
7456 	 * can fail to receive an XIR. To workaround this issue until a hardware
7457 	 * fix is implemented, we will exclude the selection of these CPUs.
7458 	 *
7459 	 * Once a fix is implemented in hardware, this code should be updated
7460 	 * to allow Jaguar CPUs that have the fix to be used. However, support
7461 	 * must be retained to skip revisions that do not have this fix.
7462 	 */
7463 
7464 	err = drmach_cpu_get_impl(dp, &impl);
7465 	if (err) {
7466 		DRMACH_PR("%s: error getting impl. of CPU id %d", fn, cpuid);
7467 		sbd_err_clear(&err);
7468 		return (-1);
7469 	}
7470 
7471 	if (IS_JAGUAR(impl) && (STARCAT_CPUID_TO_LPORT(cpuid) == 0) &&
7472 	    drmach_iocage_exclude_jaguar_port_zero) {
7473 		DRMACH_PR("%s: excluding CPU id %d: port 0 on jaguar",
7474 		    fn, cpuid);
7475 		return (-1);
7476 	}
7477 
7478 	ASSERT(oflags);
7479 	*oflags = cp->cpu_flags;
7480 
7481 	if (cpu_offline(cp, 0)) {
7482 		DRMACH_PR("%s: cpu_offline failed for CPU id %d", fn, cpuid);
7483 		return (-1);
7484 	}
7485 
7486 	if (cpu_poweroff(cp)) {
7487 		DRMACH_PR("%s: cpu_poweroff failed for CPU id %d", fn, cpuid);
7488 		if (cpu_online(cp)) {
7489 			cmn_err(CE_WARN, "failed to online CPU id %d "
7490 			    "during I/O cage test selection", cpuid);
7491 		}
7492 		if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
7493 		    drmach_cpu_intr_disable(cp) != 0) {
7494 			cmn_err(CE_WARN, "failed to restore CPU id %d "
7495 			    "no-intr during I/O cage test selection", cpuid);
7496 		}
7497 		return (-1);
7498 	}
7499 
7500 	if (cpu_unconfigure(cpuid)) {
7501 		DRMACH_PR("%s: cpu_unconfigure failed for CPU id %d", fn,
7502 		    cpuid);
7503 		(void) cpu_configure(cpuid);
7504 		if ((cp = cpu_get(cpuid)) == NULL) {
7505 			cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
7506 			    "during I/O cage test selection", cpuid);
7507 			dp->busy = 1;
7508 			return (-1);
7509 		}
7510 		if (cpu_poweron(cp) || cpu_online(cp)) {
7511 			cmn_err(CE_WARN, "failed to %s CPU id %d "
7512 			    "during I/O cage test selection",
7513 			    cpu_is_poweredoff(cp) ?
7514 			    "poweron" : "online", cpuid);
7515 		}
7516 		if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
7517 		    drmach_cpu_intr_disable(cp) != 0) {
7518 			cmn_err(CE_WARN, "failed to restore CPU id %d "
7519 			    "no-intr during I/O cage test selection", cpuid);
7520 		}
7521 		return (-1);
7522 	}
7523 
7524 	dp->busy = 1;
7525 
7526 	DRMACH_PR("%s: acquired CPU id %d", fn, cpuid);
7527 
7528 	return (0);
7529 }
7530 
7531 /*
7532  * Attempt to acquire all the CPU devices passed in. It is
7533  * assumed that all the devices in the list are the cores of
7534  * a single CMP device. Non CMP devices can be handled as a
7535  * single core CMP by passing in a one element list.
7536  *
7537  * Success is only returned if *all* the devices in the list
7538  * can be acquired. In the failure case, none of the devices
7539  * in the list will be held as acquired.
7540  */
7541 static int
7542 drmach_iocage_cmp_acquire(drmach_device_t **dpp, cpu_flag_t *oflags)
7543 {
7544 	int	curr;
7545 	int	i;
7546 	int	rv = 0;
7547 
7548 	ASSERT((dpp != NULL) && (*dpp != NULL));
7549 
7550 	/*
7551 	 * Walk the list of CPU devices (cores of a CMP)
7552 	 * and attempt to acquire them. Bail out if an
7553 	 * error is encountered.
7554 	 */
7555 	for (curr = 0; curr < MAX_CORES_PER_CMP; curr++) {
7556 
7557 		/* check for the end of the list */
7558 		if (dpp[curr] == NULL) {
7559 			break;
7560 		}
7561 
7562 		ASSERT(DRMACH_IS_CPU_ID(dpp[curr]));
7563 		ASSERT(dpp[curr]->portid == (*dpp)->portid);
7564 
7565 		rv = drmach_iocage_cpu_acquire(dpp[curr], &oflags[curr]);
7566 		if (rv != 0) {
7567 			break;
7568 		}
7569 	}
7570 
7571 	/*
7572 	 * Check for an error.
7573 	 */
7574 	if (rv != 0) {
7575 		/*
7576 		 * Make a best effort attempt to return any cores
7577 		 * that were already acquired before the error was
7578 		 * encountered.
7579 		 */
7580 		for (i = 0; i < curr; i++) {
7581 			(void) drmach_iocage_cpu_return(dpp[i], oflags[i]);
7582 		}
7583 	}
7584 
7585 	return (rv);
7586 }
7587 
7588 static int
7589 drmach_iocage_cpu_return(drmach_device_t *dp, cpu_flag_t oflags)
7590 {
7591 	processorid_t	cpuid;
7592 	struct cpu	*cp;
7593 	int		rv = 0;
7594 	static char	*fn = "drmach_iocage_cpu_return";
7595 
7596 	ASSERT(DRMACH_IS_CPU_ID(dp));
7597 	ASSERT(MUTEX_HELD(&cpu_lock));
7598 
7599 	cpuid = ((drmach_cpu_t *)dp)->cpuid;
7600 
7601 	DRMACH_PR("%s: attempting to return CPU id: %d", fn, cpuid);
7602 
7603 	if (cpu_configure(cpuid)) {
7604 		cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
7605 		    "after I/O cage test", cpuid);
7606 		/*
7607 		 * The component was never set to unconfigured during the IO
7608 		 * cage test, so we need to leave marked as busy to prevent
7609 		 * further DR operations involving this component.
7610 		 */
7611 		return (-1);
7612 	}
7613 
7614 	if ((cp = cpu_get(cpuid)) == NULL) {
7615 		cmn_err(CE_WARN, "cpu_get failed on CPU id %d after "
7616 		    "I/O cage test", cpuid);
7617 		dp->busy = 0;
7618 		return (-1);
7619 	}
7620 
7621 	if (cpu_poweron(cp) || cpu_online(cp)) {
7622 		cmn_err(CE_WARN, "failed to %s CPU id %d after I/O "
7623 		    "cage test", cpu_is_poweredoff(cp) ?
7624 		    "poweron" : "online", cpuid);
7625 		rv = -1;
7626 	}
7627 
7628 	/*
7629 	 * drmach_iocage_cpu_acquire will accept cpus in state P_ONLINE or
7630 	 * P_NOINTR. Need to return to previous user-visible state.
7631 	 */
7632 	if (CPU_ACTIVE(cp) && cpu_flagged_nointr(oflags) &&
7633 	    drmach_cpu_intr_disable(cp) != 0) {
7634 		cmn_err(CE_WARN, "failed to restore CPU id %d "
7635 		    "no-intr after I/O cage test", cpuid);
7636 		rv = -1;
7637 	}
7638 
7639 	dp->busy = 0;
7640 
7641 	DRMACH_PR("%s: returned CPU id: %d", fn, cpuid);
7642 
7643 	return (rv);
7644 }
7645 
7646 static sbd_error_t *
7647 drmach_iocage_cpu_get(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
7648     cpu_flag_t *oflags)
7649 {
7650 	drmach_board_t	*bp;
7651 	int		b_rv;
7652 	int		b_idx;
7653 	drmachid_t	b_id;
7654 	int		found;
7655 
7656 	mutex_enter(&cpu_lock);
7657 
7658 	ASSERT(drmach_boards != NULL);
7659 
7660 	found = 0;
7661 
7662 	/*
7663 	 * Walk the board list.
7664 	 */
7665 	b_rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
7666 
7667 	while (b_rv == 0) {
7668 
7669 		int		d_rv;
7670 		int		d_idx;
7671 		drmachid_t	d_id;
7672 
7673 		bp = b_id;
7674 
7675 		if (bp->connected == 0 || bp->devices == NULL) {
7676 			b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
7677 			continue;
7678 		}
7679 
7680 		/* An AXQ restriction disqualifies MCPU's as candidates. */
7681 		if (DRMACH_BNUM2SLOT(bp->bnum) == 1) {
7682 			b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
7683 			continue;
7684 		}
7685 
7686 		/*
7687 		 * Walk the device list of this board.
7688 		 */
7689 		d_rv = drmach_array_first(bp->devices, &d_idx, &d_id);
7690 
7691 		while (d_rv == 0) {
7692 
7693 			drmach_device_t	*ndp;
7694 
7695 			/* only interested in CPU devices */
7696 			if (!DRMACH_IS_CPU_ID(d_id)) {
7697 				d_rv = drmach_array_next(bp->devices, &d_idx,
7698 				    &d_id);
7699 				continue;
7700 			}
7701 
7702 			/*
7703 			 * The following code assumes two properties
7704 			 * of a CMP device:
7705 			 *
7706 			 *   1. All cores of a CMP are grouped together
7707 			 *	in the device list.
7708 			 *
7709 			 *   2. There will only be a maximum of two cores
7710 			 *	present in the CMP.
7711 			 *
7712 			 * If either of these two properties change,
7713 			 * this code will have to be revisited.
7714 			 */
7715 
7716 			dpp[0] = d_id;
7717 			dpp[1] = NULL;
7718 
7719 			/*
7720 			 * Get the next device. It may or may not be used.
7721 			 */
7722 			d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
7723 			ndp = d_id;
7724 
7725 			if ((d_rv == 0) && DRMACH_IS_CPU_ID(d_id)) {
7726 				/*
7727 				 * The second device is only interesting for
7728 				 * this pass if it has the same portid as the
7729 				 * first device. This implies that both are
7730 				 * cores of the same CMP.
7731 				 */
7732 				if (dpp[0]->portid == ndp->portid) {
7733 					dpp[1] = d_id;
7734 				}
7735 			}
7736 
7737 			/*
7738 			 * Attempt to acquire all cores of the CMP.
7739 			 */
7740 			if (drmach_iocage_cmp_acquire(dpp, oflags) == 0) {
7741 				found = 1;
7742 				break;
7743 			}
7744 
7745 			/*
7746 			 * Check if the search for the second core was
7747 			 * successful. If not, the next iteration should
7748 			 * use that device.
7749 			 */
7750 			if (dpp[1] == NULL) {
7751 				continue;
7752 			}
7753 
7754 			d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
7755 		}
7756 
7757 		if (found)
7758 			break;
7759 
7760 		b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
7761 	}
7762 
7763 	mutex_exit(&cpu_lock);
7764 
7765 	if (!found) {
7766 		return (drerr_new(1, ESTC_IOCAGE_NO_CPU_AVAIL, NULL));
7767 	}
7768 
7769 	tbrq->cpu_portid = (*dpp)->portid;
7770 
7771 	return (NULL);
7772 }
7773 
7774 /*
7775  * Setup an iocage by acquiring a cpu and memory.
7776  */
7777 static sbd_error_t *
7778 drmach_iocage_setup(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
7779     cpu_flag_t *oflags)
7780 {
7781 	sbd_error_t *err;
7782 
7783 	err = drmach_iocage_cpu_get(tbrq, dpp, oflags);
7784 	if (!err) {
7785 		mutex_enter(&drmach_iocage_lock);
7786 		while (drmach_iocage_is_busy)
7787 			cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
7788 		drmach_iocage_is_busy = 1;
7789 		mutex_exit(&drmach_iocage_lock);
7790 		err = drmach_iocage_mem_get(tbrq);
7791 		if (err) {
7792 			mutex_enter(&drmach_iocage_lock);
7793 			drmach_iocage_is_busy = 0;
7794 			cv_signal(&drmach_iocage_cv);
7795 			mutex_exit(&drmach_iocage_lock);
7796 		}
7797 	}
7798 	return (err);
7799 }
7800 
7801 #define	DRMACH_SCHIZO_PCI_LEAF_MAX	2
7802 #define	DRMACH_SCHIZO_PCI_SLOT_MAX	8
7803 #define	DRMACH_S1P_SAMPLE_MAX		2
7804 
7805 typedef enum {
7806 	DRMACH_POST_SUSPEND = 0,
7807 	DRMACH_PRE_RESUME
7808 } drmach_sr_iter_t;
7809 
7810 typedef struct {
7811 	dev_info_t	*dip;
7812 	uint32_t	portid;
7813 	uint32_t	pcr_sel_save;
7814 	uint32_t	pic_l2_io_q[DRMACH_S1P_SAMPLE_MAX];
7815 	uint64_t	reg_basepa;
7816 } drmach_s1p_axq_t;
7817 
7818 typedef struct {
7819 	dev_info_t		*dip;
7820 	uint32_t		portid;
7821 	uint64_t		csr_basepa;
7822 	struct {
7823 		uint64_t 	slot_intr_state_diag;
7824 		uint64_t 	obio_intr_state_diag;
7825 		uint_t		nmap_regs;
7826 		uint64_t	*intr_map_regs;
7827 	} regs[DRMACH_S1P_SAMPLE_MAX];
7828 } drmach_s1p_pci_t;
7829 
7830 typedef struct {
7831 	uint64_t		csr_basepa;
7832 	struct {
7833 		uint64_t	csr;
7834 		uint64_t	errctrl;
7835 		uint64_t	errlog;
7836 	} regs[DRMACH_S1P_SAMPLE_MAX];
7837 	drmach_s1p_pci_t	pci[DRMACH_SCHIZO_PCI_LEAF_MAX];
7838 } drmach_s1p_schizo_t;
7839 
7840 typedef struct {
7841 	drmach_s1p_axq_t	axq;
7842 	drmach_s1p_schizo_t	schizo[STARCAT_SLOT1_IO_MAX];
7843 } drmach_slot1_pause_t;
7844 
7845 /*
7846  * Table of saved state for paused slot1 devices.
7847  */
7848 static drmach_slot1_pause_t *drmach_slot1_paused[STARCAT_BDSET_MAX];
7849 static int drmach_slot1_pause_init = 1;
7850 
7851 #ifdef DEBUG
7852 int drmach_slot1_pause_debug = 1;
7853 #else
7854 int drmach_slot1_pause_debug = 0;
7855 #endif /* DEBUG */
7856 
7857 static int
7858 drmach_is_slot1_pause_axq(dev_info_t *dip, char *name, int *id, uint64_t *reg)
7859 {
7860 	int		portid, exp, slot, i;
7861 	drmach_reg_t	regs[2];
7862 	int		reglen = sizeof (regs);
7863 
7864 	if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
7865 	    DDI_PROP_DONTPASS, "portid", -1)) == -1) {
7866 		return (0);
7867 	}
7868 
7869 	exp = (portid >> 5) & 0x1f;
7870 	slot = portid & 0x1;
7871 
7872 	if (slot == 0 || strncmp(name, DRMACH_AXQ_NAMEPROP,
7873 	    strlen(DRMACH_AXQ_NAMEPROP))) {
7874 		return (0);
7875 	}
7876 
7877 	mutex_enter(&cpu_lock);
7878 	for (i = 0; i < STARCAT_SLOT1_CPU_MAX; i++) {
7879 		if (cpu[MAKE_CPUID(exp, slot, i)]) {
7880 			/* maxcat cpu present */
7881 			mutex_exit(&cpu_lock);
7882 			return (0);
7883 		}
7884 	}
7885 	mutex_exit(&cpu_lock);
7886 
7887 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
7888 	    "reg", (caddr_t)regs, &reglen) != DDI_PROP_SUCCESS) {
7889 		DRMACH_PR("drmach_is_slot1_pause_axq: no reg prop for "
7890 		    "axq dip=%p\n", (void *)dip);
7891 		return (0);
7892 	}
7893 
7894 	ASSERT(id && reg);
7895 	*reg = (uint64_t)regs[0].reg_addr_hi << 32;
7896 	*reg |= (uint64_t)regs[0].reg_addr_lo;
7897 	*id = portid;
7898 
7899 	return (1);
7900 }
7901 
7902 /*
7903  * Allocate an entry in the slot1_paused state table.
7904  */
7905 static void
7906 drmach_slot1_pause_add_axq(dev_info_t *axq_dip, char *axq_name, int axq_portid,
7907     uint64_t reg, drmach_slot1_pause_t **slot1_paused)
7908 {
7909 	int	axq_exp;
7910 	drmach_slot1_pause_t *slot1;
7911 
7912 	axq_exp = (axq_portid >> 5) & 0x1f;
7913 
7914 	ASSERT(axq_portid & 0x1);
7915 	ASSERT(slot1_paused[axq_exp] == NULL);
7916 	ASSERT(strncmp(axq_name, DRMACH_AXQ_NAMEPROP,
7917 	    strlen(DRMACH_AXQ_NAMEPROP)) == 0);
7918 
7919 	slot1 = kmem_zalloc(sizeof (*slot1), KM_SLEEP);
7920 
7921 	/*
7922 	 * XXX This dip should really be held (via ndi_hold_devi())
7923 	 * before saving it in the axq pause structure. However that
7924 	 * would prevent DR as the pause data structures persist until
7925 	 * the next suspend. drmach code should be modified to free the
7926 	 * the slot 1 pause data structures for a boardset when its
7927 	 * slot 1 board is DRed out. The dip can then be released via
7928 	 * ndi_rele_devi() when the pause data structure is freed
7929 	 * allowing DR to proceed. Until this change is made, drmach
7930 	 * code should be careful about dereferencing the saved dip
7931 	 * as it may no longer exist.
7932 	 */
7933 	slot1->axq.dip = axq_dip;
7934 	slot1->axq.portid = axq_portid;
7935 	slot1->axq.reg_basepa = reg;
7936 	slot1_paused[axq_exp] = slot1;
7937 }
7938 
7939 static void
7940 drmach_s1p_pci_free(drmach_s1p_pci_t *pci)
7941 {
7942 	int	i;
7943 
7944 	for (i = 0; i < DRMACH_S1P_SAMPLE_MAX; i++) {
7945 		if (pci->regs[i].intr_map_regs != NULL) {
7946 			ASSERT(pci->regs[i].nmap_regs > 0);
7947 			kmem_free(pci->regs[i].intr_map_regs,
7948 			    pci->regs[i].nmap_regs * sizeof (uint64_t));
7949 		}
7950 	}
7951 }
7952 
7953 static void
7954 drmach_slot1_pause_free(drmach_slot1_pause_t **slot1_paused)
7955 {
7956 	int	i, j, k;
7957 	drmach_slot1_pause_t *slot1;
7958 
7959 	for (i = 0; i < STARCAT_BDSET_MAX; i++) {
7960 		if ((slot1 = slot1_paused[i]) == NULL)
7961 			continue;
7962 
7963 		for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
7964 			for (k = 0; k < DRMACH_SCHIZO_PCI_LEAF_MAX; k++)
7965 				drmach_s1p_pci_free(&slot1->schizo[j].pci[k]);
7966 
7967 		kmem_free(slot1, sizeof (*slot1));
7968 		slot1_paused[i] = NULL;
7969 	}
7970 }
7971 
7972 /*
7973  * Tree walk callback routine. If dip represents a Schizo PCI leaf,
7974  * fill in the appropriate info in the slot1_paused state table.
7975  */
7976 static int
7977 drmach_find_slot1_io(dev_info_t *dip, void *arg)
7978 {
7979 	int		portid, exp, ioc_unum, leaf_unum;
7980 	char		buf[OBP_MAXDRVNAME];
7981 	int		buflen = sizeof (buf);
7982 	drmach_reg_t	regs[3];
7983 	int		reglen = sizeof (regs);
7984 	uint32_t	leaf_offset;
7985 	uint64_t	schizo_csr_pa, pci_csr_pa;
7986 	drmach_s1p_pci_t *pci;
7987 	drmach_slot1_pause_t **slot1_paused = (drmach_slot1_pause_t **)arg;
7988 
7989 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
7990 	    "name", (caddr_t)buf, &buflen) != DDI_PROP_SUCCESS ||
7991 	    strncmp(buf, DRMACH_PCI_NAMEPROP, strlen(DRMACH_PCI_NAMEPROP))) {
7992 		return (DDI_WALK_CONTINUE);
7993 	}
7994 
7995 	if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
7996 	    DDI_PROP_DONTPASS, "portid", -1)) == -1) {
7997 		return (DDI_WALK_CONTINUE);
7998 	}
7999 
8000 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8001 	    "reg", (caddr_t)regs, &reglen) != DDI_PROP_SUCCESS) {
8002 		DRMACH_PR("drmach_find_slot1_io: no reg prop for pci "
8003 		    "dip=%p\n", (void *)dip);
8004 		return (DDI_WALK_CONTINUE);
8005 	}
8006 
8007 	exp = portid >> 5;
8008 	ioc_unum = portid & 0x1;
8009 	leaf_offset = regs[0].reg_addr_lo & 0x7fffff;
8010 	pci_csr_pa = (uint64_t)regs[0].reg_addr_hi << 32;
8011 	pci_csr_pa |= (uint64_t)regs[0].reg_addr_lo;
8012 	schizo_csr_pa = (uint64_t)regs[1].reg_addr_hi << 32;
8013 	schizo_csr_pa |= (uint64_t)regs[1].reg_addr_lo;
8014 
8015 	ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
8016 	ASSERT(slot1_paused[exp] != NULL);
8017 	ASSERT(leaf_offset == 0x600000 || leaf_offset == 0x700000);
8018 	ASSERT(slot1_paused[exp]->schizo[ioc_unum].csr_basepa == 0x0UL ||
8019 	    slot1_paused[exp]->schizo[ioc_unum].csr_basepa == schizo_csr_pa);
8020 
8021 	leaf_unum = (leaf_offset == 0x600000) ? 0 : 1;
8022 	slot1_paused[exp]->schizo[ioc_unum].csr_basepa = schizo_csr_pa;
8023 	pci = &slot1_paused[exp]->schizo[ioc_unum].pci[leaf_unum];
8024 
8025 	/*
8026 	 * XXX This dip should really be held (via ndi_hold_devi())
8027 	 * before saving it in the pci pause structure. However that
8028 	 * would prevent DR as the pause data structures persist until
8029 	 * the next suspend. drmach code should be modified to free the
8030 	 * the slot 1 pause data structures for a boardset when its
8031 	 * slot 1 board is DRed out. The dip can then be released via
8032 	 * ndi_rele_devi() when the pause data structure is freed
8033 	 * allowing DR to proceed. Until this change is made, drmach
8034 	 * code should be careful about dereferencing the saved dip as
8035 	 * it may no longer exist.
8036 	 */
8037 	pci->dip = dip;
8038 	pci->portid = portid;
8039 	pci->csr_basepa = pci_csr_pa;
8040 
8041 	DRMACH_PR("drmach_find_slot1_io: name=%s, portid=0x%x, dip=%p\n",
8042 	    buf, portid, (void *)dip);
8043 
8044 	return (DDI_WALK_PRUNECHILD);
8045 }
8046 
8047 static void
8048 drmach_slot1_pause_add_io(drmach_slot1_pause_t **slot1_paused)
8049 {
8050 	/*
8051 	 * Root node doesn't have to be held
8052 	 */
8053 	ddi_walk_devs(ddi_root_node(), drmach_find_slot1_io,
8054 	    (void *)slot1_paused);
8055 }
8056 
8057 /*
8058  * Save the interrupt mapping registers for each non-idle interrupt
8059  * represented by the bit pairs in the saved interrupt state
8060  * diagnostic registers for this PCI leaf.
8061  */
8062 static void
8063 drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t *pci, drmach_sr_iter_t iter)
8064 {
8065 	int	 i, cnt, ino;
8066 	uint64_t reg;
8067 	char	 *dname;
8068 	uchar_t	 Xmits;
8069 
8070 	dname = ddi_binding_name(pci->dip);
8071 	Xmits = (strcmp(dname, XMITS_BINDING_NAME) == 0)  ?  1 : 0;
8072 
8073 	/*
8074 	 * 1st pass allocates, 2nd pass populates.
8075 	 */
8076 	for (i = 0; i < 2; i++) {
8077 		cnt = ino = 0;
8078 
8079 		/*
8080 		 * PCI slot interrupts
8081 		 */
8082 		reg = pci->regs[iter].slot_intr_state_diag;
8083 		while (reg) {
8084 			/*
8085 			 * Xmits Interrupt Number Offset(ino) Assignments
8086 			 *   00-17 PCI Slot Interrupts
8087 			 *   18-1f Not Used
8088 			 */
8089 			if ((Xmits) && (ino > 0x17))
8090 				break;
8091 			if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
8092 			    COMMON_CLEAR_INTR_REG_IDLE) {
8093 				if (i) {
8094 					pci->regs[iter].intr_map_regs[cnt] =
8095 					    lddphysio(pci->csr_basepa +
8096 					    SCHIZO_IB_INTR_MAP_REG_OFFSET +
8097 					    ino * sizeof (reg));
8098 				}
8099 				++cnt;
8100 			}
8101 			++ino;
8102 			reg >>= 2;
8103 		}
8104 
8105 		/*
8106 		 * Xmits Interrupt Number Offset(ino) Assignments
8107 		 *   20-2f Not Used
8108 		 *   30-37 Internal interrupts
8109 		 *   38-3e Not Used
8110 		 */
8111 		ino = (Xmits)  ?  0x30 : 0x20;
8112 
8113 		/*
8114 		 * OBIO and internal schizo interrupts
8115 		 * Each PCI leaf has a set of mapping registers for all
8116 		 * possible interrupt sources except the NewLink interrupts.
8117 		 */
8118 		reg = pci->regs[iter].obio_intr_state_diag;
8119 		while (reg && ino <= 0x38) {
8120 			if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
8121 			    COMMON_CLEAR_INTR_REG_IDLE) {
8122 				if (i) {
8123 					pci->regs[iter].intr_map_regs[cnt] =
8124 					    lddphysio(pci->csr_basepa +
8125 					    SCHIZO_IB_INTR_MAP_REG_OFFSET +
8126 					    ino * sizeof (reg));
8127 				}
8128 				++cnt;
8129 			}
8130 			++ino;
8131 			reg >>= 2;
8132 		}
8133 
8134 		if (!i) {
8135 			pci->regs[iter].nmap_regs = cnt;
8136 			pci->regs[iter].intr_map_regs =
8137 			    kmem_zalloc(cnt * sizeof (reg), KM_SLEEP);
8138 		}
8139 	}
8140 }
8141 
8142 static void
8143 drmach_s1p_axq_update(drmach_s1p_axq_t *axq, drmach_sr_iter_t iter)
8144 {
8145 	uint32_t	reg;
8146 
8147 	if (axq->reg_basepa == 0x0UL)
8148 		return;
8149 
8150 	if (iter == DRMACH_POST_SUSPEND) {
8151 		axq->pcr_sel_save = ldphysio(axq->reg_basepa +
8152 		    AXQ_SLOT1_PERFCNT_SEL);
8153 		/*
8154 		 * Select l2_io_queue counter by writing L2_IO_Q mux
8155 		 * input to bits 0-6 of perf cntr select reg.
8156 		 */
8157 		reg = axq->pcr_sel_save;
8158 		reg &= ~AXQ_PIC_CLEAR_MASK;
8159 		reg |= L2_IO_Q;
8160 
8161 		stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL, reg);
8162 	}
8163 
8164 	axq->pic_l2_io_q[iter] = ldphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT0);
8165 
8166 	if (iter == DRMACH_PRE_RESUME) {
8167 		stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL,
8168 		    axq->pcr_sel_save);
8169 	}
8170 
8171 	DRMACH_PR("drmach_s1p_axq_update: axq #%d pic_l2_io_q[%d]=%d\n",
8172 	    ddi_get_instance(axq->dip), iter, axq->pic_l2_io_q[iter]);
8173 }
8174 
8175 static void
8176 drmach_s1p_schizo_update(drmach_s1p_schizo_t *schizo, drmach_sr_iter_t iter)
8177 {
8178 	int	i;
8179 	drmach_s1p_pci_t *pci;
8180 
8181 	if (schizo->csr_basepa == 0x0UL)
8182 		return;
8183 
8184 	schizo->regs[iter].csr =
8185 	    lddphysio(schizo->csr_basepa + SCHIZO_CB_CSR_OFFSET);
8186 	schizo->regs[iter].errctrl =
8187 	    lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRCTRL_OFFSET);
8188 	schizo->regs[iter].errlog =
8189 	    lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRLOG_OFFSET);
8190 
8191 	for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
8192 		pci = &schizo->pci[i];
8193 		if (pci->dip != NULL && pci->csr_basepa != 0x0UL) {
8194 			pci->regs[iter].slot_intr_state_diag =
8195 			    lddphysio(pci->csr_basepa +
8196 			    COMMON_IB_SLOT_INTR_STATE_DIAG_REG);
8197 
8198 			pci->regs[iter].obio_intr_state_diag =
8199 			    lddphysio(pci->csr_basepa +
8200 			    COMMON_IB_OBIO_INTR_STATE_DIAG_REG);
8201 
8202 			drmach_s1p_intr_map_reg_save(pci, iter);
8203 		}
8204 	}
8205 }
8206 
8207 /*
8208  * Called post-suspend and pre-resume to snapshot the suspend state
8209  * of slot1 AXQs and Schizos.
8210  */
8211 static void
8212 drmach_slot1_pause_update(drmach_slot1_pause_t **slot1_paused,
8213     drmach_sr_iter_t iter)
8214 {
8215 	int	i, j;
8216 	drmach_slot1_pause_t *slot1;
8217 
8218 	for (i = 0; i < STARCAT_BDSET_MAX; i++) {
8219 		if ((slot1 = slot1_paused[i]) == NULL)
8220 			continue;
8221 
8222 		drmach_s1p_axq_update(&slot1->axq, iter);
8223 		for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
8224 			drmach_s1p_schizo_update(&slot1->schizo[j], iter);
8225 	}
8226 }
8227 
8228 /*
8229  * Starcat hPCI Schizo devices.
8230  *
8231  * The name field is overloaded. NULL means the slot (interrupt concentrator
8232  * bus) is not used. intr_mask is a bit mask representing the 4 possible
8233  * interrupts per slot, on if valid (rio does not use interrupt lines 0, 1).
8234  */
8235 static struct {
8236 	char	*name;
8237 	uint8_t	intr_mask;
8238 } drmach_schz_slot_intr[][DRMACH_SCHIZO_PCI_LEAF_MAX] = {
8239 	/* Schizo 0 */		/* Schizo 1 */
8240 	{{"C3V0", 0xf},		{"C3V1", 0xf}},		/* slot 0 */
8241 	{{"C5V0", 0xf},		{"C5V1", 0xf}},		/* slot 1 */
8242 	{{"rio", 0xc},		{NULL, 0x0}},		/* slot 2 */
8243 	{{NULL, 0x0},		{NULL, 0x0}},		/* slot 3 */
8244 	{{"sbbc", 0xf},		{NULL, 0x0}},		/* slot 4 */
8245 	{{NULL, 0x0},		{NULL, 0x0}},		/* slot 5 */
8246 	{{NULL, 0x0},		{NULL, 0x0}},		/* slot 6 */
8247 	{{NULL, 0x0},		{NULL, 0x0}}		/* slot 7 */
8248 };
8249 
8250 /*
8251  * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.4.4
8252  * "Interrupt Registers", Table 22-69, page 306.
8253  */
8254 static char *
8255 drmach_schz_internal_ino2str(int ino)
8256 {
8257 	int	intr;
8258 
8259 	ASSERT(ino >= 0x30 && ino <= 0x37);
8260 
8261 	intr = ino & 0x7;
8262 	switch (intr) {
8263 		case (0x0):	return ("Uncorrectable ECC error");
8264 		case (0x1):	return ("Correctable ECC error");
8265 		case (0x2):	return ("PCI Bus A Error");
8266 		case (0x3):	return ("PCI Bus B Error");
8267 		case (0x4):	return ("Safari Bus Error");
8268 		default:	return ("Reserved");
8269 	}
8270 }
8271 
8272 #define	DRMACH_INTR_MASK_SHIFT(ino)	((ino) << 1)
8273 
8274 static void
8275 drmach_s1p_decode_slot_intr(int exp, int unum, drmach_s1p_pci_t *pci,
8276     int ino, drmach_sr_iter_t iter)
8277 {
8278 	uint8_t		intr_mask;
8279 	char		*slot_devname;
8280 	char		namebuf[OBP_MAXDRVNAME];
8281 	int		slot, intr_line, slot_valid, intr_valid;
8282 
8283 	ASSERT(ino >= 0 && ino <= 0x1f);
8284 	ASSERT((pci->regs[iter].slot_intr_state_diag &
8285 	    (COMMON_CLEAR_INTR_REG_MASK << DRMACH_INTR_MASK_SHIFT(ino))) !=
8286 	    COMMON_CLEAR_INTR_REG_IDLE);
8287 
8288 	slot = (ino >> 2) & 0x7;
8289 	intr_line = ino & 0x3;
8290 
8291 	slot_devname = drmach_schz_slot_intr[slot][unum].name;
8292 	slot_valid = (slot_devname == NULL) ? 0 : 1;
8293 	if (!slot_valid) {
8294 		(void) snprintf(namebuf, sizeof (namebuf), "slot %d (INVALID)",
8295 		    slot);
8296 		slot_devname = namebuf;
8297 	}
8298 
8299 	intr_mask = drmach_schz_slot_intr[slot][unum].intr_mask;
8300 	intr_valid = (1 << intr_line) & intr_mask;
8301 
8302 	prom_printf("IO%d/P%d PCI slot interrupt: ino=0x%x, source device=%s, "
8303 	    "interrupt line=%d%s\n", exp, unum, ino, slot_devname, intr_line,
8304 	    (slot_valid && !intr_valid) ? " (INVALID)" : "");
8305 }
8306 
8307 /*
8308  * Log interrupt source device info for all valid, pending interrupts
8309  * on each Schizo PCI leaf. Called if Schizo has logged a Safari bus
8310  * error in the error ctrl reg.
8311  */
8312 static void
8313 drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t *schizo, int exp,
8314     int unum, drmach_sr_iter_t iter)
8315 {
8316 	uint64_t	reg;
8317 	int		i, n, ino;
8318 	drmach_s1p_pci_t *pci;
8319 
8320 	ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
8321 	ASSERT(unum < STARCAT_SLOT1_IO_MAX);
8322 
8323 	/*
8324 	 * Check the saved interrupt mapping registers. If interrupt is valid,
8325 	 * map the ino to the Schizo source device and check that the pci
8326 	 * slot and interrupt line are valid.
8327 	 */
8328 	for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
8329 		pci = &schizo->pci[i];
8330 		for (n = 0; n < pci->regs[iter].nmap_regs; n++) {
8331 			reg = pci->regs[iter].intr_map_regs[n];
8332 			if (reg & COMMON_INTR_MAP_REG_VALID) {
8333 				ino = reg & COMMON_INTR_MAP_REG_INO;
8334 
8335 				if (ino <= 0x1f) {
8336 					/*
8337 					 * PCI slot interrupt
8338 					 */
8339 					drmach_s1p_decode_slot_intr(exp, unum,
8340 					    pci, ino, iter);
8341 				} else if (ino <= 0x2f) {
8342 					/*
8343 					 * OBIO interrupt
8344 					 */
8345 					prom_printf("IO%d/P%d OBIO interrupt: "
8346 					    "ino=0x%x\n", exp, unum, ino);
8347 				} else if (ino <= 0x37) {
8348 					/*
8349 					 * Internal interrupt
8350 					 */
8351 					prom_printf("IO%d/P%d Internal "
8352 					    "interrupt: ino=0x%x (%s)\n",
8353 					    exp, unum, ino,
8354 					    drmach_schz_internal_ino2str(ino));
8355 				} else {
8356 					/*
8357 					 * NewLink interrupt
8358 					 */
8359 					prom_printf("IO%d/P%d NewLink "
8360 					    "interrupt: ino=0x%x\n", exp,
8361 					    unum, ino);
8362 				}
8363 
8364 				DRMACH_PR("drmach_s1p_schizo_log_intr: "
8365 				    "exp=%d, schizo=%d, pci_leaf=%c, "
8366 				    "ino=0x%x, intr_map_reg=0x%lx\n",
8367 				    exp, unum, (i == 0) ? 'A' : 'B', ino, reg);
8368 			}
8369 		}
8370 	}
8371 }
8372 
8373 /*
8374  * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.2.4
8375  * "Safari Error Control/Log Registers", Table 22-11, page 248.
8376  */
8377 #define	DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR	(0x1ull << 4)
8378 
8379 /*
8380  * Check for possible error indicators prior to resuming the
8381  * AXQ driver, which will de-assert slot1 AXQ_DOMCTRL_PAUSE.
8382  */
8383 static void
8384 drmach_slot1_pause_verify(drmach_slot1_pause_t **slot1_paused,
8385     drmach_sr_iter_t iter)
8386 {
8387 	int	i, j;
8388 	int 	errflag = 0;
8389 	drmach_slot1_pause_t *slot1;
8390 
8391 	/*
8392 	 * Check for logged schizo bus error and pending interrupts.
8393 	 */
8394 	for (i = 0; i < STARCAT_BDSET_MAX; i++) {
8395 		if ((slot1 = slot1_paused[i]) == NULL)
8396 			continue;
8397 
8398 		for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++) {
8399 			if (slot1->schizo[j].csr_basepa == 0x0UL)
8400 				continue;
8401 
8402 			if (slot1->schizo[j].regs[iter].errlog &
8403 			    DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR) {
8404 				if (!errflag) {
8405 					prom_printf("DR WARNING: interrupt "
8406 					    "attempt detected during "
8407 					    "copy-rename (%s):\n",
8408 					    (iter == DRMACH_POST_SUSPEND) ?
8409 					    "post suspend" : "pre resume");
8410 					++errflag;
8411 				}
8412 				drmach_s1p_schizo_log_intr(&slot1->schizo[j],
8413 				    i, j, iter);
8414 			}
8415 		}
8416 	}
8417 
8418 	/*
8419 	 * Check for changes in axq l2_io_q performance counters (2nd pass only)
8420 	 */
8421 	if (iter == DRMACH_PRE_RESUME) {
8422 		for (i = 0; i < STARCAT_BDSET_MAX; i++) {
8423 			if ((slot1 = slot1_paused[i]) == NULL)
8424 				continue;
8425 
8426 			if (slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND] !=
8427 			    slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]) {
8428 				prom_printf("DR WARNING: IO transactions "
8429 				    "detected on IO%d during copy-rename: "
8430 				    "AXQ l2_io_q performance counter "
8431 				    "start=%d, end=%d\n", i,
8432 				    slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND],
8433 				    slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]);
8434 			}
8435 		}
8436 	}
8437 }
8438 
8439 struct drmach_sr_list {
8440 	dev_info_t		*dip;
8441 	struct drmach_sr_list	*next;
8442 	struct drmach_sr_list	*prev;
8443 };
8444 
8445 static struct drmach_sr_ordered {
8446 	char			*name;
8447 	struct drmach_sr_list	*ring;
8448 } drmach_sr_ordered[] = {
8449 	{ "iosram",			NULL },
8450 	{ "address-extender-queue",	NULL },
8451 	{ NULL,				NULL }, /* terminator -- required */
8452 };
8453 
8454 static void
8455 drmach_sr_insert(struct drmach_sr_list **lp, dev_info_t *dip)
8456 {
8457 	struct drmach_sr_list *np;
8458 
8459 	DRMACH_PR("drmach_sr_insert: adding dip %p\n", (void *)dip);
8460 
8461 	np = (struct drmach_sr_list *)kmem_alloc(
8462 	    sizeof (struct drmach_sr_list), KM_SLEEP);
8463 
8464 	ndi_hold_devi(dip);
8465 	np->dip = dip;
8466 
8467 	if (*lp == NULL) {
8468 		/* establish list */
8469 		*lp = np->next = np->prev = np;
8470 	} else {
8471 		/* place new node behind head node on ring list */
8472 		np->prev = (*lp)->prev;
8473 		np->next = *lp;
8474 		np->prev->next = np;
8475 		np->next->prev = np;
8476 	}
8477 }
8478 
8479 static void
8480 drmach_sr_delete(struct drmach_sr_list **lp, dev_info_t *dip)
8481 {
8482 	DRMACH_PR("drmach_sr_delete: searching for dip %p\n", (void *)dip);
8483 
8484 	if (*lp) {
8485 		struct drmach_sr_list *xp;
8486 
8487 		/* start search with mostly likely node */
8488 		xp = (*lp)->prev;
8489 		do {
8490 			if (xp->dip == dip) {
8491 				xp->prev->next = xp->next;
8492 				xp->next->prev = xp->prev;
8493 
8494 				if (xp == *lp)
8495 					*lp = xp->next;
8496 				if (xp == *lp)
8497 					*lp = NULL;
8498 				xp->dip = NULL;
8499 				ndi_rele_devi(dip);
8500 				kmem_free(xp, sizeof (*xp));
8501 
8502 				DRMACH_PR("drmach_sr_delete:"
8503 				    " disposed sr node for dip %p",
8504 				    (void *)dip);
8505 				return;
8506 			}
8507 
8508 			DRMACH_PR("drmach_sr_delete: still searching\n");
8509 
8510 			xp = xp->prev;
8511 		} while (xp != (*lp)->prev);
8512 	}
8513 
8514 	/* every dip should be found during resume */
8515 	DRMACH_PR("ERROR: drmach_sr_delete: can't find dip %p", (void *)dip);
8516 }
8517 
8518 int
8519 drmach_verify_sr(dev_info_t *dip, int sflag)
8520 {
8521 	int	rv;
8522 	int	len;
8523 	char    name[OBP_MAXDRVNAME];
8524 
8525 	if (drmach_slot1_pause_debug) {
8526 		if (sflag && drmach_slot1_pause_init) {
8527 			drmach_slot1_pause_free(drmach_slot1_paused);
8528 			drmach_slot1_pause_init = 0;
8529 		} else if (!sflag && !drmach_slot1_pause_init) {
8530 			/* schedule init for next suspend */
8531 			drmach_slot1_pause_init = 1;
8532 		}
8533 	}
8534 
8535 	rv = ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8536 	    "name", &len);
8537 	if (rv == DDI_PROP_SUCCESS) {
8538 		int		portid;
8539 		uint64_t	reg;
8540 		struct drmach_sr_ordered *op;
8541 
8542 		rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
8543 		    DDI_PROP_DONTPASS, "name", (caddr_t)name, &len);
8544 
8545 		if (rv != DDI_PROP_SUCCESS)
8546 			return (0);
8547 
8548 		if (drmach_slot1_pause_debug && sflag &&
8549 		    drmach_is_slot1_pause_axq(dip, name, &portid, &reg)) {
8550 			drmach_slot1_pause_add_axq(dip, name, portid, reg,
8551 			    drmach_slot1_paused);
8552 		}
8553 
8554 		for (op = drmach_sr_ordered; op->name; op++) {
8555 			if (strncmp(op->name, name, strlen(op->name)) == 0) {
8556 				if (sflag)
8557 					drmach_sr_insert(&op->ring, dip);
8558 				else
8559 					drmach_sr_delete(&op->ring, dip);
8560 				return (1);
8561 			}
8562 		}
8563 	}
8564 
8565 	return (0);
8566 }
8567 
8568 static void
8569 drmach_sr_dip(dev_info_t *dip, int suspend)
8570 {
8571 	int	 rv;
8572 	major_t	 maj;
8573 	char	*name, *name_addr, *aka;
8574 
8575 	if ((name = ddi_get_name(dip)) == NULL)
8576 		name = "<null name>";
8577 	else if ((maj = ddi_name_to_major(name)) != -1)
8578 		aka = ddi_major_to_name(maj);
8579 	else
8580 		aka = "<unknown>";
8581 
8582 	if ((name_addr = ddi_get_name_addr(dip)) == NULL)
8583 		name_addr = "<null>";
8584 
8585 	prom_printf("\t%s %s@%s (aka %s)\n",
8586 	    suspend ? "suspending" : "resuming",
8587 	    name, name_addr, aka);
8588 
8589 	if (suspend) {
8590 		rv = devi_detach(dip, DDI_SUSPEND);
8591 	} else {
8592 		rv = devi_attach(dip, DDI_RESUME);
8593 	}
8594 
8595 	if (rv != DDI_SUCCESS) {
8596 		prom_printf("\tFAILED to %s %s@%s\n",
8597 		    suspend ? "suspend" : "resume",
8598 		    name, name_addr);
8599 	}
8600 }
8601 
8602 void
8603 drmach_suspend_last()
8604 {
8605 	struct drmach_sr_ordered *op;
8606 
8607 	if (drmach_slot1_pause_debug)
8608 		drmach_slot1_pause_add_io(drmach_slot1_paused);
8609 
8610 	/*
8611 	 * The ordering array declares the strict sequence in which
8612 	 * the named drivers are to suspended. Each element in
8613 	 * the array may have a double-linked ring list of driver
8614 	 * instances (dip) in the order in which they were presented
8615 	 * to drmach_verify_sr. If present, walk the list in the
8616 	 * forward direction to suspend each instance.
8617 	 */
8618 	for (op = drmach_sr_ordered; op->name; op++) {
8619 		if (op->ring) {
8620 			struct drmach_sr_list *rp;
8621 
8622 			rp = op->ring;
8623 			do {
8624 				drmach_sr_dip(rp->dip, 1);
8625 				rp = rp->next;
8626 			} while (rp != op->ring);
8627 		}
8628 	}
8629 
8630 	if (drmach_slot1_pause_debug) {
8631 		drmach_slot1_pause_update(drmach_slot1_paused,
8632 		    DRMACH_POST_SUSPEND);
8633 		drmach_slot1_pause_verify(drmach_slot1_paused,
8634 		    DRMACH_POST_SUSPEND);
8635 	}
8636 }
8637 
8638 void
8639 drmach_resume_first()
8640 {
8641 	struct drmach_sr_ordered *op = drmach_sr_ordered +
8642 	    (sizeof (drmach_sr_ordered) / sizeof (drmach_sr_ordered[0]));
8643 
8644 	if (drmach_slot1_pause_debug) {
8645 		drmach_slot1_pause_update(drmach_slot1_paused,
8646 		    DRMACH_PRE_RESUME);
8647 		drmach_slot1_pause_verify(drmach_slot1_paused,
8648 		    DRMACH_PRE_RESUME);
8649 	}
8650 
8651 	op -= 1;	/* point at terminating element */
8652 
8653 	/*
8654 	 * walk ordering array and rings backwards to resume dips
8655 	 * in reverse order in which they were suspended
8656 	 */
8657 	while (--op >= drmach_sr_ordered) {
8658 		if (op->ring) {
8659 			struct drmach_sr_list *rp;
8660 
8661 			rp = op->ring->prev;
8662 			do {
8663 				drmach_sr_dip(rp->dip, 0);
8664 				rp = rp->prev;
8665 			} while (rp != op->ring->prev);
8666 		}
8667 	}
8668 }
8669 
8670 /*
8671  * Log a DR sysevent.
8672  * Return value: 0 success, non-zero failure.
8673  */
8674 int
8675 drmach_log_sysevent(int board, char *hint, int flag, int verbose)
8676 {
8677 	sysevent_t			*ev;
8678 	sysevent_id_t			eid;
8679 	int				rv, km_flag;
8680 	sysevent_value_t		evnt_val;
8681 	sysevent_attr_list_t		*evnt_attr_list = NULL;
8682 	char				attach_pnt[MAXNAMELEN];
8683 
8684 	km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
8685 	attach_pnt[0] = '\0';
8686 	if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
8687 		rv = -1;
8688 		goto logexit;
8689 	}
8690 	if (verbose)
8691 		DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
8692 		    attach_pnt, hint, flag, verbose);
8693 
8694 	if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
8695 	    SUNW_KERN_PUB"dr", km_flag)) == NULL) {
8696 		rv = -2;
8697 		goto logexit;
8698 	}
8699 	evnt_val.value_type = SE_DATA_TYPE_STRING;
8700 	evnt_val.value.sv_string = attach_pnt;
8701 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID,
8702 	    &evnt_val, km_flag)) != 0)
8703 		goto logexit;
8704 
8705 	evnt_val.value_type = SE_DATA_TYPE_STRING;
8706 	evnt_val.value.sv_string = hint;
8707 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT,
8708 	    &evnt_val, km_flag)) != 0) {
8709 		sysevent_free_attr(evnt_attr_list);
8710 		goto logexit;
8711 	}
8712 
8713 	(void) sysevent_attach_attributes(ev, evnt_attr_list);
8714 
8715 	/*
8716 	 * Log the event but do not sleep waiting for its
8717 	 * delivery. This provides insulation from syseventd.
8718 	 */
8719 	rv = log_sysevent(ev, SE_NOSLEEP, &eid);
8720 
8721 logexit:
8722 	if (ev)
8723 		sysevent_free(ev);
8724 	if ((rv != 0) && verbose)
8725 		cmn_err(CE_WARN,
8726 		    "drmach_log_sysevent failed (rv %d) for %s  %s\n",
8727 		    rv, attach_pnt, hint);
8728 
8729 	return (rv);
8730 }
8731 
8732 /*
8733  * Initialize the mem_slice portion of a claim/unconfig/unclaim mailbox message.
8734  * Only the valid entries are modified, so the array should be zeroed out
8735  * initially.
8736  */
8737 static void
8738 drmach_msg_memslice_init(dr_memslice_t slice_arr[]) {
8739 	int	i;
8740 	char	c;
8741 
8742 	ASSERT(mutex_owned(&drmach_slice_table_lock));
8743 
8744 	for (i = 0; i < AXQ_MAX_EXP; i++) {
8745 		c = drmach_slice_table[i];
8746 
8747 		if (c & 0x20) {
8748 			slice_arr[i].valid = 1;
8749 			slice_arr[i].slice = c & 0x1f;
8750 		}
8751 	}
8752 }
8753 
8754 /*
8755  * Initialize the mem_regs portion of a claim/unconfig/unclaim mailbox message.
8756  * Only the valid entries are modified, so the array should be zeroed out
8757  * initially.
8758  */
8759 static void
8760 drmach_msg_memregs_init(dr_memregs_t regs_arr[]) {
8761 	int		rv, exp, mcnum, bank;
8762 	uint64_t	madr;
8763 	drmachid_t	id;
8764 	drmach_board_t	*bp;
8765 	drmach_mem_t	*mp;
8766 	dr_memregs_t	*memregs;
8767 
8768 	/* CONSTCOND */
8769 	ASSERT(DRMACH_MC_NBANKS == (PMBANKS_PER_PORT * LMBANKS_PER_PMBANK));
8770 
8771 	for (exp = 0; exp < 18; exp++) {
8772 		rv = drmach_array_get(drmach_boards,
8773 		    DRMACH_EXPSLOT2BNUM(exp, 0), &id);
8774 		ASSERT(rv == 0);	/* should never be out of bounds */
8775 		if (id == NULL) {
8776 			continue;
8777 		}
8778 
8779 		memregs = &regs_arr[exp];
8780 		bp = (drmach_board_t *)id;
8781 		for (mp = bp->mem; mp != NULL; mp = mp->next) {
8782 			mcnum = mp->dev.portid & 0x3;
8783 			for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
8784 				drmach_mem_read_madr(mp, bank, &madr);
8785 				if (madr & DRMACH_MC_VALID_MASK) {
8786 					DRMACH_PR("%d.%d.%d.madr = 0x%lx\n",
8787 					    exp, mcnum, bank, madr);
8788 					memregs->madr[mcnum][bank].hi =
8789 					    DRMACH_U64_TO_MCREGHI(madr);
8790 					memregs->madr[mcnum][bank].lo =
8791 					    DRMACH_U64_TO_MCREGLO(madr);
8792 				}
8793 			}
8794 		}
8795 	}
8796 }
8797 
8798 /*
8799  * Do not allow physical address range modification if either board on this
8800  * expander has processors in NULL LPA mode (CBASE=CBND=NULL).
8801  *
8802  * A side effect of NULL proc LPA mode in Starcat SSM is that local reads will
8803  * install the cache line as owned/dirty as a result of the RTSR transaction.
8804  * See section 5.2.3 of the Safari spec.  All processors will read the bus sync
8805  * list before the rename after flushing local caches.  When copy-rename
8806  * requires changing the physical address ranges (i.e. smaller memory target),
8807  * the bus sync list contains physical addresses that will not exist after the
8808  * rename.  If these cache lines are owned due to a RTSR, a system error can
8809  * occur following the rename when these cache lines are evicted and a writeback
8810  * is attempted.
8811  *
8812  * Incoming parameter represents either the copy-rename source or a candidate
8813  * target memory board.  On Starcat, only slot0 boards may have memory.
8814  */
8815 int
8816 drmach_allow_memrange_modify(drmachid_t s0id)
8817 {
8818 	drmach_board_t	*s0bp, *s1bp;
8819 	drmachid_t	s1id;
8820 	int		rv;
8821 
8822 	s0bp = s0id;
8823 
8824 	ASSERT(DRMACH_IS_BOARD_ID(s0id));
8825 	ASSERT(DRMACH_BNUM2SLOT(s0bp->bnum) == 0);
8826 
8827 	if (s0bp->flags & DRMACH_NULL_PROC_LPA) {
8828 		/*
8829 		 * This is reason enough to fail the request, no need
8830 		 * to check the device list for cpus.
8831 		 */
8832 		return (0);
8833 	}
8834 
8835 	/*
8836 	 * Check for MCPU board on the same expander.
8837 	 *
8838 	 * The board flag DRMACH_NULL_PROC_LPA can be set for all board
8839 	 * types, as it is derived at from the POST gdcd board flag
8840 	 * L1SSFLG_THIS_L1_NULL_PROC_LPA, which can be set (and should be
8841 	 * ignored) for boards with no processors.  Since NULL proc LPA
8842 	 * applies only to processors, we walk the devices array to detect
8843 	 * MCPUs.
8844 	 */
8845 	rv = drmach_array_get(drmach_boards, s0bp->bnum + 1, &s1id);
8846 	s1bp = s1id;
8847 	if (rv == 0 && s1bp != NULL) {
8848 
8849 		ASSERT(DRMACH_IS_BOARD_ID(s1id));
8850 		ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
8851 		ASSERT(DRMACH_BNUM2EXP(s0bp->bnum) ==
8852 		    DRMACH_BNUM2EXP(s1bp->bnum));
8853 
8854 		if ((s1bp->flags & DRMACH_NULL_PROC_LPA) &&
8855 		    s1bp->devices != NULL) {
8856 			int		d_idx;
8857 			drmachid_t	d_id;
8858 
8859 			rv = drmach_array_first(s1bp->devices, &d_idx, &d_id);
8860 			while (rv == 0) {
8861 				if (DRMACH_IS_CPU_ID(d_id)) {
8862 					/*
8863 					 * Fail MCPU in NULL LPA mode.
8864 					 */
8865 					return (0);
8866 				}
8867 
8868 				rv = drmach_array_next(s1bp->devices, &d_idx,
8869 				    &d_id);
8870 			}
8871 		}
8872 	}
8873 
8874 	return (1);
8875 }
8876