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