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