xref: /titanic_50/usr/src/uts/sun4u/opl/io/drmach.c (revision 711890bc9379ceea66272dc8d4981812224ea86e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 
29 #include <sys/debug.h>
30 #include <sys/types.h>
31 #include <sys/varargs.h>
32 #include <sys/errno.h>
33 #include <sys/cred.h>
34 #include <sys/dditypes.h>
35 #include <sys/devops.h>
36 #include <sys/modctl.h>
37 #include <sys/poll.h>
38 #include <sys/conf.h>
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41 #include <sys/sunndi.h>
42 #include <sys/ndi_impldefs.h>
43 #include <sys/stat.h>
44 #include <sys/kmem.h>
45 #include <sys/vmem.h>
46 #include <sys/opl_olympus_regs.h>
47 #include <sys/cpuvar.h>
48 #include <sys/cpupart.h>
49 #include <sys/mem_config.h>
50 #include <sys/ddi_impldefs.h>
51 #include <sys/systm.h>
52 #include <sys/machsystm.h>
53 #include <sys/autoconf.h>
54 #include <sys/cmn_err.h>
55 #include <sys/sysmacros.h>
56 #include <sys/x_call.h>
57 #include <sys/promif.h>
58 #include <sys/prom_plat.h>
59 #include <sys/membar.h>
60 #include <vm/seg_kmem.h>
61 #include <sys/mem_cage.h>
62 #include <sys/stack.h>
63 #include <sys/archsystm.h>
64 #include <vm/hat_sfmmu.h>
65 #include <sys/pte.h>
66 #include <sys/mmu.h>
67 #include <sys/cpu_module.h>
68 #include <sys/obpdefs.h>
69 #include <sys/note.h>
70 #include <sys/ontrap.h>
71 #include <sys/cpu_sgnblk_defs.h>
72 #include <sys/opl.h>
73 
74 
75 #include <sys/promimpl.h>
76 #include <sys/prom_plat.h>
77 #include <sys/kobj.h>
78 
79 #include <sys/sysevent.h>
80 #include <sys/sysevent/dr.h>
81 #include <sys/sysevent/eventdefs.h>
82 
83 #include <sys/drmach.h>
84 #include <sys/dr_util.h>
85 
86 #include <sys/fcode.h>
87 #include <sys/opl_cfg.h>
88 
89 extern void		bcopy32_il(uint64_t, uint64_t);
90 extern void		flush_cache_il(void);
91 extern void		drmach_sleep_il(void);
92 
93 typedef struct {
94 	struct drmach_node	*node;
95 	void			*data;
96 } drmach_node_walk_args_t;
97 
98 typedef struct drmach_node {
99 	void		*here;
100 
101 	pnode_t		(*get_dnode)(struct drmach_node *node);
102 	int		(*walk)(struct drmach_node *node, void *data,
103 				int (*cb)(drmach_node_walk_args_t *args));
104 	dev_info_t	*(*n_getdip)(struct drmach_node *node);
105 	int		(*n_getproplen)(struct drmach_node *node, char *name,
106 				int *len);
107 	int		(*n_getprop)(struct drmach_node *node, char *name,
108 				void *buf, int len);
109 	int		(*get_parent)(struct drmach_node *node,
110 				struct drmach_node *pnode);
111 } drmach_node_t;
112 
113 typedef struct {
114 	int		 min_index;
115 	int		 max_index;
116 	int		 arr_sz;
117 	drmachid_t	*arr;
118 } drmach_array_t;
119 
120 typedef struct {
121 	void		*isa;
122 
123 	void		(*dispose)(drmachid_t);
124 	sbd_error_t	*(*release)(drmachid_t);
125 	sbd_error_t	*(*status)(drmachid_t, drmach_status_t *);
126 
127 	char		 name[MAXNAMELEN];
128 } drmach_common_t;
129 
130 typedef	struct {
131 	uint32_t	core_present;
132 	uint32_t	core_hotadded;
133 	uint32_t	core_started;
134 } drmach_cmp_t;
135 
136 typedef struct {
137 	drmach_common_t	 cm;
138 	int		 bnum;
139 	int		 assigned;
140 	int		 powered;
141 	int		 connected;
142 	int		 cond;
143 	drmach_node_t	*tree;
144 	drmach_array_t	*devices;
145 	int		boot_board;	/* if board exists on bootup */
146 	drmach_cmp_t	cores[OPL_MAX_COREID_PER_BOARD];
147 } drmach_board_t;
148 
149 typedef struct {
150 	drmach_common_t	 cm;
151 	drmach_board_t	*bp;
152 	int		 unum;
153 	int		portid;
154 	int		 busy;
155 	int		 powered;
156 	const char	*type;
157 	drmach_node_t	*node;
158 } drmach_device_t;
159 
160 typedef struct drmach_cpu {
161 	drmach_device_t  dev;
162 	processorid_t    cpuid;
163 	int		sb;
164 	int		chipid;
165 	int		coreid;
166 	int		strandid;
167 	int		status;
168 #define	OPL_CPU_HOTADDED	1
169 } drmach_cpu_t;
170 
171 typedef struct drmach_mem {
172 	drmach_device_t  dev;
173 	uint64_t	slice_base;
174 	uint64_t	slice_size;
175 	uint64_t	base_pa;	/* lowest installed memory base */
176 	uint64_t	nbytes;		/* size of installed memory */
177 	struct memlist *memlist;
178 } drmach_mem_t;
179 
180 typedef struct drmach_io {
181 	drmach_device_t  dev;
182 	int	channel;
183 	int	leaf;
184 } drmach_io_t;
185 
186 typedef struct drmach_domain_info {
187 	uint32_t	floating;
188 	int		allow_dr;
189 } drmach_domain_info_t;
190 
191 drmach_domain_info_t drmach_domain;
192 
193 typedef struct {
194 	int		 flags;
195 	drmach_device_t	*dp;
196 	sbd_error_t	*err;
197 	dev_info_t	*dip;
198 } drmach_config_args_t;
199 
200 typedef struct {
201 	drmach_board_t	*obj;
202 	int		 ndevs;
203 	void		*a;
204 	sbd_error_t	*(*found)(void *a, const char *, int, drmachid_t);
205 	sbd_error_t	*err;
206 } drmach_board_cb_data_t;
207 
208 static drmach_array_t	*drmach_boards;
209 
210 static sbd_error_t	*drmach_device_new(drmach_node_t *,
211 				drmach_board_t *, int, drmachid_t *);
212 static sbd_error_t	*drmach_cpu_new(drmach_device_t *, drmachid_t *);
213 static sbd_error_t	*drmach_mem_new(drmach_device_t *, drmachid_t *);
214 static sbd_error_t	*drmach_io_new(drmach_device_t *, drmachid_t *);
215 
216 static dev_info_t	*drmach_node_ddi_get_dip(drmach_node_t *np);
217 static int		 drmach_node_ddi_get_prop(drmach_node_t *np,
218 				char *name, void *buf, int len);
219 static int		 drmach_node_ddi_get_proplen(drmach_node_t *np,
220 				char *name, int *len);
221 
222 static int 		drmach_get_portid(drmach_node_t *);
223 static	sbd_error_t	*drmach_i_status(drmachid_t, drmach_status_t *);
224 static int		opl_check_dr_status();
225 static void		drmach_io_dispose(drmachid_t);
226 static sbd_error_t	*drmach_io_release(drmachid_t);
227 static sbd_error_t	*drmach_io_status(drmachid_t, drmach_status_t *);
228 static int 		drmach_init(void);
229 static void 		drmach_fini(void);
230 static void		drmach_swap_pa(drmach_mem_t *, drmach_mem_t *);
231 static drmach_board_t	*drmach_get_board_by_bnum(int);
232 
233 /* options for the second argument in drmach_add_remove_cpu() */
234 #define	HOTADD_CPU	1
235 #define	HOTREMOVE_CPU	2
236 
237 #define	ON_BOARD_CORE_NUM(x)	(((uint_t)(x) / OPL_MAX_STRANDID_PER_CORE) & \
238 	(OPL_MAX_COREID_PER_BOARD - 1))
239 
240 extern struct cpu	*SIGBCPU;
241 
242 static int		drmach_name2type_idx(char *);
243 static drmach_board_t	*drmach_board_new(int, int);
244 
245 #ifdef DEBUG
246 
247 #define	DRMACH_PR		if (drmach_debug) printf
248 int drmach_debug = 1;		 /* set to non-zero to enable debug messages */
249 #else
250 
251 #define	DRMACH_PR		_NOTE(CONSTANTCONDITION) if (0) printf
252 #endif /* DEBUG */
253 
254 
255 #define	DRMACH_OBJ(id)		((drmach_common_t *)id)
256 
257 #define	DRMACH_IS_BOARD_ID(id)	\
258 	((id != 0) &&		\
259 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
260 
261 #define	DRMACH_IS_CPU_ID(id)	\
262 	((id != 0) &&		\
263 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
264 
265 #define	DRMACH_IS_MEM_ID(id)	\
266 	((id != 0) &&		\
267 	(DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
268 
269 #define	DRMACH_IS_IO_ID(id)	\
270 	((id != 0) &&		\
271 	(DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
272 
273 #define	DRMACH_IS_DEVICE_ID(id)					\
274 	((id != 0) &&						\
275 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
276 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
277 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
278 
279 #define	DRMACH_IS_ID(id)					\
280 	((id != 0) &&						\
281 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new ||	\
282 	    DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
283 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
284 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
285 
286 #define	DRMACH_INTERNAL_ERROR() \
287 	drerr_new(1, EOPL_INTERNAL, drmach_ie_fmt, __LINE__)
288 
289 static char		*drmach_ie_fmt = "drmach.c %d";
290 
291 static struct {
292 	const char	*name;
293 	const char	*type;
294 	sbd_error_t	*(*new)(drmach_device_t *, drmachid_t *);
295 } drmach_name2type[] = {
296 	{ "cpu",	DRMACH_DEVTYPE_CPU,		drmach_cpu_new },
297 	{ "pseudo-mc",	DRMACH_DEVTYPE_MEM,		drmach_mem_new },
298 	{ "pci",	DRMACH_DEVTYPE_PCI,		drmach_io_new  },
299 };
300 
301 /* utility */
302 #define	MBYTE	(1048576ull)
303 
304 /*
305  * drmach autoconfiguration data structures and interfaces
306  */
307 
308 extern struct mod_ops mod_miscops;
309 
310 static struct modlmisc modlmisc = {
311 	&mod_miscops,
312 	"OPL DR 1.1"
313 };
314 
315 static struct modlinkage modlinkage = {
316 	MODREV_1,
317 	(void *)&modlmisc,
318 	NULL
319 };
320 
321 static krwlock_t drmach_boards_rwlock;
322 
323 typedef const char	*fn_t;
324 
325 int
326 _init(void)
327 {
328 	int err;
329 
330 	if ((err = drmach_init()) != 0) {
331 		return (err);
332 	}
333 
334 	if ((err = mod_install(&modlinkage)) != 0) {
335 		drmach_fini();
336 	}
337 
338 	return (err);
339 }
340 
341 int
342 _fini(void)
343 {
344 	int	err;
345 
346 	if ((err = mod_remove(&modlinkage)) == 0)
347 		drmach_fini();
348 
349 	return (err);
350 }
351 
352 int
353 _info(struct modinfo *modinfop)
354 {
355 	return (mod_info(&modlinkage, modinfop));
356 }
357 
358 struct drmach_mc_lookup {
359 	int	bnum;
360 	drmach_board_t	*bp;
361 	dev_info_t *dip;	/* rv - set if found */
362 };
363 
364 #define	_ptob64(p) ((uint64_t)(p) << PAGESHIFT)
365 #define	_b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
366 
367 static int
368 drmach_setup_mc_info(dev_info_t *dip, drmach_mem_t *mp)
369 {
370 	uint64_t	memory_ranges[128];
371 	int len;
372 	struct memlist	*ml;
373 	int rv;
374 	hwd_sb_t *hwd;
375 	hwd_memory_t *pm;
376 
377 	len = sizeof (memory_ranges);
378 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
379 		DDI_PROP_DONTPASS, "sb-mem-ranges",
380 	    (caddr_t)&memory_ranges[0], &len) != DDI_PROP_SUCCESS) {
381 		mp->slice_base = 0;
382 		mp->slice_size = 0;
383 		return (-1);
384 	}
385 	mp->slice_base = memory_ranges[0];
386 	mp->slice_size = memory_ranges[1];
387 
388 	if (!mp->dev.bp->boot_board) {
389 		int i;
390 
391 		rv = opl_read_hwd(mp->dev.bp->bnum, NULL,  NULL, NULL, &hwd);
392 
393 		if (rv != 0) {
394 			return (-1);
395 		}
396 
397 		ml = NULL;
398 		pm = &hwd->sb_cmu.cmu_memory;
399 		for (i = 0; i < HWD_MAX_MEM_CHUNKS; i++) {
400 			if (pm->mem_chunks[i].chnk_size > 0) {
401 				ml = memlist_add_span(ml,
402 					pm->mem_chunks[i].chnk_start_address,
403 					pm->mem_chunks[i].chnk_size);
404 			}
405 		}
406 	} else {
407 		/*
408 		 * we intersect phys_install to get base_pa.
409 		 * This only works at bootup time.
410 		 */
411 
412 		memlist_read_lock();
413 		ml = memlist_dup(phys_install);
414 		memlist_read_unlock();
415 
416 		ml = memlist_del_span(ml, 0ull, mp->slice_base);
417 		if (ml) {
418 			uint64_t basepa, endpa;
419 			endpa = _ptob64(physmax + 1);
420 
421 			basepa = mp->slice_base + mp->slice_size;
422 
423 			ml = memlist_del_span(ml, basepa, endpa - basepa);
424 		}
425 	}
426 
427 	if (ml) {
428 		uint64_t nbytes = 0;
429 		struct memlist *p;
430 		for (p = ml; p; p = p->next) {
431 			nbytes += p->size;
432 		}
433 		if ((mp->nbytes = nbytes) > 0)
434 			mp->base_pa = ml->address;
435 		else
436 			mp->base_pa = 0;
437 		mp->memlist = ml;
438 	} else {
439 		mp->base_pa = 0;
440 		mp->nbytes = 0;
441 	}
442 	return (0);
443 }
444 
445 
446 struct drmach_hotcpu {
447 	drmach_board_t *bp;
448 	int	bnum;
449 	int	core_id;
450 	int 	rv;
451 	int	option;
452 };
453 
454 static int
455 drmach_cpu_cb(dev_info_t *dip, void *arg)
456 {
457 	struct drmach_hotcpu *p = (struct drmach_hotcpu *)arg;
458 	char name[OBP_MAXDRVNAME];
459 	int len = OBP_MAXDRVNAME;
460 	int bnum, core_id, strand_id;
461 	drmach_board_t *bp;
462 
463 	if (dip == ddi_root_node()) {
464 		return (DDI_WALK_CONTINUE);
465 	}
466 
467 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
468 	    DDI_PROP_DONTPASS, "name",
469 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
470 		return (DDI_WALK_PRUNECHILD);
471 	}
472 
473 	/* only cmp has board number */
474 	bnum = -1;
475 	len = sizeof (bnum);
476 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
477 	    DDI_PROP_DONTPASS, OBP_BOARDNUM,
478 	    (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
479 		bnum = -1;
480 	}
481 
482 	if (strcmp(name, "cmp") == 0) {
483 		if (bnum != p->bnum)
484 			return (DDI_WALK_PRUNECHILD);
485 		return (DDI_WALK_CONTINUE);
486 	}
487 	/* we have already pruned all unwanted cores and cpu's above */
488 	if (strcmp(name, "core") == 0) {
489 		return (DDI_WALK_CONTINUE);
490 	}
491 	if (strcmp(name, "cpu") == 0) {
492 		processorid_t cpuid;
493 		len = sizeof (cpuid);
494 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
495 		    DDI_PROP_DONTPASS, "cpuid",
496 		    (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
497 			p->rv = -1;
498 			return (DDI_WALK_TERMINATE);
499 		}
500 
501 		core_id = p->core_id;
502 
503 		bnum = LSB_ID(cpuid);
504 
505 		if (ON_BOARD_CORE_NUM(cpuid) != core_id)
506 			return (DDI_WALK_CONTINUE);
507 
508 		bp = p->bp;
509 		ASSERT(bnum == bp->bnum);
510 
511 		if (p->option == HOTADD_CPU) {
512 			if (prom_hotaddcpu(cpuid) != 0) {
513 				p->rv = -1;
514 				return (DDI_WALK_TERMINATE);
515 			}
516 			strand_id = STRAND_ID(cpuid);
517 			bp->cores[core_id].core_hotadded |= (1 << strand_id);
518 		} else if (p->option == HOTREMOVE_CPU) {
519 			if (prom_hotremovecpu(cpuid) != 0) {
520 				p->rv = -1;
521 				return (DDI_WALK_TERMINATE);
522 			}
523 			strand_id = STRAND_ID(cpuid);
524 			bp->cores[core_id].core_hotadded &= ~(1 << strand_id);
525 		}
526 		return (DDI_WALK_CONTINUE);
527 	}
528 
529 	return (DDI_WALK_PRUNECHILD);
530 }
531 
532 
533 static int
534 drmach_add_remove_cpu(int bnum, int core_id, int option)
535 {
536 	struct drmach_hotcpu arg;
537 	drmach_board_t *bp;
538 
539 	bp = drmach_get_board_by_bnum(bnum);
540 	ASSERT(bp);
541 
542 	arg.bp = bp;
543 	arg.bnum = bnum;
544 	arg.core_id = core_id;
545 	arg.rv = 0;
546 	arg.option = option;
547 	ddi_walk_devs(ddi_root_node(), drmach_cpu_cb, (void *)&arg);
548 	return (arg.rv);
549 }
550 
551 struct drmach_setup_core_arg {
552 	drmach_board_t *bp;
553 };
554 
555 static int
556 drmach_setup_core_cb(dev_info_t *dip, void *arg)
557 {
558 	struct drmach_setup_core_arg *p = (struct drmach_setup_core_arg *)arg;
559 	char name[OBP_MAXDRVNAME];
560 	int len = OBP_MAXDRVNAME;
561 	int bnum;
562 	int core_id, strand_id;
563 
564 	if (dip == ddi_root_node()) {
565 		return (DDI_WALK_CONTINUE);
566 	}
567 
568 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
569 	    DDI_PROP_DONTPASS, "name",
570 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
571 		return (DDI_WALK_PRUNECHILD);
572 	}
573 
574 	/* only cmp has board number */
575 	bnum = -1;
576 	len = sizeof (bnum);
577 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
578 	    DDI_PROP_DONTPASS, OBP_BOARDNUM,
579 	    (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
580 		bnum = -1;
581 	}
582 
583 	if (strcmp(name, "cmp") == 0) {
584 		if (bnum != p->bp->bnum)
585 			return (DDI_WALK_PRUNECHILD);
586 		return (DDI_WALK_CONTINUE);
587 	}
588 	/* we have already pruned all unwanted cores and cpu's above */
589 	if (strcmp(name, "core") == 0) {
590 		return (DDI_WALK_CONTINUE);
591 	}
592 	if (strcmp(name, "cpu") == 0) {
593 		processorid_t cpuid;
594 		len = sizeof (cpuid);
595 		if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
596 		    DDI_PROP_DONTPASS, "cpuid",
597 		    (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
598 			return (DDI_WALK_TERMINATE);
599 		}
600 		bnum = LSB_ID(cpuid);
601 		ASSERT(bnum == p->bp->bnum);
602 		core_id = ON_BOARD_CORE_NUM(cpuid);
603 		strand_id = STRAND_ID(cpuid);
604 		p->bp->cores[core_id].core_present |= (1 << strand_id);
605 		return (DDI_WALK_CONTINUE);
606 	}
607 
608 	return (DDI_WALK_PRUNECHILD);
609 }
610 
611 
612 static void
613 drmach_setup_core_info(drmach_board_t *obj)
614 {
615 	struct drmach_setup_core_arg arg;
616 	int i;
617 
618 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
619 		obj->cores[i].core_present = 0;
620 		obj->cores[i].core_hotadded = 0;
621 		obj->cores[i].core_started = 0;
622 	}
623 	arg.bp = obj;
624 	ddi_walk_devs(ddi_root_node(), drmach_setup_core_cb, (void *)&arg);
625 
626 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
627 		if (obj->boot_board) {
628 			obj->cores[i].core_hotadded =
629 				obj->cores[i].core_started =
630 				obj->cores[i].core_present;
631 		}
632 	}
633 }
634 
635 /*
636  * drmach_node_* routines serve the purpose of separating the
637  * rest of the code from the device tree and OBP.  This is necessary
638  * because of In-Kernel-Probing.  Devices probed after stod, are probed
639  * by the in-kernel-prober, not OBP.  These devices, therefore, do not
640  * have dnode ids.
641  */
642 
643 typedef struct {
644 	drmach_node_walk_args_t	*nwargs;
645 	int 			(*cb)(drmach_node_walk_args_t *args);
646 	int			err;
647 } drmach_node_ddi_walk_args_t;
648 
649 static int
650 drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
651 {
652 	drmach_node_ddi_walk_args_t	*nargs;
653 
654 	nargs = (drmach_node_ddi_walk_args_t *)arg;
655 
656 	/*
657 	 * dip doesn't have to be held here as we are called
658 	 * from ddi_walk_devs() which holds the dip.
659 	 */
660 	nargs->nwargs->node->here = (void *)dip;
661 
662 	nargs->err = nargs->cb(nargs->nwargs);
663 
664 
665 	/*
666 	 * Set "here" to NULL so that unheld dip is not accessible
667 	 * outside ddi_walk_devs()
668 	 */
669 	nargs->nwargs->node->here = NULL;
670 
671 	if (nargs->err)
672 		return (DDI_WALK_TERMINATE);
673 	else
674 		return (DDI_WALK_CONTINUE);
675 }
676 
677 static int
678 drmach_node_ddi_walk(drmach_node_t *np, void *data,
679 		int (*cb)(drmach_node_walk_args_t *args))
680 {
681 	drmach_node_walk_args_t		args;
682 	drmach_node_ddi_walk_args_t	nargs;
683 
684 
685 	/* initialized args structure for callback */
686 	args.node = np;
687 	args.data = data;
688 
689 	nargs.nwargs = &args;
690 	nargs.cb = cb;
691 	nargs.err = 0;
692 
693 	/*
694 	 * Root node doesn't have to be held in any way.
695 	 */
696 	ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb,
697 		(void *)&nargs);
698 
699 	return (nargs.err);
700 }
701 
702 static int
703 drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
704 {
705 	dev_info_t	*ndip;
706 	static char	*fn = "drmach_node_ddi_get_parent";
707 
708 	ndip = np->n_getdip(np);
709 	if (ndip == NULL) {
710 		cmn_err(CE_WARN, "%s: NULL dip", fn);
711 		return (-1);
712 	}
713 
714 	bcopy(np, pp, sizeof (drmach_node_t));
715 
716 	pp->here = (void *)ddi_get_parent(ndip);
717 	if (pp->here == NULL) {
718 		cmn_err(CE_WARN, "%s: NULL parent dip", fn);
719 		return (-1);
720 	}
721 
722 	return (0);
723 }
724 
725 /*ARGSUSED*/
726 static pnode_t
727 drmach_node_ddi_get_dnode(drmach_node_t *np)
728 {
729 	return ((pnode_t)NULL);
730 }
731 
732 static drmach_node_t *
733 drmach_node_new(void)
734 {
735 	drmach_node_t *np;
736 
737 	np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
738 
739 	np->get_dnode = drmach_node_ddi_get_dnode;
740 	np->walk = drmach_node_ddi_walk;
741 	np->n_getdip = drmach_node_ddi_get_dip;
742 	np->n_getproplen = drmach_node_ddi_get_proplen;
743 	np->n_getprop = drmach_node_ddi_get_prop;
744 	np->get_parent = drmach_node_ddi_get_parent;
745 
746 	return (np);
747 }
748 
749 static void
750 drmach_node_dispose(drmach_node_t *np)
751 {
752 	kmem_free(np, sizeof (*np));
753 }
754 
755 static dev_info_t *
756 drmach_node_ddi_get_dip(drmach_node_t *np)
757 {
758 	return ((dev_info_t *)np->here);
759 }
760 
761 static int
762 drmach_node_walk(drmach_node_t *np, void *param,
763 		int (*cb)(drmach_node_walk_args_t *args))
764 {
765 	return (np->walk(np, param, cb));
766 }
767 
768 static int
769 drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
770 {
771 	int		rv = 0;
772 	dev_info_t	*ndip;
773 	static char	*fn = "drmach_node_ddi_get_prop";
774 
775 
776 	ndip = np->n_getdip(np);
777 	if (ndip == NULL) {
778 		cmn_err(CE_WARN, "%s: NULL dip", fn);
779 		rv = -1;
780 	} else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
781 	    DDI_PROP_DONTPASS, name,
782 	    (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
783 		rv = -1;
784 	}
785 
786 	return (rv);
787 }
788 
789 static int
790 drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
791 {
792 	int		rv = 0;
793 	dev_info_t	*ndip;
794 
795 	ndip = np->n_getdip(np);
796 	if (ndip == NULL) {
797 		rv = -1;
798 	} else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS,
799 		name, len) != DDI_PROP_SUCCESS) {
800 		rv = -1;
801 	}
802 
803 	return (rv);
804 }
805 
806 static drmachid_t
807 drmach_node_dup(drmach_node_t *np)
808 {
809 	drmach_node_t *dup;
810 
811 	dup = drmach_node_new();
812 	dup->here = np->here;
813 	dup->get_dnode = np->get_dnode;
814 	dup->walk = np->walk;
815 	dup->n_getdip = np->n_getdip;
816 	dup->n_getproplen = np->n_getproplen;
817 	dup->n_getprop = np->n_getprop;
818 	dup->get_parent = np->get_parent;
819 
820 	return (dup);
821 }
822 
823 /*
824  * drmach_array provides convenient array construction, access,
825  * bounds checking and array destruction logic.
826  */
827 
828 static drmach_array_t *
829 drmach_array_new(int min_index, int max_index)
830 {
831 	drmach_array_t *arr;
832 
833 	arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
834 
835 	arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
836 	if (arr->arr_sz > 0) {
837 		arr->min_index = min_index;
838 		arr->max_index = max_index;
839 
840 		arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
841 		return (arr);
842 	} else {
843 		kmem_free(arr, sizeof (*arr));
844 		return (0);
845 	}
846 }
847 
848 static int
849 drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
850 {
851 	if (idx < arr->min_index || idx > arr->max_index)
852 		return (-1);
853 	else {
854 		arr->arr[idx - arr->min_index] = val;
855 		return (0);
856 	}
857 	/*NOTREACHED*/
858 }
859 
860 static int
861 drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
862 {
863 	if (idx < arr->min_index || idx > arr->max_index)
864 		return (-1);
865 	else {
866 		*val = arr->arr[idx - arr->min_index];
867 		return (0);
868 	}
869 	/*NOTREACHED*/
870 }
871 
872 static int
873 drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
874 {
875 	int rv;
876 
877 	*idx = arr->min_index;
878 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
879 		*idx += 1;
880 
881 	return (rv);
882 }
883 
884 static int
885 drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
886 {
887 	int rv;
888 
889 	*idx += 1;
890 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
891 		*idx += 1;
892 
893 	return (rv);
894 }
895 
896 static void
897 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
898 {
899 	drmachid_t	val;
900 	int		idx;
901 	int		rv;
902 
903 	rv = drmach_array_first(arr, &idx, &val);
904 	while (rv == 0) {
905 		(*disposer)(val);
906 		rv = drmach_array_next(arr, &idx, &val);
907 	}
908 
909 	kmem_free(arr->arr, arr->arr_sz);
910 	kmem_free(arr, sizeof (*arr));
911 }
912 
913 static drmach_board_t *
914 drmach_get_board_by_bnum(int bnum)
915 {
916 	drmachid_t id;
917 
918 	if (drmach_array_get(drmach_boards, bnum, &id) == 0)
919 		return ((drmach_board_t *)id);
920 	else
921 		return (NULL);
922 }
923 
924 static pnode_t
925 drmach_node_get_dnode(drmach_node_t *np)
926 {
927 	return (np->get_dnode(np));
928 }
929 
930 /*ARGSUSED*/
931 sbd_error_t *
932 drmach_configure(drmachid_t id, int flags)
933 {
934 	drmach_device_t		*dp;
935 	sbd_error_t		*err = NULL;
936 	dev_info_t		*rdip;
937 	dev_info_t		*fdip = NULL;
938 
939 	if (DRMACH_IS_CPU_ID(id)) {
940 		return (NULL);
941 	}
942 	if (!DRMACH_IS_DEVICE_ID(id))
943 		return (drerr_new(0, EOPL_INAPPROP, NULL));
944 	dp = id;
945 	rdip = dp->node->n_getdip(dp->node);
946 
947 	ASSERT(rdip);
948 
949 	ASSERT(e_ddi_branch_held(rdip));
950 
951 	if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
952 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
953 		dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
954 
955 		(void) ddi_pathname(dip, path);
956 		err = drerr_new(1,  EOPL_DRVFAIL, path);
957 
958 		kmem_free(path, MAXPATHLEN);
959 
960 		/* If non-NULL, fdip is returned held and must be released */
961 		if (fdip != NULL)
962 			ddi_release_devi(fdip);
963 	}
964 
965 	return (err);
966 }
967 
968 
969 static sbd_error_t *
970 drmach_device_new(drmach_node_t *node,
971 	drmach_board_t *bp, int portid, drmachid_t *idp)
972 {
973 	int		 i;
974 	int		 rv;
975 	drmach_device_t	proto;
976 	sbd_error_t	*err;
977 	char		 name[OBP_MAXDRVNAME];
978 
979 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
980 	if (rv) {
981 		/* every node is expected to have a name */
982 		err = drerr_new(1, EOPL_GETPROP,
983 			"device node %s: property %s",
984 			ddi_node_name(node->n_getdip(node)), "name");
985 		return (err);
986 	}
987 
988 	/*
989 	 * The node currently being examined is not listed in the name2type[]
990 	 * array.  In this case, the node is no interest to drmach.  Both
991 	 * dp and err are initialized here to yield nothing (no device or
992 	 * error structure) for this case.
993 	 */
994 	i = drmach_name2type_idx(name);
995 
996 
997 	if (i < 0) {
998 		*idp = (drmachid_t)0;
999 		return (NULL);
1000 	}
1001 
1002 	/* device specific new function will set unum */
1003 
1004 	bzero(&proto, sizeof (proto));
1005 	proto.type = drmach_name2type[i].type;
1006 	proto.bp = bp;
1007 	proto.node = node;
1008 	proto.portid = portid;
1009 
1010 	return (drmach_name2type[i].new(&proto, idp));
1011 }
1012 
1013 static void
1014 drmach_device_dispose(drmachid_t id)
1015 {
1016 	drmach_device_t *self = id;
1017 
1018 	self->cm.dispose(id);
1019 }
1020 
1021 
1022 static drmach_board_t *
1023 drmach_board_new(int bnum, int boot_board)
1024 {
1025 	static sbd_error_t *drmach_board_release(drmachid_t);
1026 	static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
1027 
1028 	drmach_board_t	*bp;
1029 
1030 	bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
1031 
1032 	bp->cm.isa = (void *)drmach_board_new;
1033 	bp->cm.release = drmach_board_release;
1034 	bp->cm.status = drmach_board_status;
1035 
1036 	(void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
1037 
1038 	bp->bnum = bnum;
1039 	bp->devices = NULL;
1040 	bp->connected = boot_board;
1041 	bp->tree = drmach_node_new();
1042 	bp->assigned = boot_board;
1043 	bp->powered = boot_board;
1044 	bp->boot_board = boot_board;
1045 
1046 	/*
1047 	 * If this is not bootup initialization, we have to wait till
1048 	 * IKP sets up the device nodes in drmach_board_connect().
1049 	 */
1050 	if (boot_board)
1051 		drmach_setup_core_info(bp);
1052 
1053 	drmach_array_set(drmach_boards, bnum, bp);
1054 	return (bp);
1055 }
1056 
1057 static void
1058 drmach_board_dispose(drmachid_t id)
1059 {
1060 	drmach_board_t *bp;
1061 
1062 	ASSERT(DRMACH_IS_BOARD_ID(id));
1063 	bp = id;
1064 
1065 	if (bp->tree)
1066 		drmach_node_dispose(bp->tree);
1067 
1068 	if (bp->devices)
1069 		drmach_array_dispose(bp->devices, drmach_device_dispose);
1070 
1071 	kmem_free(bp, sizeof (*bp));
1072 }
1073 
1074 static sbd_error_t *
1075 drmach_board_status(drmachid_t id, drmach_status_t *stat)
1076 {
1077 	sbd_error_t	*err = NULL;
1078 	drmach_board_t	*bp;
1079 
1080 	if (!DRMACH_IS_BOARD_ID(id))
1081 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1082 	bp = id;
1083 
1084 	stat->assigned = bp->assigned;
1085 	stat->powered = bp->powered;
1086 	stat->busy = 0;			/* assume not busy */
1087 	stat->configured = 0;		/* assume not configured */
1088 	stat->empty = 0;
1089 	stat->cond = bp->cond = SBD_COND_OK;
1090 	strncpy(stat->type, "System Brd", sizeof (stat->type));
1091 	stat->info[0] = '\0';
1092 
1093 	if (bp->devices) {
1094 		int		 rv;
1095 		int		 d_idx;
1096 		drmachid_t	 d_id;
1097 
1098 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
1099 		while (rv == 0) {
1100 			drmach_status_t	d_stat;
1101 
1102 			err = drmach_i_status(d_id, &d_stat);
1103 			if (err)
1104 				break;
1105 
1106 			stat->busy |= d_stat.busy;
1107 			stat->configured |= d_stat.configured;
1108 
1109 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
1110 		}
1111 	}
1112 
1113 	return (err);
1114 }
1115 
1116 int
1117 drmach_board_is_floating(drmachid_t id)
1118 {
1119 	drmach_board_t *bp;
1120 
1121 	if (!DRMACH_IS_BOARD_ID(id))
1122 		return (0);
1123 
1124 	bp = (drmach_board_t *)id;
1125 
1126 	return ((drmach_domain.floating & (1 << bp->bnum)) ? 1 : 0);
1127 }
1128 
1129 static int
1130 drmach_init(void)
1131 {
1132 	dev_info_t	*rdip;
1133 	int		i, rv, len;
1134 	int		*floating;
1135 
1136 	rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
1137 
1138 	drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
1139 
1140 	rdip = ddi_root_node();
1141 
1142 	if (ddi_getproplen(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1143 		"floating-boards", &len) != DDI_PROP_SUCCESS) {
1144 		cmn_err(CE_WARN, "Cannot get floating-boards proplen\n");
1145 	} else {
1146 		floating = (int *)kmem_alloc(len, KM_SLEEP);
1147 		rv = ddi_prop_op(DDI_DEV_T_ANY, rdip,
1148 			PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS,
1149 			"floating-boards", (caddr_t)floating, &len);
1150 		if (rv != DDI_PROP_SUCCESS) {
1151 			cmn_err(CE_WARN, "Cannot get floating-boards prop\n");
1152 		} else {
1153 			drmach_domain.floating = 0;
1154 			for (i = 0; i < len / sizeof (int); i++) {
1155 				drmach_domain.floating |= (1 << floating[i]);
1156 			}
1157 		}
1158 		kmem_free(floating, len);
1159 	}
1160 	drmach_domain.allow_dr = opl_check_dr_status();
1161 
1162 	rdip = ddi_get_child(ddi_root_node());
1163 	do {
1164 		int		 bnum;
1165 		drmachid_t	 id;
1166 
1167 		bnum = -1;
1168 		bnum = ddi_getprop(DDI_DEV_T_ANY, rdip,
1169 			DDI_PROP_DONTPASS, OBP_BOARDNUM, -1);
1170 		if (bnum == -1)
1171 			continue;
1172 
1173 		if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
1174 			cmn_err(CE_WARN, "Device node 0x%p has"
1175 				" invalid property value, %s=%d",
1176 					rdip, OBP_BOARDNUM, bnum);
1177 			goto error;
1178 		} else if (id == NULL) {
1179 			(void) drmach_board_new(bnum, 1);
1180 		}
1181 	} while ((rdip = ddi_get_next_sibling(rdip)) != NULL);
1182 
1183 	opl_hold_devtree();
1184 
1185 	/*
1186 	 * Initialize the IKP feature.
1187 	 *
1188 	 * This can be done only after DR has acquired a hold on all the
1189 	 * device nodes that are interesting to IKP.
1190 	 */
1191 	if (opl_init_cfg() != 0) {
1192 		cmn_err(CE_WARN, "DR - IKP initialization failed");
1193 
1194 		opl_release_devtree();
1195 
1196 		goto error;
1197 	}
1198 
1199 	return (0);
1200 error:
1201 	drmach_array_dispose(drmach_boards, drmach_board_dispose);
1202 	rw_destroy(&drmach_boards_rwlock);
1203 	return (ENXIO);
1204 }
1205 
1206 static void
1207 drmach_fini(void)
1208 {
1209 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
1210 	drmach_array_dispose(drmach_boards, drmach_board_dispose);
1211 	drmach_boards = NULL;
1212 	rw_exit(&drmach_boards_rwlock);
1213 
1214 	/*
1215 	 * Walk immediate children of the root devinfo node
1216 	 * releasing holds acquired on branches in drmach_init()
1217 	 */
1218 
1219 	opl_release_devtree();
1220 
1221 	rw_destroy(&drmach_boards_rwlock);
1222 }
1223 
1224 /*
1225  *	Each system board contains 2 Oberon PCI bridge and
1226  *	1 CMUCH.
1227  *	Each oberon has 2 channels.
1228  *	Each channel has 2 pci-ex leaf.
1229  *	Each CMUCH has 1 pci bus.
1230  *
1231  *
1232  *	Device Path:
1233  *	/pci@<portid>,reg
1234  *
1235  *	where
1236  *	portid[10] = 0
1237  *	portid[9:0] = LLEAF_ID[9:0] of the Oberon Channel
1238  *
1239  *	LLEAF_ID[9:8] = 0
1240  *	LLEAF_ID[8:4] = LSB_ID[4:0]
1241  *	LLEAF_ID[3:1] = IO Channel#[2:0] (0,1,2,3 for Oberon)
1242  *			channel 4 is pcicmu
1243  *	LLEAF_ID[0] = PCI Leaf Number (0 for leaf-A, 1 for leaf-B)
1244  *
1245  *	Properties:
1246  *	name = pci
1247  *	device_type = "pciex"
1248  *	board# = LSBID
1249  *	reg = int32 * 2, Oberon CSR space of the leaf and the UBC space
1250  *	portid = Jupiter Bus Device ID ((LSB_ID << 3)|pciport#)
1251  */
1252 
1253 static sbd_error_t *
1254 drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
1255 {
1256 	drmach_io_t	*ip;
1257 
1258 	int		 portid;
1259 
1260 	portid = proto->portid;
1261 	ASSERT(portid != -1);
1262 	proto->unum = portid & (MAX_IO_UNITS_PER_BOARD - 1);
1263 
1264 	ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
1265 	bcopy(proto, &ip->dev, sizeof (ip->dev));
1266 	ip->dev.node = drmach_node_dup(proto->node);
1267 	ip->dev.cm.isa = (void *)drmach_io_new;
1268 	ip->dev.cm.dispose = drmach_io_dispose;
1269 	ip->dev.cm.release = drmach_io_release;
1270 	ip->dev.cm.status = drmach_io_status;
1271 	ip->channel = (portid >> 1) & 0x7;
1272 	ip->leaf = (portid & 0x1);
1273 
1274 	snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
1275 		ip->dev.type, ip->dev.unum);
1276 
1277 	*idp = (drmachid_t)ip;
1278 	return (NULL);
1279 }
1280 
1281 
1282 static void
1283 drmach_io_dispose(drmachid_t id)
1284 {
1285 	drmach_io_t *self;
1286 
1287 	ASSERT(DRMACH_IS_IO_ID(id));
1288 
1289 	self = id;
1290 	if (self->dev.node)
1291 		drmach_node_dispose(self->dev.node);
1292 
1293 	kmem_free(self, sizeof (*self));
1294 }
1295 
1296 /*ARGSUSED*/
1297 sbd_error_t *
1298 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
1299 {
1300 	drmach_board_t	*bp = (drmach_board_t *)id;
1301 	sbd_error_t	*err = NULL;
1302 
1303 	/* allow status and ncm operations to always succeed */
1304 	if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) {
1305 		return (NULL);
1306 	}
1307 
1308 	/* check all other commands for the required option string */
1309 
1310 	if ((opts->size > 0) && (opts->copts != NULL)) {
1311 
1312 		DRMACH_PR("platform options: %s\n", opts->copts);
1313 
1314 		if (strstr(opts->copts, "opldr") == NULL) {
1315 			err = drerr_new(1, EOPL_SUPPORT, NULL);
1316 		}
1317 	} else {
1318 		err = drerr_new(1, EOPL_SUPPORT, NULL);
1319 	}
1320 
1321 	if (!err && id && DRMACH_IS_BOARD_ID(id)) {
1322 		switch (cmd) {
1323 			case SBD_CMD_TEST:
1324 			case SBD_CMD_STATUS:
1325 			case SBD_CMD_GETNCM:
1326 				break;
1327 			case SBD_CMD_CONNECT:
1328 				if (bp->connected)
1329 					err = drerr_new(0, ESBD_STATE, NULL);
1330 				else if (!drmach_domain.allow_dr)
1331 					err = drerr_new(1, EOPL_SUPPORT,
1332 						NULL);
1333 				break;
1334 			case SBD_CMD_DISCONNECT:
1335 				if (!bp->connected)
1336 					err = drerr_new(0, ESBD_STATE, NULL);
1337 				else if (!drmach_domain.allow_dr)
1338 					err = drerr_new(1, EOPL_SUPPORT,
1339 						NULL);
1340 				break;
1341 			default:
1342 				if (!drmach_domain.allow_dr)
1343 					err = drerr_new(1, EOPL_SUPPORT,
1344 						NULL);
1345 				break;
1346 
1347 		}
1348 	}
1349 
1350 	return (err);
1351 }
1352 
1353 /*ARGSUSED*/
1354 sbd_error_t *
1355 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
1356 {
1357 	return (NULL);
1358 }
1359 
1360 sbd_error_t *
1361 drmach_board_assign(int bnum, drmachid_t *id)
1362 {
1363 	sbd_error_t	*err = NULL;
1364 
1365 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
1366 
1367 	if (drmach_array_get(drmach_boards, bnum, id) == -1) {
1368 		err = drerr_new(1, EOPL_BNUM, "%d", bnum);
1369 	} else {
1370 		drmach_board_t	*bp;
1371 
1372 		if (*id)
1373 			rw_downgrade(&drmach_boards_rwlock);
1374 
1375 		bp = *id;
1376 		if (!(*id))
1377 			bp = *id  =
1378 				(drmachid_t)drmach_board_new(bnum, 0);
1379 		bp->assigned = 1;
1380 	}
1381 
1382 	rw_exit(&drmach_boards_rwlock);
1383 
1384 	return (err);
1385 }
1386 
1387 /*ARGSUSED*/
1388 sbd_error_t *
1389 drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
1390 {
1391 	drmach_board_t	*obj = (drmach_board_t *)id;
1392 
1393 	if (!DRMACH_IS_BOARD_ID(id))
1394 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1395 
1396 	if (opl_probe_sb(obj->bnum) != 0)
1397 		return (drerr_new(1, EOPL_PROBE, NULL));
1398 
1399 	(void) prom_attach_notice(obj->bnum);
1400 
1401 	drmach_setup_core_info(obj);
1402 
1403 	obj->connected = 1;
1404 
1405 	return (NULL);
1406 }
1407 
1408 static int drmach_cache_flush_flag[NCPU];
1409 
1410 /*ARGSUSED*/
1411 static void
1412 drmach_flush_cache(uint64_t id, uint64_t dummy)
1413 {
1414 	extern void cpu_flush_ecache(void);
1415 
1416 	cpu_flush_ecache();
1417 	drmach_cache_flush_flag[id] = 0;
1418 }
1419 
1420 static void
1421 drmach_flush_all()
1422 {
1423 	cpuset_t	xc_cpuset;
1424 	int		i;
1425 
1426 	xc_cpuset = cpu_ready_set;
1427 	for (i = 0; i < NCPU; i++) {
1428 		if (CPU_IN_SET(xc_cpuset, i)) {
1429 			drmach_cache_flush_flag[i] = 1;
1430 			xc_one(i, drmach_flush_cache, i, 0);
1431 			while (drmach_cache_flush_flag[i]) {
1432 				DELAY(1000);
1433 			}
1434 		}
1435 	}
1436 }
1437 
1438 static int
1439 drmach_disconnect_cpus(drmach_board_t *bp)
1440 {
1441 	int i, bnum;
1442 
1443 	bnum = bp->bnum;
1444 
1445 	for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
1446 	    if (bp->cores[i].core_present) {
1447 		if (bp->cores[i].core_started)
1448 		    return (-1);
1449 		if (bp->cores[i].core_hotadded) {
1450 		    if (drmach_add_remove_cpu(bnum, i, HOTREMOVE_CPU)) {
1451 			cmn_err(CE_WARN,
1452 			    "Failed to remove CMP %d on board %d\n",
1453 			    i, bnum);
1454 			return (-1);
1455 		    }
1456 		}
1457 	    }
1458 	}
1459 	return (0);
1460 }
1461 
1462 /*ARGSUSED*/
1463 sbd_error_t *
1464 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
1465 {
1466 	drmach_board_t *obj;
1467 	int rv = 0;
1468 	sbd_error_t		*err = NULL;
1469 
1470 
1471 	if (!DRMACH_IS_BOARD_ID(id))
1472 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1473 
1474 
1475 
1476 	obj = (drmach_board_t *)id;
1477 
1478 	if (drmach_disconnect_cpus(obj)) {
1479 		err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
1480 		return (err);
1481 	}
1482 
1483 	rv = opl_unprobe_sb(obj->bnum);
1484 
1485 	if (rv == 0) {
1486 		prom_detach_notice(obj->bnum);
1487 		obj->connected = 0;
1488 
1489 	} else
1490 		err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
1491 
1492 	return (err);
1493 }
1494 
1495 static int
1496 drmach_get_portid(drmach_node_t *np)
1497 {
1498 	int		portid;
1499 	char		type[OBP_MAXPROPNAME];
1500 
1501 	if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
1502 		return (portid);
1503 
1504 	/*
1505 	 * Get the device_type property to see if we should
1506 	 * continue processing this node.
1507 	 */
1508 	if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
1509 		return (-1);
1510 
1511 	if (strcmp(type, OPL_CPU_NODE) == 0) {
1512 		/*
1513 		 * We return cpuid because it has no portid
1514 		 */
1515 		if (np->n_getprop(np, "cpuid", &portid, sizeof (portid)) == 0)
1516 			return (portid);
1517 	}
1518 
1519 	return (-1);
1520 }
1521 
1522 /*
1523  * This is a helper function to determine if a given
1524  * node should be considered for a dr operation according
1525  * to predefined dr type nodes and the node's name.
1526  * Formal Parameter : The name of a device node.
1527  * Return Value: -1, name does not map to a valid dr type.
1528  *		 A value greater or equal to 0, name is a valid dr type.
1529  */
1530 static int
1531 drmach_name2type_idx(char *name)
1532 {
1533 	int 	index, ntypes;
1534 
1535 	if (name == NULL)
1536 		return (-1);
1537 
1538 	/*
1539 	 * Determine how many possible types are currently supported
1540 	 * for dr.
1541 	 */
1542 	ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
1543 
1544 	/* Determine if the node's name correspond to a predefined type. */
1545 	for (index = 0; index < ntypes; index++) {
1546 		if (strcmp(drmach_name2type[index].name, name) == 0)
1547 			/* The node is an allowed type for dr. */
1548 			return (index);
1549 	}
1550 
1551 	/*
1552 	 * If the name of the node does not map to any of the
1553 	 * types in the array drmach_name2type then the node is not of
1554 	 * interest to dr.
1555 	 */
1556 	return (-1);
1557 }
1558 
1559 /*
1560  * there is some complication on OPL:
1561  * - pseudo-mc nodes do not have portid property
1562  * - portid[9:5] of cmp node is LSB #, portid[7:3] of pci is LSB#
1563  * - cmp has board#
1564  * - core and cpu nodes do not have portid and board# properties
1565  * starcat uses portid to derive the board# but that does not work
1566  * for us.  starfire reads board# property to filter the devices.
1567  * That does not work either.  So for these specific device,
1568  * we use specific hard coded methods to get the board# -
1569  * cpu: LSB# = CPUID[9:5]
1570  */
1571 
1572 static int
1573 drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
1574 {
1575 	drmach_node_t			*node = args->node;
1576 	drmach_board_cb_data_t		*data = args->data;
1577 	drmach_board_t			*obj = data->obj;
1578 
1579 	int		rv, portid;
1580 	int		bnum;
1581 	drmachid_t	id;
1582 	drmach_device_t	*device;
1583 	char name[OBP_MAXDRVNAME];
1584 
1585 	portid = drmach_get_portid(node);
1586 	/*
1587 	 * core, cpu and pseudo-mc do not have portid
1588 	 * we use cpuid as the portid of the cpu node
1589 	 * for pseudo-mc, we do not use portid info.
1590 	 */
1591 
1592 	rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
1593 	if (rv)
1594 		return (0);
1595 
1596 
1597 	rv = node->n_getprop(node, OBP_BOARDNUM, &bnum, sizeof (bnum));
1598 
1599 	if (rv) {
1600 		/*
1601 		 * cpu does not have board# property.  We use
1602 		 * CPUID[9:5]
1603 		 */
1604 		if (strcmp("cpu", name) == 0) {
1605 			bnum = (portid >> 5) & 0x1f;
1606 		} else
1607 			return (0);
1608 	}
1609 
1610 
1611 	if (bnum != obj->bnum)
1612 		return (0);
1613 
1614 	if (drmach_name2type_idx(name) < 0) {
1615 		return (0);
1616 	}
1617 
1618 	/*
1619 	 * Create a device data structure from this node data.
1620 	 * The call may yield nothing if the node is not of interest
1621 	 * to drmach.
1622 	 */
1623 	data->err = drmach_device_new(node, obj, portid, &id);
1624 	if (data->err)
1625 		return (-1);
1626 	else if (!id) {
1627 		/*
1628 		 * drmach_device_new examined the node we passed in
1629 		 * and determined that it was one not of interest to
1630 		 * drmach.  So, it is skipped.
1631 		 */
1632 		return (0);
1633 	}
1634 
1635 	rv = drmach_array_set(obj->devices, data->ndevs++, id);
1636 	if (rv) {
1637 		data->err = DRMACH_INTERNAL_ERROR();
1638 		return (-1);
1639 	}
1640 	device = id;
1641 
1642 	data->err = (*data->found)(data->a, device->type, device->unum, id);
1643 	return (data->err == NULL ? 0 : -1);
1644 }
1645 
1646 sbd_error_t *
1647 drmach_board_find_devices(drmachid_t id, void *a,
1648 	sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
1649 {
1650 	drmach_board_t		*bp = (drmach_board_t *)id;
1651 	sbd_error_t		*err;
1652 	int			 max_devices;
1653 	int			 rv;
1654 	drmach_board_cb_data_t	data;
1655 
1656 
1657 	if (!DRMACH_IS_BOARD_ID(id))
1658 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1659 
1660 	max_devices  = MAX_CPU_UNITS_PER_BOARD;
1661 	max_devices += MAX_MEM_UNITS_PER_BOARD;
1662 	max_devices += MAX_IO_UNITS_PER_BOARD;
1663 
1664 	bp->devices = drmach_array_new(0, max_devices);
1665 
1666 	if (bp->tree == NULL)
1667 		bp->tree = drmach_node_new();
1668 
1669 	data.obj = bp;
1670 	data.ndevs = 0;
1671 	data.found = found;
1672 	data.a = a;
1673 	data.err = NULL;
1674 
1675 	rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
1676 	if (rv == 0)
1677 		err = NULL;
1678 	else {
1679 		drmach_array_dispose(bp->devices, drmach_device_dispose);
1680 		bp->devices = NULL;
1681 
1682 		if (data.err)
1683 			err = data.err;
1684 		else
1685 			err = DRMACH_INTERNAL_ERROR();
1686 	}
1687 
1688 	return (err);
1689 }
1690 
1691 int
1692 drmach_board_lookup(int bnum, drmachid_t *id)
1693 {
1694 	int	rv = 0;
1695 
1696 	rw_enter(&drmach_boards_rwlock, RW_READER);
1697 	if (drmach_array_get(drmach_boards, bnum, id)) {
1698 		*id = 0;
1699 		rv = -1;
1700 	}
1701 	rw_exit(&drmach_boards_rwlock);
1702 	return (rv);
1703 }
1704 
1705 sbd_error_t *
1706 drmach_board_name(int bnum, char *buf, int buflen)
1707 {
1708 	snprintf(buf, buflen, "SB%d", bnum);
1709 	return (NULL);
1710 }
1711 
1712 sbd_error_t *
1713 drmach_board_poweroff(drmachid_t id)
1714 {
1715 	drmach_board_t	*bp;
1716 	sbd_error_t	*err;
1717 	drmach_status_t	 stat;
1718 
1719 	if (!DRMACH_IS_BOARD_ID(id))
1720 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1721 	bp = id;
1722 
1723 	err = drmach_board_status(id, &stat);
1724 
1725 	if (!err) {
1726 		if (stat.configured || stat.busy)
1727 			err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
1728 		else {
1729 			bp->powered = 0;
1730 		}
1731 	}
1732 	return (err);
1733 }
1734 
1735 sbd_error_t *
1736 drmach_board_poweron(drmachid_t id)
1737 {
1738 	drmach_board_t	*bp;
1739 
1740 	if (!DRMACH_IS_BOARD_ID(id))
1741 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1742 	bp = id;
1743 
1744 	bp->powered = 1;
1745 
1746 	return (NULL);
1747 }
1748 
1749 static sbd_error_t *
1750 drmach_board_release(drmachid_t id)
1751 {
1752 	if (!DRMACH_IS_BOARD_ID(id))
1753 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1754 	return (NULL);
1755 }
1756 
1757 /*ARGSUSED*/
1758 sbd_error_t *
1759 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
1760 {
1761 	return (NULL);
1762 }
1763 
1764 sbd_error_t *
1765 drmach_board_unassign(drmachid_t id)
1766 {
1767 	drmach_board_t	*bp;
1768 	sbd_error_t	*err;
1769 	drmach_status_t	 stat;
1770 
1771 
1772 	if (!DRMACH_IS_BOARD_ID(id)) {
1773 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1774 	}
1775 	bp = id;
1776 
1777 	rw_enter(&drmach_boards_rwlock, RW_WRITER);
1778 
1779 	err = drmach_board_status(id, &stat);
1780 	if (err) {
1781 		rw_exit(&drmach_boards_rwlock);
1782 		return (err);
1783 	}
1784 	if (stat.configured || stat.busy) {
1785 		err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
1786 	} else {
1787 		if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
1788 			err = DRMACH_INTERNAL_ERROR();
1789 		else
1790 			drmach_board_dispose(bp);
1791 	}
1792 	rw_exit(&drmach_boards_rwlock);
1793 	return (err);
1794 }
1795 
1796 /*
1797  * We have to do more on OPL - e.g. set up sram tte, read cpuid, strand id,
1798  * implementation #, etc
1799  */
1800 
1801 static sbd_error_t *
1802 drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
1803 {
1804 	static void drmach_cpu_dispose(drmachid_t);
1805 	static sbd_error_t *drmach_cpu_release(drmachid_t);
1806 	static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
1807 
1808 	int		 portid;
1809 	drmach_cpu_t	*cp = NULL;
1810 
1811 	/* portid is CPUID of the node */
1812 	portid = proto->portid;
1813 	ASSERT(portid != -1);
1814 
1815 	/* unum = (CMP/CHIP ID) + (ON_BOARD_CORE_NUM * MAX_CMPID_PER_BOARD) */
1816 	proto->unum = ((portid/OPL_MAX_CPUID_PER_CMP) &
1817 		(OPL_MAX_CMPID_PER_BOARD - 1)) +
1818 		((portid & (OPL_MAX_CPUID_PER_CMP - 1)) *
1819 		(OPL_MAX_CMPID_PER_BOARD));
1820 
1821 	cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
1822 	bcopy(proto, &cp->dev, sizeof (cp->dev));
1823 	cp->dev.node = drmach_node_dup(proto->node);
1824 	cp->dev.cm.isa = (void *)drmach_cpu_new;
1825 	cp->dev.cm.dispose = drmach_cpu_dispose;
1826 	cp->dev.cm.release = drmach_cpu_release;
1827 	cp->dev.cm.status = drmach_cpu_status;
1828 
1829 	snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
1830 		cp->dev.type, cp->dev.unum);
1831 
1832 /*
1833  *	CPU ID representation
1834  *	CPUID[9:5] = SB#
1835  *	CPUID[4:3] = Chip#
1836  *	CPUID[2:1] = Core# (Only 2 core for OPL)
1837  *	CPUID[0:0] = Strand#
1838  */
1839 
1840 /*
1841  *	reg property of the strand contains strand ID
1842  *	reg property of the parent node contains core ID
1843  *	We should use them.
1844  */
1845 	cp->cpuid = portid;
1846 	cp->sb = (portid >> 5) & 0x1f;
1847 	cp->chipid = (portid >> 3) & 0x3;
1848 	cp->coreid = (portid >> 1) & 0x3;
1849 	cp->strandid = portid & 0x1;
1850 
1851 	*idp = (drmachid_t)cp;
1852 	return (NULL);
1853 }
1854 
1855 
1856 static void
1857 drmach_cpu_dispose(drmachid_t id)
1858 {
1859 	drmach_cpu_t	*self;
1860 
1861 	ASSERT(DRMACH_IS_CPU_ID(id));
1862 
1863 	self = id;
1864 	if (self->dev.node)
1865 		drmach_node_dispose(self->dev.node);
1866 
1867 	kmem_free(self, sizeof (*self));
1868 }
1869 
1870 static int
1871 drmach_cpu_start(struct cpu *cp)
1872 {
1873 	int		cpuid = cp->cpu_id;
1874 	extern int	restart_other_cpu(int);
1875 
1876 	ASSERT(MUTEX_HELD(&cpu_lock));
1877 	ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
1878 
1879 	cp->cpu_flags &= ~CPU_POWEROFF;
1880 
1881 	/*
1882 	 * NOTE: restart_other_cpu pauses cpus during the
1883 	 *	 slave cpu start.  This helps to quiesce the
1884 	 *	 bus traffic a bit which makes the tick sync
1885 	 *	 routine in the prom more robust.
1886 	 */
1887 	DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
1888 
1889 	restart_other_cpu(cpuid);
1890 
1891 	return (0);
1892 }
1893 
1894 static sbd_error_t *
1895 drmach_cpu_release(drmachid_t id)
1896 {
1897 	if (!DRMACH_IS_CPU_ID(id))
1898 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1899 
1900 	return (NULL);
1901 }
1902 
1903 static sbd_error_t *
1904 drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
1905 {
1906 	drmach_cpu_t *cp;
1907 	drmach_device_t *dp;
1908 
1909 	ASSERT(DRMACH_IS_CPU_ID(id));
1910 	cp = (drmach_cpu_t *)id;
1911 	dp = &cp->dev;
1912 
1913 	stat->assigned = dp->bp->assigned;
1914 	stat->powered = dp->bp->powered;
1915 	mutex_enter(&cpu_lock);
1916 	stat->configured = (cpu_get(cp->cpuid) != NULL);
1917 	mutex_exit(&cpu_lock);
1918 	stat->busy = dp->busy;
1919 	strncpy(stat->type, dp->type, sizeof (stat->type));
1920 	stat->info[0] = '\0';
1921 
1922 	return (NULL);
1923 }
1924 
1925 sbd_error_t *
1926 drmach_cpu_disconnect(drmachid_t id)
1927 {
1928 
1929 	if (!DRMACH_IS_CPU_ID(id))
1930 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1931 
1932 	return (NULL);
1933 }
1934 
1935 sbd_error_t *
1936 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
1937 {
1938 	drmach_cpu_t *cpu;
1939 
1940 	if (!DRMACH_IS_CPU_ID(id))
1941 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1942 	cpu = (drmach_cpu_t *)id;
1943 
1944 	/* get from cpu directly on OPL */
1945 	*cpuid = cpu->cpuid;
1946 	return (NULL);
1947 }
1948 
1949 sbd_error_t *
1950 drmach_cpu_get_impl(drmachid_t id, int *ip)
1951 {
1952 	drmach_device_t *cpu;
1953 	drmach_node_t	*np;
1954 	drmach_node_t	pp;
1955 	int		impl;
1956 	char		type[OBP_MAXPROPNAME];
1957 
1958 	if (!DRMACH_IS_CPU_ID(id))
1959 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1960 
1961 	cpu = id;
1962 	np = cpu->node;
1963 
1964 	if (np->get_parent(np, &pp) != 0) {
1965 		return (DRMACH_INTERNAL_ERROR());
1966 	}
1967 
1968 	/* the parent should be core */
1969 
1970 	if (pp.n_getprop(&pp, "device_type", &type, sizeof (type)) != 0) {
1971 		return (drerr_new(0, EOPL_GETPROP, NULL));
1972 	}
1973 
1974 	if (strcmp(type, OPL_CORE_NODE) == 0) {
1975 		if (pp.n_getprop(&pp, "implementation#",
1976 			&impl, sizeof (impl)) != 0) {
1977 			return (drerr_new(0, EOPL_GETPROP, NULL));
1978 		}
1979 	} else {
1980 		return (DRMACH_INTERNAL_ERROR());
1981 	}
1982 
1983 	*ip = impl;
1984 
1985 	return (NULL);
1986 }
1987 
1988 sbd_error_t *
1989 drmach_get_dip(drmachid_t id, dev_info_t **dip)
1990 {
1991 	drmach_device_t	*dp;
1992 
1993 	if (!DRMACH_IS_DEVICE_ID(id))
1994 		return (drerr_new(0, EOPL_INAPPROP, NULL));
1995 	dp = id;
1996 
1997 	*dip = dp->node->n_getdip(dp->node);
1998 	return (NULL);
1999 }
2000 
2001 sbd_error_t *
2002 drmach_io_is_attached(drmachid_t id, int *yes)
2003 {
2004 	drmach_device_t *dp;
2005 	dev_info_t	*dip;
2006 	int		state;
2007 
2008 	if (!DRMACH_IS_IO_ID(id))
2009 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2010 	dp = id;
2011 
2012 	dip = dp->node->n_getdip(dp->node);
2013 	if (dip == NULL) {
2014 		*yes = 0;
2015 		return (NULL);
2016 	}
2017 
2018 	state = ddi_get_devstate(dip);
2019 	*yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) ||
2020 	    (state == DDI_DEVSTATE_UP));
2021 
2022 	return (NULL);
2023 }
2024 
2025 struct drmach_io_cb {
2026 	char	*name;	/* name of the node */
2027 	int	(*func)(dev_info_t *);
2028 	int	rv;
2029 	dev_info_t *dip;
2030 };
2031 
2032 #define	DRMACH_IO_POST_ATTACH	0
2033 #define	DRMACH_IO_PRE_RELEASE	1
2034 
2035 static int
2036 drmach_io_cb_check(dev_info_t *dip, void *arg)
2037 {
2038 	struct drmach_io_cb *p = (struct drmach_io_cb *)arg;
2039 	char name[OBP_MAXDRVNAME];
2040 	int len = OBP_MAXDRVNAME;
2041 
2042 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
2043 		DDI_PROP_DONTPASS, "name",
2044 	    (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
2045 		return (DDI_WALK_PRUNECHILD);
2046 	}
2047 
2048 	if (strcmp(name, p->name) == 0) {
2049 		ndi_hold_devi(dip);
2050 		p->dip = dip;
2051 		return (DDI_WALK_TERMINATE);
2052 	}
2053 
2054 	return (DDI_WALK_CONTINUE);
2055 }
2056 
2057 
2058 static int
2059 drmach_console_ops(drmachid_t *id, int state)
2060 {
2061 	drmach_io_t *obj = (drmach_io_t *)id;
2062 	struct drmach_io_cb arg;
2063 	int (*msudetp)(dev_info_t *);
2064 	int (*msuattp)(dev_info_t *);
2065 	dev_info_t *dip, *pdip;
2066 	int circ;
2067 
2068 	/* 4 is pcicmu channel */
2069 	if (obj->channel != 4)
2070 		return (0);
2071 
2072 	arg.name = "serial";
2073 	arg.func = NULL;
2074 	if (state == DRMACH_IO_PRE_RELEASE) {
2075 		msudetp = (int (*)(dev_info_t *))
2076 		    modgetsymvalue("oplmsu_dr_detach", 0);
2077 		if (msudetp != NULL)
2078 			arg.func = msudetp;
2079 	} else if (state == DRMACH_IO_POST_ATTACH) {
2080 		msuattp = (int (*)(dev_info_t *))
2081 		    modgetsymvalue("oplmsu_dr_attach", 0);
2082 		if (msuattp != NULL)
2083 			arg.func = msuattp;
2084 	} else {
2085 		return (0);
2086 	}
2087 
2088 	if (arg.func == NULL) {
2089 		return (0);
2090 	}
2091 
2092 	arg.rv = 0;
2093 	arg.dip = NULL;
2094 
2095 	dip = obj->dev.node->n_getdip(obj->dev.node);
2096 	if (pdip = ddi_get_parent(dip)) {
2097 		ndi_hold_devi(pdip);
2098 		ndi_devi_enter(pdip, &circ);
2099 	} else {
2100 		/* this cannot happen unless something bad happens */
2101 		return (-1);
2102 	}
2103 
2104 	ddi_walk_devs(dip, drmach_io_cb_check, (void *)&arg);
2105 
2106 	ndi_devi_exit(pdip, circ);
2107 	ndi_rele_devi(pdip);
2108 
2109 	if (arg.dip) {
2110 		arg.rv = (*arg.func)(arg.dip);
2111 		ndi_rele_devi(arg.dip);
2112 	} else {
2113 		arg.rv = -1;
2114 	}
2115 
2116 	return (arg.rv);
2117 }
2118 
2119 sbd_error_t *
2120 drmach_io_pre_release(drmachid_t id)
2121 {
2122 	int rv;
2123 
2124 	if (!DRMACH_IS_IO_ID(id))
2125 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2126 
2127 	rv = drmach_console_ops(id, DRMACH_IO_PRE_RELEASE);
2128 
2129 	if (rv != 0)
2130 		cmn_err(CE_WARN, "IO callback failed in pre-release\n");
2131 
2132 	return (NULL);
2133 }
2134 
2135 static sbd_error_t *
2136 drmach_io_release(drmachid_t id)
2137 {
2138 	if (!DRMACH_IS_IO_ID(id))
2139 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2140 	return (NULL);
2141 }
2142 
2143 sbd_error_t *
2144 drmach_io_unrelease(drmachid_t id)
2145 {
2146 	if (!DRMACH_IS_IO_ID(id))
2147 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2148 	return (NULL);
2149 }
2150 
2151 /*ARGSUSED*/
2152 sbd_error_t *
2153 drmach_io_post_release(drmachid_t id)
2154 {
2155 	return (NULL);
2156 }
2157 
2158 /*ARGSUSED*/
2159 sbd_error_t *
2160 drmach_io_post_attach(drmachid_t id)
2161 {
2162 	int rv;
2163 
2164 	if (!DRMACH_IS_IO_ID(id))
2165 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2166 
2167 	rv = drmach_console_ops(id, DRMACH_IO_POST_ATTACH);
2168 
2169 	if (rv != 0)
2170 		cmn_err(CE_WARN, "IO callback failed in post-attach\n");
2171 
2172 	return (0);
2173 }
2174 
2175 static sbd_error_t *
2176 drmach_io_status(drmachid_t id, drmach_status_t *stat)
2177 {
2178 	drmach_device_t *dp;
2179 	sbd_error_t	*err;
2180 	int		 configured;
2181 
2182 	ASSERT(DRMACH_IS_IO_ID(id));
2183 	dp = id;
2184 
2185 	err = drmach_io_is_attached(id, &configured);
2186 	if (err)
2187 		return (err);
2188 
2189 	stat->assigned = dp->bp->assigned;
2190 	stat->powered = dp->bp->powered;
2191 	stat->configured = (configured != 0);
2192 	stat->busy = dp->busy;
2193 	strncpy(stat->type, dp->type, sizeof (stat->type));
2194 	stat->info[0] = '\0';
2195 
2196 	return (NULL);
2197 }
2198 
2199 static sbd_error_t *
2200 drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
2201 {
2202 	static void drmach_mem_dispose(drmachid_t);
2203 	static sbd_error_t *drmach_mem_release(drmachid_t);
2204 	static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
2205 	dev_info_t *dip;
2206 	int rv;
2207 
2208 	drmach_mem_t	*mp;
2209 
2210 	rv = 0;
2211 
2212 	if ((proto->node->n_getproplen(proto->node, "mc-addr", &rv) < 0) ||
2213 		(rv <= 0)) {
2214 		*idp = (drmachid_t)0;
2215 		return (NULL);
2216 	}
2217 
2218 	mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
2219 	proto->unum = 0;
2220 
2221 	bcopy(proto, &mp->dev, sizeof (mp->dev));
2222 	mp->dev.node = drmach_node_dup(proto->node);
2223 	mp->dev.cm.isa = (void *)drmach_mem_new;
2224 	mp->dev.cm.dispose = drmach_mem_dispose;
2225 	mp->dev.cm.release = drmach_mem_release;
2226 	mp->dev.cm.status = drmach_mem_status;
2227 
2228 	snprintf(mp->dev.cm.name,
2229 		sizeof (mp->dev.cm.name), "%s", mp->dev.type);
2230 
2231 	dip = mp->dev.node->n_getdip(mp->dev.node);
2232 	if (drmach_setup_mc_info(dip, mp) != 0) {
2233 		return (drerr_new(1, EOPL_MC_SETUP, NULL));
2234 	}
2235 
2236 	/* make sure we do not create memoryless nodes */
2237 	if (mp->nbytes == 0) {
2238 		*idp = (drmachid_t)NULL;
2239 		kmem_free(mp, sizeof (drmach_mem_t));
2240 	} else
2241 		*idp = (drmachid_t)mp;
2242 
2243 	return (NULL);
2244 }
2245 
2246 static void
2247 drmach_mem_dispose(drmachid_t id)
2248 {
2249 	drmach_mem_t *mp;
2250 
2251 	ASSERT(DRMACH_IS_MEM_ID(id));
2252 
2253 
2254 	mp = id;
2255 
2256 	if (mp->dev.node)
2257 		drmach_node_dispose(mp->dev.node);
2258 
2259 	if (mp->memlist) {
2260 		memlist_delete(mp->memlist);
2261 		mp->memlist = NULL;
2262 	}
2263 
2264 	kmem_free(mp, sizeof (*mp));
2265 }
2266 
2267 sbd_error_t *
2268 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
2269 {
2270 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
2271 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
2272 	int		rv;
2273 
2274 	ASSERT(size != 0);
2275 
2276 	if (!DRMACH_IS_MEM_ID(id))
2277 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2278 
2279 	kcage_range_lock();
2280 	rv = kcage_range_add(basepfn, npages, 1);
2281 	kcage_range_unlock();
2282 	if (rv == ENOMEM) {
2283 		cmn_err(CE_WARN, "%ld megabytes not available to kernel cage",
2284 			(size == 0 ? 0 : size / MBYTE));
2285 	} else if (rv != 0) {
2286 		/* catch this in debug kernels */
2287 		ASSERT(0);
2288 
2289 		cmn_err(CE_WARN, "unexpected kcage_range_add"
2290 			" return value %d", rv);
2291 	}
2292 
2293 	if (rv) {
2294 		return (DRMACH_INTERNAL_ERROR());
2295 	}
2296 	else
2297 		return (NULL);
2298 }
2299 
2300 sbd_error_t *
2301 drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
2302 {
2303 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
2304 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
2305 	int		rv;
2306 
2307 	if (!DRMACH_IS_MEM_ID(id))
2308 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2309 
2310 	if (size > 0) {
2311 		kcage_range_lock();
2312 		rv = kcage_range_delete_post_mem_del(basepfn, npages);
2313 		kcage_range_unlock();
2314 		if (rv != 0) {
2315 			cmn_err(CE_WARN,
2316 			    "unexpected kcage_range_delete_post_mem_del"
2317 			    " return value %d", rv);
2318 			return (DRMACH_INTERNAL_ERROR());
2319 		}
2320 	}
2321 
2322 	return (NULL);
2323 }
2324 
2325 sbd_error_t *
2326 drmach_mem_disable(drmachid_t id)
2327 {
2328 	if (!DRMACH_IS_MEM_ID(id))
2329 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2330 	else {
2331 		drmach_flush_all();
2332 		return (NULL);
2333 	}
2334 }
2335 
2336 sbd_error_t *
2337 drmach_mem_enable(drmachid_t id)
2338 {
2339 	if (!DRMACH_IS_MEM_ID(id))
2340 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2341 	else
2342 		return (NULL);
2343 }
2344 
2345 sbd_error_t *
2346 drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem)
2347 {
2348 	drmach_mem_t *mp;
2349 
2350 	if (!DRMACH_IS_MEM_ID(id))
2351 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2352 
2353 	mp = (drmach_mem_t *)id;
2354 
2355 	/*
2356 	 * This is only used by dr to round up/down the memory
2357 	 * for copying. Our unit of memory isolation is 64 MB.
2358 	 */
2359 
2360 	mem->mi_alignment_mask = (64 * 1024 * 1024 - 1);
2361 	mem->mi_basepa = mp->base_pa;
2362 	mem->mi_size = mp->nbytes;
2363 	mem->mi_slice_size = mp->slice_size;
2364 
2365 	return (NULL);
2366 }
2367 
2368 sbd_error_t *
2369 drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa)
2370 {
2371 	drmach_mem_t *mp;
2372 
2373 	if (!DRMACH_IS_MEM_ID(id))
2374 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2375 
2376 	mp = (drmach_mem_t *)id;
2377 
2378 	*pa = mp->base_pa;
2379 	return (NULL);
2380 }
2381 
2382 sbd_error_t *
2383 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
2384 {
2385 	drmach_mem_t	*mem;
2386 	int		rv;
2387 	struct memlist	*mlist;
2388 
2389 	if (!DRMACH_IS_MEM_ID(id))
2390 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2391 
2392 	mem = (drmach_mem_t *)id;
2393 	mlist = memlist_dup(mem->memlist);
2394 
2395 #ifdef DEBUG
2396 	/*
2397 	 * Make sure the incoming memlist doesn't already
2398 	 * intersect with what's present in the system (phys_install).
2399 	 */
2400 	memlist_read_lock();
2401 	rv = memlist_intersect(phys_install, mlist);
2402 	memlist_read_unlock();
2403 	if (rv) {
2404 		DRMACH_PR("Derived memlist intersects"
2405 			" with phys_install\n");
2406 		memlist_dump(mlist);
2407 
2408 		DRMACH_PR("phys_install memlist:\n");
2409 		memlist_dump(phys_install);
2410 
2411 		memlist_delete(mlist);
2412 		return (DRMACH_INTERNAL_ERROR());
2413 	}
2414 
2415 	DRMACH_PR("Derived memlist:");
2416 	memlist_dump(mlist);
2417 #endif
2418 	*ml = mlist;
2419 
2420 	return (NULL);
2421 }
2422 
2423 sbd_error_t *
2424 drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
2425 {
2426 	drmach_mem_t	*mem;
2427 
2428 	if (!DRMACH_IS_MEM_ID(id))
2429 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2430 
2431 	mem = (drmach_mem_t *)id;
2432 
2433 	*bytes = mem->slice_size;
2434 
2435 	return (NULL);
2436 }
2437 
2438 
2439 /* ARGSUSED */
2440 processorid_t
2441 drmach_mem_cpu_affinity(drmachid_t id)
2442 {
2443 	return (CPU_CURRENT);
2444 }
2445 
2446 static sbd_error_t *
2447 drmach_mem_release(drmachid_t id)
2448 {
2449 	if (!DRMACH_IS_MEM_ID(id))
2450 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2451 	return (NULL);
2452 }
2453 
2454 static sbd_error_t *
2455 drmach_mem_status(drmachid_t id, drmach_status_t *stat)
2456 {
2457 	drmach_mem_t *dp;
2458 	uint64_t	 pa, slice_size;
2459 	struct memlist	*ml;
2460 
2461 	ASSERT(DRMACH_IS_MEM_ID(id));
2462 	dp = id;
2463 
2464 	/* get starting physical address of target memory */
2465 	pa = dp->base_pa;
2466 
2467 	/* round down to slice boundary */
2468 	slice_size = dp->slice_size;
2469 	pa &= ~(slice_size - 1);
2470 
2471 	/* stop at first span that is in slice */
2472 	memlist_read_lock();
2473 	for (ml = phys_install; ml; ml = ml->next)
2474 		if (ml->address >= pa && ml->address < pa + slice_size)
2475 			break;
2476 	memlist_read_unlock();
2477 
2478 	stat->assigned = dp->dev.bp->assigned;
2479 	stat->powered = dp->dev.bp->powered;
2480 	stat->configured = (ml != NULL);
2481 	stat->busy = dp->dev.busy;
2482 	strncpy(stat->type, dp->dev.type, sizeof (stat->type));
2483 	stat->info[0] = '\0';
2484 
2485 	return (NULL);
2486 }
2487 
2488 
2489 sbd_error_t *
2490 drmach_board_deprobe(drmachid_t id)
2491 {
2492 	drmach_board_t	*bp;
2493 
2494 	if (!DRMACH_IS_BOARD_ID(id))
2495 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2496 
2497 	bp = id;
2498 
2499 	cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum);
2500 
2501 	if (bp->tree) {
2502 		drmach_node_dispose(bp->tree);
2503 		bp->tree = NULL;
2504 	}
2505 	if (bp->devices) {
2506 		drmach_array_dispose(bp->devices, drmach_device_dispose);
2507 		bp->devices = NULL;
2508 	}
2509 
2510 	bp->boot_board = 0;
2511 
2512 	return (NULL);
2513 }
2514 
2515 /*ARGSUSED*/
2516 static sbd_error_t *
2517 drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
2518 {
2519 	drmach_board_t		*bp = (drmach_board_t *)id;
2520 	sbd_error_t		*err = NULL;
2521 	int	rv;
2522 
2523 	if (!DRMACH_IS_BOARD_ID(id))
2524 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2525 
2526 	DRMACH_PR("calling opl_probe_board for bnum=%d\n", bp->bnum);
2527 	rv = opl_probe_sb(bp->bnum);
2528 	if (rv != 0) {
2529 		err = drerr_new(1, EOPL_PROBE, bp->cm.name);
2530 		return (err);
2531 	}
2532 	return (err);
2533 }
2534 
2535 /*ARGSUSED*/
2536 static sbd_error_t *
2537 drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
2538 {
2539 	drmach_board_t	*bp;
2540 	sbd_error_t	*err = NULL;
2541 	int	rv;
2542 
2543 	if (!DRMACH_IS_BOARD_ID(id))
2544 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2545 	bp = (drmach_board_t *)id;
2546 
2547 	cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
2548 
2549 	rv = opl_unprobe_sb(bp->bnum);
2550 	if (rv != 0) {
2551 		err = drerr_new(1, EOPL_DEPROBE, bp->cm.name);
2552 	}
2553 
2554 	return (err);
2555 }
2556 
2557 
2558 /*ARGSUSED*/
2559 sbd_error_t *
2560 drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
2561 {
2562 	struct memlist	*ml;
2563 	uint64_t	src_pa;
2564 	uint64_t	dst_pa;
2565 	uint64_t	dst;
2566 
2567 	dst_pa = va_to_pa(&dst);
2568 
2569 	memlist_read_lock();
2570 	for (ml = phys_install; ml; ml = ml->next) {
2571 		uint64_t	nbytes;
2572 
2573 		src_pa = ml->address;
2574 		nbytes = ml->size;
2575 
2576 		while (nbytes != 0ull) {
2577 
2578 			/* copy 32 bytes at arc_pa to dst_pa */
2579 			bcopy32_il(src_pa, dst_pa);
2580 
2581 			/* increment by 32 bytes */
2582 			src_pa += (4 * sizeof (uint64_t));
2583 
2584 			/* decrement by 32 bytes */
2585 			nbytes -= (4 * sizeof (uint64_t));
2586 		}
2587 	}
2588 	memlist_read_unlock();
2589 
2590 	return (NULL);
2591 }
2592 
2593 static struct {
2594 	const char	*name;
2595 	sbd_error_t	*(*handler)(drmachid_t id, drmach_opts_t *opts);
2596 } drmach_pt_arr[] = {
2597 	{ "readmem",		drmach_pt_readmem		},
2598 	{ "ikprobe",	drmach_pt_ikprobe	},
2599 	{ "ikdeprobe",	drmach_pt_ikdeprobe	},
2600 
2601 	/* the following line must always be last */
2602 	{ NULL,			NULL				}
2603 };
2604 
2605 /*ARGSUSED*/
2606 sbd_error_t *
2607 drmach_passthru(drmachid_t id, drmach_opts_t *opts)
2608 {
2609 	int		i;
2610 	sbd_error_t	*err;
2611 
2612 	i = 0;
2613 	while (drmach_pt_arr[i].name != NULL) {
2614 		int len = strlen(drmach_pt_arr[i].name);
2615 
2616 		if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
2617 			break;
2618 
2619 		i += 1;
2620 	}
2621 
2622 	if (drmach_pt_arr[i].name == NULL)
2623 		err = drerr_new(0, EOPL_UNKPTCMD, opts->copts);
2624 	else
2625 		err = (*drmach_pt_arr[i].handler)(id, opts);
2626 
2627 	return (err);
2628 }
2629 
2630 sbd_error_t *
2631 drmach_release(drmachid_t id)
2632 {
2633 	drmach_common_t *cp;
2634 
2635 	if (!DRMACH_IS_DEVICE_ID(id))
2636 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2637 	cp = id;
2638 
2639 	return (cp->release(id));
2640 }
2641 
2642 sbd_error_t *
2643 drmach_status(drmachid_t id, drmach_status_t *stat)
2644 {
2645 	drmach_common_t *cp;
2646 	sbd_error_t	*err;
2647 
2648 	rw_enter(&drmach_boards_rwlock, RW_READER);
2649 
2650 	if (!DRMACH_IS_ID(id)) {
2651 		rw_exit(&drmach_boards_rwlock);
2652 		return (drerr_new(0, EOPL_NOTID, NULL));
2653 	}
2654 	cp = (drmach_common_t *)id;
2655 	err = cp->status(id, stat);
2656 
2657 	rw_exit(&drmach_boards_rwlock);
2658 
2659 	return (err);
2660 }
2661 
2662 static sbd_error_t *
2663 drmach_i_status(drmachid_t id, drmach_status_t *stat)
2664 {
2665 	drmach_common_t *cp;
2666 
2667 	if (!DRMACH_IS_ID(id))
2668 		return (drerr_new(0, EOPL_NOTID, NULL));
2669 	cp = id;
2670 
2671 	return (cp->status(id, stat));
2672 }
2673 
2674 /*ARGSUSED*/
2675 sbd_error_t *
2676 drmach_unconfigure(drmachid_t id, int flags)
2677 {
2678 	drmach_device_t *dp;
2679 	dev_info_t	*rdip, *fdip = NULL;
2680 	char name[OBP_MAXDRVNAME];
2681 	int rv;
2682 
2683 	if (DRMACH_IS_CPU_ID(id))
2684 		return (NULL);
2685 
2686 	if (!DRMACH_IS_DEVICE_ID(id))
2687 		return (drerr_new(0, EOPL_INAPPROP, NULL));
2688 
2689 	dp = id;
2690 
2691 	rdip = dp->node->n_getdip(dp->node);
2692 
2693 	ASSERT(rdip);
2694 
2695 	rv = dp->node->n_getprop(dp->node, "name", name, OBP_MAXDRVNAME);
2696 
2697 	if (rv)
2698 		return (NULL);
2699 
2700 	/*
2701 	 * Note: FORCE flag is no longer necessary under devfs
2702 	 */
2703 
2704 	ASSERT(e_ddi_branch_held(rdip));
2705 	if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) {
2706 		sbd_error_t	*err;
2707 		char		*path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2708 
2709 		/*
2710 		 * If non-NULL, fdip is returned held and must be released.
2711 		 */
2712 		if (fdip != NULL) {
2713 			(void) ddi_pathname(fdip, path);
2714 			ndi_rele_devi(fdip);
2715 		} else {
2716 			(void) ddi_pathname(rdip, path);
2717 		}
2718 
2719 		err = drerr_new(1, EOPL_DRVFAIL, path);
2720 
2721 		kmem_free(path, MAXPATHLEN);
2722 
2723 		return (err);
2724 	}
2725 
2726 	return (NULL);
2727 }
2728 
2729 
2730 int
2731 drmach_cpu_poweron(struct cpu *cp)
2732 {
2733 	int bnum, cpuid, onb_core_num, strand_id;
2734 	drmach_board_t *bp;
2735 
2736 	DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
2737 
2738 	cpuid = cp->cpu_id;
2739 	bnum = LSB_ID(cpuid);
2740 	onb_core_num = ON_BOARD_CORE_NUM(cpuid);
2741 	strand_id = STRAND_ID(cpuid);
2742 	bp = drmach_get_board_by_bnum(bnum);
2743 
2744 	ASSERT(bp);
2745 	if (bp->cores[onb_core_num].core_hotadded == 0) {
2746 		if (drmach_add_remove_cpu(bnum, onb_core_num,
2747 			HOTADD_CPU) != 0) {
2748 			cmn_err(CE_WARN, "Failed to add CMP %d on board %d\n",
2749 				onb_core_num, bnum);
2750 			return (EIO);
2751 		}
2752 	}
2753 
2754 	ASSERT(MUTEX_HELD(&cpu_lock));
2755 
2756 	if (drmach_cpu_start(cp) != 0) {
2757 		if (bp->cores[onb_core_num].core_started == 0) {
2758 			/*
2759 			 * we must undo the hotadd or no one will do that
2760 			 * If this fails, we will do this again in
2761 			 * drmach_board_disconnect.
2762 			 */
2763 			if (drmach_add_remove_cpu(bnum, onb_core_num,
2764 				HOTREMOVE_CPU) != 0) {
2765 				cmn_err(CE_WARN, "Failed to remove CMP %d "
2766 					"on board %d\n",
2767 					onb_core_num, bnum);
2768 			}
2769 		}
2770 		return (EBUSY);
2771 	} else {
2772 		bp->cores[onb_core_num].core_started |= (1 << strand_id);
2773 		return (0);
2774 	}
2775 }
2776 
2777 int
2778 drmach_cpu_poweroff(struct cpu *cp)
2779 {
2780 	int 		rv = 0;
2781 	processorid_t	cpuid = cp->cpu_id;
2782 
2783 	DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
2784 
2785 	ASSERT(MUTEX_HELD(&cpu_lock));
2786 
2787 	/*
2788 	 * Capture all CPUs (except for detaching proc) to prevent
2789 	 * crosscalls to the detaching proc until it has cleared its
2790 	 * bit in cpu_ready_set.
2791 	 *
2792 	 * The CPU's remain paused and the prom_mutex is known to be free.
2793 	 * This prevents the x-trap victim from blocking when doing prom
2794 	 * IEEE-1275 calls at a high PIL level.
2795 	 */
2796 
2797 	promsafe_pause_cpus();
2798 
2799 	/*
2800 	 * Quiesce interrupts on the target CPU. We do this by setting
2801 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
2802 	 * prevent it from receiving cross calls and cross traps.
2803 	 * This prevents the processor from receiving any new soft interrupts.
2804 	 */
2805 	mp_cpu_quiesce(cp);
2806 
2807 	rv = prom_stopcpu_bycpuid(cpuid);
2808 	if (rv == 0)
2809 		cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
2810 
2811 	start_cpus();
2812 
2813 	if (rv == 0) {
2814 		int bnum, onb_core_num, strand_id;
2815 		drmach_board_t *bp;
2816 
2817 		CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
2818 
2819 		bnum = LSB_ID(cpuid);
2820 		onb_core_num = ON_BOARD_CORE_NUM(cpuid);
2821 		strand_id = STRAND_ID(cpuid);
2822 		bp = drmach_get_board_by_bnum(bnum);
2823 		ASSERT(bp);
2824 
2825 		bp->cores[onb_core_num].core_started &= ~(1 << strand_id);
2826 		if (bp->cores[onb_core_num].core_started == 0) {
2827 			if (drmach_add_remove_cpu(bnum, onb_core_num,
2828 				HOTREMOVE_CPU) != 0) {
2829 				cmn_err(CE_WARN,
2830 					"Failed to remove CMP %d LSB %d\n",
2831 					onb_core_num, bnum);
2832 				return (EIO);
2833 			}
2834 		}
2835 	}
2836 
2837 	return (rv);
2838 }
2839 
2840 /*ARGSUSED*/
2841 int
2842 drmach_verify_sr(dev_info_t *dip, int sflag)
2843 {
2844 	return (0);
2845 }
2846 
2847 void
2848 drmach_suspend_last(void)
2849 {
2850 }
2851 
2852 void
2853 drmach_resume_first(void)
2854 {
2855 }
2856 
2857 /*
2858  * Log a DR sysevent.
2859  * Return value: 0 success, non-zero failure.
2860  */
2861 int
2862 drmach_log_sysevent(int board, char *hint, int flag, int verbose)
2863 {
2864 	sysevent_t			*ev;
2865 	sysevent_id_t			eid;
2866 	int				rv, km_flag;
2867 	sysevent_value_t		evnt_val;
2868 	sysevent_attr_list_t		*evnt_attr_list = NULL;
2869 	char				attach_pnt[MAXNAMELEN];
2870 
2871 	km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
2872 	attach_pnt[0] = '\0';
2873 	if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
2874 		rv = -1;
2875 		goto logexit;
2876 	}
2877 	if (verbose)
2878 		DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
2879 			attach_pnt, hint, flag, verbose);
2880 
2881 	if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
2882 		SUNW_KERN_PUB"dr", km_flag)) == NULL) {
2883 		rv = -2;
2884 		goto logexit;
2885 	}
2886 	evnt_val.value_type = SE_DATA_TYPE_STRING;
2887 	evnt_val.value.sv_string = attach_pnt;
2888 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID,
2889 		&evnt_val, km_flag)) != 0)
2890 		goto logexit;
2891 
2892 	evnt_val.value_type = SE_DATA_TYPE_STRING;
2893 	evnt_val.value.sv_string = hint;
2894 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT,
2895 		&evnt_val, km_flag)) != 0) {
2896 		sysevent_free_attr(evnt_attr_list);
2897 		goto logexit;
2898 	}
2899 
2900 	(void) sysevent_attach_attributes(ev, evnt_attr_list);
2901 
2902 	/*
2903 	 * Log the event but do not sleep waiting for its
2904 	 * delivery. This provides insulation from syseventd.
2905 	 */
2906 	rv = log_sysevent(ev, SE_NOSLEEP, &eid);
2907 
2908 logexit:
2909 	if (ev)
2910 		sysevent_free(ev);
2911 	if ((rv != 0) && verbose)
2912 		cmn_err(CE_WARN,
2913 			"drmach_log_sysevent failed (rv %d) for %s  %s\n",
2914 			rv, attach_pnt, hint);
2915 
2916 	return (rv);
2917 }
2918 
2919 #define	OPL_DR_STATUS_PROP "dr-status"
2920 
2921 static int
2922 opl_check_dr_status()
2923 {
2924 	pnode_t	node;
2925 	int	rtn, len;
2926 	char	*str;
2927 
2928 	node = prom_rootnode();
2929 	if (node == OBP_BADNODE) {
2930 		return (1);
2931 	}
2932 
2933 	len = prom_getproplen(node, OPL_DR_STATUS_PROP);
2934 	if (len == -1) {
2935 		/*
2936 		 * dr-status doesn't exist when DR is activated and
2937 		 * any warning messages aren't needed.
2938 		 */
2939 		return (1);
2940 	}
2941 
2942 	str = (char *)kmem_zalloc(len+1, KM_SLEEP);
2943 	rtn = prom_getprop(node, OPL_DR_STATUS_PROP, str);
2944 	kmem_free(str, len + 1);
2945 	if (rtn == -1) {
2946 		return (1);
2947 	} else {
2948 		return (0);
2949 	}
2950 }
2951 
2952 /* we are allocating memlist from TLB locked pages to avoid tlbmisses */
2953 
2954 static struct memlist *
2955 drmach_memlist_add_span(drmach_copy_rename_program_t *p,
2956 	struct memlist *mlist, uint64_t base, uint64_t len)
2957 {
2958 	struct memlist	*ml, *tl, *nl;
2959 
2960 	if (len == 0ull)
2961 		return (NULL);
2962 
2963 	if (mlist == NULL) {
2964 		mlist = p->free_mlist;
2965 		if (mlist == NULL)
2966 			return (NULL);
2967 		p->free_mlist = mlist->next;
2968 		mlist->address = base;
2969 		mlist->size = len;
2970 		mlist->next = mlist->prev = NULL;
2971 
2972 		return (mlist);
2973 	}
2974 
2975 	for (tl = ml = mlist; ml; tl = ml, ml = ml->next) {
2976 		if (base < ml->address) {
2977 			if ((base + len) < ml->address) {
2978 				nl = p->free_mlist;
2979 				if (nl == NULL)
2980 					return (NULL);
2981 				p->free_mlist = nl->next;
2982 				nl->address = base;
2983 				nl->size = len;
2984 				nl->next = ml;
2985 				if ((nl->prev = ml->prev) != NULL)
2986 					nl->prev->next = nl;
2987 				ml->prev = nl;
2988 				if (mlist == ml)
2989 					mlist = nl;
2990 			} else {
2991 				ml->size = MAX((base + len),
2992 					(ml->address + ml->size)) -
2993 					base;
2994 				ml->address = base;
2995 			}
2996 			break;
2997 
2998 		} else if (base <= (ml->address + ml->size)) {
2999 			ml->size = MAX((base + len),
3000 				(ml->address + ml->size)) -
3001 				MIN(ml->address, base);
3002 			ml->address = MIN(ml->address, base);
3003 			break;
3004 		}
3005 	}
3006 	if (ml == NULL) {
3007 		nl = p->free_mlist;
3008 		if (nl == NULL)
3009 			return (NULL);
3010 		p->free_mlist = nl->next;
3011 		nl->address = base;
3012 		nl->size = len;
3013 		nl->next = NULL;
3014 		nl->prev = tl;
3015 		tl->next = nl;
3016 	}
3017 
3018 	return (mlist);
3019 }
3020 
3021 /*
3022  * The routine performs the necessary memory COPY and MC adr SWITCH.
3023  * Both operations MUST be at the same "level" so that the stack is
3024  * maintained correctly between the copy and switch.  The switch
3025  * portion implements a caching mechanism to guarantee the code text
3026  * is cached prior to execution.  This is to guard against possible
3027  * memory access while the MC adr's are being modified.
3028  *
3029  * IMPORTANT: The _drmach_copy_rename_end() function must immediately
3030  * follow drmach_copy_rename_prog__relocatable() so that the correct
3031  * "length" of the drmach_copy_rename_prog__relocatable can be
3032  * calculated.  This routine MUST be a LEAF function, i.e. it can
3033  * make NO function calls, primarily for two reasons:
3034  *
3035  *	1. We must keep the stack consistent across the "switch".
3036  *	2. Function calls are compiled to relative offsets, and
3037  *	   we execute this function we'll be executing it from
3038  *	   a copied version in a different area of memory, thus
3039  *	   the relative offsets will be bogus.
3040  *
3041  * Moreover, it must have the "__relocatable" suffix to inform DTrace
3042  * providers (and anything else, for that matter) that this
3043  * function's text is manually relocated elsewhere before it is
3044  * executed.  That is, it cannot be safely instrumented with any
3045  * methodology that is PC-relative.
3046  */
3047 
3048 /*
3049  * We multiply this to system_clock_frequency so we
3050  * are setting a delay of fmem_timeout second for
3051  * the rename command.  The spec says 15 second is
3052  * enough but the Fujitsu HW team suggested 17 sec.
3053  */
3054 static int	fmem_timeout = 17;
3055 static int	min_copy_size_per_sec = 20 * 1024 * 1024;
3056 int drmach_disable_mcopy = 0;
3057 
3058 /*
3059  * The following delay loop executes sleep instruction to yield the
3060  * CPU to other strands.  If this is not done, some strand will tie
3061  * up the CPU in busy loops while the other strand cannot do useful
3062  * work.  The copy procedure will take a much longer time without this.
3063  */
3064 #define	DR_DELAY_IL(ms, freq)					\
3065 	{							\
3066 		uint64_t start;					\
3067 		uint64_t nstick;				\
3068 		volatile uint64_t now;				\
3069 		nstick = ((uint64_t)ms * freq)/1000;		\
3070 		start = drmach_get_stick_il();			\
3071 		now = start;					\
3072 		while ((now - start) <= nstick) {		\
3073 			drmach_sleep_il();			\
3074 			now = drmach_get_stick_il();		\
3075 		}						\
3076 	}
3077 
3078 static int
3079 drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog,
3080 	int cpuid)
3081 {
3082 	struct memlist		*ml;
3083 	register int		rtn;
3084 	int			i;
3085 	register uint64_t	curr, limit;
3086 	extern uint64_t		drmach_get_stick_il();
3087 	extern void		membar_sync_il();
3088 	extern void		flush_instr_mem_il(void*);
3089 	extern void		flush_windows_il(void);
3090 	uint64_t		copy_start;
3091 
3092 	/*
3093 	 * flush_windows is moved here to make sure all
3094 	 * registers used in the callers are flushed to
3095 	 * memory before the copy.
3096 	 *
3097 	 * If flush_windows() is called too early in the
3098 	 * calling function, the compiler might put some
3099 	 * data in the local registers after flush_windows().
3100 	 * After FMA, if there is any fill trap, the registers
3101 	 * will contain stale data.
3102 	 */
3103 
3104 	flush_windows_il();
3105 
3106 	prog->critical->stat[cpuid] = FMEM_LOOP_COPY_READY;
3107 	membar_sync_il();
3108 
3109 	if (prog->data->cpuid == cpuid) {
3110 		limit = drmach_get_stick_il();
3111 		limit += prog->critical->delay;
3112 		for (i = 0; i < NCPU; i++) {
3113 			if (CPU_IN_SET(prog->data->cpu_slave_set, i)) {
3114 			/* wait for all CPU's to be ready */
3115 			    for (;;) {
3116 				if (prog->critical->stat[i] ==
3117 					FMEM_LOOP_COPY_READY) {
3118 					break;
3119 				}
3120 				DR_DELAY_IL(1, prog->data->stick_freq);
3121 			    }
3122 			    curr = drmach_get_stick_il();
3123 			    if (curr > limit) {
3124 				prog->data->fmem_status.error =
3125 					FMEM_XC_TIMEOUT;
3126 				return (FMEM_XC_TIMEOUT);
3127 			    }
3128 			}
3129 		}
3130 		prog->data->fmem_status.stat = FMEM_LOOP_COPY_READY;
3131 		membar_sync_il();
3132 		copy_start = drmach_get_stick_il();
3133 	} else {
3134 		for (;;) {
3135 			if (prog->data->fmem_status.stat ==
3136 				FMEM_LOOP_COPY_READY) {
3137 				break;
3138 			}
3139 			if (prog->data->fmem_status.error) {
3140 				prog->data->error[cpuid] = FMEM_TERMINATE;
3141 				return (FMEM_TERMINATE);
3142 			}
3143 			DR_DELAY_IL(1, prog->data->stick_freq);
3144 		}
3145 	}
3146 
3147 	/*
3148 	 * DO COPY.
3149 	 */
3150 	if (CPU_IN_SET(prog->data->cpu_copy_set, cpuid)) {
3151 	    for (ml = prog->data->cpu_ml[cpuid]; ml; ml = ml->next) {
3152 		uint64_t	s_pa, t_pa;
3153 		uint64_t	nbytes;
3154 
3155 		s_pa = prog->data->s_copybasepa + ml->address;
3156 		t_pa = prog->data->t_copybasepa + ml->address;
3157 		nbytes = ml->size;
3158 
3159 		while (nbytes != 0ull) {
3160 			/* If the master has detected error, we just bail out */
3161 			if (prog->data->fmem_status.error) {
3162 				prog->data->error[cpuid] = FMEM_TERMINATE;
3163 				return (FMEM_TERMINATE);
3164 			}
3165 			/*
3166 			 * This copy does NOT use an ASI
3167 			 * that avoids the Ecache, therefore
3168 			 * the dst_pa addresses may remain
3169 			 * in our Ecache after the dst_pa
3170 			 * has been removed from the system.
3171 			 * A subsequent write-back to memory
3172 			 * will cause an ARB-stop because the
3173 			 * physical address no longer exists
3174 			 * in the system. Therefore we must
3175 			 * flush out local Ecache after we
3176 			 * finish the copy.
3177 			 */
3178 
3179 			/* copy 32 bytes at src_pa to dst_pa */
3180 			bcopy32_il(s_pa, t_pa);
3181 
3182 			/* increment the counter to signal that we are alive */
3183 			prog->stat->nbytes[cpuid] += 32;
3184 
3185 			/* increment by 32 bytes */
3186 			s_pa += (4 * sizeof (uint64_t));
3187 			t_pa += (4 * sizeof (uint64_t));
3188 
3189 			/* decrement by 32 bytes */
3190 			nbytes -= (4 * sizeof (uint64_t));
3191 		}
3192 	    }
3193 	    prog->critical->stat[cpuid] = FMEM_LOOP_COPY_DONE;
3194 	    membar_sync_il();
3195 	}
3196 
3197 	/*
3198 	 * Since bcopy32_il() does NOT use an ASI to bypass
3199 	 * the Ecache, we need to flush our Ecache after
3200 	 * the copy is complete.
3201 	 */
3202 	flush_cache_il();
3203 
3204 	/*
3205 	 * drmach_fmem_exec_script()
3206 	 */
3207 	if (prog->data->cpuid == cpuid) {
3208 		uint64_t	last, now;
3209 
3210 		limit = copy_start + prog->data->copy_delay;
3211 		for (i = 0; i < NCPU; i++) {
3212 			if (CPU_IN_SET(prog->data->cpu_slave_set, i)) {
3213 			    for (;;) {
3214 				/* we get FMEM_LOOP_FMEM_READY in normal case */
3215 				if (prog->critical->stat[i] ==
3216 					FMEM_LOOP_FMEM_READY) {
3217 					break;
3218 				}
3219 				/* got error traps */
3220 				if (prog->critical->stat[i] ==
3221 					FMEM_COPY_ERROR) {
3222 					prog->data->fmem_status.error =
3223 						FMEM_COPY_ERROR;
3224 					return (FMEM_COPY_ERROR);
3225 				}
3226 				/* if we have not reached limit, wait more */
3227 				curr = drmach_get_stick_il();
3228 				if (curr <= limit)
3229 					continue;
3230 
3231 				prog->data->slowest_cpuid = i;
3232 				prog->data->copy_wait_time =
3233 					curr - copy_start;
3234 
3235 				/* now check if slave is alive */
3236 				last = prog->stat->nbytes[i];
3237 
3238 				DR_DELAY_IL(1, prog->data->stick_freq);
3239 
3240 				now = prog->stat->nbytes[i];
3241 				if (now <= last) {
3242 					/* no progress, perhaps just finished */
3243 					DR_DELAY_IL(1, prog->data->stick_freq);
3244 					if (prog->critical->stat[i] ==
3245 						FMEM_LOOP_FMEM_READY)
3246 						break;
3247 					/* copy error */
3248 					if (prog->critical->stat[i] ==
3249 						FMEM_COPY_ERROR) {
3250 						prog->data->fmem_status.error =
3251 							FMEM_COPY_ERROR;
3252 						return (FMEM_COPY_ERROR);
3253 					}
3254 					prog->data->fmem_status.error =
3255 					    FMEM_COPY_TIMEOUT;
3256 					return (FMEM_COPY_TIMEOUT);
3257 				}
3258 			    }
3259 			}
3260 		}
3261 		prog->critical->stat[cpuid] = FMEM_LOOP_FMEM_READY;
3262 		prog->data->fmem_status.stat  = FMEM_LOOP_FMEM_READY;
3263 
3264 		membar_sync_il();
3265 		flush_instr_mem_il((void*) (prog->critical));
3266 		/*
3267 		 * drmach_fmem_exec_script()
3268 		 */
3269 		rtn = prog->critical->fmem((void *)prog->critical, PAGESIZE);
3270 		return (rtn);
3271 	} else {
3272 		flush_instr_mem_il((void*) (prog->critical));
3273 		/*
3274 		 * drmach_fmem_loop_script()
3275 		 */
3276 		rtn = prog->critical->loop((void *)(prog->critical),
3277 			PAGESIZE, (void *)&(prog->critical->stat[cpuid]));
3278 		prog->data->error[cpuid] = rtn;
3279 		/* slave thread does not care the rv */
3280 		return (0);
3281 	}
3282 }
3283 
3284 static void
3285 drmach_copy_rename_end(void)
3286 {
3287 	/*
3288 	 * IMPORTANT:	This function's location MUST be located immediately
3289 	 *		following drmach_copy_rename_prog__relocatable to
3290 	 *		accurately estimate its size.  Note that this assumes
3291 	 *		the compiler keeps these functions in the order in
3292 	 *		which they appear :-o
3293 	 */
3294 }
3295 
3296 
3297 static void
3298 drmach_setup_memlist(drmach_copy_rename_program_t *p)
3299 {
3300 	struct memlist *ml;
3301 	caddr_t buf;
3302 	int nbytes, s;
3303 
3304 	nbytes = PAGESIZE;
3305 	s = roundup(sizeof (struct memlist), sizeof (void *));
3306 	p->free_mlist = NULL;
3307 	buf = p->memlist_buffer;
3308 	while (nbytes >= sizeof (struct memlist)) {
3309 		ml = (struct memlist *)buf;
3310 		ml->next = p->free_mlist;
3311 		p->free_mlist = ml;
3312 		buf += s;
3313 		nbytes -= s;
3314 	}
3315 }
3316 
3317 static void
3318 drmach_lock_critical(caddr_t va, caddr_t new_va)
3319 {
3320 	tte_t tte;
3321 	int i;
3322 
3323 	kpreempt_disable();
3324 
3325 	for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
3326 		vtag_flushpage(new_va, (uint64_t)ksfmmup);
3327 		sfmmu_memtte(&tte, va_to_pfn(va),
3328 			PROC_DATA|HAT_NOSYNC, TTE8K);
3329 		tte.tte_intlo |= TTE_LCK_INT;
3330 		sfmmu_dtlb_ld_kva(new_va, &tte);
3331 		sfmmu_itlb_ld_kva(new_va, &tte);
3332 		va += PAGESIZE;
3333 		new_va += PAGESIZE;
3334 	}
3335 }
3336 
3337 static void
3338 drmach_unlock_critical(caddr_t va)
3339 {
3340 	int i;
3341 
3342 	for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
3343 		vtag_flushpage(va, (uint64_t)ksfmmup);
3344 		va += PAGESIZE;
3345 	}
3346 
3347 	kpreempt_enable();
3348 }
3349 
3350 sbd_error_t *
3351 drmach_copy_rename_init(drmachid_t t_id, drmachid_t s_id,
3352 	struct memlist *c_ml, drmachid_t *pgm_id)
3353 {
3354 	drmach_mem_t	*s_mem;
3355 	drmach_mem_t	*t_mem;
3356 	struct memlist	*x_ml;
3357 	uint64_t	s_copybasepa, t_copybasepa;
3358 	uint_t		len;
3359 	caddr_t		bp, wp;
3360 	int			s_bd, t_bd, cpuid, active_cpus, i;
3361 	uint64_t		c_addr;
3362 	size_t			c_size, copy_sz, sz;
3363 	extern void		drmach_fmem_loop_script();
3364 	extern void		drmach_fmem_loop_script_rtn();
3365 	extern int		drmach_fmem_exec_script();
3366 	extern void		drmach_fmem_exec_script_end();
3367 	sbd_error_t	*err;
3368 	drmach_copy_rename_program_t *prog = NULL;
3369 	drmach_copy_rename_program_t *prog_kmem = NULL;
3370 	void		(*mc_suspend)(void);
3371 	void		(*mc_resume)(void);
3372 	int		(*scf_fmem_start)(int, int);
3373 	int		(*scf_fmem_end)(void);
3374 	int		(*scf_fmem_cancel)(void);
3375 	uint64_t	(*scf_get_base_addr)(void);
3376 
3377 	if (!DRMACH_IS_MEM_ID(s_id))
3378 		return (drerr_new(0, EOPL_INAPPROP, NULL));
3379 	if (!DRMACH_IS_MEM_ID(t_id))
3380 		return (drerr_new(0, EOPL_INAPPROP, NULL));
3381 
3382 	for (i = 0; i < NCPU; i++) {
3383 		int lsb_id, onb_core_num, strand_id;
3384 		drmach_board_t *bp;
3385 
3386 		/*
3387 		 * this kind of CPU will spin in cache
3388 		 */
3389 		if (CPU_IN_SET(cpu_ready_set, i))
3390 			continue;
3391 
3392 		/*
3393 		 * Now check for any inactive CPU's that
3394 		 * have been hotadded.  This can only occur in
3395 		 * error condition in drmach_cpu_poweron().
3396 		 */
3397 		lsb_id = LSB_ID(i);
3398 		onb_core_num = ON_BOARD_CORE_NUM(i);
3399 		strand_id = STRAND_ID(i);
3400 		bp = drmach_get_board_by_bnum(lsb_id);
3401 		if (bp == NULL)
3402 			continue;
3403 		if (bp->cores[onb_core_num].core_hotadded &
3404 		    (1 << strand_id)) {
3405 		    if (!(bp->cores[onb_core_num].core_started &
3406 			(1 << strand_id))) {
3407 			return (drerr_new(1, EOPL_CPU_STATE, NULL));
3408 		    }
3409 		}
3410 	}
3411 
3412 	mc_suspend = (void (*)(void))
3413 	    modgetsymvalue("opl_mc_suspend", 0);
3414 	mc_resume = (void (*)(void))
3415 	    modgetsymvalue("opl_mc_resume", 0);
3416 
3417 	if (mc_suspend == NULL || mc_resume == NULL) {
3418 		return (drerr_new(1, EOPL_MC_OPL, NULL));
3419 	}
3420 
3421 	scf_fmem_start = (int (*)(int, int))
3422 	    modgetsymvalue("scf_fmem_start", 0);
3423 	if (scf_fmem_start == NULL) {
3424 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
3425 	}
3426 	scf_fmem_end = (int (*)(void))
3427 	    modgetsymvalue("scf_fmem_end", 0);
3428 	if (scf_fmem_end == NULL) {
3429 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
3430 	}
3431 	scf_fmem_cancel = (int (*)(void))
3432 	    modgetsymvalue("scf_fmem_cancel", 0);
3433 	if (scf_fmem_cancel == NULL) {
3434 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
3435 	}
3436 	scf_get_base_addr = (uint64_t (*)(void))
3437 	    modgetsymvalue("scf_get_base_addr", 0);
3438 	if (scf_get_base_addr == NULL) {
3439 		return (drerr_new(1, EOPL_SCF_FMEM, NULL));
3440 	}
3441 	s_mem = s_id;
3442 	t_mem = t_id;
3443 
3444 	s_bd = s_mem->dev.bp->bnum;
3445 	t_bd = t_mem->dev.bp->bnum;
3446 
3447 	/* calculate source and target base pa */
3448 
3449 	s_copybasepa = s_mem->slice_base;
3450 	t_copybasepa = t_mem->slice_base;
3451 
3452 	/* adjust copy memlist addresses to be relative to copy base pa */
3453 	x_ml = c_ml;
3454 	while (x_ml != NULL) {
3455 		x_ml->address -= s_copybasepa;
3456 		x_ml = x_ml->next;
3457 	}
3458 
3459 	/*
3460 	 * bp will be page aligned, since we're calling
3461 	 * kmem_zalloc() with an exact multiple of PAGESIZE.
3462 	 */
3463 
3464 	prog_kmem = (drmach_copy_rename_program_t *)kmem_zalloc(
3465 		DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, KM_SLEEP);
3466 
3467 	prog_kmem->prog = prog_kmem;
3468 
3469 	/*
3470 	 * To avoid MTLB hit, we allocate a new VM space and remap
3471 	 * the kmem_alloc buffer to that address.  This solves
3472 	 * 2 problems we found:
3473 	 * - the kmem_alloc buffer can be just a chunk inside
3474 	 *   a much larger, e.g. 4MB buffer and MTLB will occur
3475 	 *   if there are both a 4MB and a 8K TLB mapping to
3476 	 *   the same VA range.
3477 	 * - the kmem mapping got dropped into the TLB by other
3478 	 *   strands, unintentionally.
3479 	 * Note that the pointers like data, critical, memlist_buffer,
3480 	 * and stat inside the copy rename structure are mapped to this
3481 	 * alternate VM space so we must make sure we lock the TLB mapping
3482 	 * whenever we access data pointed to by these pointers.
3483 	 */
3484 
3485 	prog = prog_kmem->locked_prog = vmem_alloc(heap_arena,
3486 		DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, VM_SLEEP);
3487 	wp = bp = (caddr_t)prog;
3488 
3489 	/* Now remap prog_kmem to prog */
3490 	drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
3491 
3492 	/* All pointers in prog are based on the alternate mapping */
3493 	prog->data = (drmach_copy_rename_data_t *)roundup(((uint64_t)prog +
3494 		sizeof (drmach_copy_rename_program_t)), sizeof (void *));
3495 
3496 	ASSERT(((uint64_t)prog->data + sizeof (drmach_copy_rename_data_t))
3497 		<= ((uint64_t)prog + PAGESIZE));
3498 
3499 	prog->critical = (drmach_copy_rename_critical_t *)
3500 		(wp + DRMACH_FMEM_CRITICAL_PAGE * PAGESIZE);
3501 
3502 	prog->memlist_buffer = (caddr_t)(wp +
3503 		DRMACH_FMEM_MLIST_PAGE * PAGESIZE);
3504 
3505 	prog->stat = (drmach_cr_stat_t *)(wp +
3506 		DRMACH_FMEM_STAT_PAGE * PAGESIZE);
3507 
3508 	/* LINTED */
3509 	ASSERT(sizeof (drmach_cr_stat_t)
3510 		<= ((DRMACH_FMEM_LOCKED_PAGES - DRMACH_FMEM_STAT_PAGE)
3511 		* PAGESIZE));
3512 
3513 	prog->critical->scf_reg_base = (uint64_t)-1;
3514 	prog->critical->scf_td[0] = (s_bd & 0xff);
3515 	prog->critical->scf_td[1] = (t_bd & 0xff);
3516 	for (i = 2; i < 15; i++) {
3517 		prog->critical->scf_td[i]   = 0;
3518 	}
3519 	prog->critical->scf_td[15] = ((0xaa + s_bd + t_bd) & 0xff);
3520 
3521 	bp = (caddr_t)prog->critical;
3522 	len = sizeof (drmach_copy_rename_critical_t);
3523 	wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
3524 
3525 	len = (uint_t)((ulong_t)drmach_copy_rename_end -
3526 		(ulong_t)drmach_copy_rename_prog__relocatable);
3527 
3528 	/*
3529 	 * We always leave 1K nop's to prevent the processor from
3530 	 * speculative execution that causes memory access
3531 	 */
3532 	wp = wp + len + 1024;
3533 
3534 	len = (uint_t)((ulong_t)drmach_fmem_exec_script_end -
3535 		(ulong_t)drmach_fmem_exec_script);
3536 	/* this is the entry point of the loop script */
3537 	wp = wp + len + 1024;
3538 
3539 	len = (uint_t)((ulong_t)drmach_fmem_exec_script -
3540 		(ulong_t)drmach_fmem_loop_script);
3541 	wp = wp + len + 1024;
3542 
3543 	/* now we make sure there is 1K extra */
3544 
3545 	if ((wp - bp) > PAGESIZE) {
3546 		err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
3547 		goto out;
3548 	}
3549 
3550 	bp = (caddr_t)prog->critical;
3551 	len = sizeof (drmach_copy_rename_critical_t);
3552 	wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
3553 
3554 	prog->critical->run = (int (*)())(wp);
3555 	len = (uint_t)((ulong_t)drmach_copy_rename_end -
3556 		(ulong_t)drmach_copy_rename_prog__relocatable);
3557 
3558 	bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len);
3559 
3560 	wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
3561 
3562 	prog->critical->fmem = (int (*)())(wp);
3563 	len = (int)((ulong_t)drmach_fmem_exec_script_end -
3564 		(ulong_t)drmach_fmem_exec_script);
3565 	bcopy((caddr_t)drmach_fmem_exec_script, wp, len);
3566 
3567 	len = (int)((ulong_t)drmach_fmem_exec_script_end -
3568 		(ulong_t)drmach_fmem_exec_script);
3569 	wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
3570 
3571 	prog->critical->loop = (int (*)())(wp);
3572 	len = (int)((ulong_t)drmach_fmem_exec_script -
3573 		(ulong_t)drmach_fmem_loop_script);
3574 	bcopy((caddr_t)drmach_fmem_loop_script, (void *)wp, len);
3575 	len = (int)((ulong_t)drmach_fmem_loop_script_rtn-
3576 		(ulong_t)drmach_fmem_loop_script);
3577 	prog->critical->loop_rtn = (void (*)()) (wp+len);
3578 
3579 	/* now we are committed, call SCF, soft suspend mac patrol */
3580 	if ((*scf_fmem_start)(s_bd, t_bd)) {
3581 		err = drerr_new(1, EOPL_SCF_FMEM_START, NULL);
3582 		goto out;
3583 	}
3584 	prog->data->scf_fmem_end = scf_fmem_end;
3585 	prog->data->scf_fmem_cancel = scf_fmem_cancel;
3586 	prog->data->scf_get_base_addr = scf_get_base_addr;
3587 	prog->data->fmem_status.op |= OPL_FMEM_SCF_START;
3588 	/* soft suspend mac patrol */
3589 	(*mc_suspend)();
3590 	prog->data->fmem_status.op |= OPL_FMEM_MC_SUSPEND;
3591 	prog->data->mc_resume = mc_resume;
3592 
3593 	prog->critical->inst_loop_ret  =
3594 		*(uint64_t *)(prog->critical->loop_rtn);
3595 
3596 	/*
3597 	 * 0x30800000 is op code "ba,a	+0"
3598 	 */
3599 
3600 	*(uint_t *)(prog->critical->loop_rtn) = (uint_t)(0x30800000);
3601 
3602 	/*
3603 	 * set the value of SCF FMEM TIMEOUT
3604 	 */
3605 	prog->critical->delay = fmem_timeout * system_clock_freq;
3606 
3607 	prog->data->s_mem = (drmachid_t)s_mem;
3608 	prog->data->t_mem = (drmachid_t)t_mem;
3609 
3610 	cpuid = CPU->cpu_id;
3611 	prog->data->cpuid = cpuid;
3612 	prog->data->cpu_ready_set = cpu_ready_set;
3613 	prog->data->cpu_slave_set = cpu_ready_set;
3614 	prog->data->slowest_cpuid = (processorid_t)-1;
3615 	prog->data->copy_wait_time = 0;
3616 	CPUSET_DEL(prog->data->cpu_slave_set, cpuid);
3617 
3618 	for (i = 0; i < NCPU; i++) {
3619 		prog->data->cpu_ml[i] = NULL;
3620 	}
3621 
3622 	active_cpus = 0;
3623 	if (drmach_disable_mcopy) {
3624 		active_cpus = 1;
3625 		CPUSET_ADD(prog->data->cpu_copy_set, cpuid);
3626 	} else {
3627 		for (i = 0; i < NCPU; i++) {
3628 			if (CPU_IN_SET(cpu_ready_set, i) &&
3629 				CPU_ACTIVE(cpu[i])) {
3630 				CPUSET_ADD(prog->data->cpu_copy_set, i);
3631 				active_cpus++;
3632 			}
3633 		}
3634 	}
3635 
3636 	drmach_setup_memlist(prog);
3637 
3638 	x_ml = c_ml;
3639 	sz = 0;
3640 	while (x_ml != NULL) {
3641 		sz += x_ml->size;
3642 		x_ml = x_ml->next;
3643 	}
3644 
3645 	copy_sz = sz/active_cpus;
3646 	copy_sz = roundup(copy_sz, MMU_PAGESIZE4M);
3647 
3648 	while (sz > copy_sz*active_cpus) {
3649 		copy_sz += MMU_PAGESIZE4M;
3650 	}
3651 
3652 	prog->data->stick_freq = system_clock_freq;
3653 	prog->data->copy_delay = ((copy_sz / min_copy_size_per_sec) + 2) *
3654 		system_clock_freq;
3655 
3656 	x_ml = c_ml;
3657 	c_addr = x_ml->address;
3658 	c_size = x_ml->size;
3659 
3660 	for (i = 0; i < NCPU; i++) {
3661 		prog->stat->nbytes[i] = 0;
3662 		if (!CPU_IN_SET(prog->data->cpu_copy_set, i)) {
3663 			continue;
3664 		}
3665 		sz = copy_sz;
3666 
3667 		while (sz) {
3668 			if (c_size > sz) {
3669 				prog->data->cpu_ml[i] =
3670 					drmach_memlist_add_span(prog,
3671 					prog->data->cpu_ml[i],
3672 					c_addr, sz);
3673 				c_addr += sz;
3674 				c_size -= sz;
3675 				break;
3676 			} else {
3677 				sz -= c_size;
3678 				prog->data->cpu_ml[i] = drmach_memlist_add_span(
3679 					prog, prog->data->cpu_ml[i],
3680 						c_addr, c_size);
3681 				x_ml = x_ml->next;
3682 				if (x_ml != NULL) {
3683 					c_addr = x_ml->address;
3684 					c_size = x_ml->size;
3685 				} else {
3686 					goto end;
3687 				}
3688 			}
3689 		}
3690 	}
3691 end:
3692 	prog->data->s_copybasepa = s_copybasepa;
3693 	prog->data->t_copybasepa = t_copybasepa;
3694 	prog->data->c_ml = c_ml;
3695 	*pgm_id = prog_kmem;
3696 
3697 	/* Unmap the alternate space.  It will have to be remapped again */
3698 	drmach_unlock_critical((caddr_t)prog);
3699 	return (NULL);
3700 out:
3701 	if (prog != NULL) {
3702 		drmach_unlock_critical((caddr_t)prog);
3703 		vmem_free(heap_arena, prog,
3704 			DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
3705 	}
3706 	if (prog_kmem != NULL) {
3707 		kmem_free(prog_kmem, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
3708 	}
3709 	return (err);
3710 }
3711 
3712 sbd_error_t *
3713 drmach_copy_rename_fini(drmachid_t id)
3714 {
3715 	drmach_copy_rename_program_t	*prog = id;
3716 	sbd_error_t			*err = NULL;
3717 	int				rv;
3718 
3719 	/*
3720 	 * Note that we have to delay calling SCF to find out the
3721 	 * status of the FMEM operation here because SCF cannot
3722 	 * respond while it is suspended.
3723 	 * This create a small window when we are sure about the
3724 	 * base address of the system board.
3725 	 * If there is any call to mc-opl to get memory unum,
3726 	 * mc-opl will return UNKNOWN as the unum.
3727 	 */
3728 
3729 	/*
3730 	 * we have to remap again because all the pointer like data,
3731 	 * critical in prog are based on the alternate vmem space.
3732 	 */
3733 	(void) drmach_lock_critical((caddr_t)prog, (caddr_t)prog->locked_prog);
3734 
3735 	if (prog->data->c_ml != NULL)
3736 		memlist_delete(prog->data->c_ml);
3737 
3738 	if ((prog->data->fmem_status.op &
3739 		(OPL_FMEM_SCF_START| OPL_FMEM_MC_SUSPEND)) !=
3740 		(OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) {
3741 		cmn_err(CE_PANIC, "drmach_copy_rename_fini: "
3742 			"invalid op code %x\n",
3743 				prog->data->fmem_status.op);
3744 	}
3745 
3746 	/* possible ops are SCF_START, MC_SUSPEND */
3747 	if (prog->critical->fmem_issued) {
3748 		if (prog->data->fmem_status.error != FMEM_NO_ERROR)
3749 			cmn_err(CE_PANIC, "scf fmem request failed. "
3750 				"error code = 0x%x.",
3751 				prog->data->fmem_status.error);
3752 		rv = (*prog->data->scf_fmem_end)();
3753 		if (rv) {
3754 			cmn_err(CE_PANIC, "scf_fmem_end() failed rv=%d", rv);
3755 		}
3756 		/*
3757 		 * If we get here, rename is successful.
3758 		 * Do all the copy rename post processing.
3759 		 */
3760 		drmach_swap_pa((drmach_mem_t *)prog->data->s_mem,
3761 			(drmach_mem_t *)prog->data->t_mem);
3762 	} else {
3763 		if (prog->data->fmem_status.error != 0) {
3764 			cmn_err(CE_WARN, "Kernel Migration fails. 0x%x",
3765 				prog->data->fmem_status.error);
3766 			err = drerr_new(1, EOPL_FMEM_ERROR, "FMEM error = 0x%x",
3767 				prog->data->fmem_status.error);
3768 		}
3769 		rv = (*prog->data->scf_fmem_cancel)();
3770 		if (rv) {
3771 		    cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x", rv);
3772 		    if (!err)
3773 			err = drerr_new(1, EOPL_SCF_FMEM_CANCEL,
3774 			    "rv = 0x%x", rv);
3775 		}
3776 	}
3777 	/* soft resume mac patrol */
3778 	(*prog->data->mc_resume)();
3779 
3780 	drmach_unlock_critical((caddr_t)prog->locked_prog);
3781 
3782 	vmem_free(heap_arena, prog->locked_prog,
3783 		DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
3784 	kmem_free(prog, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
3785 	return (err);
3786 }
3787 
3788 /*ARGSUSED*/
3789 static void
3790 drmach_copy_rename_slave(struct regs *rp, drmachid_t id)
3791 {
3792 	drmach_copy_rename_program_t	*prog =
3793 		(drmach_copy_rename_program_t *)id;
3794 	register int			cpuid;
3795 	extern void			drmach_flush();
3796 	extern void			membar_sync_il();
3797 	extern void			drmach_flush_icache();
3798 	on_trap_data_t			otd;
3799 
3800 	cpuid = CPU->cpu_id;
3801 
3802 	if (on_trap(&otd, OT_DATA_EC)) {
3803 		no_trap();
3804 		prog->data->error[cpuid] = FMEM_COPY_ERROR;
3805 		prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
3806 		drmach_flush_icache();
3807 		membar_sync_il();
3808 		return;
3809 	}
3810 
3811 
3812 	/*
3813 	 * jmp drmach_copy_rename_prog().
3814 	 */
3815 
3816 	drmach_flush(prog->critical, PAGESIZE);
3817 	(void) prog->critical->run(prog, cpuid);
3818 	drmach_flush_icache();
3819 
3820 	no_trap();
3821 
3822 	prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
3823 
3824 	membar_sync_il();
3825 }
3826 
3827 static void
3828 drmach_swap_pa(drmach_mem_t *s_mem, drmach_mem_t *t_mem)
3829 {
3830 	uint64_t s_base, t_base;
3831 	drmach_board_t *s_board, *t_board;
3832 	struct memlist *ml;
3833 
3834 	s_board = s_mem->dev.bp;
3835 	t_board = t_mem->dev.bp;
3836 	if (s_board == NULL || t_board == NULL) {
3837 		cmn_err(CE_PANIC, "Cannot locate source or target board\n");
3838 		return;
3839 	}
3840 	s_base = s_mem->slice_base;
3841 	t_base = t_mem->slice_base;
3842 
3843 	s_mem->slice_base = t_base;
3844 	s_mem->base_pa = (s_mem->base_pa - s_base) + t_base;
3845 
3846 	for (ml = s_mem->memlist; ml; ml = ml->next) {
3847 		ml->address = ml->address - s_base + t_base;
3848 	}
3849 
3850 	t_mem->slice_base = s_base;
3851 	t_mem->base_pa = (t_mem->base_pa - t_base) + s_base;
3852 
3853 	for (ml = t_mem->memlist; ml; ml = ml->next) {
3854 		ml->address = ml->address - t_base + s_base;
3855 	}
3856 
3857 	/*
3858 	 * IKP has to update the sb-mem-ranges for mac patrol driver
3859 	 * when it resumes, it will re-read the sb-mem-range property
3860 	 * to get the new base address
3861 	 */
3862 	if (oplcfg_pa_swap(s_board->bnum, t_board->bnum) != 0)
3863 		cmn_err(CE_PANIC, "Could not update device nodes\n");
3864 }
3865 
3866 void
3867 drmach_copy_rename(drmachid_t id)
3868 {
3869 	drmach_copy_rename_program_t	*prog_kmem = id;
3870 	drmach_copy_rename_program_t	*prog;
3871 	cpuset_t	cpuset;
3872 	int		cpuid;
3873 	uint64_t	inst;
3874 	register int	rtn;
3875 	extern int	in_sync;
3876 	int		old_in_sync;
3877 	extern void	drmach_sys_trap();
3878 	extern void	drmach_flush();
3879 	extern void	drmach_flush_icache();
3880 	extern uint64_t	patch_inst(uint64_t *, uint64_t);
3881 	on_trap_data_t	otd;
3882 
3883 
3884 	prog = prog_kmem->locked_prog;
3885 
3886 
3887 	/*
3888 	 * We must immediately drop in the TLB because all pointers
3889 	 * are based on the alternate vmem space.
3890 	 */
3891 
3892 	(void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
3893 
3894 	/*
3895 	 * we call scf to get the base address here becuase if scf
3896 	 * has not been suspended yet, the active path can be changing and
3897 	 * sometimes it is not even mapped.  We call the interface when
3898 	 * the OS has been quiesced.
3899 	 */
3900 	prog->critical->scf_reg_base = (*prog->data->scf_get_base_addr)();
3901 
3902 	if (prog->critical->scf_reg_base == (uint64_t)-1 ||
3903 		prog->critical->scf_reg_base == NULL) {
3904 		prog->data->fmem_status.error = FMEM_SCF_ERR;
3905 		drmach_unlock_critical((caddr_t)prog);
3906 		return;
3907 	}
3908 
3909 	cpuset = prog->data->cpu_ready_set;
3910 
3911 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
3912 		if (CPU_IN_SET(cpuset, cpuid)) {
3913 			prog->critical->stat[cpuid] = FMEM_LOOP_START;
3914 			prog->data->error[cpuid] = FMEM_NO_ERROR;
3915 		}
3916 	}
3917 
3918 	old_in_sync = in_sync;
3919 	in_sync = 1;
3920 	cpuid = CPU->cpu_id;
3921 
3922 	CPUSET_DEL(cpuset, cpuid);
3923 
3924 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
3925 		if (CPU_IN_SET(cpuset, cpuid)) {
3926 			xc_one(cpuid, (xcfunc_t *)drmach_lock_critical,
3927 				(uint64_t)prog_kmem, (uint64_t)prog);
3928 		}
3929 	}
3930 
3931 	cpuid = CPU->cpu_id;
3932 
3933 	xt_some(cpuset, (xcfunc_t *)drmach_sys_trap,
3934 				(uint64_t)drmach_copy_rename_slave,
3935 				(uint64_t)prog);
3936 	xt_sync(cpuset);
3937 
3938 	if (on_trap(&otd, OT_DATA_EC)) {
3939 		rtn = FMEM_COPY_ERROR;
3940 		drmach_flush_icache();
3941 		goto done;
3942 	}
3943 
3944 	/*
3945 	 * jmp drmach_copy_rename_prog().
3946 	 */
3947 
3948 	drmach_flush(prog->critical, PAGESIZE);
3949 	rtn = prog->critical->run(prog, cpuid);
3950 	drmach_flush_icache();
3951 
3952 
3953 done:
3954 	no_trap();
3955 	if (rtn == FMEM_HW_ERROR) {
3956 		kpreempt_enable();
3957 		prom_panic("URGENT_ERROR_TRAP is "
3958 			"detected during FMEM.\n");
3959 	}
3960 
3961 	/*
3962 	 * In normal case, all slave CPU's are still spinning in
3963 	 * the assembly code.  The master has to patch the instruction
3964 	 * to get them out.
3965 	 * In error case, e.g. COPY_ERROR, some slave CPU's might
3966 	 * have aborted and already returned and sset LOOP_EXIT status.
3967 	 * Some CPU might still be copying.
3968 	 * In any case, some delay is necessary to give them
3969 	 * enough time to set the LOOP_EXIT status.
3970 	 */
3971 
3972 	for (;;) {
3973 		inst = patch_inst((uint64_t *)prog->critical->loop_rtn,
3974 			prog->critical->inst_loop_ret);
3975 		if (prog->critical->inst_loop_ret == inst) {
3976 			break;
3977 		}
3978 	}
3979 
3980 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
3981 		uint64_t	last, now;
3982 		if (!CPU_IN_SET(cpuset, cpuid)) {
3983 			continue;
3984 		}
3985 		last = prog->stat->nbytes[cpuid];
3986 		/*
3987 		 * Wait for all CPU to exit.
3988 		 * However we do not want an infinite loop
3989 		 * so we detect hangup situation here.
3990 		 * If the slave CPU is still copying data,
3991 		 * we will continue to wait.
3992 		 * In error cases, the master has already set
3993 		 * fmem_status.error to abort the copying.
3994 		 * 1 m.s delay for them to abort copying and
3995 		 * return to drmach_copy_rename_slave to set
3996 		 * FMEM_LOOP_EXIT status should be enough.
3997 		 */
3998 		for (;;) {
3999 			if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT)
4000 				break;
4001 			drmach_sleep_il();
4002 			drv_usecwait(1000);
4003 			now = prog->stat->nbytes[cpuid];
4004 			if (now <= last) {
4005 			    drv_usecwait(1000);
4006 			    if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT)
4007 				break;
4008 			    cmn_err(CE_PANIC,
4009 				"CPU %d hang during Copy Rename", cpuid);
4010 			}
4011 			last = now;
4012 		}
4013 		if (prog->data->error[cpuid] == FMEM_HW_ERROR) {
4014 			prom_panic("URGENT_ERROR_TRAP is "
4015 				"detected during FMEM.\n");
4016 		}
4017 	}
4018 
4019 	/*
4020 	 * This must be done after all strands have exit.
4021 	 * Removing the TLB entry will affect both strands
4022 	 * in the same core.
4023 	 */
4024 
4025 	for (cpuid = 0; cpuid < NCPU; cpuid++) {
4026 		if (CPU_IN_SET(cpuset, cpuid)) {
4027 			xc_one(cpuid, (xcfunc_t *)drmach_unlock_critical,
4028 				(uint64_t)prog, 0);
4029 		}
4030 	}
4031 
4032 	in_sync = old_in_sync;
4033 
4034 	/*
4035 	 * we should unlock before the following lock to keep the kpreempt
4036 	 * count correct.
4037 	 */
4038 	(void) drmach_unlock_critical((caddr_t)prog);
4039 
4040 	/*
4041 	 * we must remap again.  TLB might have been removed in above xcall.
4042 	 */
4043 
4044 	(void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
4045 
4046 	if (prog->data->fmem_status.error == 0)
4047 		prog->data->fmem_status.error = rtn;
4048 
4049 	if (prog->data->copy_wait_time > 0) {
4050 		DRMACH_PR("Unexpected long wait time %ld seconds "
4051 			"during copy rename on CPU %d\n",
4052 			prog->data->copy_wait_time/prog->data->stick_freq,
4053 			prog->data->slowest_cpuid);
4054 	}
4055 	drmach_unlock_critical((caddr_t)prog);
4056 }
4057