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