xref: /titanic_52/usr/src/uts/sun4u/starfire/io/drmach.c (revision d58fda4376e4bf67072ce2e69f6f47036f9dbb68)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/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/processor.h>
47 #include <sys/spitregs.h>
48 #include <sys/cpuvar.h>
49 #include <sys/cpupart.h>
50 #include <sys/mem_config.h>
51 #include <sys/ddi_impldefs.h>
52 #include <sys/systm.h>
53 #include <sys/machsystm.h>
54 #include <sys/autoconf.h>
55 #include <sys/cmn_err.h>
56 #include <sys/sysmacros.h>
57 #include <sys/x_call.h>
58 #include <sys/promif.h>
59 #include <sys/prom_plat.h>
60 #include <sys/membar.h>
61 #include <vm/seg_kmem.h>
62 #include <sys/mem_cage.h>
63 #include <sys/stack.h>
64 #include <sys/archsystm.h>
65 #include <vm/hat_sfmmu.h>
66 #include <sys/pte.h>
67 #include <sys/mmu.h>
68 #include <sys/cpu_module.h>
69 #include <sys/obpdefs.h>
70 #include <sys/note.h>
71 
72 #include <sys/starfire.h>	/* plat_max_... decls */
73 #include <sys/cvc.h>
74 #include <sys/cpu_sgnblk_defs.h>
75 #include <sys/drmach.h>
76 #include <sys/dr_util.h>
77 #include <sys/pda.h>
78 
79 #include <sys/sysevent.h>
80 #include <sys/sysevent/dr.h>
81 #include <sys/sysevent/eventdefs.h>
82 
83 
84 extern void		bcopy32_il(uint64_t, uint64_t);
85 extern void		flush_ecache_il(
86 				uint64_t physaddr, int size, int linesz);
87 extern uint_t		ldphysio_il(uint64_t physaddr);
88 extern void		stphysio_il(uint64_t physaddr, uint_t value);
89 
90 extern uint64_t		mc_get_mem_alignment(void);
91 extern uint64_t		mc_get_asr_addr(pnode_t);
92 extern uint64_t		mc_get_idle_addr(pnode_t);
93 extern uint64_t		mc_get_alignment_mask(pnode_t);
94 extern int		mc_read_asr(pnode_t, uint_t *);
95 extern int		mc_write_asr(pnode_t, uint_t);
96 extern uint64_t		mc_asr_to_pa(uint_t);
97 extern uint_t		mc_pa_to_asr(uint_t, uint64_t);
98 
99 extern int		pc_madr_add(int, int, int, int);
100 
101 typedef struct {
102 	struct drmach_node	*node;
103 	void			*data;
104 } drmach_node_walk_args_t;
105 
106 typedef struct drmach_node {
107 	void		*here;
108 
109 	pnode_t		 (*get_dnode)(struct drmach_node *node);
110 	int		 (*walk)(struct drmach_node *node, void *data,
111 				int (*cb)(drmach_node_walk_args_t *args));
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 	sbd_error_t	*(*release)(drmachid_t);
125 	sbd_error_t	*(*status)(drmachid_t, drmach_status_t *);
126 
127 	char		 name[MAXNAMELEN];
128 } drmach_common_t;
129 
130 typedef struct {
131 	drmach_common_t	 cm;
132 	int		 bnum;
133 	int		 assigned;
134 	int		 powered;
135 	int		 connect_cpuid;
136 	int		 cond;
137 	drmach_node_t	*tree;
138 	drmach_array_t	*devices;
139 } drmach_board_t;
140 
141 typedef struct {
142 	drmach_common_t	 cm;
143 	drmach_board_t	*bp;
144 	int		 unum;
145 	int		 busy;
146 	int		 powered;
147 	const char	*type;
148 	drmach_node_t	*node;
149 } drmach_device_t;
150 
151 typedef struct {
152 	int		 flags;
153 	drmach_device_t	*dp;
154 	sbd_error_t	*err;
155 	dev_info_t	*dip;
156 } drmach_config_args_t;
157 
158 typedef struct {
159 	uint64_t	 idle_addr;
160 	drmach_device_t	*mem;
161 } drmach_mc_idle_script_t;
162 
163 typedef struct {
164 	uint64_t	masr_addr;
165 	uint_t		masr;
166 	uint_t		_filler;
167 } drmach_rename_script_t;
168 
169 typedef struct {
170 	void		(*run)(void *arg);
171 	caddr_t		data;
172 	pda_handle_t	*ph;
173 	struct memlist	*c_ml;
174 	uint64_t	s_copybasepa;
175 	uint64_t	t_copybasepa;
176 	drmach_device_t	*restless_mc;	/* diagnostic output */
177 } drmach_copy_rename_program_t;
178 
179 typedef enum {
180 	DO_IDLE,
181 	DO_UNIDLE,
182 	DO_PAUSE,
183 	DO_UNPAUSE
184 } drmach_iopc_op_t;
185 
186 typedef struct {
187 	drmach_board_t	*obj;
188 	int		 ndevs;
189 	void		*a;
190 	sbd_error_t	*(*found)(void *a, const char *, int, drmachid_t);
191 	sbd_error_t	*err;
192 } drmach_board_cb_data_t;
193 
194 static caddr_t		 drmach_shutdown_va;
195 
196 static int		 drmach_initialized;
197 static drmach_array_t	*drmach_boards;
198 
199 static int		 drmach_cpu_delay = 100;
200 static int		 drmach_cpu_ntries = 50000;
201 
202 volatile uchar_t	*drmach_xt_mb;
203 
204 /*
205  * Do not change the drmach_shutdown_mbox structure without
206  * considering the drmach_shutdown_asm assembly language code.
207  */
208 struct drmach_shutdown_mbox {
209 	uint64_t	estack;
210 	uint64_t	flushaddr;
211 	int		size;
212 	int		linesize;
213 	uint64_t	physaddr;
214 };
215 struct drmach_shutdown_mbox	*drmach_shutdown_asm_mbox;
216 static sbd_error_t	*drmach_device_new(drmach_node_t *,
217 				drmach_board_t *, drmach_device_t **);
218 static sbd_error_t	*drmach_cpu_new(drmach_device_t *);
219 static sbd_error_t	*drmach_mem_new(drmach_device_t *);
220 static sbd_error_t	*drmach_io_new(drmach_device_t *);
221 
222 extern struct cpu	*SIGBCPU;
223 
224 #ifdef DEBUG
225 
226 #define	DRMACH_PR		if (drmach_debug) printf
227 int drmach_debug = 0;		 /* set to non-zero to enable debug messages */
228 #else
229 
230 #define	DRMACH_PR		_NOTE(CONSTANTCONDITION) if (0) printf
231 #endif /* DEBUG */
232 
233 #define	DRMACH_OBJ(id)		((drmach_common_t *)id)
234 
235 #define	DRMACH_IS_BOARD_ID(id)	\
236 	((id != 0) &&		\
237 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
238 
239 #define	DRMACH_IS_CPU_ID(id)	\
240 	((id != 0) &&		\
241 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
242 
243 #define	DRMACH_IS_MEM_ID(id)	\
244 	((id != 0) &&		\
245 	(DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
246 
247 #define	DRMACH_IS_IO_ID(id)	\
248 	((id != 0) &&		\
249 	(DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
250 
251 #define	DRMACH_IS_DEVICE_ID(id)					\
252 	((id != 0) &&						\
253 	(DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
254 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
255 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
256 
257 #define	DRMACH_IS_ID(id)					\
258 	((id != 0) &&						\
259 	(DRMACH_OBJ(id)->isa == (void *)drmach_board_new ||	\
260 	    DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new ||	\
261 	    DRMACH_OBJ(id)->isa == (void *)drmach_mem_new ||	\
262 	    DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
263 
264 #define	DRMACH_CPUID2BNUM(cpuid) \
265 	((cpuid) / MAX_CPU_UNITS_PER_BOARD)
266 
267 #define	DRMACH_INTERNAL_ERROR() \
268 	drerr_new(1, ESTF_INTERNAL, drmach_ie_fmt, __LINE__)
269 static char		*drmach_ie_fmt = "drmach.c %d";
270 
271 static struct {
272 	const char	 *name;
273 	const char	 *type;
274 	sbd_error_t	 *(*new)(drmach_device_t *);
275 } name2type[] = {
276 	{ "SUNW,UltraSPARC",	DRMACH_DEVTYPE_CPU,  drmach_cpu_new },
277 	{ "mem-unit",		DRMACH_DEVTYPE_MEM,  drmach_mem_new },
278 	{ "pci",		DRMACH_DEVTYPE_PCI,  drmach_io_new  },
279 	{ "sbus",		DRMACH_DEVTYPE_SBUS, drmach_io_new  },
280 };
281 
282 /* node types to cleanup when a board is unconfigured */
283 #define	MISC_COUNTER_TIMER_DEVNAME	"counter-timer"
284 #define	MISC_PERF_COUNTER_DEVNAME	"perf-counter"
285 
286 /* utility */
287 #define	MBYTE	(1048576ull)
288 
289 /*
290  * This is necessary because the CPU support needs
291  * to call cvc_assign_iocpu.
292  */
293 #ifndef lint
294 static char _depends_on[] = "drv/cvc";
295 #endif  /* lint */
296 
297 /*
298  * drmach autoconfiguration data structures and interfaces
299  */
300 
301 extern struct mod_ops mod_miscops;
302 
303 static struct modlmisc modlmisc = {
304 	&mod_miscops,
305 	"Sun Enterprise 10000 DR %I%"
306 };
307 
308 static struct modlinkage modlinkage = {
309 	MODREV_1,
310 	(void *)&modlmisc,
311 	NULL
312 };
313 
314 static kmutex_t drmach_i_lock;
315 
316 int
317 _init(void)
318 {
319 	int err;
320 
321 	/* check that we have the correct version of obp */
322 	if (prom_test("SUNW,UE10000,add-brd") != 0) {
323 
324 		cmn_err(CE_WARN, "!OBP/SSP upgrade is required to enable "
325 		    "DR Functionality");
326 
327 		return (-1);
328 	}
329 
330 	mutex_init(&drmach_i_lock, NULL, MUTEX_DRIVER, NULL);
331 
332 	drmach_xt_mb = (uchar_t *)vmem_alloc(static_alloc_arena,
333 	    NCPU * sizeof (uchar_t), VM_SLEEP);
334 	drmach_shutdown_asm_mbox = (struct drmach_shutdown_mbox *)
335 	    vmem_alloc(static_alloc_arena, sizeof (struct drmach_shutdown_mbox),
336 	    VM_SLEEP);
337 
338 	if ((err = mod_install(&modlinkage)) != 0) {
339 		mutex_destroy(&drmach_i_lock);
340 		vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
341 		    NCPU * sizeof (uchar_t));
342 		vmem_free(static_alloc_arena, (void *)drmach_shutdown_asm_mbox,
343 		    sizeof (struct drmach_shutdown_mbox));
344 	}
345 
346 	return (err);
347 }
348 
349 int
350 _fini(void)
351 {
352 	static int drmach_fini(void);
353 
354 	if (drmach_fini())
355 		return (DDI_FAILURE);
356 	else
357 		return (mod_remove(&modlinkage));
358 }
359 
360 int
361 _info(struct modinfo *modinfop)
362 {
363 	return (mod_info(&modlinkage, modinfop));
364 }
365 
366 static pnode_t
367 drmach_node_obp_get_dnode(drmach_node_t *np)
368 {
369 	return ((pnode_t)np->here);
370 }
371 
372 static int
373 drmach_node_obp_walk(drmach_node_t *np, void *data,
374 		int (*cb)(drmach_node_walk_args_t *args))
375 {
376 	pnode_t			nodeid;
377 	int			rv;
378 	drmach_node_walk_args_t	args;
379 
380 	/* initialized args structure for callback */
381 	args.node = np;
382 	args.data = data;
383 
384 	nodeid = prom_childnode(prom_rootnode());
385 
386 	/* save our new position with in the tree */
387 	np->here = (void *)nodeid;
388 
389 	rv = 0;
390 	while (nodeid != OBP_NONODE) {
391 		rv = (*cb)(&args);
392 		if (rv)
393 			break;
394 
395 		nodeid = prom_nextnode(nodeid);
396 
397 		/* save our new position with in the tree */
398 		np->here = (void *)nodeid;
399 	}
400 
401 	return (rv);
402 }
403 
404 static drmach_node_t *
405 drmach_node_new(void)
406 {
407 	drmach_node_t *np;
408 
409 	np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
410 
411 	np->get_dnode = drmach_node_obp_get_dnode;
412 	np->walk = drmach_node_obp_walk;
413 
414 	return (np);
415 }
416 
417 static void
418 drmach_node_dispose(drmach_node_t *np)
419 {
420 	kmem_free(np, sizeof (*np));
421 }
422 
423 static dev_info_t *
424 drmach_node_get_dip(drmach_node_t *np)
425 {
426 	pnode_t nodeid;
427 
428 	nodeid = np->get_dnode(np);
429 	if (nodeid == OBP_NONODE)
430 		return (NULL);
431 	else {
432 		dev_info_t *dip;
433 
434 		/* The root node doesn't have to be held */
435 		dip = e_ddi_nodeid_to_dip(nodeid);
436 		if (dip) {
437 			/*
438 			 * Branch rooted at dip is already held, so release
439 			 * hold acquired in e_ddi_nodeid_to_dip()
440 			 */
441 			ddi_release_devi(dip);
442 			ASSERT(e_ddi_branch_held(dip));
443 		}
444 
445 		return (dip);
446 	}
447 	/*NOTREACHED*/
448 }
449 
450 static pnode_t
451 drmach_node_get_dnode(drmach_node_t *np)
452 {
453 	return (np->get_dnode(np));
454 }
455 
456 static int
457 drmach_node_walk(drmach_node_t *np, void *param,
458 		int (*cb)(drmach_node_walk_args_t *args))
459 {
460 	return (np->walk(np, param, cb));
461 }
462 
463 static int
464 drmach_node_get_prop(drmach_node_t *np, char *name, void *buf)
465 {
466 	pnode_t	nodeid;
467 	int	rv;
468 
469 	nodeid = np->get_dnode(np);
470 	if (nodeid == OBP_NONODE)
471 		rv = -1;
472 	else if (prom_getproplen(nodeid, (caddr_t)name) < 0)
473 		rv = -1;
474 	else {
475 		(void) prom_getprop(nodeid, (caddr_t)name, (caddr_t)buf);
476 		rv = 0;
477 	}
478 
479 	return (rv);
480 }
481 
482 static int
483 drmach_node_get_proplen(drmach_node_t *np, char *name, int *len)
484 {
485 	pnode_t	 nodeid;
486 	int	 rv;
487 
488 	nodeid = np->get_dnode(np);
489 	if (nodeid == OBP_NONODE)
490 		rv = -1;
491 	else {
492 		*len = prom_getproplen(nodeid, (caddr_t)name);
493 		rv = (*len < 0 ? -1 : 0);
494 	}
495 
496 	return (rv);
497 }
498 
499 static drmachid_t
500 drmach_node_dup(drmach_node_t *np)
501 {
502 	drmach_node_t *dup;
503 
504 	dup = drmach_node_new();
505 	dup->here = np->here;
506 
507 	return (dup);
508 }
509 
510 /*
511  * drmach_array provides convenient array construction, access,
512  * bounds checking and array destruction logic.
513  */
514 
515 static drmach_array_t *
516 drmach_array_new(int min_index, int max_index)
517 {
518 	drmach_array_t *arr;
519 
520 	arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
521 
522 	arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
523 	if (arr->arr_sz > 0) {
524 		arr->min_index = min_index;
525 		arr->max_index = max_index;
526 
527 		arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
528 		return (arr);
529 	} else {
530 		kmem_free(arr, sizeof (*arr));
531 		return (0);
532 	}
533 }
534 
535 static int
536 drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
537 {
538 	if (idx < arr->min_index || idx > arr->max_index)
539 		return (-1);
540 	else {
541 		arr->arr[idx - arr->min_index] = val;
542 		return (0);
543 	}
544 	/*NOTREACHED*/
545 }
546 
547 static int
548 drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
549 {
550 	if (idx < arr->min_index || idx > arr->max_index)
551 		return (-1);
552 	else {
553 		*val = arr->arr[idx - arr->min_index];
554 		return (0);
555 	}
556 	/*NOTREACHED*/
557 }
558 
559 static int
560 drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
561 {
562 	int rv;
563 
564 	*idx = arr->min_index;
565 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
566 		*idx += 1;
567 
568 	return (rv);
569 }
570 
571 static int
572 drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
573 {
574 	int rv;
575 
576 	*idx += 1;
577 	while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
578 		*idx += 1;
579 
580 	return (rv);
581 }
582 
583 static void
584 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
585 {
586 	drmachid_t	val;
587 	int		idx;
588 	int		rv;
589 
590 	rv = drmach_array_first(arr, &idx, &val);
591 	while (rv == 0) {
592 		(*disposer)(val);
593 		rv = drmach_array_next(arr, &idx, &val);
594 	}
595 
596 	kmem_free(arr->arr, arr->arr_sz);
597 	kmem_free(arr, sizeof (*arr));
598 }
599 
600 /*ARGSUSED*/
601 static int
602 drmach_prom_select(pnode_t nodeid, void *arg, uint_t flags)
603 {
604 	int			rprop[64];
605 	pnode_t			saved;
606 	drmach_config_args_t	*ap = (drmach_config_args_t *)arg;
607 	drmach_device_t		*dp = ap->dp;
608 	sbd_error_t		*err;
609 
610 	saved = drmach_node_get_dnode(dp->node);
611 
612 	if (nodeid != saved)
613 		return (DDI_FAILURE);
614 
615 	if (saved == OBP_NONODE) {
616 		err = DRMACH_INTERNAL_ERROR();
617 		DRERR_SET_C(&ap->err, &err);
618 		return (DDI_FAILURE);
619 	}
620 
621 	if (prom_getprop(nodeid, OBP_REG, (caddr_t)rprop) <= 0) {
622 		return (DDI_FAILURE);
623 	}
624 
625 	return (DDI_SUCCESS);
626 }
627 
628 /*ARGSUSED*/
629 static void
630 drmach_branch_callback(dev_info_t *rdip, void *arg, uint_t flags)
631 {
632 	drmach_config_args_t	*ap = (drmach_config_args_t *)arg;
633 
634 	ASSERT(ap->dip == NULL);
635 
636 	ap->dip = rdip;
637 }
638 
639 sbd_error_t *
640 drmach_configure(drmachid_t id, int flags)
641 {
642 	drmach_device_t		*dp;
643 	sbd_error_t		*err;
644 	drmach_config_args_t	ca;
645 	devi_branch_t		b = {0};
646 	dev_info_t		*fdip = NULL;
647 
648 	if (!DRMACH_IS_DEVICE_ID(id))
649 		return (drerr_new(0, ESTF_INAPPROP, NULL));
650 	dp = id;
651 
652 	ca.dp = dp;
653 	ca.flags = flags;
654 	ca.err = NULL;		/* will be set if error detected */
655 	ca.dip = NULL;
656 
657 	b.arg = &ca;
658 	b.type = DEVI_BRANCH_PROM;
659 	b.create.prom_branch_select = drmach_prom_select;
660 	b.devi_branch_callback = drmach_branch_callback;
661 
662 	if (e_ddi_branch_create(ddi_root_node(), &b, &fdip,
663 	    DEVI_BRANCH_CHILD | DEVI_BRANCH_CONFIGURE) != 0) {
664 		char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
665 
666 		/*
667 		 * If non-NULL, fdip is returned held and must be released.
668 		 */
669 		if (fdip != NULL) {
670 			(void) ddi_pathname(fdip, path);
671 			ddi_release_devi(fdip);
672 		} else if (ca.dip != NULL) {
673 			/* safe to call ddi_pathname as dip already held */
674 			(void) ddi_pathname(ca.dip, path);
675 		} else {
676 			(void) strcpy(path, "<none>");
677 		}
678 
679 		err = drerr_new(1, ESTF_DRVFAIL, path);
680 		DRERR_SET_C(&ca.err, &err);
681 		kmem_free(path, MAXPATHLEN);
682 	}
683 
684 	return (ca.err);
685 }
686 
687 static sbd_error_t *
688 drmach_device_new(drmach_node_t *node,
689 	drmach_board_t *bp, drmach_device_t **dpp)
690 {
691 	int		 i;
692 	int		 rv;
693 	drmach_device_t	*dp;
694 	sbd_error_t	*err;
695 	char		 name[OBP_MAXDRVNAME];
696 
697 	rv = drmach_node_get_prop(node, OBP_NAME, name);
698 	if (rv) {
699 		/* every node is expected to have a name */
700 		err = drerr_new(1, ESTF_GETPROP,
701 			"PROM Node 0x%x: property %s",
702 			(uint_t)node->get_dnode(node), OBP_NAME);
703 
704 		return (err);
705 	}
706 
707 	/*
708 	 * The node currently being examined is not listed in the name2type[]
709 	 * array.  In this case, the node is no interest to drmach.  Both
710 	 * dp and err are initialized here to yield nothing (no device or
711 	 * error structure) for this case.
712 	 */
713 	for (i = 0; i < sizeof (name2type) / sizeof (name2type[0]); i++)
714 		if (strcmp(name2type[i].name, name) == 0)
715 			break;
716 
717 	if (i < sizeof (name2type) / sizeof (name2type[0])) {
718 		dp = kmem_zalloc(sizeof (drmach_device_t), KM_SLEEP);
719 
720 		dp->bp = bp;
721 		dp->unum = -1;
722 		dp->node = drmach_node_dup(node);
723 		dp->type = name2type[i].type;
724 
725 		err = (name2type[i].new)(dp);
726 		if (err) {
727 			drmach_node_dispose(node);
728 			kmem_free(dp, sizeof (*dp));
729 			dp = NULL;
730 		}
731 
732 		*dpp = dp;
733 		return (err);
734 	}
735 
736 	/*
737 	 * The node currently being examined is not listed in the name2type[]
738 	 * array.  In this case, the node is no interest to drmach.  Both
739 	 * dp and err are initialized here to yield nothing (no device or
740 	 * error structure) for this case.
741 	 */
742 	*dpp = NULL;
743 	return (NULL);
744 }
745 
746 static void
747 drmach_device_dispose(drmachid_t id)
748 {
749 	drmach_device_t *self = id;
750 
751 	if (self->node)
752 		drmach_node_dispose(self->node);
753 
754 	kmem_free(self, sizeof (*self));
755 }
756 
757 static sbd_error_t *
758 drmach_device_get_prop(drmach_device_t *dp, char *name, void *buf)
759 {
760 	sbd_error_t	*err = NULL;
761 	int		 rv;
762 
763 	rv = drmach_node_get_prop(dp->node, name, buf);
764 	if (rv) {
765 		err = drerr_new(1, ESTF_GETPROP,
766 			"%s::%s: property %s",
767 			dp->bp->cm.name, dp->cm.name, name);
768 	}
769 
770 	return (err);
771 }
772 
773 static sbd_error_t *
774 drmach_device_get_proplen(drmach_device_t *dp, char *name, int *len)
775 {
776 	sbd_error_t	*err = NULL;
777 	int		 rv;
778 
779 	rv = drmach_node_get_proplen(dp->node, name, len);
780 	if (rv) {
781 		err = drerr_new(1, ESTF_GETPROPLEN,
782 			"%s::%s: property %s",
783 			dp->bp->cm.name, dp->cm.name, name);
784 	}
785 
786 	return (err);
787 }
788 
789 static drmach_board_t *
790 drmach_board_new(int bnum)
791 {
792 	static sbd_error_t *drmach_board_release(drmachid_t);
793 	static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
794 
795 	drmach_board_t	*bp;
796 
797 	bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
798 
799 	bp->cm.isa = (void *)drmach_board_new;
800 	bp->cm.release = drmach_board_release;
801 	bp->cm.status = drmach_board_status;
802 
803 	(void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
804 
805 	bp->bnum = bnum;
806 	bp->devices = NULL;
807 	bp->connect_cpuid = -1;
808 	bp->tree = drmach_node_new();
809 	bp->assigned = !drmach_initialized;
810 	bp->powered = !drmach_initialized;
811 
812 	drmach_array_set(drmach_boards, bnum, bp);
813 	return (bp);
814 }
815 
816 static void
817 drmach_board_dispose(drmachid_t id)
818 {
819 	drmach_board_t *bp;
820 
821 	ASSERT(DRMACH_IS_BOARD_ID(id));
822 	bp = id;
823 
824 	if (bp->tree)
825 		drmach_node_dispose(bp->tree);
826 
827 	if (bp->devices)
828 		drmach_array_dispose(bp->devices, drmach_device_dispose);
829 
830 	kmem_free(bp, sizeof (*bp));
831 }
832 
833 static sbd_error_t *
834 drmach_board_status(drmachid_t id, drmach_status_t *stat)
835 {
836 	sbd_error_t	*err = NULL;
837 	drmach_board_t	*bp;
838 
839 	if (!DRMACH_IS_BOARD_ID(id))
840 		return (drerr_new(0, ESTF_INAPPROP, NULL));
841 	bp = id;
842 
843 	stat->assigned = bp->assigned;
844 	stat->powered = bp->powered;
845 	stat->busy = 0;			/* assume not busy */
846 	stat->configured = 0;		/* assume not configured */
847 	stat->empty = 0;
848 	stat->cond = bp->cond = SBD_COND_OK;
849 	strncpy(stat->type, "System Brd", sizeof (stat->type));
850 	stat->info[0] = '\0';
851 
852 	if (bp->devices) {
853 		int		 rv;
854 		int		 d_idx;
855 		drmachid_t	 d_id;
856 
857 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
858 		while (rv == 0) {
859 			drmach_status_t	d_stat;
860 
861 			err = drmach_status(d_id, &d_stat);
862 			if (err)
863 				break;
864 
865 			stat->busy |= d_stat.busy;
866 			stat->configured |= d_stat.configured;
867 
868 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
869 		}
870 	}
871 
872 	return (err);
873 }
874 
875 /* a simple routine to reduce redundancy of this common logic */
876 static pda_handle_t
877 drmach_pda_open(void)
878 {
879 	pda_handle_t ph;
880 
881 	ph = pda_open();
882 	if (ph == NULL) {
883 		/* catch in debug kernels */
884 		ASSERT(0);
885 		cmn_err(CE_WARN, "pda_open failed");
886 	}
887 
888 	return (ph);
889 }
890 
891 #ifdef DEBUG
892 int drmach_init_break = 0;
893 #endif
894 
895 static int
896 hold_rele_branch(dev_info_t *rdip, void *arg)
897 {
898 	int	i;
899 	int	*holdp = (int *)arg;
900 	char	*name = ddi_node_name(rdip);
901 
902 	/*
903 	 * For Starfire, we must be children of the root devinfo node
904 	 */
905 	ASSERT(ddi_get_parent(rdip) == ddi_root_node());
906 
907 	for (i = 0; i < sizeof (name2type) / sizeof (name2type[0]); i++)
908 		if (strcmp(name2type[i].name, name) == 0)
909 			break;
910 
911 	if (i == sizeof (name2type) / sizeof (name2type[0])) {
912 		/* Not of interest to us */
913 		return (DDI_WALK_PRUNECHILD);
914 	}
915 
916 	if (*holdp) {
917 		ASSERT(!e_ddi_branch_held(rdip));
918 		e_ddi_branch_hold(rdip);
919 	} else {
920 		ASSERT(e_ddi_branch_held(rdip));
921 		e_ddi_branch_rele(rdip);
922 	}
923 
924 	return (DDI_WALK_PRUNECHILD);
925 }
926 
927 static int
928 drmach_init(void)
929 {
930 	pnode_t		nodeid;
931 	dev_info_t	*rdip;
932 	int		hold, circ;
933 
934 #ifdef DEBUG
935 	if (drmach_init_break)
936 		debug_enter("drmach_init: drmach_init_break set\n");
937 #endif
938 	mutex_enter(&drmach_i_lock);
939 	if (drmach_initialized) {
940 		mutex_exit(&drmach_i_lock);
941 		return (0);
942 	}
943 
944 	drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
945 
946 	nodeid = prom_childnode(prom_rootnode());
947 	do {
948 		int		 bnum;
949 		drmachid_t	 id;
950 
951 		bnum = -1;
952 		(void) prom_getprop(nodeid, OBP_BOARDNUM, (caddr_t)&bnum);
953 		if (bnum == -1)
954 			continue;
955 
956 		if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
957 			cmn_err(CE_WARN, "OBP node 0x%x has"
958 				" invalid property value, %s=%d",
959 				nodeid, OBP_BOARDNUM, bnum);
960 
961 			/* clean up */
962 			drmach_array_dispose(
963 				drmach_boards, drmach_board_dispose);
964 
965 			mutex_exit(&drmach_i_lock);
966 			return (-1);
967 		} else if (id == NULL)
968 			(void) drmach_board_new(bnum);
969 	} while ((nodeid = prom_nextnode(nodeid)) != OBP_NONODE);
970 
971 	drmach_shutdown_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
972 
973 	/*
974 	 * Walk immediate children of devinfo root node and hold
975 	 * all devinfo branches of interest.
976 	 */
977 	hold = 1;
978 	rdip = ddi_root_node();
979 
980 	ndi_devi_enter(rdip, &circ);
981 	ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
982 	ndi_devi_exit(rdip, circ);
983 
984 	drmach_initialized = 1;
985 
986 	mutex_exit(&drmach_i_lock);
987 
988 	return (0);
989 }
990 
991 static int
992 drmach_fini(void)
993 {
994 	dev_info_t	*rdip;
995 	int		hold, circ;
996 
997 	if (drmach_initialized) {
998 		int		busy = 0;
999 		int		rv;
1000 		int		idx;
1001 		drmachid_t	id;
1002 
1003 		ASSERT(drmach_boards != NULL);
1004 
1005 		rv = drmach_array_first(drmach_boards, &idx, &id);
1006 		while (rv == 0) {
1007 			sbd_error_t	*err;
1008 			drmach_status_t stat;
1009 
1010 			err = drmach_board_status(id, &stat);
1011 			if (err) {
1012 				/* catch in debug kernels */
1013 				ASSERT(0);
1014 				sbd_err_clear(&err);
1015 				busy = 1;
1016 			} else
1017 				busy |= stat.busy;
1018 
1019 			rv = drmach_array_next(drmach_boards, &idx, &id);
1020 		}
1021 
1022 		if (busy)
1023 			return (-1);
1024 
1025 		drmach_array_dispose(drmach_boards, drmach_board_dispose);
1026 		drmach_boards = NULL;
1027 
1028 		vmem_free(heap_arena, drmach_shutdown_va, PAGESIZE);
1029 
1030 		/*
1031 		 * Walk immediate children of the root devinfo node
1032 		 * releasing holds acquired on branches in drmach_init()
1033 		 */
1034 		hold = 0;
1035 		rdip = ddi_root_node();
1036 
1037 		ndi_devi_enter(rdip, &circ);
1038 		ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
1039 		ndi_devi_exit(rdip, circ);
1040 
1041 		mutex_destroy(&drmach_i_lock);
1042 
1043 		drmach_initialized = 0;
1044 	}
1045 	if (drmach_xt_mb != NULL) {
1046 		vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
1047 		    NCPU * sizeof (uchar_t));
1048 	}
1049 	if (drmach_shutdown_asm_mbox != NULL) {
1050 		vmem_free(static_alloc_arena, (void *)drmach_shutdown_asm_mbox,
1051 		    sizeof (struct drmach_shutdown_mbox));
1052 	}
1053 	return (0);
1054 }
1055 
1056 static sbd_error_t *
1057 drmach_get_mc_asr_addr(drmachid_t id, uint64_t *pa)
1058 {
1059 	drmach_device_t	*dp;
1060 	pnode_t		nodeid;
1061 	uint64_t	addr;
1062 
1063 	if (!DRMACH_IS_MEM_ID(id))
1064 		return (drerr_new(0, ESTF_INAPPROP, NULL));
1065 	dp = id;
1066 
1067 	nodeid = drmach_node_get_dnode(dp->node);
1068 	if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
1069 		return (DRMACH_INTERNAL_ERROR());
1070 
1071 	addr = mc_get_asr_addr(nodeid);
1072 	if (addr == (uint64_t)-1)
1073 		return (DRMACH_INTERNAL_ERROR());
1074 
1075 	*pa = addr;
1076 	return (NULL);
1077 }
1078 
1079 static sbd_error_t *
1080 drmach_get_mc_idle_addr(drmachid_t id, uint64_t *pa)
1081 {
1082 	drmach_device_t	*dp;
1083 	pnode_t		nodeid;
1084 	uint64_t	addr;
1085 
1086 	if (!DRMACH_IS_MEM_ID(id))
1087 		return (drerr_new(0, ESTF_INAPPROP, NULL));
1088 	dp = id;
1089 
1090 	nodeid = drmach_node_get_dnode(dp->node);
1091 	if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
1092 		return (DRMACH_INTERNAL_ERROR());
1093 
1094 	addr = mc_get_idle_addr(nodeid);
1095 	if (addr == (uint64_t)-1)
1096 		return (DRMACH_INTERNAL_ERROR());
1097 
1098 	*pa = addr;
1099 	return (NULL);
1100 }
1101 
1102 static sbd_error_t *
1103 drmach_read_mc_asr(drmachid_t id, uint_t *mcregp)
1104 {
1105 	drmach_device_t	*dp;
1106 	pnode_t		 nodeid;
1107 	sbd_error_t	*err;
1108 
1109 	if (!DRMACH_IS_MEM_ID(id))
1110 		return (drerr_new(0, ESTF_INAPPROP, NULL));
1111 	dp = id;
1112 
1113 	nodeid = drmach_node_get_dnode(dp->node);
1114 	if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
1115 		err = DRMACH_INTERNAL_ERROR();
1116 	else if (mc_read_asr(nodeid, mcregp) == -1)
1117 		err = DRMACH_INTERNAL_ERROR();
1118 	else
1119 		err = NULL;
1120 
1121 	return (err);
1122 }
1123 
1124 static sbd_error_t *
1125 drmach_write_mc_asr(drmachid_t id, uint_t mcreg)
1126 {
1127 	drmach_device_t	*dp;
1128 	pnode_t		 nodeid;
1129 	sbd_error_t	*err;
1130 
1131 	if (!DRMACH_IS_MEM_ID(id))
1132 		return (drerr_new(0, ESTF_INAPPROP, NULL));
1133 	dp = id;
1134 
1135 	nodeid = drmach_node_get_dnode(dp->node);
1136 	if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
1137 		err = DRMACH_INTERNAL_ERROR();
1138 	else if (mc_write_asr(nodeid, mcreg) == -1)
1139 		err = DRMACH_INTERNAL_ERROR();
1140 	else
1141 		err = NULL;
1142 
1143 	return (err);
1144 }
1145 
1146 static struct memlist *
1147 memlist_add_span(struct memlist *mlist, uint64_t base, uint64_t len)
1148 {
1149 	struct memlist	*ml, *tl, *nl;
1150 
1151 	if (len == 0ull)
1152 		return (NULL);
1153 
1154 	if (mlist == NULL) {
1155 		mlist = GETSTRUCT(struct memlist, 1);
1156 		mlist->address = base;
1157 		mlist->size = len;
1158 		mlist->next = mlist->prev = NULL;
1159 
1160 		return (mlist);
1161 	}
1162 
1163 	for (tl = ml = mlist; ml; tl = ml, ml = ml->next) {
1164 		if (base < ml->address) {
1165 			if ((base + len) < ml->address) {
1166 				nl = GETSTRUCT(struct memlist, 1);
1167 				nl->address = base;
1168 				nl->size = len;
1169 				nl->next = ml;
1170 				if ((nl->prev = ml->prev) != NULL)
1171 					nl->prev->next = nl;
1172 				ml->prev = nl;
1173 				if (mlist == ml)
1174 					mlist = nl;
1175 			} else {
1176 				ml->size = MAX((base + len),
1177 						(ml->address + ml->size)) -
1178 						base;
1179 				ml->address = base;
1180 			}
1181 			break;
1182 
1183 		} else if (base <= (ml->address + ml->size)) {
1184 			ml->size = MAX((base + len),
1185 					(ml->address + ml->size)) -
1186 					MIN(ml->address, base);
1187 			ml->address = MIN(ml->address, base);
1188 			break;
1189 		}
1190 	}
1191 	if (ml == NULL) {
1192 		nl = GETSTRUCT(struct memlist, 1);
1193 		nl->address = base;
1194 		nl->size = len;
1195 		nl->next = NULL;
1196 		nl->prev = tl;
1197 		tl->next = nl;
1198 	}
1199 
1200 	memlist_coalesce(mlist);
1201 
1202 	return (mlist);
1203 }
1204 
1205 static sbd_error_t *
1206 drmach_prep_rename_script(drmach_device_t *s_mem, drmach_device_t *t_mem,
1207 	uint64_t t_slice_offset, caddr_t buf, int buflen)
1208 {
1209 	int			i, b, m;
1210 	drmach_mc_idle_script_t	*isp;
1211 	drmach_rename_script_t	*rsp;
1212 	int			s_bd, t_bd;
1213 	uint_t			s_masr, t_masr;
1214 	uint64_t		s_new_basepa, t_new_basepa;
1215 	int			b_idx, rv;
1216 	sbd_error_t		*err;
1217 	drmachid_t		 b_id;
1218 	drmach_board_t		*brd;
1219 
1220 #ifdef DEBUG
1221 	/*
1222 	 * Starfire CPU/MEM/IO boards have only one MC per board.
1223 	 * This function has been coded with that fact in mind.
1224 	 */
1225 	ASSERT(MAX_MEM_UNITS_PER_BOARD == 1);
1226 
1227 	/*
1228 	 * calculate the maximum space that could be consumed,
1229 	 * then verify the available buffer space is adequate.
1230 	 */
1231 	m  = sizeof (drmach_mc_idle_script_t *) * 2; /* two MCs */
1232 	b  = sizeof (drmach_rename_script_t *) * 3 * MAX_CPU_UNITS_PER_BOARD;
1233 	b += sizeof (drmach_rename_script_t *) * 3 * MAX_IO_UNITS_PER_BOARD;
1234 	b *= MAX_BOARDS;
1235 	b += sizeof (drmach_rename_script_t *) * 3;
1236 	b += sizeof (drmach_rename_script_t *) * 1;
1237 	ASSERT(m + b < buflen);
1238 #endif
1239 
1240 	/*
1241 	 * construct an array of MC idle register addresses of
1242 	 * both MCs.  The array is zero terminated -- as expected
1243 	 * by drmach_copy_rename_prog__relocatable().
1244 	 */
1245 	isp = (drmach_mc_idle_script_t *)buf;
1246 
1247 	/* source mc */
1248 	err = drmach_get_mc_idle_addr(s_mem, &isp->idle_addr);
1249 	if (err)
1250 		return (err);
1251 	isp->mem = s_mem;
1252 	isp += 1;
1253 
1254 	/* target mc */
1255 	err = drmach_get_mc_idle_addr(t_mem, &isp->idle_addr);
1256 	if (err)
1257 		return (err);
1258 	isp->mem = t_mem;
1259 	isp += 1;
1260 
1261 	/* terminator */
1262 	isp->idle_addr = 0;
1263 	isp->mem = NULL;
1264 	isp += 1;
1265 
1266 	/* fetch source mc asr register value */
1267 	err = drmach_read_mc_asr(s_mem, &s_masr);
1268 	if (err)
1269 		return (err);
1270 	else if (s_masr & STARFIRE_MC_INTERLEAVE_MASK) {
1271 		return (drerr_new(1, ESTF_INTERBOARD, "%s::%s",
1272 				s_mem->bp->cm.name, s_mem->cm.name));
1273 	}
1274 
1275 	/* fetch target mc asr register value */
1276 	err = drmach_read_mc_asr(t_mem, &t_masr);
1277 	if (err)
1278 		return (err);
1279 	else if (t_masr & STARFIRE_MC_INTERLEAVE_MASK) {
1280 		return (drerr_new(1, ESTF_INTERBOARD, "%s::%s",
1281 				t_mem->bp->cm.name, t_mem->cm.name));
1282 	}
1283 
1284 	/* get new source base pa from target's masr */
1285 	s_new_basepa = mc_asr_to_pa(t_masr);
1286 
1287 	/*
1288 	 * remove any existing slice offset to realign
1289 	 * memory with board's slice boundary
1290 	 */
1291 	s_new_basepa &= ~ (mc_get_mem_alignment() - 1);
1292 
1293 	/* get new target base pa from source's masr */
1294 	t_new_basepa  = mc_asr_to_pa(s_masr);
1295 
1296 	/* remove any existing slice offset, then apply new offset */
1297 	t_new_basepa &= ~ (mc_get_mem_alignment() - 1);
1298 	t_new_basepa += t_slice_offset;
1299 
1300 	/* encode new base pa into s_masr.  turn off mem present bit */
1301 	s_masr  = mc_pa_to_asr(s_masr, s_new_basepa);
1302 	s_masr &= ~STARFIRE_MC_MEM_PRESENT_MASK;
1303 
1304 	/* encode new base pa into t_masr.  turn on mem present bit */
1305 	t_masr  = mc_pa_to_asr(t_masr, t_new_basepa);
1306 	t_masr |= STARFIRE_MC_MEM_PRESENT_MASK;
1307 
1308 	/*
1309 	 * Step 0:	Mark source memory as not present.
1310 	 */
1311 	m = 0;
1312 	rsp = (drmach_rename_script_t *)isp;
1313 	err = drmach_get_mc_asr_addr(s_mem, &rsp[m].masr_addr);
1314 	if (err)
1315 		return (err);
1316 	rsp[m].masr = s_masr;
1317 	m++;
1318 
1319 	/*
1320 	 * Step 1:	Write source base address to target MC
1321 	 *		with present bit off.
1322 	 */
1323 	err = drmach_get_mc_asr_addr(t_mem, &rsp[m].masr_addr);
1324 	if (err)
1325 		return (err);
1326 	rsp[m].masr = t_masr & ~STARFIRE_MC_MEM_PRESENT_MASK;
1327 	m++;
1328 
1329 	/*
1330 	 * Step 2:	Now rewrite target reg with present bit on.
1331 	 */
1332 	rsp[m].masr_addr = rsp[m-1].masr_addr;
1333 	rsp[m].masr = t_masr;
1334 	m++;
1335 
1336 	s_bd = s_mem->bp->bnum;
1337 	t_bd = t_mem->bp->bnum;
1338 
1339 	DRMACH_PR("preparing script for CPU and IO units:\n");
1340 
1341 	rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
1342 	if (rv) {
1343 		/* catch this in debug kernels */
1344 		ASSERT(0);
1345 		return (DRMACH_INTERNAL_ERROR());
1346 	}
1347 
1348 	do {
1349 		int			 d_idx;
1350 		drmachid_t		 d_id;
1351 		drmach_device_t		*device;
1352 
1353 		ASSERT(DRMACH_IS_BOARD_ID(b_id));
1354 		brd = b_id;
1355 		b = brd->bnum;
1356 
1357 		/*
1358 		 * Step 3:	Update PC MADR tables for CPUs.
1359 		 */
1360 		rv = drmach_array_first(brd->devices, &d_idx, &d_id);
1361 		if (rv) {
1362 			/* must mean no devices on this board */
1363 			break;
1364 		}
1365 
1366 		DRMACH_PR("\t%s\n", brd->cm.name);
1367 
1368 		do {
1369 			ASSERT(DRMACH_IS_DEVICE_ID(d_id));
1370 
1371 			if (!DRMACH_IS_CPU_ID(d_id))
1372 				continue;
1373 
1374 			device = d_id;
1375 			i = device->unum;
1376 
1377 			DRMACH_PR("\t\t%s\n", device->cm.name);
1378 
1379 			/*
1380 			 * Disabled detaching mem node.
1381 			 */
1382 			rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, s_bd, i);
1383 			rsp[m].masr = s_masr;
1384 			m++;
1385 			/*
1386 			 * Always write masr with present bit
1387 			 * off and then again with it on.
1388 			 */
1389 			rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, t_bd, i);
1390 			rsp[m].masr = t_masr & ~STARFIRE_MC_MEM_PRESENT_MASK;
1391 			m++;
1392 			rsp[m].masr_addr = rsp[m-1].masr_addr;
1393 			rsp[m].masr = t_masr;
1394 			m++;
1395 
1396 		} while (drmach_array_next(brd->devices, &d_idx, &d_id) == 0);
1397 
1398 		/*
1399 		 * Step 4:	Update PC MADR tables for IOs.
1400 		 */
1401 		rv = drmach_array_first(brd->devices, &d_idx, &d_id);
1402 		/* this worked for previous loop, must work here too */
1403 		ASSERT(rv == 0);
1404 
1405 		do {
1406 			ASSERT(DRMACH_IS_DEVICE_ID(d_id));
1407 
1408 			if (!DRMACH_IS_IO_ID(d_id))
1409 				continue;
1410 
1411 			device = d_id;
1412 			i = device->unum;
1413 
1414 			DRMACH_PR("\t\t%s\n", device->cm.name);
1415 
1416 			/*
1417 			 * Disabled detaching mem node.
1418 			 */
1419 			rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, s_bd, i+4);
1420 			rsp[m].masr = s_masr;
1421 			m++;
1422 			/*
1423 			 * Always write masr with present bit
1424 			 * off and then again with it on.
1425 			 */
1426 			rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, t_bd, i+4);
1427 			rsp[m].masr = t_masr & ~STARFIRE_MC_MEM_PRESENT_MASK;
1428 			m++;
1429 			rsp[m].masr_addr = rsp[m-1].masr_addr;
1430 			rsp[m].masr = t_masr;
1431 			m++;
1432 
1433 		} while (drmach_array_next(brd->devices, &d_idx, &d_id) == 0);
1434 	} while (drmach_array_next(drmach_boards, &b_idx, &b_id) == 0);
1435 
1436 	/*
1437 	 * Zero masr_addr value indicates the END.
1438 	 */
1439 	rsp[m].masr_addr = 0ull;
1440 	rsp[m].masr = 0;
1441 	DRMACH_PR("number of steps in rename script = %d\n", m);
1442 	m++;
1443 
1444 	/* paranoia */
1445 	ASSERT((caddr_t)&rsp[m] <= buf + buflen);
1446 
1447 #ifdef DEBUG
1448 	{
1449 		int	j;
1450 
1451 		DRMACH_PR("mc idle register address list:");
1452 		isp = (drmach_mc_idle_script_t *)buf;
1453 		DRMACH_PR("source mc idle addr 0x%llx, mem id %p",
1454 			isp[0].idle_addr, isp[0].mem);
1455 		DRMACH_PR("target mc idle addr 0x%llx, mem id %p",
1456 			isp[1].idle_addr, isp[1].mem);
1457 		ASSERT(isp[2].idle_addr == 0);
1458 
1459 		DRMACH_PR("copy-rename script:");
1460 		for (j = 0; j < m; j++) {
1461 			DRMACH_PR("0x%llx = 0x%08x",
1462 				rsp[j].masr_addr, rsp[j].masr);
1463 		}
1464 
1465 		DELAY(1000000);
1466 	}
1467 #endif
1468 
1469 	/* return number of bytes consumed */
1470 	b = (caddr_t)&rsp[m] - buf;
1471 	DRMACH_PR("total number of bytes consumed is %d\n", b);
1472 	ASSERT(b <= buflen);
1473 
1474 #ifdef lint
1475 	buflen = buflen;
1476 #endif
1477 
1478 	return (NULL);
1479 }
1480 
1481 /*
1482  * The routine performs the necessary memory COPY and MC adr SWITCH.
1483  * Both operations MUST be at the same "level" so that the stack is
1484  * maintained correctly between the copy and switch.  The switch
1485  * portion implements a caching mechanism to guarantee the code text
1486  * is cached prior to execution.  This is to guard against possible
1487  * memory access while the MC adr's are being modified.
1488  *
1489  * IMPORTANT: The _drmach_copy_rename_end() function must immediately
1490  * follow drmach_copy_rename_prog__relocatable() so that the correct
1491  * "length" of the drmach_copy_rename_prog__relocatable can be
1492  * calculated.  This routine MUST be a LEAF function, i.e. it can
1493  * make NO function calls, primarily for two reasons:
1494  *
1495  *	1. We must keep the stack consistent across the "switch".
1496  *	2. Function calls are compiled to relative offsets, and
1497  *	   we execute this function we'll be executing it from
1498  *	   a copied version in a different area of memory, thus
1499  *	   the relative offsets will be bogus.
1500  *
1501  * Moreover, it must have the "__relocatable" suffix to inform DTrace
1502  * providers (and anything else, for that matter) that this
1503  * function's text is manually relocated elsewhere before it is
1504  * executed.  That is, it cannot be safely instrumented with any
1505  * methodology that is PC-relative.
1506  */
1507 static void
1508 drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog)
1509 {
1510 	extern void drmach_exec_script_il(drmach_rename_script_t *rsp);
1511 
1512 	drmach_mc_idle_script_t		*isp;
1513 	struct memlist			*ml;
1514 	int				csize;
1515 	int				lnsize;
1516 	uint64_t			caddr;
1517 
1518 	isp = (drmach_mc_idle_script_t *)prog->data;
1519 
1520 	caddr = ecache_flushaddr;
1521 	csize = (cpunodes[CPU->cpu_id].ecache_size << 1);
1522 	lnsize = cpunodes[CPU->cpu_id].ecache_linesize;
1523 
1524 	/*
1525 	 * DO COPY.
1526 	 */
1527 	for (ml = prog->c_ml; ml; ml = ml->next) {
1528 		uint64_t	s_pa, t_pa;
1529 		uint64_t	nbytes;
1530 
1531 		s_pa = prog->s_copybasepa + ml->address;
1532 		t_pa = prog->t_copybasepa + ml->address;
1533 		nbytes = ml->size;
1534 
1535 		while (nbytes != 0ull) {
1536 			/*
1537 			 * This copy does NOT use an ASI
1538 			 * that avoids the Ecache, therefore
1539 			 * the dst_pa addresses may remain
1540 			 * in our Ecache after the dst_pa
1541 			 * has been removed from the system.
1542 			 * A subsequent write-back to memory
1543 			 * will cause an ARB-stop because the
1544 			 * physical address no longer exists
1545 			 * in the system. Therefore we must
1546 			 * flush out local Ecache after we
1547 			 * finish the copy.
1548 			 */
1549 
1550 			/* copy 32 bytes at src_pa to dst_pa */
1551 			bcopy32_il(s_pa, t_pa);
1552 
1553 			/* increment by 32 bytes */
1554 			s_pa += (4 * sizeof (uint64_t));
1555 			t_pa += (4 * sizeof (uint64_t));
1556 
1557 			/* decrement by 32 bytes */
1558 			nbytes -= (4 * sizeof (uint64_t));
1559 		}
1560 	}
1561 
1562 	/*
1563 	 * Since bcopy32_il() does NOT use an ASI to bypass
1564 	 * the Ecache, we need to flush our Ecache after
1565 	 * the copy is complete.
1566 	 */
1567 	flush_ecache_il(caddr, csize, lnsize);		/* inline version */
1568 
1569 	/*
1570 	 * Wait for MCs to go idle.
1571 	 */
1572 	do {
1573 		register int	t = 10;
1574 		register uint_t	v;
1575 
1576 		/* loop t cycles waiting for each mc to indicate it's idle */
1577 		do {
1578 			v = ldphysio_il(isp->idle_addr)
1579 				& STARFIRE_MC_IDLE_MASK;
1580 
1581 		} while (v != STARFIRE_MC_IDLE_MASK && t-- > 0);
1582 
1583 		/* bailout if timedout */
1584 		if (t <= 0) {
1585 			prog->restless_mc = isp->mem;
1586 			return;
1587 		}
1588 
1589 		isp += 1;
1590 
1591 		/* stop if terminating zero has been reached */
1592 	} while (isp->idle_addr != 0);
1593 
1594 	/* advance passed terminating zero */
1595 	isp += 1;
1596 
1597 	/*
1598 	 * The following inline assembly routine caches
1599 	 * the rename script and then caches the code that
1600 	 * will do the rename.  This is necessary
1601 	 * so that we don't have any memory references during
1602 	 * the reprogramming.  We accomplish this by first
1603 	 * jumping through the code to guarantee it's cached
1604 	 * before we actually execute it.
1605 	 */
1606 	drmach_exec_script_il((drmach_rename_script_t *)isp);
1607 }
1608 
1609 static void
1610 drmach_copy_rename_end(void)
1611 {
1612 	/*
1613 	 * IMPORTANT:	This function's location MUST be located immediately
1614 	 *		following drmach_copy_rename_prog__relocatable to
1615 	 *		accurately estimate its size.  Note that this assumes
1616 	 *		the compiler keeps these functions in the order in
1617 	 *		which they appear :-o
1618 	 */
1619 }
1620 
1621 sbd_error_t *
1622 drmach_copy_rename_init(drmachid_t t_id, uint64_t t_slice_offset,
1623 	drmachid_t s_id, struct memlist *c_ml, drmachid_t *pgm_id)
1624 {
1625 	drmach_device_t	*s_mem;
1626 	drmach_device_t	*t_mem;
1627 	struct memlist	*x_ml;
1628 	uint64_t	off_mask, s_copybasepa, t_copybasepa, t_basepa;
1629 	int		len;
1630 	caddr_t		bp, wp;
1631 	pda_handle_t	ph;
1632 	sbd_error_t	*err;
1633 	drmach_copy_rename_program_t *prog;
1634 
1635 	if (!DRMACH_IS_MEM_ID(s_id))
1636 		return (drerr_new(0, ESTF_INAPPROP, NULL));
1637 	if (!DRMACH_IS_MEM_ID(t_id))
1638 		return (drerr_new(0, ESTF_INAPPROP, NULL));
1639 	s_mem = s_id;
1640 	t_mem = t_id;
1641 
1642 	/* get starting physical address of target memory */
1643 	err = drmach_mem_get_base_physaddr(t_id, &t_basepa);
1644 	if (err)
1645 		return (err);
1646 
1647 	/* calculate slice offset mask from slice size */
1648 	off_mask = mc_get_mem_alignment() - 1;
1649 
1650 	/* calculate source and target base pa */
1651 	s_copybasepa = c_ml->address;
1652 	t_copybasepa = t_basepa + ((c_ml->address & off_mask) - t_slice_offset);
1653 
1654 	/* paranoia */
1655 	ASSERT((c_ml->address & off_mask) >= t_slice_offset);
1656 
1657 	/* adjust copy memlist addresses to be relative to copy base pa */
1658 	x_ml = c_ml;
1659 	while (x_ml != NULL) {
1660 		x_ml->address -= s_copybasepa;
1661 		x_ml = x_ml->next;
1662 	}
1663 
1664 #ifdef DEBUG
1665 	{
1666 	uint64_t s_basepa, s_size, t_size;
1667 
1668 	x_ml = c_ml;
1669 	while (x_ml->next != NULL)
1670 		x_ml = x_ml->next;
1671 
1672 	DRMACH_PR("source copy span: base pa 0x%llx, end pa 0x%llx\n",
1673 		s_copybasepa,
1674 		s_copybasepa + x_ml->address + x_ml->size);
1675 
1676 	DRMACH_PR("target copy span: base pa 0x%llx, end pa 0x%llx\n",
1677 		t_copybasepa,
1678 		t_copybasepa + x_ml->address + x_ml->size);
1679 
1680 	DRMACH_PR("copy memlist (relative to copy base pa):\n");
1681 	MEMLIST_DUMP(c_ml);
1682 
1683 	err = drmach_mem_get_base_physaddr(s_id, &s_basepa);
1684 	ASSERT(err == NULL);
1685 
1686 	err = drmach_mem_get_size(s_id, &s_size);
1687 	ASSERT(err == NULL);
1688 
1689 	err = drmach_mem_get_size(t_id, &t_size);
1690 	ASSERT(err == NULL);
1691 
1692 	DRMACH_PR("current source base pa 0x%llx, size 0x%llx\n",
1693 		s_basepa, s_size);
1694 	DRMACH_PR("current target base pa 0x%llx, size 0x%llx\n",
1695 		t_basepa, t_size);
1696 
1697 	ASSERT(s_copybasepa + x_ml->address + x_ml->size <= s_basepa + s_size);
1698 	ASSERT(t_copybasepa + x_ml->address + x_ml->size <= t_basepa + t_size);
1699 	}
1700 #endif
1701 
1702 	ph = drmach_pda_open();
1703 	if (ph == NULL)
1704 		return (DRMACH_INTERNAL_ERROR());
1705 
1706 	/*
1707 	 * bp will be page aligned, since we're calling
1708 	 * kmem_zalloc() with an exact multiple of PAGESIZE.
1709 	 */
1710 	wp = bp = kmem_zalloc(PAGESIZE, KM_SLEEP);
1711 
1712 	/* allocate space for copy rename struct */
1713 	len = sizeof (drmach_copy_rename_program_t);
1714 	DRMACH_PR("prog = 0x%p, header len %d\n", wp, len);
1715 	prog = (drmach_copy_rename_program_t *)wp;
1716 	wp += (len + ecache_alignsize - 1) & ~ (ecache_alignsize - 1);
1717 
1718 	/*
1719 	 * Copy the code for the copy-rename routine into
1720 	 * a page aligned piece of memory.  We do this to guarantee
1721 	 * that we're executing within the same page and thus reduce
1722 	 * the possibility of cache collisions between different
1723 	 * pages.
1724 	 */
1725 	len = (int)((ulong_t)drmach_copy_rename_end -
1726 		    (ulong_t)drmach_copy_rename_prog__relocatable);
1727 	ASSERT(wp + len < bp + PAGESIZE);
1728 	bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len);
1729 
1730 	DRMACH_PR("copy-rename function 0x%p, len %d\n", wp, len);
1731 	prog->run = (void (*)())wp;
1732 	wp += (len + ecache_alignsize - 1) & ~ (ecache_alignsize - 1);
1733 
1734 	/*
1735 	 * Prepare data page that will contain script of
1736 	 * operations to perform during copy-rename.
1737 	 * Allocate temporary buffer to hold script.
1738 	 */
1739 	err = drmach_prep_rename_script(s_mem, t_mem, t_slice_offset,
1740 		wp, PAGESIZE - (wp - bp));
1741 	if (err) {
1742 		(void) drmach_copy_rename_fini(prog);
1743 		return (err);
1744 	}
1745 
1746 	DRMACH_PR("copy-rename script 0x%p, len %d\n", wp, len);
1747 	prog->data = wp;
1748 	wp += (len + ecache_alignsize - 1) & ~ (ecache_alignsize - 1);
1749 
1750 	prog->ph = ph;
1751 	prog->s_copybasepa = s_copybasepa;
1752 	prog->t_copybasepa = t_copybasepa;
1753 	prog->c_ml = c_ml;
1754 	*pgm_id = prog;
1755 
1756 	return (NULL);
1757 }
1758 
1759 sbd_error_t *
1760 drmach_copy_rename_fini(drmachid_t id)
1761 {
1762 	drmach_copy_rename_program_t	*prog = id;
1763 	sbd_error_t			*err = NULL;
1764 
1765 	if (prog->c_ml != NULL)
1766 		memlist_delete(prog->c_ml);
1767 
1768 	if (prog->ph != NULL)
1769 		pda_close(prog->ph);
1770 
1771 	if (prog->restless_mc != 0) {
1772 		cmn_err(CE_WARN, "MC did not idle; OBP Node 0x%x",
1773 			(uint_t)drmach_node_get_dnode(prog->restless_mc->node));
1774 
1775 		err = DRMACH_INTERNAL_ERROR();
1776 	}
1777 
1778 	kmem_free(prog, PAGESIZE);
1779 
1780 	return (err);
1781 }
1782 
1783 static sbd_error_t *
1784 drmach_io_new(drmach_device_t *dp)
1785 {
1786 	static sbd_error_t *drmach_io_release(drmachid_t);
1787 	static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
1788 
1789 	sbd_error_t	*err;
1790 	int		 portid;
1791 
1792 	err = drmach_device_get_prop(dp, "upa-portid", &portid);
1793 	if (err == NULL) {
1794 		ASSERT(portid & 0x40);
1795 		dp->unum = portid & 1;
1796 	}
1797 
1798 	dp->cm.isa = (void *)drmach_io_new;
1799 	dp->cm.release = drmach_io_release;
1800 	dp->cm.status = drmach_io_status;
1801 
1802 	snprintf(dp->cm.name, sizeof (dp->cm.name), "%s%d", dp->type, dp->unum);
1803 
1804 	return (err);
1805 }
1806 
1807 static void
1808 drmach_iopc_op(pda_handle_t ph, drmach_iopc_op_t op)
1809 {
1810 	register int b;
1811 
1812 	for (b = 0; b < MAX_BOARDS; b++) {
1813 		int		p;
1814 		ushort_t	bda_ioc;
1815 		board_desc_t	*bdesc;
1816 
1817 		if (pda_board_present(ph, b) == 0)
1818 			continue;
1819 
1820 		bdesc = (board_desc_t *)pda_get_board_info(ph, b);
1821 		/*
1822 		 * Update PCs for IOCs.
1823 		 */
1824 		bda_ioc = bdesc->bda_ioc;
1825 		for (p = 0; p < MAX_IOCS; p++) {
1826 			u_longlong_t	idle_addr;
1827 			uchar_t		value;
1828 
1829 			if (BDA_NBL(bda_ioc, p) != BDAN_GOOD)
1830 				continue;
1831 
1832 			idle_addr = STARFIRE_BB_PC_ADDR(b, p, 1);
1833 
1834 			switch (op) {
1835 			case DO_PAUSE:
1836 				value = STARFIRE_BB_PC_PAUSE(p);
1837 				break;
1838 
1839 			case DO_IDLE:
1840 				value = STARFIRE_BB_PC_IDLE(p);
1841 				break;
1842 
1843 			case DO_UNPAUSE:
1844 				value = ldbphysio(idle_addr);
1845 				value &= ~STARFIRE_BB_PC_PAUSE(p);
1846 				break;
1847 
1848 			case DO_UNIDLE:
1849 				value = ldbphysio(idle_addr);
1850 				value &= ~STARFIRE_BB_PC_IDLE(p);
1851 				break;
1852 
1853 			default:
1854 				cmn_err(CE_PANIC,
1855 					"drmach_iopc_op: unknown op (%d)",
1856 					(int)op);
1857 				/*NOTREACHED*/
1858 			}
1859 			stbphysio(idle_addr, value);
1860 		}
1861 	}
1862 }
1863 
1864 void
1865 drmach_copy_rename(drmachid_t id)
1866 {
1867 	drmach_copy_rename_program_t	*prog = id;
1868 	uint64_t			neer;
1869 
1870 	/*
1871 	 * UPA IDLE
1872 	 * Protocol = PAUSE -> IDLE -> UNPAUSE
1873 	 * In reality since we only "idle" the IOPCs it's sufficient
1874 	 * to just issue the IDLE operation since (in theory) all IOPCs
1875 	 * in the field are PC6.  However, we'll be robust and do the
1876 	 * proper workaround protocol so that we never have to worry!
1877 	 */
1878 	drmach_iopc_op(prog->ph, DO_PAUSE);
1879 	drmach_iopc_op(prog->ph, DO_IDLE);
1880 	DELAY(100);
1881 	drmach_iopc_op(prog->ph, DO_UNPAUSE);
1882 	DELAY(100);
1883 
1884 	/* disable CE reporting */
1885 	neer = get_error_enable();
1886 	set_error_enable(neer & ~EER_CEEN);
1887 
1888 	/* run the copy/rename program */
1889 	prog->run(prog);
1890 
1891 	/* enable CE reporting */
1892 	set_error_enable(neer);
1893 
1894 	/*
1895 	 * UPA UNIDLE
1896 	 * Protocol = UNIDLE
1897 	 */
1898 	drmach_iopc_op(prog->ph, DO_UNIDLE);
1899 	DELAY(100);
1900 }
1901 
1902 /*
1903  * The counter-timer and perf-counter nodes are not being cleaned
1904  * up after a board that was present at start of day is detached.
1905  * If the board has become unconfigured with this operation, walk
1906  * the prom tree and find all counter-timer and perf-counter nodes
1907  * that have the same board number as the board that was just
1908  * unconfigured and remove them.
1909  */
1910 static sbd_error_t *
1911 drmach_remove_counter_nodes(drmachid_t id)
1912 {
1913 	int		num;
1914 	char		name[OBP_MAXDRVNAME];
1915 	pnode_t		child;
1916 	dev_info_t	*dip;
1917 	sbd_error_t	*err;
1918 	drmach_status_t	stat;
1919 	drmach_board_t	*bp;
1920 
1921 	if (!DRMACH_IS_BOARD_ID(id)) {
1922 		return (drerr_new(0, ESTF_INAPPROP, NULL));
1923 	}
1924 
1925 	if ((err = drmach_board_status(id, &stat)) != NULL) {
1926 		return (err);
1927 	}
1928 
1929 	/*
1930 	 * Only clean up the counter-timer and perf-counter
1931 	 * nodes when the entire board is unconfigured.
1932 	 */
1933 	if (stat.configured) {
1934 		return (NULL);
1935 	}
1936 
1937 	bp = (drmach_board_t *)id;
1938 
1939 	err = NULL;
1940 
1941 	for (child = prom_childnode(prom_rootnode()); child != OBP_NONODE;
1942 	    child = prom_nextnode(child)) {
1943 
1944 		if (prom_getprop(child, OBP_BOARDNUM, (caddr_t)&num) == -1) {
1945 			continue;
1946 		}
1947 
1948 		if (bp->bnum != num) {
1949 			continue;
1950 		}
1951 
1952 		if (prom_getprop(child, OBP_NAME, (caddr_t)name) == -1) {
1953 			continue;
1954 		}
1955 
1956 		if (strncmp(name, MISC_COUNTER_TIMER_DEVNAME, OBP_MAXDRVNAME) &&
1957 		    strncmp(name, MISC_PERF_COUNTER_DEVNAME, OBP_MAXDRVNAME)) {
1958 				continue;
1959 		}
1960 
1961 		/* Root node doesn't have to be held */
1962 		dip = e_ddi_nodeid_to_dip(child);
1963 
1964 		/*
1965 		 * If the node is only in the OBP tree, then
1966 		 * we don't have to remove it.
1967 		 */
1968 		if (dip) {
1969 			dev_info_t *fdip = NULL;
1970 
1971 			DRMACH_PR("removing %s devinfo node\n", name);
1972 
1973 			e_ddi_branch_hold(dip);
1974 			ddi_release_devi(dip); /* held in e_ddi_nodeid_to_dip */
1975 
1976 			if (e_ddi_branch_destroy(dip, &fdip, 0)) {
1977 				char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1978 
1979 				/*
1980 				 * If non-NULL, fdip is held and must be
1981 				 * released.
1982 				 */
1983 				if (fdip != NULL) {
1984 					(void) ddi_pathname(fdip, path);
1985 					ddi_release_devi(fdip);
1986 				} else {
1987 					(void) ddi_pathname(dip, path);
1988 				}
1989 
1990 				err = drerr_new(1, ESTF_DRVFAIL, path);
1991 				kmem_free(path, MAXPATHLEN);
1992 				e_ddi_branch_rele(dip);
1993 				break;
1994 			}
1995 		}
1996 	}
1997 
1998 	return (err);
1999 }
2000 
2001 /*ARGSUSED*/
2002 sbd_error_t *
2003 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
2004 {
2005 	/* allow status and ncm operations to always succeed */
2006 	if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) {
2007 		return (NULL);
2008 	}
2009 
2010 	/* check all other commands for the required option string */
2011 	if ((opts->size > 0) && (opts->copts != NULL)) {
2012 
2013 		DRMACH_PR("platform options: %s\n", opts->copts);
2014 
2015 		if (strstr(opts->copts, "xfdr") != NULL) {
2016 			return (NULL);
2017 		}
2018 	}
2019 
2020 	return (drerr_new(0, ESTF_SUPPORT, NULL));
2021 }
2022 
2023 /*ARGSUSED*/
2024 sbd_error_t *
2025 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
2026 {
2027 	sbd_error_t	*err = NULL;
2028 
2029 	switch (cmd) {
2030 	case SBD_CMD_UNCONFIGURE:
2031 
2032 		err = drmach_remove_counter_nodes(id);
2033 		break;
2034 
2035 	case SBD_CMD_CONFIGURE:
2036 	case SBD_CMD_DISCONNECT:
2037 	case SBD_CMD_CONNECT:
2038 	case SBD_CMD_GETNCM:
2039 	case SBD_CMD_STATUS:
2040 		break;
2041 
2042 	default:
2043 		break;
2044 	}
2045 
2046 	return (err);
2047 }
2048 
2049 sbd_error_t *
2050 drmach_board_assign(int bnum, drmachid_t *id)
2051 {
2052 	sbd_error_t	*err;
2053 
2054 	if (!drmach_initialized && drmach_init() == -1) {
2055 		err = DRMACH_INTERNAL_ERROR();
2056 	} else if (drmach_array_get(drmach_boards, bnum, id) == -1) {
2057 		err = drerr_new(1, ESTF_BNUM, "%d", bnum);
2058 	} else if (*id != NULL) {
2059 		err = NULL;
2060 	} else {
2061 		drmach_board_t	*bp;
2062 
2063 		*id  = (drmachid_t)drmach_board_new(bnum);
2064 		bp = *id;
2065 		bp->assigned = 1;
2066 		err = NULL;
2067 	}
2068 
2069 	return (err);
2070 }
2071 
2072 static int
2073 drmach_attach_board(void *arg)
2074 {
2075 	drmach_board_t	*obj = (drmach_board_t *)arg;
2076 	cpuset_t	cset;
2077 	int		retval;
2078 
2079 	/*
2080 	 * OBP disables traps during the board probe.
2081 	 * So, in order to prevent cross-call/cross-trap timeouts,
2082 	 * and thus panics, we effectively block anybody from
2083 	 * issuing xc's/xt's by doing a promsafe_xc_attention.
2084 	 * In the previous version of Starfire DR (2.6), a timeout
2085 	 * suspension mechanism was implemented in the send-mondo
2086 	 * assembly.  That mechanism is unnecessary with the
2087 	 * existence of xc_attention/xc_dismissed.
2088 	 */
2089 	cset = cpu_ready_set;
2090 	promsafe_xc_attention(cset);
2091 
2092 	retval = prom_starfire_add_brd(obj->connect_cpuid);
2093 
2094 	xc_dismissed(cset);
2095 
2096 	return (retval);
2097 }
2098 
2099 sbd_error_t *
2100 drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
2101 {
2102 	drmach_board_t	*obj = (drmach_board_t *)id;
2103 	int		retval;
2104 	sbd_error_t	*err;
2105 	char		*cptr, *copts;
2106 
2107 	if (!DRMACH_IS_BOARD_ID(id))
2108 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2109 
2110 	if (opts->size > 0)
2111 		copts = opts->copts;
2112 
2113 	if ((cptr = strstr(copts, "cpuid=")) != NULL) {
2114 		int cpuid;
2115 
2116 		cptr += strlen("cpuid=");
2117 		cpuid = stoi(&cptr);
2118 
2119 		if (DRMACH_CPUID2BNUM(cpuid) == obj->bnum) {
2120 			obj->connect_cpuid = cpuid;
2121 			obj->assigned = 1;
2122 		} else
2123 			return (drerr_new(1, ESTF_SETCPUVAL, "%d", cpuid));
2124 	} else {
2125 		/* cpuid was not specified */
2126 		obj->connect_cpuid = -1;
2127 	}
2128 
2129 	if (obj->connect_cpuid == -1) {
2130 		err =  drerr_new(1, ESTF_NOCPUID, obj->cm.name);
2131 		return (err);
2132 	}
2133 
2134 	cmn_err(CE_CONT, "DRMACH: PROM attach %s CPU %d\n",
2135 		obj->cm.name, obj->connect_cpuid);
2136 
2137 	retval = prom_tree_update(drmach_attach_board, obj);
2138 
2139 	if (retval == 0)
2140 		err = NULL;
2141 	else {
2142 		cmn_err(CE_WARN, "prom error: prom_starfire_add_brd(%d) "
2143 			"returned %d", obj->connect_cpuid, retval);
2144 
2145 		err = drerr_new(1, ESTF_PROBE, obj->cm.name);
2146 	}
2147 
2148 	obj->connect_cpuid = -1;
2149 
2150 	return (err);
2151 }
2152 
2153 /*ARGSUSED*/
2154 sbd_error_t *
2155 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
2156 {
2157 	drmach_board_t		*bp;
2158 	int			rv;
2159 	int			d_idx;	/* device index */
2160 	drmachid_t		d_id;	/* device ID */
2161 	sbd_error_t		*err;
2162 
2163 	if (!DRMACH_IS_BOARD_ID(id))
2164 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2165 
2166 	bp = id;
2167 
2168 	/*
2169 	 * We need to make sure all of the board's device nodes
2170 	 * have been removed from the Solaris device tree before
2171 	 * continuing with the disconnect. Otherwise, we could
2172 	 * disconnect the board and remove the OBP device tree
2173 	 * nodes with Solaris device tree nodes remaining.
2174 	 *
2175 	 * On Starfire, Solaris device tree nodes are deleted
2176 	 * during unconfigure by drmach_unconfigure(). It's
2177 	 * necessary to do this here because drmach_unconfigure()
2178 	 * failures are not handled during unconfigure.
2179 	 */
2180 	if (bp->devices) {
2181 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
2182 		while (rv == 0) {
2183 			err = drmach_unconfigure(d_id, DRMACH_DEVI_REMOVE);
2184 			if (err)
2185 				return (err);
2186 
2187 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
2188 		}
2189 	}
2190 
2191 	/*
2192 	 * Starfire board Solaris device tree counter nodes,
2193 	 * which are only present on start-of-day boards, are
2194 	 * removed in the dr_post_op() code flow after the
2195 	 * board is unconfigured. We call the counter node
2196 	 * removal function here because unconfigure errors
2197 	 * can cause the dr_post_op() function to be skipped
2198 	 * after an unconfigure operation even though all of
2199 	 * the board's devices have been transitioned to the
2200 	 * unconfigured state.
2201 	 */
2202 	err = drmach_remove_counter_nodes(id);
2203 	if (err)
2204 		return (err);
2205 
2206 	return (NULL);
2207 }
2208 
2209 static int
2210 drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
2211 {
2212 	drmach_node_t			*node = args->node;
2213 	drmach_board_cb_data_t		*data = args->data;
2214 	drmach_board_t			*obj = data->obj;
2215 
2216 	int		 rv;
2217 	int		 bnum;
2218 	drmach_device_t	*device;
2219 
2220 	rv = drmach_node_get_prop(node, OBP_BOARDNUM, &bnum);
2221 	if (rv) {
2222 		/*
2223 		 * if the node does not have a board# property, then
2224 		 * by that information alone it is known that drmach
2225 		 * is not interested in it.
2226 		 */
2227 		return (0);
2228 	} else if (bnum != obj->bnum)
2229 		return (0);
2230 
2231 	/*
2232 	 * Create a device data structure from this node data.
2233 	 * The call may yield nothing if the node is not of interest
2234 	 * to drmach.
2235 	 */
2236 	data->err = drmach_device_new(node, obj, &device);
2237 	if (data->err)
2238 		return (-1);
2239 	else if (device == NULL) {
2240 		/*
2241 		 * drmach_device_new examined the node we passed in
2242 		 * and determined that it was one not of interest to
2243 		 * drmach.  So, it is skipped.
2244 		 */
2245 		return (0);
2246 	}
2247 
2248 	rv = drmach_array_set(obj->devices, data->ndevs++, device);
2249 	if (rv) {
2250 		drmach_device_dispose(device);
2251 		data->err = DRMACH_INTERNAL_ERROR();
2252 		return (-1);
2253 	}
2254 
2255 	data->err = (*data->found)(data->a, device->type, device->unum, device);
2256 	return (data->err == NULL ? 0 : -1);
2257 }
2258 
2259 sbd_error_t *
2260 drmach_board_find_devices(drmachid_t id, void *a,
2261 	sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
2262 {
2263 	extern int		 plat_max_cpu_units_per_board();
2264 	extern int		 plat_max_mem_units_per_board();
2265 	extern int		 plat_max_io_units_per_board();
2266 
2267 	drmach_board_t		*obj = (drmach_board_t *)id;
2268 	sbd_error_t		*err;
2269 	int			 max_devices;
2270 	int			 rv;
2271 	drmach_board_cb_data_t	data;
2272 
2273 	max_devices  = plat_max_cpu_units_per_board();
2274 	max_devices += plat_max_mem_units_per_board();
2275 	max_devices += plat_max_io_units_per_board();
2276 
2277 	obj->devices = drmach_array_new(0, max_devices);
2278 
2279 	data.obj = obj;
2280 	data.ndevs = 0;
2281 	data.found = found;
2282 	data.a = a;
2283 	data.err = NULL;
2284 
2285 	rv = drmach_node_walk(obj->tree, &data, drmach_board_find_devices_cb);
2286 	if (rv == 0)
2287 		err = NULL;
2288 	else {
2289 		drmach_array_dispose(obj->devices, drmach_device_dispose);
2290 		obj->devices = NULL;
2291 
2292 		if (data.err)
2293 			err = data.err;
2294 		else
2295 			err = DRMACH_INTERNAL_ERROR();
2296 	}
2297 
2298 	return (err);
2299 }
2300 
2301 int
2302 drmach_board_lookup(int bnum, drmachid_t *id)
2303 {
2304 	int	rv = 0;
2305 
2306 	if (!drmach_initialized && drmach_init() == -1) {
2307 		*id = 0;
2308 		rv = -1;
2309 	} else if (drmach_array_get(drmach_boards, bnum, id)) {
2310 		*id = 0;
2311 		rv = -1;
2312 	}
2313 	return (rv);
2314 }
2315 
2316 sbd_error_t *
2317 drmach_board_name(int bnum, char *buf, int buflen)
2318 {
2319 	snprintf(buf, buflen, "SB%d", bnum);
2320 	return (NULL);
2321 }
2322 
2323 sbd_error_t *
2324 drmach_board_poweroff(drmachid_t id)
2325 {
2326 	drmach_board_t	*bp;
2327 	sbd_error_t	*err;
2328 	drmach_status_t	 stat;
2329 
2330 	if (!DRMACH_IS_BOARD_ID(id))
2331 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2332 	bp = id;
2333 
2334 	err = drmach_board_status(id, &stat);
2335 	if (err)
2336 		return (err);
2337 	else if (stat.configured || stat.busy)
2338 		return (drerr_new(0, ESTF_CONFIGBUSY, bp->cm.name));
2339 	else {
2340 		/* board power off is essentially a noop for Starfire */
2341 		bp->powered = 0;
2342 		return (NULL);
2343 	}
2344 	/*NOTREACHED*/
2345 }
2346 
2347 sbd_error_t *
2348 drmach_board_poweron(drmachid_t id)
2349 {
2350 	drmach_board_t	*bp;
2351 
2352 	if (!DRMACH_IS_BOARD_ID(id))
2353 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2354 	bp = id;
2355 
2356 	/* board power on is essentially a noop for Starfire */
2357 	bp->powered = 1;
2358 
2359 	return (NULL);
2360 }
2361 
2362 static sbd_error_t *
2363 drmach_board_release(drmachid_t id)
2364 {
2365 	if (!DRMACH_IS_BOARD_ID(id))
2366 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2367 	return (NULL);
2368 }
2369 
2370 /*ARGSUSED*/
2371 sbd_error_t *
2372 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
2373 {
2374 	return (NULL);
2375 }
2376 
2377 sbd_error_t *
2378 drmach_board_unassign(drmachid_t id)
2379 {
2380 	drmach_board_t	*bp;
2381 	sbd_error_t	*err;
2382 	drmach_status_t	 stat;
2383 
2384 	if (!DRMACH_IS_BOARD_ID(id))
2385 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2386 	bp = id;
2387 
2388 	err = drmach_board_status(id, &stat);
2389 	if (err)
2390 		return (err);
2391 	else if (stat.configured || stat.busy)
2392 		return (drerr_new(0, ESTF_CONFIGBUSY, bp->cm.name));
2393 	else if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
2394 		return (DRMACH_INTERNAL_ERROR());
2395 	else {
2396 		drmach_board_dispose(bp);
2397 		return (NULL);
2398 	}
2399 	/*NOTREACHED*/
2400 }
2401 
2402 static sbd_error_t *
2403 drmach_cpu_new(drmach_device_t *dp)
2404 {
2405 	static sbd_error_t *drmach_cpu_release(drmachid_t);
2406 	static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
2407 
2408 	sbd_error_t	*err;
2409 	int		 portid;
2410 
2411 	err = drmach_device_get_prop(dp, "upa-portid", &portid);
2412 	if (err == NULL)
2413 		dp->unum = portid & 3;
2414 
2415 	dp->cm.isa = (void *)drmach_cpu_new;
2416 	dp->cm.release = drmach_cpu_release;
2417 	dp->cm.status = drmach_cpu_status;
2418 
2419 	snprintf(dp->cm.name, sizeof (dp->cm.name), "%s%d", dp->type, dp->unum);
2420 
2421 	return (err);
2422 }
2423 
2424 /*
2425  * drmach_cpu_obp_detach()
2426  *  This requires two steps, first, we must put the cpuid into the OBP
2427  *  idle loop (Idle in Program) state.  Then we call OBP to place the CPU
2428  *  into the "Detached" state, which does any special processing to
2429  *  actually detach the cpu, such as flushing ecache, and also ensures
2430  *  that a subsequent breakpoint won't restart the cpu (if it was just in
2431  *  Idle in Program state).
2432  */
2433 static void
2434 drmach_cpu_obp_detach(int cpuid)
2435 {
2436 	/*
2437 	 * Cpu may not be under OBP's control. Eg, if cpu exited to download
2438 	 * helper on a prior attach.
2439 	 */
2440 	if (CPU_SGN_EXISTS(cpuid) &&
2441 			!SGN_CPU_IS_OS(cpuid) &&
2442 			!SGN_CPU_IS_OBP(cpuid)) {
2443 		cmn_err(CE_WARN,
2444 			"unexpected signature (0x%x) for cpu %d",
2445 			get_cpu_sgn(cpuid), cpuid);
2446 	}
2447 
2448 	/*
2449 	 * Now we place the CPU into the "Detached" idle loop in OBP.
2450 	 * This is so that the CPU won't be restarted if we break into
2451 	 * OBP with a breakpoint or BREAK key from the console, and also
2452 	 * if we need to do any special processing, such as flushing the
2453 	 * cpu's ecache, disabling interrupts (by turning of the ET bit in
2454 	 * the PSR) and/or spinning in BBSRAM rather than global memory.
2455 	 */
2456 	DRMACH_PR("prom_starfire_rm_cpu(%d)\n", cpuid);
2457 	prom_starfire_rm_cpu(cpuid);
2458 }
2459 
2460 /*
2461  * drmach_cpu_obp_is_detached() returns TRUE if the cpu sigblock signature state
2462  * is SIGBST_DETACHED; otherwise it returns FALSE. This routine should only
2463  * be called after we have asked OBP to detach the CPU. It should NOT be
2464  * called as a check during any other flow.
2465  */
2466 static int
2467 drmach_cpu_obp_is_detached(int cpuid)
2468 {
2469 	if (!CPU_SGN_EXISTS(cpuid) ||
2470 		(SGN_CPU_IS_OS(cpuid) && SGN_CPU_STATE_IS_DETACHED(cpuid)))
2471 		return (1);
2472 	else
2473 		return (0);
2474 }
2475 
2476 static int
2477 drmach_cpu_start(struct cpu *cp)
2478 {
2479 	int		cpuid = cp->cpu_id;
2480 	int		ntries = drmach_cpu_ntries;
2481 	extern void	restart_other_cpu(int);
2482 
2483 	ASSERT(MUTEX_HELD(&cpu_lock));
2484 	ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
2485 
2486 	cp->cpu_flags &= ~CPU_POWEROFF;
2487 
2488 	/*
2489 	 * NOTE: restart_other_cpu pauses cpus during the
2490 	 *	 slave cpu start.  This helps to quiesce the
2491 	 *	 bus traffic a bit which makes the tick sync
2492 	 *	 routine in the prom more robust.
2493 	 */
2494 	DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
2495 
2496 	prom_starfire_add_cpu(cpuid);
2497 
2498 	restart_other_cpu(cpuid);
2499 
2500 	/*
2501 	 * Wait for the cpu to reach its idle thread before
2502 	 * we zap him with a request to blow away the mappings
2503 	 * he (might) have for the drmach_shutdown_asm code
2504 	 * he may have executed on unconfigure.
2505 	 */
2506 	while ((cp->cpu_thread != cp->cpu_idle_thread) && (ntries > 0)) {
2507 		DELAY(drmach_cpu_delay);
2508 		ntries--;
2509 	}
2510 
2511 	DRMACH_PR("waited %d out of %d loops for cpu %d\n",
2512 		drmach_cpu_ntries - ntries, drmach_cpu_ntries, cpuid);
2513 
2514 	xt_one(cpuid, vtag_flushpage_tl1,
2515 		(uint64_t)drmach_shutdown_va, (uint64_t)KCONTEXT);
2516 
2517 	return (0);
2518 }
2519 
2520 /*
2521  * A detaching CPU is xcalled with an xtrap to drmach_cpu_stop_self() after
2522  * it has been offlined. The function of this routine is to get the cpu
2523  * spinning in a safe place. The requirement is that the system will not
2524  * reference anything on the detaching board (memory and i/o is detached
2525  * elsewhere) and that the CPU not reference anything on any other board
2526  * in the system.  This isolation is required during and after the writes
2527  * to the domain masks to remove the board from the domain.
2528  *
2529  * To accomplish this isolation the following is done:
2530  *	1) Create a locked mapping to a location in BBSRAM where
2531  *	   the cpu will execute.
2532  *	2) Copy the target function (drmach_shutdown_asm) in which
2533  *	   the cpu will execute into BBSRAM.
2534  *	3) Jump into function with BBSRAM.
2535  *	   Function will:
2536  *	   3.1) Flush its Ecache (displacement).
2537  *	   3.2) Flush its Dcache with HW mechanism.
2538  *	   3.3) Flush its Icache with HW mechanism.
2539  *	   3.4) Flush all valid and _unlocked_ D-TLB entries.
2540  *	   3.5) Flush all valid and _unlocked_ I-TLB entries.
2541  *	   3.6) Clear xt_mb to signal completion. Note: cache line is
2542  *		recovered by drmach_cpu_poweroff().
2543  *	4) Jump into a tight loop.
2544  */
2545 #define	DRMACH_BBSRAM_OFFSET	0x1000
2546 
2547 static void
2548 drmach_cpu_stop_self(void)
2549 {
2550 	int		cpuid = (int)CPU->cpu_id;
2551 	tte_t		tte;
2552 	volatile uint_t	*src, *dst;
2553 	uint_t		funclen;
2554 	uint64_t	bbsram_pa, bbsram_offset;
2555 	uint_t		bbsram_pfn;
2556 	uint64_t	bbsram_addr;
2557 	void		(*bbsram_func)(uint64_t);
2558 	extern void	drmach_shutdown_asm(uint64_t);
2559 	extern void	drmach_shutdown_asm_end(void);
2560 
2561 	funclen = (uint_t)drmach_shutdown_asm_end - (uint_t)drmach_shutdown_asm;
2562 	ASSERT(funclen <= MMU_PAGESIZE);
2563 	/*
2564 	 * We'll start from the 0th's base.
2565 	 */
2566 	bbsram_pa = STARFIRE_UPAID2UPS(cpuid) | STARFIRE_PSI_BASE;
2567 	bbsram_offset = bbsram_pa | 0xfe0ULL;
2568 	bbsram_pa += ldphysio(bbsram_offset) + DRMACH_BBSRAM_OFFSET;
2569 
2570 	bbsram_pfn = (uint_t)(bbsram_pa >> MMU_PAGESHIFT);
2571 
2572 	bbsram_addr = (uint64_t)drmach_shutdown_va;
2573 	drmach_shutdown_asm_mbox->estack = bbsram_addr + (uint64_t)funclen;
2574 
2575 	tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
2576 			TTE_PFN_INTHI(bbsram_pfn);
2577 	tte.tte_intlo = TTE_PFN_INTLO(bbsram_pfn) |
2578 			TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
2579 	sfmmu_dtlb_ld(drmach_shutdown_va, KCONTEXT, &tte);	/* load dtlb */
2580 	sfmmu_itlb_ld(drmach_shutdown_va, KCONTEXT, &tte);	/* load itlb */
2581 
2582 	for (src = (uint_t *)drmach_shutdown_asm, dst = (uint_t *)bbsram_addr;
2583 		src < (uint_t *)drmach_shutdown_asm_end; src++, dst++)
2584 		*dst = *src;
2585 
2586 	bbsram_func = (void (*)())bbsram_addr;
2587 	drmach_shutdown_asm_mbox->flushaddr = ecache_flushaddr;
2588 	drmach_shutdown_asm_mbox->size = (cpunodes[cpuid].ecache_size << 1);
2589 	drmach_shutdown_asm_mbox->linesize = cpunodes[cpuid].ecache_linesize;
2590 	drmach_shutdown_asm_mbox->physaddr
2591 				    = va_to_pa((void *)&drmach_xt_mb[cpuid]);
2592 
2593 	/*
2594 	 * Signal to drmach_cpu_poweroff() is via drmach_xt_mb cleared
2595 	 * by asm code
2596 	 */
2597 
2598 	(*bbsram_func)(va_to_pa((void *)drmach_shutdown_asm_mbox));
2599 }
2600 
2601 static void
2602 drmach_cpu_shutdown_self(void)
2603 {
2604 	cpu_t		*cp = CPU;
2605 	int		cpuid = cp->cpu_id;
2606 	extern void	flush_windows(void);
2607 
2608 	flush_windows();
2609 
2610 	(void) spl8();
2611 
2612 	ASSERT(cp->cpu_intr_actv == 0);
2613 	ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
2614 	    cp->cpu_thread == cp->cpu_startup_thread);
2615 
2616 	cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
2617 
2618 	drmach_cpu_stop_self();
2619 
2620 	cmn_err(CE_PANIC, "CPU %d FAILED TO SHUTDOWN", cpuid);
2621 }
2622 
2623 /* a helper routine to keep the math in one place */
2624 static processorid_t
2625 drmach_cpu_calc_id(drmach_device_t *dp)
2626 {
2627 	return (dp->bp->bnum * MAX_CPU_UNITS_PER_BOARD + dp->unum);
2628 }
2629 
2630 /*
2631  * Move bootproc (SIGBCPU) to another cpu.  If dst_cpu is NULL, a
2632  * destination cpu is chosen from the set of cpus not located on the
2633  * same board as the current bootproc cpu.
2634  */
2635 static sbd_error_t *
2636 drmach_cpu_juggle_bootproc(drmach_device_t *dst_cpu)
2637 {
2638 	processorid_t	 cpuid;
2639 	struct cpu	*cp;
2640 	sbd_error_t	*err;
2641 	int		 rv;
2642 
2643 	ASSERT(MUTEX_HELD(&cpu_lock));
2644 
2645 	/* dst_cpu is NULL when target cpu is unspecified. So, pick one. */
2646 	if (dst_cpu == NULL) {
2647 		int avoid_board = DRMACH_CPUID2BNUM(SIGBCPU->cpu_id);
2648 		int max_cpuid = MAX_BOARDS * MAX_CPU_UNITS_PER_BOARD;
2649 
2650 		for (cpuid = 0; cpuid < max_cpuid; cpuid++)
2651 			if (DRMACH_CPUID2BNUM(cpuid) != avoid_board) {
2652 				cp = cpu_get(cpuid);
2653 				if (cp != NULL && cpu_is_online(cp))
2654 					break;
2655 			}
2656 
2657 		if (cpuid == max_cpuid) {
2658 			err = drerr_new(1, ESTF_JUGGLE, NULL);
2659 			return (err);
2660 		}
2661 
2662 		/* else, cp points to the selected target cpu */
2663 	} else {
2664 		cpuid = drmach_cpu_calc_id(dst_cpu);
2665 
2666 		if ((cp = cpu_get(cpuid)) == NULL) {
2667 			err = drerr_new(1, ESTF_NODEV, "%s::%s",
2668 				dst_cpu->bp->cm.name, dst_cpu->cm.name);
2669 			return (err);
2670 		}
2671 
2672 		if (cpuid == SIGBCPU->cpu_id) {
2673 			cmn_err(CE_WARN,
2674 				"SIGBCPU(%d) same as new selection(%d)",
2675 				SIGBCPU->cpu_id, cpuid);
2676 
2677 			/* technically not an error, but a no-op */
2678 			return (NULL);
2679 		}
2680 	}
2681 
2682 	cmn_err(CE_NOTE, "?relocating SIGBCPU from %d to %d",
2683 		SIGBCPU->cpu_id, cpuid);
2684 
2685 	DRMACH_PR("moving SIGBCPU to CPU %d\n", cpuid);
2686 
2687 	/*
2688 	 * Tell OBP to initialize cvc-offset field of new CPU0
2689 	 * so that it's in sync with OBP and cvc_server
2690 	 */
2691 	prom_starfire_init_console(cpuid);
2692 
2693 	/*
2694 	 * Assign cvc to new cpu0's bbsram for I/O.  This has to be
2695 	 * done BEFORE cpu0 is moved via obp, since this logic
2696 	 * will cause obp_helper to switch to a different bbsram for
2697 	 * cvc I/O.  We don't want cvc writing to a buffer from which
2698 	 * nobody will pick up the data!
2699 	 */
2700 	cvc_assign_iocpu(cpuid);
2701 
2702 	rv = prom_starfire_move_cpu0(cpuid);
2703 
2704 	if (rv == 0) {
2705 		SIGBCPU = cp;
2706 
2707 		DRMACH_PR("successfully juggled to CPU %d\n", cpuid);
2708 		return (NULL);
2709 	} else {
2710 		DRMACH_PR("prom error: prom_starfire_move_cpu0(%d) "
2711 			"returned %d\n", cpuid, rv);
2712 
2713 		/*
2714 		 * The move failed, hopefully obp_helper is still back
2715 		 * at the old bootproc.  Move cvc back there.
2716 		 */
2717 		cvc_assign_iocpu(SIGBCPU->cpu_id);
2718 
2719 
2720 		err = drerr_new(1, ESTF_MOVESIGB, "CPU %d", cpuid);
2721 		return (err);
2722 	}
2723 	/*NOTREACHED*/
2724 }
2725 
2726 static sbd_error_t *
2727 drmach_cpu_release(drmachid_t id)
2728 {
2729 	drmach_device_t	*dp;
2730 	processorid_t	 cpuid;
2731 	struct cpu	*cp;
2732 	sbd_error_t	*err;
2733 
2734 	if (!DRMACH_IS_CPU_ID(id))
2735 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2736 	dp = id;
2737 	cpuid = drmach_cpu_calc_id(dp);
2738 
2739 	ASSERT(MUTEX_HELD(&cpu_lock));
2740 
2741 	cp = cpu_get(cpuid);
2742 	if (cp == NULL)
2743 		err = DRMACH_INTERNAL_ERROR();
2744 	else if (SIGBCPU->cpu_id == cp->cpu_id)
2745 		err = drmach_cpu_juggle_bootproc(NULL);
2746 	else
2747 		err = NULL;
2748 
2749 	return (err);
2750 }
2751 
2752 static sbd_error_t *
2753 drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
2754 {
2755 	drmach_device_t *dp;
2756 
2757 	ASSERT(DRMACH_IS_CPU_ID(id));
2758 	dp = id;
2759 
2760 	stat->assigned = dp->bp->assigned;
2761 	stat->powered = dp->bp->powered;
2762 	mutex_enter(&cpu_lock);
2763 	stat->configured = (cpu_get(drmach_cpu_calc_id(dp)) != NULL);
2764 	mutex_exit(&cpu_lock);
2765 	stat->busy = dp->busy;
2766 	strncpy(stat->type, dp->type, sizeof (stat->type));
2767 	stat->info[0] = '\0';
2768 
2769 	return (NULL);
2770 }
2771 
2772 sbd_error_t *
2773 drmach_cpu_disconnect(drmachid_t id)
2774 {
2775 	drmach_device_t	*cpu;
2776 	int		 cpuid;
2777 	int		 ntries;
2778 	int		 p;
2779 	u_longlong_t	 pc_addr;
2780 	uchar_t		 rvalue;
2781 
2782 	if (!DRMACH_IS_CPU_ID(id))
2783 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2784 	cpu = id;
2785 
2786 	cpuid = drmach_cpu_calc_id(cpu);
2787 	if (SIGBCPU->cpu_id == cpuid) {
2788 		/* this cpu is SIGBCPU, can't disconnect */
2789 		return (drerr_new(1, ESTF_HASSIGB, "%s::%s",
2790 				cpu->bp->cm.name, cpu->cm.name));
2791 	}
2792 
2793 	/*
2794 	 * Make sure SIGBST_DETACHED is set before
2795 	 * mapping out the sig block.
2796 	 */
2797 	ntries = drmach_cpu_ntries;
2798 	while (!drmach_cpu_obp_is_detached(cpuid) && ntries) {
2799 		DELAY(drmach_cpu_delay);
2800 		ntries--;
2801 	}
2802 	if (!drmach_cpu_obp_is_detached(cpuid)) {
2803 		cmn_err(CE_WARN, "failed to mark cpu %d detached in sigblock",
2804 			cpuid);
2805 	}
2806 
2807 	/* map out signature block */
2808 	if (CPU_SGN_EXISTS(cpuid)) {
2809 		CPU_SGN_MAPOUT(cpuid);
2810 	}
2811 
2812 	/*
2813 	 * We now PC IDLE the processor to guarantee we
2814 	 * stop any transactions from coming from it.
2815 	 */
2816 	p = cpu->unum & 1;
2817 	pc_addr = STARFIRE_BB_PC_ADDR(cpu->bp->bnum, cpu->unum, 0);
2818 
2819 	DRMACH_PR("PC idle cpu %d (addr = 0x%llx, port = %d, p = %d)",
2820 		drmach_cpu_calc_id(cpu), pc_addr, cpu->unum, p);
2821 
2822 	rvalue = ldbphysio(pc_addr);
2823 	rvalue |= STARFIRE_BB_PC_IDLE(p);
2824 	stbphysio(pc_addr, rvalue);
2825 	DELAY(50000);
2826 
2827 	return (NULL);
2828 }
2829 
2830 sbd_error_t *
2831 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
2832 {
2833 	drmach_device_t *cpu;
2834 
2835 	if (!DRMACH_IS_CPU_ID(id))
2836 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2837 	cpu = id;
2838 
2839 	*cpuid = drmach_cpu_calc_id(cpu);
2840 	return (NULL);
2841 }
2842 
2843 sbd_error_t *
2844 drmach_cpu_get_impl(drmachid_t id, int *ip)
2845 {
2846 	drmach_device_t *cpu;
2847 	int		impl;
2848 
2849 	if (!DRMACH_IS_CPU_ID(id))
2850 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2851 
2852 	cpu = id;
2853 
2854 	if (drmach_node_get_prop(cpu->node, "implementation#", &impl) == -1) {
2855 		return (DRMACH_INTERNAL_ERROR());
2856 	}
2857 
2858 	*ip = impl;
2859 
2860 	return (NULL);
2861 }
2862 
2863 void
2864 drmach_cpu_flush_ecache_sync(void)
2865 {
2866 	ASSERT(curthread->t_bound_cpu == CPU);
2867 
2868 	/*
2869 	 * Now let's flush our ecache thereby removing all references
2870 	 * to the target (detaching) memory from all ecache's in
2871 	 * system.
2872 	 */
2873 	cpu_flush_ecache();
2874 
2875 	/*
2876 	 * Delay 100 usec out of paranoia to insure everything
2877 	 * (hardware queues) has drained before we start reprogramming
2878 	 * the hardware.
2879 	 */
2880 	DELAY(100);
2881 }
2882 
2883 sbd_error_t *
2884 drmach_get_dip(drmachid_t id, dev_info_t **dip)
2885 {
2886 	drmach_device_t	*dp;
2887 
2888 	if (!DRMACH_IS_DEVICE_ID(id))
2889 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2890 	dp = id;
2891 
2892 	*dip = drmach_node_get_dip(dp->node);
2893 	return (NULL);
2894 }
2895 
2896 sbd_error_t *
2897 drmach_io_is_attached(drmachid_t id, int *yes)
2898 {
2899 	drmach_device_t *dp;
2900 	dev_info_t	*dip;
2901 	int		state;
2902 
2903 	if (!DRMACH_IS_IO_ID(id))
2904 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2905 	dp = id;
2906 
2907 	dip = drmach_node_get_dip(dp->node);
2908 	if (dip == NULL) {
2909 		*yes = 0;
2910 		return (NULL);
2911 	}
2912 
2913 	state = ddi_get_devstate(dip);
2914 	*yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) ||
2915 	    (state == DDI_DEVSTATE_UP));
2916 
2917 	return (NULL);
2918 }
2919 
2920 sbd_error_t *
2921 drmach_io_pre_release(drmachid_t id)
2922 {
2923 	if (!DRMACH_IS_IO_ID(id))
2924 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2925 	return (NULL);
2926 }
2927 
2928 static sbd_error_t *
2929 drmach_io_release(drmachid_t id)
2930 {
2931 	if (!DRMACH_IS_IO_ID(id))
2932 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2933 	return (NULL);
2934 }
2935 
2936 sbd_error_t *
2937 drmach_io_unrelease(drmachid_t id)
2938 {
2939 	if (!DRMACH_IS_IO_ID(id))
2940 		return (drerr_new(0, ESTF_INAPPROP, NULL));
2941 	return (NULL);
2942 }
2943 
2944 /*ARGSUSED*/
2945 sbd_error_t *
2946 drmach_io_post_release(drmachid_t id)
2947 {
2948 	return (NULL);
2949 }
2950 
2951 /*ARGSUSED*/
2952 sbd_error_t *
2953 drmach_io_post_attach(drmachid_t id)
2954 {
2955 	return (NULL);
2956 }
2957 
2958 static sbd_error_t *
2959 drmach_io_status(drmachid_t id, drmach_status_t *stat)
2960 {
2961 	drmach_device_t *dp;
2962 	sbd_error_t	*err;
2963 	int		 configured;
2964 
2965 	ASSERT(DRMACH_IS_IO_ID(id));
2966 	dp = id;
2967 
2968 	err = drmach_io_is_attached(id, &configured);
2969 	if (err)
2970 		return (err);
2971 
2972 	stat->assigned = dp->bp->assigned;
2973 	stat->powered = dp->bp->powered;
2974 	stat->configured = (configured != 0);
2975 	stat->busy = dp->busy;
2976 	strncpy(stat->type, dp->type, sizeof (stat->type));
2977 	stat->info[0] = '\0';
2978 
2979 	return (NULL);
2980 }
2981 
2982 static sbd_error_t *
2983 drmach_mem_new(drmach_device_t *dp)
2984 {
2985 	static sbd_error_t *drmach_mem_release(drmachid_t);
2986 	static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
2987 
2988 	dp->unum = 0;
2989 	dp->cm.isa = (void *)drmach_mem_new;
2990 	dp->cm.release = drmach_mem_release;
2991 	dp->cm.status = drmach_mem_status;
2992 
2993 	snprintf(dp->cm.name, sizeof (dp->cm.name), "%s", dp->type);
2994 
2995 	return (NULL);
2996 }
2997 
2998 sbd_error_t *
2999 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
3000 {
3001 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
3002 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
3003 	pda_handle_t	ph;
3004 	int		rv;
3005 
3006 	ASSERT(size != 0);
3007 
3008 	if (!DRMACH_IS_MEM_ID(id))
3009 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3010 
3011 	kcage_range_lock();
3012 	rv = kcage_range_add(basepfn, npages, 1);
3013 	kcage_range_unlock();
3014 	if (rv == ENOMEM) {
3015 		cmn_err(CE_WARN, "%lld megabytes not available to kernel cage",
3016 			(size == 0 ? 0 : size / MBYTE));
3017 	} else if (rv != 0) {
3018 		/* catch this in debug kernels */
3019 		ASSERT(0);
3020 
3021 		cmn_err(CE_WARN, "unexpected kcage_range_add"
3022 			" return value %d", rv);
3023 	}
3024 
3025 	/*
3026 	 * Update the PDA (post2obp) structure with the
3027 	 * range of the newly added memory.
3028 	 */
3029 	ph = drmach_pda_open();
3030 	if (ph != NULL) {
3031 		pda_mem_add_span(ph, basepa, size);
3032 		pda_close(ph);
3033 	}
3034 
3035 	return (NULL);
3036 }
3037 
3038 sbd_error_t *
3039 drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
3040 {
3041 	drmach_device_t	*mem = id;
3042 	pfn_t		basepfn = (pfn_t)(basepa >> PAGESHIFT);
3043 	pgcnt_t		npages = (pgcnt_t)(size >> PAGESHIFT);
3044 	uint_t		mcreg;
3045 	sbd_error_t	*err;
3046 	pda_handle_t	ph;
3047 	int		rv;
3048 
3049 	err = drmach_read_mc_asr(id, &mcreg);
3050 	if (err)
3051 		return (err);
3052 	else if (mcreg & STARFIRE_MC_INTERLEAVE_MASK) {
3053 		return (drerr_new(1, ESTF_INTERBOARD, "%s::%s",
3054 				mem->bp->cm.name, mem->cm.name));
3055 	}
3056 
3057 	if (size > 0) {
3058 		kcage_range_lock();
3059 		rv = kcage_range_delete_post_mem_del(basepfn, npages);
3060 		kcage_range_unlock();
3061 		if (rv != 0) {
3062 			cmn_err(CE_WARN,
3063 			    "unexpected kcage_range_delete_post_mem_del"
3064 			    " return value %d", rv);
3065 			return (DRMACH_INTERNAL_ERROR());
3066 		}
3067 	}
3068 
3069 	/*
3070 	 * Update the PDA (post2obp) structure with the
3071 	 * range of removed memory.
3072 	 */
3073 	ph = drmach_pda_open();
3074 	if (ph != NULL) {
3075 		if (size > 0)
3076 			pda_mem_del_span(ph, basepa, size);
3077 
3078 		/* update PDA to board's new mc register settings */
3079 		pda_mem_sync(ph, mem->bp->bnum, 0);
3080 
3081 		pda_close(ph);
3082 	}
3083 
3084 	return (NULL);
3085 }
3086 
3087 /* support routine for enable and disable */
3088 static sbd_error_t *
3089 drmach_mem_update_interconnect(drmachid_t id, uint_t mcreg)
3090 {
3091 	drmach_device_t	*dp;
3092 	pda_handle_t	 ph;
3093 	int		 b;
3094 
3095 	if (!DRMACH_IS_MEM_ID(id))
3096 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3097 	dp = id;
3098 
3099 	ph = drmach_pda_open();
3100 	if (ph == NULL)
3101 		return (DRMACH_INTERNAL_ERROR());
3102 
3103 	for (b = 0; b < MAX_BOARDS; b++) {
3104 		int		p;
3105 		int		rv;
3106 		ushort_t	bda_proc, bda_ioc;
3107 		board_desc_t	*bdesc;
3108 
3109 		if (pda_board_present(ph, b) == 0)
3110 			continue;
3111 
3112 		bdesc = (board_desc_t *)pda_get_board_info(ph, b);
3113 
3114 		/*
3115 		 * Update PCs for CPUs.
3116 		 */
3117 
3118 		/* make sure definition in platmod is in sync with pda */
3119 		ASSERT(MAX_PROCMODS == MAX_CPU_UNITS_PER_BOARD);
3120 
3121 		bda_proc = bdesc->bda_proc;
3122 		for (p = 0; p < MAX_PROCMODS; p++) {
3123 			if (BDA_NBL(bda_proc, p) != BDAN_GOOD)
3124 				continue;
3125 
3126 			rv = pc_madr_add(b, dp->bp->bnum, p, mcreg);
3127 			if (rv) {
3128 				pda_close(ph);
3129 				return (DRMACH_INTERNAL_ERROR());
3130 			}
3131 		}
3132 
3133 		/*
3134 		 * Update PCs for IOCs.
3135 		 */
3136 
3137 		/* make sure definition in platmod is in sync with pda */
3138 		ASSERT(MAX_IOCS == MAX_IO_UNITS_PER_BOARD);
3139 
3140 		bda_ioc = bdesc->bda_ioc;
3141 		for (p = 0; p < MAX_IOCS; p++) {
3142 			if (BDA_NBL(bda_ioc, p) != BDAN_GOOD)
3143 				continue;
3144 
3145 			rv = pc_madr_add(b, dp->bp->bnum, p + 4, mcreg);
3146 			if (rv) {
3147 				pda_close(ph);
3148 				return (DRMACH_INTERNAL_ERROR());
3149 			}
3150 		}
3151 	}
3152 
3153 	pda_close(ph);
3154 	return (NULL);
3155 }
3156 
3157 sbd_error_t *
3158 drmach_mem_disable(drmachid_t id)
3159 {
3160 	sbd_error_t	*err;
3161 	uint_t		 mcreg;
3162 
3163 	err = drmach_read_mc_asr(id, &mcreg);
3164 	if (err == NULL) {
3165 		ASSERT(mcreg & STARFIRE_MC_MEM_PRESENT_MASK);
3166 
3167 		/* Turn off presence bit. */
3168 		mcreg &= ~STARFIRE_MC_MEM_PRESENT_MASK;
3169 
3170 		err = drmach_mem_update_interconnect(id, mcreg);
3171 		if (err == NULL)
3172 			err = drmach_write_mc_asr(id, mcreg);
3173 	}
3174 
3175 	return (err);
3176 }
3177 
3178 sbd_error_t *
3179 drmach_mem_enable(drmachid_t id)
3180 {
3181 	sbd_error_t	*err;
3182 	uint_t		 mcreg;
3183 
3184 	err = drmach_read_mc_asr(id, &mcreg);
3185 	if (err == NULL) {
3186 		mcreg |= STARFIRE_MC_MEM_PRESENT_MASK;
3187 
3188 		err = drmach_write_mc_asr(id, mcreg);
3189 		if (err == NULL)
3190 			err = drmach_mem_update_interconnect(id, mcreg);
3191 	}
3192 
3193 	return (err);
3194 }
3195 
3196 sbd_error_t *
3197 drmach_mem_get_alignment(drmachid_t id, uint64_t *mask)
3198 {
3199 	drmach_device_t	*mem;
3200 	sbd_error_t	*err;
3201 	pnode_t		 nodeid;
3202 
3203 	if (!DRMACH_IS_MEM_ID(id))
3204 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3205 	mem = id;
3206 
3207 	nodeid = drmach_node_get_dnode(mem->node);
3208 	if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
3209 		err = DRMACH_INTERNAL_ERROR();
3210 	else {
3211 		uint64_t size;
3212 
3213 		size = mc_get_alignment_mask(nodeid);
3214 		if (size == (uint64_t)-1)
3215 			err = DRMACH_INTERNAL_ERROR();
3216 		else {
3217 			*mask = size - 1;
3218 			err = NULL;
3219 		}
3220 	}
3221 
3222 	return (err);
3223 }
3224 
3225 sbd_error_t *
3226 drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa)
3227 {
3228 	sbd_error_t	*err;
3229 	uint_t		 mcreg;
3230 
3231 	err = drmach_read_mc_asr(id, &mcreg);
3232 	if (err == NULL)
3233 		*pa = mc_asr_to_pa(mcreg);
3234 
3235 	return (err);
3236 }
3237 
3238 /*
3239  * Use of this routine after copy/rename will yield incorrect results,
3240  * because the OBP MEMAVAIL property will not correctly reflect the
3241  * programming of the MCs.
3242  */
3243 sbd_error_t *
3244 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
3245 {
3246 	drmach_device_t	*mem;
3247 	int		rv, i, rlen, rblks;
3248 	sbd_error_t	*err;
3249 	struct memlist	*mlist;
3250 	struct sf_memunit_regspec *rlist;
3251 
3252 	if (!DRMACH_IS_MEM_ID(id))
3253 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3254 	mem = id;
3255 
3256 	err = drmach_device_get_proplen(mem, "dr-available", &rlen);
3257 	if (err)
3258 		return (err);
3259 
3260 	rlist = kmem_zalloc(rlen, KM_SLEEP);
3261 
3262 	err = drmach_device_get_prop(mem, "dr-available", rlist);
3263 	if (err) {
3264 		kmem_free(rlist, rlen);
3265 		return (err);
3266 	}
3267 
3268 	mlist = NULL;
3269 	rblks = rlen / sizeof (struct sf_memunit_regspec);
3270 	for (i = 0; i < rblks; i++) {
3271 		uint64_t	addr, size;
3272 
3273 		addr  = (uint64_t)rlist[i].regspec_addr_hi << 32;
3274 		addr |= (uint64_t)rlist[i].regspec_addr_lo;
3275 		size  = (uint64_t)rlist[i].regspec_size_hi << 32;
3276 		size |= (uint64_t)rlist[i].regspec_size_lo;
3277 
3278 		mlist = memlist_add_span(mlist, addr, size);
3279 	}
3280 
3281 	kmem_free(rlist, rlen);
3282 
3283 	/*
3284 	 * Make sure the incoming memlist doesn't already
3285 	 * intersect with what's present in the system (phys_install).
3286 	 */
3287 	memlist_read_lock();
3288 	rv = memlist_intersect(phys_install, mlist);
3289 	memlist_read_unlock();
3290 	if (rv) {
3291 #ifdef DEBUG
3292 		DRMACH_PR("OBP derived memlist intersects"
3293 			" with phys_install\n");
3294 		memlist_dump(mlist);
3295 
3296 		DRMACH_PR("phys_install memlist:\n");
3297 		memlist_dump(phys_install);
3298 #endif
3299 
3300 		memlist_delete(mlist);
3301 		return (DRMACH_INTERNAL_ERROR());
3302 	}
3303 
3304 #ifdef DEBUG
3305 	DRMACH_PR("OBP derived memlist:");
3306 	memlist_dump(mlist);
3307 #endif
3308 
3309 	*ml = mlist;
3310 	return (NULL);
3311 }
3312 
3313 sbd_error_t *
3314 drmach_mem_get_size(drmachid_t id, uint64_t *bytes)
3315 {
3316 	drmach_device_t	*mem;
3317 	pda_handle_t	ph;
3318 	pgcnt_t		npages;
3319 
3320 	if (!DRMACH_IS_MEM_ID(id))
3321 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3322 	mem = id;
3323 
3324 	ph = drmach_pda_open();
3325 	if (ph == NULL)
3326 		return (DRMACH_INTERNAL_ERROR());
3327 
3328 	npages = pda_get_mem_size(ph, mem->bp->bnum);
3329 	*bytes = (uint64_t)npages << PAGESHIFT;
3330 
3331 	pda_close(ph);
3332 	return (NULL);
3333 }
3334 
3335 sbd_error_t *
3336 drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
3337 {
3338 	if (!DRMACH_IS_MEM_ID(id))
3339 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3340 
3341 	*bytes = mc_get_mem_alignment();
3342 	return (NULL);
3343 }
3344 
3345 /* field debugging tool */
3346 processorid_t drmach_mem_cpu_affinity_nail = 0;
3347 
3348 processorid_t
3349 drmach_mem_cpu_affinity(drmachid_t id)
3350 {
3351 	drmach_device_t	*mp;
3352 	drmach_board_t	*bp;
3353 	processorid_t	 cpuid;
3354 
3355 	if (!DRMACH_IS_MEM_ID(id))
3356 		return (CPU_CURRENT);
3357 
3358 	if (drmach_mem_cpu_affinity_nail) {
3359 		cpuid = drmach_mem_cpu_affinity_nail;
3360 
3361 		if (cpuid < 0 || cpuid > NCPU)
3362 			return (CPU_CURRENT);
3363 
3364 		mutex_enter(&cpu_lock);
3365 		if (cpu[cpuid] == NULL || !CPU_ACTIVE(cpu[cpuid]))
3366 			cpuid = CPU_CURRENT;
3367 		mutex_exit(&cpu_lock);
3368 
3369 		return (cpuid);
3370 	}
3371 
3372 	/* try to choose a proc on the target board */
3373 	mp = id;
3374 	bp = mp->bp;
3375 	if (bp->devices) {
3376 		int		rv;
3377 		int		d_idx;
3378 		drmachid_t	d_id;
3379 
3380 		rv = drmach_array_first(bp->devices, &d_idx, &d_id);
3381 		while (rv == 0) {
3382 			if (DRMACH_IS_CPU_ID(d_id)) {
3383 				cpuid = drmach_cpu_calc_id(d_id);
3384 
3385 				mutex_enter(&cpu_lock);
3386 				if (cpu[cpuid] && CPU_ACTIVE(cpu[cpuid])) {
3387 					mutex_exit(&cpu_lock);
3388 					DRMACH_PR("drmach_mem_cpu_affinity: "
3389 					    "selected cpuid=%d\n", cpuid);
3390 					return (cpuid);
3391 				} else {
3392 					mutex_exit(&cpu_lock);
3393 				}
3394 			}
3395 
3396 			rv = drmach_array_next(bp->devices, &d_idx, &d_id);
3397 		}
3398 	}
3399 
3400 	/* otherwise, this proc, wherever it is */
3401 	DRMACH_PR("drmach_mem_cpu_affinity: using default CPU_CURRENT\n");
3402 
3403 	return (CPU_CURRENT);
3404 }
3405 
3406 static sbd_error_t *
3407 drmach_mem_release(drmachid_t id)
3408 {
3409 	if (!DRMACH_IS_MEM_ID(id))
3410 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3411 	return (NULL);
3412 }
3413 
3414 static sbd_error_t *
3415 drmach_mem_status(drmachid_t id, drmach_status_t *stat)
3416 {
3417 	drmach_device_t *dp;
3418 	sbd_error_t	*err;
3419 	uint64_t	 pa, slice_size;
3420 	struct memlist	*ml;
3421 
3422 	ASSERT(DRMACH_IS_MEM_ID(id));
3423 	dp = id;
3424 
3425 	/* get starting physical address of target memory */
3426 	err = drmach_mem_get_base_physaddr(id, &pa);
3427 	if (err)
3428 		return (err);
3429 
3430 	/* round down to slice boundary */
3431 	slice_size = mc_get_mem_alignment();
3432 	pa &= ~ (slice_size - 1);
3433 
3434 	/* stop at first span that is in slice */
3435 	memlist_read_lock();
3436 	for (ml = phys_install; ml; ml = ml->next)
3437 		if (ml->address >= pa && ml->address < pa + slice_size)
3438 			break;
3439 	memlist_read_unlock();
3440 
3441 	stat->assigned = dp->bp->assigned;
3442 	stat->powered = dp->bp->powered;
3443 	stat->configured = (ml != NULL);
3444 	stat->busy = dp->busy;
3445 	strncpy(stat->type, dp->type, sizeof (stat->type));
3446 	stat->info[0] = '\0';
3447 
3448 	return (NULL);
3449 }
3450 
3451 static int
3452 drmach_detach_board(void *arg)
3453 {
3454 	cpuset_t	cset;
3455 	int		retval;
3456 	drmach_board_t	*bp = (drmach_board_t *)arg;
3457 
3458 	cset = cpu_ready_set;
3459 	promsafe_xc_attention(cset);
3460 
3461 	retval = prom_starfire_rm_brd(bp->bnum);
3462 
3463 	xc_dismissed(cset);
3464 
3465 	return (retval);
3466 }
3467 
3468 sbd_error_t *
3469 drmach_board_deprobe(drmachid_t id)
3470 {
3471 	drmach_board_t	*bp;
3472 	int		 retval;
3473 
3474 	if (!DRMACH_IS_BOARD_ID(id))
3475 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3476 	bp = id;
3477 
3478 	cmn_err(CE_CONT, "DR: PROM detach board %d\n", bp->bnum);
3479 
3480 	retval = prom_tree_update(drmach_detach_board, bp);
3481 
3482 	if (retval == 0)
3483 		return (NULL);
3484 	else {
3485 		cmn_err(CE_WARN, "prom error: prom_starfire_rm_brd(%d) "
3486 			"returned %d", bp->bnum, retval);
3487 		return (drerr_new(1, ESTF_DEPROBE, "%s", bp->cm.name));
3488 	}
3489 }
3490 
3491 /*ARGSUSED*/
3492 static sbd_error_t *
3493 drmach_pt_juggle_bootproc(drmachid_t id, drmach_opts_t *opts)
3494 {
3495 	drmach_device_t	*cpu;
3496 	sbd_error_t	*err;
3497 
3498 	if (!DRMACH_IS_CPU_ID(id))
3499 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3500 	cpu = id;
3501 
3502 	mutex_enter(&cpu_lock);
3503 
3504 	err = drmach_cpu_juggle_bootproc(cpu);
3505 
3506 	mutex_exit(&cpu_lock);
3507 
3508 	return (err);
3509 }
3510 
3511 /*ARGSUSED*/
3512 static sbd_error_t *
3513 drmach_pt_dump_pdainfo(drmachid_t id, drmach_opts_t *opts)
3514 {
3515 	drmach_board_t	*bp;
3516 	int		board;
3517 	int		i;
3518 	pda_handle_t	ph;
3519 	board_desc_t	*bdesc;
3520 
3521 	if (!DRMACH_IS_BOARD_ID(id))
3522 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3523 	bp = id;
3524 	board = bp->bnum;
3525 
3526 	ph = drmach_pda_open();
3527 	if (ph == NULL)
3528 		return (DRMACH_INTERNAL_ERROR());
3529 
3530 	if (pda_board_present(ph, board) == 0) {
3531 		cmn_err(CE_CONT, "board %d is MISSING\n", board);
3532 		pda_close(ph);
3533 		return (DRMACH_INTERNAL_ERROR());
3534 	}
3535 
3536 	cmn_err(CE_CONT, "board %d is PRESENT\n", board);
3537 
3538 	bdesc = (board_desc_t *)pda_get_board_info(ph, board);
3539 	if (bdesc == NULL) {
3540 		cmn_err(CE_CONT,
3541 			"no board descriptor found for board %d\n",
3542 			board);
3543 		pda_close(ph);
3544 		return (DRMACH_INTERNAL_ERROR());
3545 	}
3546 
3547 	/* make sure definition in platmod is in sync with pda */
3548 	ASSERT(MAX_PROCMODS == MAX_CPU_UNITS_PER_BOARD);
3549 
3550 	for (i = 0; i < MAX_PROCMODS; i++) {
3551 		if (BDA_NBL(bdesc->bda_proc, i) == BDAN_GOOD)
3552 			cmn_err(CE_CONT,
3553 				"proc %d.%d PRESENT\n", board, i);
3554 		else
3555 			cmn_err(CE_CONT,
3556 				"proc %d.%d MISSING\n", board, i);
3557 	}
3558 
3559 	for (i = 0; i < MAX_MGROUPS; i++) {
3560 		if (BDA_NBL(bdesc->bda_mgroup, i) == BDAN_GOOD)
3561 			cmn_err(CE_CONT,
3562 				"mgroup %d.%d PRESENT\n", board, i);
3563 		else
3564 			cmn_err(CE_CONT,
3565 				"mgroup %d.%d MISSING\n", board, i);
3566 	}
3567 
3568 	/* make sure definition in platmod is in sync with pda */
3569 	ASSERT(MAX_IOCS == MAX_IO_UNITS_PER_BOARD);
3570 
3571 	for (i = 0; i < MAX_IOCS; i++) {
3572 		int	s;
3573 
3574 		if (BDA_NBL(bdesc->bda_ioc, i) == BDAN_GOOD) {
3575 			cmn_err(CE_CONT,
3576 				"ioc %d.%d PRESENT\n", board, i);
3577 			for (s = 0; s < MAX_SLOTS_PER_IOC; s++) {
3578 				if (BDA_NBL(bdesc->bda_ios[i], s) != BDAN_GOOD)
3579 					continue;
3580 				cmn_err(CE_CONT,
3581 					"..scard %d.%d.%d PRESENT\n",
3582 					board, i, s);
3583 			}
3584 		} else {
3585 			cmn_err(CE_CONT,
3586 				"ioc %d.%d MISSING\n",
3587 				board, i);
3588 		}
3589 	}
3590 
3591 	cmn_err(CE_CONT,
3592 		"board %d memsize = %d pages\n",
3593 		board, pda_get_mem_size(ph, board));
3594 
3595 	pda_close(ph);
3596 
3597 	return (NULL);
3598 }
3599 
3600 /*ARGSUSED*/
3601 sbd_error_t *
3602 drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
3603 {
3604 	struct memlist	*ml;
3605 	uint64_t	src_pa;
3606 	uint64_t	dst_pa;
3607 	uint64_t	dst;
3608 
3609 	dst_pa = va_to_pa(&dst);
3610 
3611 	memlist_read_lock();
3612 	for (ml = phys_install; ml; ml = ml->next) {
3613 		uint64_t	nbytes;
3614 
3615 		src_pa = ml->address;
3616 		nbytes = ml->size;
3617 
3618 		while (nbytes != 0ull) {
3619 
3620 			/* copy 32 bytes at arc_pa to dst_pa */
3621 			bcopy32_il(src_pa, dst_pa);
3622 
3623 			/* increment by 32 bytes */
3624 			src_pa += (4 * sizeof (uint64_t));
3625 
3626 			/* decrement by 32 bytes */
3627 			nbytes -= (4 * sizeof (uint64_t));
3628 		}
3629 	}
3630 	memlist_read_unlock();
3631 
3632 	return (NULL);
3633 }
3634 
3635 static struct {
3636 	const char	*name;
3637 	sbd_error_t	*(*handler)(drmachid_t id, drmach_opts_t *opts);
3638 } drmach_pt_arr[] = {
3639 	{ "juggle",		drmach_pt_juggle_bootproc	},
3640 	{ "pda",		drmach_pt_dump_pdainfo		},
3641 	{ "readmem",		drmach_pt_readmem		},
3642 
3643 	/* the following line must always be last */
3644 	{ NULL,			NULL				}
3645 };
3646 
3647 /*ARGSUSED*/
3648 sbd_error_t *
3649 drmach_passthru(drmachid_t id, drmach_opts_t *opts)
3650 {
3651 	int		i;
3652 	sbd_error_t	*err;
3653 
3654 	i = 0;
3655 	while (drmach_pt_arr[i].name != NULL) {
3656 		int len = strlen(drmach_pt_arr[i].name);
3657 
3658 		if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
3659 			break;
3660 
3661 		i += 1;
3662 	}
3663 
3664 	if (drmach_pt_arr[i].name == NULL)
3665 		err = drerr_new(0, ESTF_UNKPTCMD, opts->copts);
3666 	else
3667 		err = (*drmach_pt_arr[i].handler)(id, opts);
3668 
3669 	return (err);
3670 }
3671 
3672 sbd_error_t *
3673 drmach_release(drmachid_t id)
3674 {
3675 	drmach_common_t *cp;
3676 	if (!DRMACH_IS_DEVICE_ID(id))
3677 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3678 	cp = id;
3679 
3680 	return (cp->release(id));
3681 }
3682 
3683 sbd_error_t *
3684 drmach_status(drmachid_t id, drmach_status_t *stat)
3685 {
3686 	drmach_common_t *cp;
3687 
3688 	if (!DRMACH_IS_ID(id))
3689 		return (drerr_new(0, ESTF_NOTID, NULL));
3690 	cp = id;
3691 
3692 	return (cp->status(id, stat));
3693 }
3694 
3695 sbd_error_t *
3696 drmach_unconfigure(drmachid_t id, int flags)
3697 {
3698 	drmach_device_t	*dp;
3699 	pnode_t		 nodeid;
3700 	dev_info_t	*dip, *fdip = NULL;
3701 	uint_t 		ddi_flags;
3702 
3703 	if (!DRMACH_IS_DEVICE_ID(id))
3704 		return (drerr_new(0, ESTF_INAPPROP, NULL));
3705 
3706 	dp = id;
3707 
3708 	nodeid = drmach_node_get_dnode(dp->node);
3709 	if (nodeid == OBP_NONODE)
3710 		return (DRMACH_INTERNAL_ERROR());
3711 
3712 	dip = e_ddi_nodeid_to_dip(nodeid);
3713 	if (dip == NULL)
3714 		return (NULL);
3715 
3716 	/*
3717 	 * Branch already held, so hold acquired in
3718 	 * e_ddi_nodeid_to_dip() can be released
3719 	 */
3720 	ddi_release_devi(dip);
3721 
3722 	ddi_flags = 0;
3723 
3724 	if (flags & DRMACH_DEVI_REMOVE)
3725 		ddi_flags |= DEVI_BRANCH_DESTROY | DEVI_BRANCH_EVENT;
3726 
3727 	/*
3728 	 * Force flag is no longer necessary. See starcat/io/drmach.c
3729 	 * for details.
3730 	 */
3731 	ASSERT(e_ddi_branch_held(dip));
3732 	if (e_ddi_branch_unconfigure(dip, &fdip, ddi_flags)) {
3733 		sbd_error_t	*err;
3734 		char		*path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
3735 
3736 		/*
3737 		 * If non-NULL, fdip is returned held and must be released.
3738 		 */
3739 		if (fdip != NULL) {
3740 			(void) ddi_pathname(fdip, path);
3741 			ndi_rele_devi(fdip);
3742 		} else {
3743 			(void) ddi_pathname(dip, path);
3744 		}
3745 
3746 		err = drerr_new(1, ESTF_DRVFAIL, path);
3747 
3748 		kmem_free(path, MAXPATHLEN);
3749 
3750 		return (err);
3751 	}
3752 
3753 	return (NULL);
3754 }
3755 
3756 /*
3757  * drmach interfaces to legacy Starfire platmod logic
3758  * linkage via runtime symbol look up, called from plat_cpu_power*
3759  */
3760 
3761 /*
3762  * Start up a cpu.  It is possible that we're attempting to restart
3763  * the cpu after an UNCONFIGURE in which case the cpu will be
3764  * spinning in its cache.  So, all we have to do is wakeup him up.
3765  * Under normal circumstances the cpu will be coming from a previous
3766  * CONNECT and thus will be spinning in OBP.  In both cases, the
3767  * startup sequence is the same.
3768  */
3769 int
3770 drmach_cpu_poweron(struct cpu *cp)
3771 {
3772 	DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
3773 
3774 	ASSERT(MUTEX_HELD(&cpu_lock));
3775 
3776 	if (drmach_cpu_start(cp) != 0)
3777 		return (EBUSY);
3778 	else
3779 		return (0);
3780 }
3781 
3782 int
3783 drmach_cpu_poweroff(struct cpu *cp)
3784 {
3785 	int		ntries, cnt;
3786 	processorid_t	cpuid = cp->cpu_id;
3787 	void		drmach_cpu_shutdown_self(void);
3788 
3789 	DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
3790 
3791 	ASSERT(MUTEX_HELD(&cpu_lock));
3792 
3793 	/*
3794 	 * Capture all CPUs (except for detaching proc) to prevent
3795 	 * crosscalls to the detaching proc until it has cleared its
3796 	 * bit in cpu_ready_set.
3797 	 *
3798 	 * The CPU's remain paused and the prom_mutex is known to be free.
3799 	 * This prevents the x-trap victim from blocking when doing prom
3800 	 * IEEE-1275 calls at a high PIL level.
3801 	 */
3802 	promsafe_pause_cpus();
3803 
3804 	/*
3805 	 * Quiesce interrupts on the target CPU. We do this by setting
3806 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
3807 	 * prevent it from receiving cross calls and cross traps.
3808 	 * This prevents the processor from receiving any new soft interrupts.
3809 	 */
3810 	mp_cpu_quiesce(cp);
3811 
3812 	/* setup xt_mb, will be cleared by drmach_shutdown_asm when ready */
3813 	drmach_xt_mb[cpuid] = 0x80;
3814 
3815 	xt_one_unchecked(cpuid, (xcfunc_t *)idle_stop_xcall,
3816 		(uint64_t)drmach_cpu_shutdown_self, NULL);
3817 
3818 	ntries = drmach_cpu_ntries;
3819 	cnt = 0;
3820 	while (drmach_xt_mb[cpuid] && ntries) {
3821 		DELAY(drmach_cpu_delay);
3822 		ntries--;
3823 		cnt++;
3824 	}
3825 
3826 	drmach_xt_mb[cpuid] = 0;	/* steal the cache line back */
3827 
3828 	start_cpus();
3829 
3830 	DRMACH_PR("waited %d out of %d tries for "
3831 		"drmach_cpu_shutdown_self on cpu%d",
3832 		drmach_cpu_ntries - ntries, drmach_cpu_ntries, cp->cpu_id);
3833 
3834 	drmach_cpu_obp_detach(cpuid);
3835 
3836 	CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
3837 
3838 	return (0);
3839 }
3840 
3841 /*ARGSUSED*/
3842 int
3843 drmach_verify_sr(dev_info_t *dip, int sflag)
3844 {
3845 	return (0);
3846 }
3847 
3848 void
3849 drmach_suspend_last(void)
3850 {
3851 }
3852 
3853 void
3854 drmach_resume_first(void)
3855 {
3856 }
3857 
3858 /*
3859  * Log a DR sysevent.
3860  * Return value: 0 success, non-zero failure.
3861  */
3862 int
3863 drmach_log_sysevent(int board, char *hint, int flag, int verbose)
3864 {
3865 	sysevent_t			*ev;
3866 	sysevent_id_t			eid;
3867 	int				rv, km_flag;
3868 	sysevent_value_t		evnt_val;
3869 	sysevent_attr_list_t		*evnt_attr_list = NULL;
3870 	char				attach_pnt[MAXNAMELEN];
3871 
3872 	km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
3873 	attach_pnt[0] = '\0';
3874 	if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
3875 		rv = -1;
3876 		goto logexit;
3877 	}
3878 	if (verbose)
3879 		DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
3880 			    attach_pnt, hint, flag, verbose);
3881 
3882 	if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
3883 				    SUNW_KERN_PUB"dr", km_flag)) == NULL) {
3884 		rv = -2;
3885 		goto logexit;
3886 	}
3887 	evnt_val.value_type = SE_DATA_TYPE_STRING;
3888 	evnt_val.value.sv_string = attach_pnt;
3889 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID,
3890 				    &evnt_val, km_flag)) != 0)
3891 		goto logexit;
3892 
3893 	evnt_val.value_type = SE_DATA_TYPE_STRING;
3894 	evnt_val.value.sv_string = hint;
3895 	if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT,
3896 				    &evnt_val, km_flag)) != 0) {
3897 		sysevent_free_attr(evnt_attr_list);
3898 		goto logexit;
3899 	}
3900 
3901 	(void) sysevent_attach_attributes(ev, evnt_attr_list);
3902 
3903 	/*
3904 	 * Log the event but do not sleep waiting for its
3905 	 * delivery. This provides insulation from syseventd.
3906 	 */
3907 	rv = log_sysevent(ev, SE_NOSLEEP, &eid);
3908 
3909 logexit:
3910 	if (ev)
3911 		sysevent_free(ev);
3912 	if ((rv != 0) && verbose)
3913 		cmn_err(CE_WARN,
3914 			    "drmach_log_sysevent failed (rv %d) for %s  %s\n",
3915 			    rv, attach_pnt, hint);
3916 
3917 	return (rv);
3918 }
3919 
3920 /*ARGSUSED*/
3921 int
3922 drmach_allow_memrange_modify(drmachid_t id)
3923 {
3924 	return (1);	/* TRUE */
3925 }
3926