xref: /illumos-gate/usr/src/uts/sun4u/ngdr/io/dr.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * PIM-DR layer of DR driver.  Provides interface between user
31  * level applications and the PSM-DR layer.
32  */
33 
34 #include <sys/note.h>
35 #include <sys/debug.h>
36 #include <sys/types.h>
37 #include <sys/errno.h>
38 #include <sys/cred.h>
39 #include <sys/dditypes.h>
40 #include <sys/devops.h>
41 #include <sys/modctl.h>
42 #include <sys/poll.h>
43 #include <sys/conf.h>
44 #include <sys/ddi.h>
45 #include <sys/sunddi.h>
46 #include <sys/sunndi.h>
47 #include <sys/stat.h>
48 #include <sys/kmem.h>
49 #include <sys/processor.h>
50 #include <sys/cpuvar.h>
51 #include <sys/mem_config.h>
52 
53 #include <sys/autoconf.h>
54 #include <sys/cmn_err.h>
55 
56 #include <sys/ddi_impldefs.h>
57 #include <sys/promif.h>
58 #include <sys/machsystm.h>
59 
60 #include <sys/dr.h>
61 #include <sys/drmach.h>
62 #include <sys/dr_util.h>
63 
64 extern int		 nulldev();
65 extern int		 nodev();
66 extern struct memlist	*phys_install;
67 
68 #ifdef DEBUG
69 uint_t	dr_debug = 0;			/* dr.h for bit values */
70 #endif /* DEBUG */
71 
72 /*
73  * NOTE: state_str, nt_str and SBD_CMD_STR are only used in a debug
74  * kernel.  They are, however, referenced during both debug and non-debug
75  * compiles.
76  */
77 
78 static char *state_str[] = {
79 	"EMPTY", "OCCUPIED", "CONNECTED", "UNCONFIGURED",
80 	"PARTIAL", "CONFIGURED", "RELEASE", "UNREFERENCED",
81 	"FATAL"
82 };
83 
84 #define	SBD_CMD_STR(c) \
85 	(((c) == SBD_CMD_ASSIGN)	? "ASSIGN"	: \
86 	((c) == SBD_CMD_UNASSIGN)	? "UNASSIGN"	: \
87 	((c) == SBD_CMD_POWERON)	? "POWERON"	: \
88 	((c) == SBD_CMD_POWEROFF)	? "POWEROFF"	: \
89 	((c) == SBD_CMD_TEST)		? "TEST"	: \
90 	((c) == SBD_CMD_CONNECT)	? "CONNECT"	: \
91 	((c) == SBD_CMD_DISCONNECT)	? "DISCONNECT"	: \
92 	((c) == SBD_CMD_CONFIGURE)	? "CONFIGURE"	: \
93 	((c) == SBD_CMD_UNCONFIGURE)	? "UNCONFIGURE"	: \
94 	((c) == SBD_CMD_GETNCM)		? "GETNCM"	: \
95 	((c) == SBD_CMD_PASSTHRU)	? "PASSTHRU"	: \
96 	((c) == SBD_CMD_STATUS)		? "STATUS"	: "unknown")
97 
98 #define	DR_GET_BOARD_DEVUNIT(sb, ut, un) (&((sb)->b_dev[NIX(ut)][un]))
99 
100 #define	DR_MAKE_MINOR(i, b)	(((i) << 16) | (b))
101 #define	DR_MINOR2INST(m)	(((m) >> 16) & 0xffff)
102 #define	DR_MINOR2BNUM(m)	((m) & 0xffff)
103 
104 /* for the DR*INTERNAL_ERROR macros.  see sys/dr.h. */
105 static char *dr_ie_fmt = "dr.c %d";
106 
107 /* struct for drmach device name to sbd_comp_type_t mapping */
108 typedef	struct {
109 	char		*s_devtype;
110 	sbd_comp_type_t	s_nodetype;
111 } dr_devname_t;
112 
113 /* struct to map starfire device attributes - name:sbd_comp_type_t */
114 static	dr_devname_t	dr_devattr[] = {
115 	{ DRMACH_DEVTYPE_MEM,	SBD_COMP_MEM },
116 	{ DRMACH_DEVTYPE_CPU,	SBD_COMP_CPU },
117 	{ DRMACH_DEVTYPE_PCI,	SBD_COMP_IO },
118 	{ DRMACH_DEVTYPE_SBUS,	SBD_COMP_IO },
119 #if defined(DRMACH_DEVTYPE_WCI)
120 	{ DRMACH_DEVTYPE_WCI,	SBD_COMP_IO },
121 #endif
122 	/* last s_devtype must be NULL, s_nodetype must be SBD_COMP_UNKNOWN */
123 	{ NULL,			SBD_COMP_UNKNOWN }
124 };
125 
126 /*
127  * Per instance soft-state structure.
128  */
129 typedef struct dr_softstate {
130 	dev_info_t	*dip;
131 	dr_board_t	*boards;
132 	kmutex_t	i_lock;
133 	int		 dr_initialized;
134 } dr_softstate_t;
135 
136 /*
137  * dr Global data elements
138  */
139 struct dr_global {
140 	dr_softstate_t	*softsp;	/* pointer to initialize soft state */
141 	kmutex_t	lock;
142 } dr_g;
143 
144 dr_unsafe_devs_t	dr_unsafe_devs;
145 
146 /*
147  * Table of known passthru commands.
148  */
149 
150 struct {
151 	char	*pt_name;
152 	int	(*pt_func)(dr_handle_t *);
153 } pt_arr[] = {
154 	"quiesce",		dr_pt_test_suspend,
155 };
156 
157 int dr_modunload_okay = 0;		/* set to non-zero to allow unload */
158 
159 /*
160  * State transition table.  States valid transitions for "board" state.
161  * Recall that non-zero return value terminates operation, however
162  * the herrno value is what really indicates an error , if any.
163  */
164 static int
165 _cmd2index(int c)
166 {
167 	/*
168 	 * Translate DR CMD to index into dr_state_transition.
169 	 */
170 	switch (c) {
171 	case SBD_CMD_CONNECT:		return (0);
172 	case SBD_CMD_DISCONNECT:	return (1);
173 	case SBD_CMD_CONFIGURE:		return (2);
174 	case SBD_CMD_UNCONFIGURE:	return (3);
175 	case SBD_CMD_ASSIGN:		return (4);
176 	case SBD_CMD_UNASSIGN:		return (5);
177 	case SBD_CMD_POWERON:		return (6);
178 	case SBD_CMD_POWEROFF:		return (7);
179 	case SBD_CMD_TEST:		return (8);
180 	default:			return (-1);
181 	}
182 }
183 
184 #define	CMD2INDEX(c)	_cmd2index(c)
185 
186 static struct dr_state_trans {
187 	int	x_cmd;
188 	struct {
189 		int	x_rv;		/* return value of pre_op */
190 		int	x_err;		/* error, if any */
191 	} x_op[DR_STATE_MAX];
192 } dr_state_transition[] = {
193 	{ SBD_CMD_CONNECT,
194 		{
195 			{ 0, 0 },			/* empty */
196 			{ 0, 0 },			/* occupied */
197 			{ -1, ESBD_STATE },		/* connected */
198 			{ -1, ESBD_STATE },		/* unconfigured */
199 			{ -1, ESBD_STATE },		/* partial */
200 			{ -1, ESBD_STATE },		/* configured */
201 			{ -1, ESBD_STATE },		/* release */
202 			{ -1, ESBD_STATE },		/* unreferenced */
203 			{ -1, ESBD_FATAL_STATE },	/* fatal */
204 		}
205 	},
206 	{ SBD_CMD_DISCONNECT,
207 		{
208 			{ -1, ESBD_STATE },		/* empty */
209 			{ 0, 0 },			/* occupied */
210 			{ 0, 0 },			/* connected */
211 			{ 0, 0 },			/* unconfigured */
212 			{ -1, ESBD_STATE },		/* partial */
213 			{ -1, ESBD_STATE },		/* configured */
214 			{ -1, ESBD_STATE },		/* release */
215 			{ -1, ESBD_STATE },		/* unreferenced */
216 			{ -1, ESBD_FATAL_STATE },	/* fatal */
217 		}
218 	},
219 	{ SBD_CMD_CONFIGURE,
220 		{
221 			{ -1, ESBD_STATE },		/* empty */
222 			{ -1, ESBD_STATE },		/* occupied */
223 			{ 0, 0 },			/* connected */
224 			{ 0, 0 },			/* unconfigured */
225 			{ 0, 0 },			/* partial */
226 			{ 0, 0 },			/* configured */
227 			{ -1, ESBD_STATE },		/* release */
228 			{ -1, ESBD_STATE },		/* unreferenced */
229 			{ -1, ESBD_FATAL_STATE },	/* fatal */
230 		}
231 	},
232 	{ SBD_CMD_UNCONFIGURE,
233 		{
234 			{ -1, ESBD_STATE },		/* empty */
235 			{ -1, ESBD_STATE },		/* occupied */
236 			{ -1, ESBD_STATE },		/* connected */
237 			{ -1, ESBD_STATE },		/* unconfigured */
238 			{ 0, 0 },			/* partial */
239 			{ 0, 0 },			/* configured */
240 			{ 0, 0 },			/* release */
241 			{ 0, 0 },			/* unreferenced */
242 			{ -1, ESBD_FATAL_STATE },	/* fatal */
243 		}
244 	},
245 	{ SBD_CMD_ASSIGN,
246 		{
247 			{ 0, 0 },			/* empty */
248 			{ 0, 0 },			/* occupied */
249 			{ -1, ESBD_STATE },		/* connected */
250 			{ -1, ESBD_STATE },		/* unconfigured */
251 			{ -1, ESBD_STATE },		/* partial */
252 			{ -1, ESBD_STATE },		/* configured */
253 			{ -1, ESBD_STATE },		/* release */
254 			{ -1, ESBD_STATE },		/* unreferenced */
255 			{ -1, ESBD_FATAL_STATE },	/* fatal */
256 		}
257 	},
258 	{ SBD_CMD_UNASSIGN,
259 		{
260 			{ 0, 0 },			/* empty */
261 			{ 0, 0 },			/* occupied */
262 			{ -1, ESBD_STATE },		/* connected */
263 			{ -1, ESBD_STATE },		/* unconfigured */
264 			{ -1, ESBD_STATE },		/* partial */
265 			{ -1, ESBD_STATE },		/* configured */
266 			{ -1, ESBD_STATE },		/* release */
267 			{ -1, ESBD_STATE },		/* unreferenced */
268 			{ -1, ESBD_FATAL_STATE },	/* fatal */
269 		}
270 	},
271 	{ SBD_CMD_POWERON,
272 		{
273 			{ 0, 0 },			/* empty */
274 			{ 0, 0 },			/* occupied */
275 			{ -1, ESBD_STATE },		/* connected */
276 			{ -1, ESBD_STATE },		/* unconfigured */
277 			{ -1, ESBD_STATE },		/* partial */
278 			{ -1, ESBD_STATE },		/* configured */
279 			{ -1, ESBD_STATE },		/* release */
280 			{ -1, ESBD_STATE },		/* unreferenced */
281 			{ -1, ESBD_FATAL_STATE },	/* fatal */
282 		}
283 	},
284 	{ SBD_CMD_POWEROFF,
285 		{
286 			{ 0, 0 },			/* empty */
287 			{ 0, 0 },			/* occupied */
288 			{ -1, ESBD_STATE },		/* connected */
289 			{ -1, ESBD_STATE },		/* unconfigured */
290 			{ -1, ESBD_STATE },		/* partial */
291 			{ -1, ESBD_STATE },		/* configured */
292 			{ -1, ESBD_STATE },		/* release */
293 			{ -1, ESBD_STATE },		/* unreferenced */
294 			{ -1, ESBD_FATAL_STATE },	/* fatal */
295 		}
296 	},
297 	{ SBD_CMD_TEST,
298 		{
299 			{ 0, 0 },			/* empty */
300 			{ 0, 0 },			/* occupied */
301 			{ -1, ESBD_STATE },		/* connected */
302 			{ -1, ESBD_STATE },		/* unconfigured */
303 			{ -1, ESBD_STATE },		/* partial */
304 			{ -1, ESBD_STATE },		/* configured */
305 			{ -1, ESBD_STATE },		/* release */
306 			{ -1, ESBD_STATE },		/* unreferenced */
307 			{ -1, ESBD_FATAL_STATE },	/* fatal */
308 		}
309 	},
310 };
311 
312 /*
313  * Global R/W lock to synchronize access across
314  * multiple boards.  Users wanting multi-board access
315  * must grab WRITE lock, others must grab READ lock.
316  */
317 krwlock_t	dr_grwlock;
318 
319 /*
320  * Head of the boardlist used as a reference point for
321  * locating board structs.
322  * TODO: eliminate dr_boardlist
323  */
324 dr_board_t	*dr_boardlist;
325 
326 /*
327  * DR support functions.
328  */
329 static dr_devset_t	dr_dev2devset(sbd_comp_id_t *cid);
330 static int		dr_check_transition(dr_board_t *bp,
331 					dr_devset_t *devsetp,
332 					struct dr_state_trans *transp,
333 					int cmd);
334 static int		dr_check_unit_attached(dr_common_unit_t *dp);
335 static sbd_error_t	*dr_init_devlists(dr_board_t *bp);
336 static void		dr_board_discovery(dr_board_t *bp);
337 static int		dr_board_init(dr_board_t *bp, dev_info_t *dip,
338 					int bd);
339 static void		dr_board_destroy(dr_board_t *bp);
340 static void		dr_board_transition(dr_board_t *bp, dr_state_t st);
341 
342 /*
343  * DR driver (DDI) entry points.
344  */
345 static int	dr_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
346 				void *arg, void **result);
347 static int	dr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
348 static int	dr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
349 static int	dr_probe(dev_info_t *dip);
350 static int	dr_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
351 				cred_t *cred_p, int *rval_p);
352 static int	dr_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
353 static int	dr_open(dev_t *dev, int flag, int otyp, cred_t *cred_p);
354 
355 /*
356  * DR command processing operations.
357  */
358 
359 static int	dr_copyin_iocmd(dr_handle_t *hp);
360 static int	dr_copyout_iocmd(dr_handle_t *hp);
361 static int	dr_copyout_errs(dr_handle_t *hp);
362 static int	dr_pre_op(dr_handle_t *hp);
363 static int	dr_post_op(dr_handle_t *hp);
364 static int	dr_exec_op(dr_handle_t *hp);
365 static void	dr_assign_board(dr_handle_t *hp);
366 static void	dr_unassign_board(dr_handle_t *hp);
367 static void	dr_connect(dr_handle_t *hp);
368 static int	dr_disconnect(dr_handle_t *hp);
369 static void	dr_dev_configure(dr_handle_t *hp);
370 static void	dr_dev_release(dr_handle_t *hp);
371 static int	dr_dev_unconfigure(dr_handle_t *hp);
372 static void	dr_dev_cancel(dr_handle_t *hp);
373 static int	dr_dev_status(dr_handle_t *hp);
374 static int	dr_get_ncm(dr_handle_t *hp);
375 static int	dr_pt_ioctl(dr_handle_t *hp);
376 static void	dr_poweron_board(dr_handle_t *hp);
377 static void	dr_poweroff_board(dr_handle_t *hp);
378 static void	dr_test_board(dr_handle_t *hp);
379 
380 
381 
382 /*
383  * Autoconfiguration data structures
384  */
385 
386 struct cb_ops dr_cb_ops = {
387 	dr_open,	/* open */
388 	dr_close,	/* close */
389 	nodev,		/* strategy */
390 	nodev,		/* print */
391 	nodev,		/* dump */
392 	nodev,		/* read */
393 	nodev,		/* write */
394 	dr_ioctl,	/* ioctl */
395 	nodev,		/* devmap */
396 	nodev,		/* mmap */
397 	nodev,		/* segmap */
398 	nochpoll,	/* chpoll */
399 	ddi_prop_op,	/* cb_prop_op */
400 	NULL,		/* struct streamtab */
401 	D_NEW | D_MP | D_MTSAFE,	/* compatibility flags */
402 	CB_REV,		/* Rev */
403 	nodev,		/* cb_aread */
404 	nodev		/* cb_awrite */
405 };
406 
407 struct dev_ops dr_dev_ops = {
408 	DEVO_REV,	/* build version */
409 	0,		/* dev ref count */
410 	dr_getinfo,	/* getinfo */
411 	nulldev,	/* identify */
412 	dr_probe,	/* probe */
413 	dr_attach,	/* attach */
414 	dr_detach,	/* detach */
415 	nodev,		/* reset */
416 	&dr_cb_ops,	/* cb_ops */
417 	(struct bus_ops *)NULL, /* bus ops */
418 	NULL		/* power */
419 };
420 
421 extern struct mod_ops mod_driverops;
422 
423 static struct modldrv modldrv = {
424 	&mod_driverops,
425 	"Dynamic Reconfiguration %I%",
426 	&dr_dev_ops
427 };
428 
429 static struct modlinkage modlinkage = {
430 	MODREV_1,
431 	(void *)&modldrv,
432 	NULL
433 };
434 
435 /*
436  * Driver entry points.
437  */
438 int
439 _init(void)
440 {
441 	int	err;
442 
443 	/*
444 	 * If you need to support multiple nodes (instances), then
445 	 * whatever the maximum number of supported nodes is would
446 	 * need to passed as the third parameter to ddi_soft_state_init().
447 	 * Alternative would be to dynamically fini and re-init the
448 	 * soft state structure each time a node is attached.
449 	 */
450 	err = ddi_soft_state_init((void **)&dr_g.softsp,
451 					sizeof (dr_softstate_t), 1);
452 	if (err)
453 		return (err);
454 
455 	mutex_init(&dr_g.lock, NULL, MUTEX_DRIVER, NULL);
456 	rw_init(&dr_grwlock, NULL, RW_DEFAULT, NULL);
457 
458 	return (mod_install(&modlinkage));
459 }
460 
461 int
462 _fini(void)
463 {
464 	int	err;
465 
466 	if ((err = mod_remove(&modlinkage)) != 0)
467 		return (err);
468 
469 	mutex_destroy(&dr_g.lock);
470 	rw_destroy(&dr_grwlock);
471 
472 	ddi_soft_state_fini((void **)&dr_g.softsp);
473 
474 	return (0);
475 }
476 
477 int
478 _info(struct modinfo *modinfop)
479 {
480 	return (mod_info(&modlinkage, modinfop));
481 }
482 
483 /*ARGSUSED1*/
484 static int
485 dr_open(dev_t *dev, int flag, int otyp, cred_t *cred_p)
486 {
487 	int		 instance;
488 	dr_softstate_t	*softsp;
489 	dr_board_t	*bp;
490 	/*
491 	 * Don't open unless we've attached.
492 	 */
493 	instance = DR_MINOR2INST(getminor(*dev));
494 	softsp = ddi_get_soft_state(dr_g.softsp, instance);
495 	if (softsp == NULL)
496 		return (ENXIO);
497 
498 	mutex_enter(&softsp->i_lock);
499 	if (!softsp->dr_initialized) {
500 		int		 bd;
501 		int		 rv = 0;
502 
503 		bp = softsp->boards;
504 
505 		/* initialize each array element */
506 		for (bd = 0; bd < MAX_BOARDS; bd++, bp++) {
507 			rv = dr_board_init(bp, softsp->dip, bd);
508 			if (rv)
509 				break;
510 		}
511 
512 		if (rv == 0) {
513 			softsp->dr_initialized = 1;
514 		} else {
515 			/* destroy elements initialized thus far */
516 			while (--bp >= softsp->boards)
517 				dr_board_destroy(bp);
518 
519 
520 			/* TODO: should this be another errno val ? */
521 			mutex_exit(&softsp->i_lock);
522 			return (ENXIO);
523 		}
524 	}
525 	mutex_exit(&softsp->i_lock);
526 
527 	bp = &softsp->boards[DR_MINOR2BNUM(getminor(*dev))];
528 
529 	/*
530 	 * prevent opening of a dyn-ap for a board
531 	 * that does not exist
532 	 */
533 	if (!bp->b_assigned) {
534 		if (drmach_board_lookup(bp->b_num, &bp->b_id) != 0)
535 			return (ENODEV);
536 	}
537 
538 	return (0);
539 }
540 
541 /*ARGSUSED*/
542 static int
543 dr_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
544 {
545 	return (0);
546 }
547 
548 /*
549  * Enable/disable Starcat DR features.
550  */
551 #ifndef _STARFIRE
552 int dr_enable = 1;
553 int slot1_dr_enable = 1;
554 #endif /* _STARFIRE */
555 
556 /*ARGSUSED3*/
557 static int
558 dr_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
559 	cred_t *cred_p, int *rval_p)
560 {
561 	static int	dr_dev_type_to_nt(char *);
562 
563 	int		rv = 0;
564 	int		instance;
565 	int		bd;
566 	dr_handle_t	*hp;
567 	dr_softstate_t	*softsp;
568 	static fn_t	f = "dr_ioctl";
569 
570 	PR_ALL("%s...\n", f);
571 
572 	instance = DR_MINOR2INST(getminor(dev));
573 	softsp = ddi_get_soft_state(dr_g.softsp, instance);
574 	if (softsp == NULL) {
575 		cmn_err(CE_WARN, "dr%d: module not yet attached", instance);
576 		return (ENXIO);
577 	}
578 
579 #ifndef _STARFIRE
580 	if (!dr_enable) {
581 		switch (cmd) {
582 			case SBD_CMD_STATUS:
583 			case SBD_CMD_GETNCM:
584 			case SBD_CMD_PASSTHRU:
585 				break;
586 			default:
587 				return (ENOTSUP);
588 		}
589 	}
590 #endif /* _STARFIRE */
591 
592 	bd = DR_MINOR2BNUM(getminor(dev));
593 	if (bd >= MAX_BOARDS)
594 		return (ENXIO);
595 
596 #ifndef _STARFIRE
597 	if (!slot1_dr_enable && (bd & 0x1)) {
598 		switch (cmd) {
599 			case SBD_CMD_STATUS:
600 			case SBD_CMD_GETNCM:
601 			case SBD_CMD_PASSTHRU:
602 				break;
603 			default:
604 				return (ENOTSUP);
605 		}
606 	}
607 #endif /* _STARFIRE */
608 
609 	/* get and initialize storage for new handle */
610 	hp = GETSTRUCT(dr_handle_t, 1);
611 	hp->h_bd = &softsp->boards[bd];
612 	hp->h_err = NULL;
613 	hp->h_dev = getminor(dev);
614 	hp->h_cmd = cmd;
615 	hp->h_mode = mode;
616 	hp->h_iap = (sbd_ioctl_arg_t *)arg;
617 
618 	/* copy sbd command into handle */
619 	rv = dr_copyin_iocmd(hp);
620 	if (rv) {
621 		FREESTRUCT(hp, dr_handle_t, 1);
622 		return (EINVAL);
623 	}
624 
625 	/* translate canonical name to component type */
626 	if (hp->h_sbdcmd.cmd_cm.c_id.c_name[0] != '\0') {
627 		hp->h_sbdcmd.cmd_cm.c_id.c_type =
628 			dr_dev_type_to_nt(hp->h_sbdcmd.cmd_cm.c_id.c_name);
629 
630 		PR_ALL("%s: c_name = %s, c_type = %d\n",
631 			f,
632 			hp->h_sbdcmd.cmd_cm.c_id.c_name,
633 			hp->h_sbdcmd.cmd_cm.c_id.c_type);
634 	} else {
635 		/*EMPTY*/
636 		PR_ALL("%s: c_name is NULL\n", f);
637 	}
638 
639 	/* determine scope of operation */
640 	hp->h_devset = dr_dev2devset(&hp->h_sbdcmd.cmd_cm.c_id);
641 
642 	switch (hp->h_cmd) {
643 	case SBD_CMD_STATUS:
644 	case SBD_CMD_GETNCM:
645 		/* no locks needed for these commands */
646 		break;
647 
648 	default:
649 		rw_enter(&dr_grwlock, RW_WRITER);
650 		mutex_enter(&hp->h_bd->b_lock);
651 
652 		/*
653 		 * If we're dealing with memory at all, then we have
654 		 * to keep the "exclusive" global lock held.  This is
655 		 * necessary since we will probably need to look at
656 		 * multiple board structs.  Otherwise, we only have
657 		 * to deal with the board in question and so can drop
658 		 * the global lock to "shared".
659 		 */
660 		rv = DEVSET_IN_SET(hp->h_devset, SBD_COMP_MEM, DEVSET_ANYUNIT);
661 		if (rv == 0)
662 			rw_downgrade(&dr_grwlock);
663 		break;
664 	}
665 	rv = 0;
666 
667 	if (rv == 0)
668 		rv = dr_pre_op(hp);
669 	if (rv == 0)
670 		rv = dr_exec_op(hp);
671 	if (rv == 0)
672 		rv = dr_post_op(hp);
673 
674 	if (rv == -1)
675 		rv = EIO;
676 
677 	if (hp->h_err != NULL)
678 		if (!(rv = dr_copyout_errs(hp)))
679 			rv = EIO;
680 
681 	/* undo locking, if any, done before dr_pre_op */
682 	switch (hp->h_cmd) {
683 	case SBD_CMD_STATUS:
684 	case SBD_CMD_GETNCM:
685 		break;
686 
687 	case SBD_CMD_ASSIGN:
688 	case SBD_CMD_UNASSIGN:
689 	case SBD_CMD_POWERON:
690 	case SBD_CMD_POWEROFF:
691 	case SBD_CMD_CONNECT:
692 	case SBD_CMD_CONFIGURE:
693 	case SBD_CMD_UNCONFIGURE:
694 	case SBD_CMD_DISCONNECT:
695 		/* Board changed state. Log a sysevent. */
696 		if (rv == 0)
697 			(void) drmach_log_sysevent(hp->h_bd->b_num, "",
698 						    SE_SLEEP, 1);
699 		/* Fall through */
700 
701 	default:
702 		mutex_exit(&hp->h_bd->b_lock);
703 		rw_exit(&dr_grwlock);
704 	}
705 
706 	if (hp->h_opts.size != 0)
707 		FREESTRUCT(hp->h_opts.copts, char, hp->h_opts.size);
708 
709 	FREESTRUCT(hp, dr_handle_t, 1);
710 
711 	return (rv);
712 }
713 
714 /*ARGSUSED*/
715 static int
716 dr_probe(dev_info_t *dip)
717 {
718 	return (DDI_PROBE_SUCCESS);
719 }
720 
721 static int
722 dr_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
723 {
724 	int		rv, rv2;
725 	int		bd;
726 	int 		instance;
727 	sbd_error_t	*err;
728 	dr_softstate_t	*softsp;
729 
730 	instance = ddi_get_instance(dip);
731 
732 	switch (cmd) {
733 
734 	case DDI_ATTACH:
735 
736 		rw_enter(&dr_grwlock, RW_WRITER);
737 
738 		rv = ddi_soft_state_zalloc(dr_g.softsp, instance);
739 		if (rv != DDI_SUCCESS) {
740 			cmn_err(CE_WARN, "dr%d: failed to alloc soft-state",
741 				instance);
742 			return (DDI_FAILURE);
743 		}
744 
745 		/* initialize softstate structure */
746 		softsp = ddi_get_soft_state(dr_g.softsp, instance);
747 		softsp->dip = dip;
748 
749 		mutex_init(&softsp->i_lock, NULL, MUTEX_DRIVER, NULL);
750 
751 		/* allocate board array (aka boardlist) */
752 		softsp->boards = GETSTRUCT(dr_board_t, MAX_BOARDS);
753 
754 		/* TODO: eliminate dr_boardlist */
755 		dr_boardlist = softsp->boards;
756 
757 		/* initialize each array element */
758 		rv = DDI_SUCCESS;
759 		for (bd = 0; bd < MAX_BOARDS; bd++) {
760 			dr_board_t	*bp = &softsp->boards[bd];
761 			char		*p, *name;
762 			int		 l, minor_num;
763 
764 			/*
765 			 * initialized board attachment point path
766 			 * (relative to pseudo) in a form immediately
767 			 * reusable as an cfgadm command argument.
768 			 * TODO: clean this up
769 			 */
770 			p = bp->b_path;
771 			l = sizeof (bp->b_path);
772 			(void) snprintf(p, l, "dr@%d:", instance);
773 			while (*p != '\0') {
774 				l--;
775 				p++;
776 			}
777 
778 			name = p;
779 			err = drmach_board_name(bd, p, l);
780 			if (err) {
781 				sbd_err_clear(&err);
782 				rv = DDI_FAILURE;
783 				break;
784 			}
785 
786 			minor_num = DR_MAKE_MINOR(instance, bd);
787 			rv = ddi_create_minor_node(dip, name, S_IFCHR,
788 				minor_num, DDI_NT_SBD_ATTACHMENT_POINT, NULL);
789 			if (rv != DDI_SUCCESS)
790 				rv = DDI_FAILURE;
791 		}
792 
793 		if (rv == DDI_SUCCESS) {
794 			/*
795 			 * Announce the node's presence.
796 			 */
797 			ddi_report_dev(dip);
798 		} else {
799 			ddi_remove_minor_node(dip, NULL);
800 		}
801 		/*
802 		 * Init registered unsafe devs.
803 		 */
804 		dr_unsafe_devs.devnames = NULL;
805 		rv2 = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip,
806 			DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
807 			"unsupported-io-drivers", &dr_unsafe_devs.devnames,
808 			&dr_unsafe_devs.ndevs);
809 
810 		if (rv2 != DDI_PROP_SUCCESS)
811 			dr_unsafe_devs.ndevs = 0;
812 
813 		rw_exit(&dr_grwlock);
814 		return (rv);
815 
816 	default:
817 		return (DDI_FAILURE);
818 	}
819 
820 	/*NOTREACHED*/
821 }
822 
823 static int
824 dr_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
825 {
826 	int 		instance;
827 	dr_softstate_t	*softsp;
828 
829 	switch (cmd) {
830 	case DDI_DETACH:
831 		if (!dr_modunload_okay)
832 			return (DDI_FAILURE);
833 
834 		rw_enter(&dr_grwlock, RW_WRITER);
835 
836 		instance = ddi_get_instance(dip);
837 		softsp = ddi_get_soft_state(dr_g.softsp, instance);
838 
839 		/* TODO: eliminate dr_boardlist */
840 		ASSERT(softsp->boards == dr_boardlist);
841 
842 		/* remove all minor nodes */
843 		ddi_remove_minor_node(dip, NULL);
844 
845 		if (softsp->dr_initialized) {
846 			int bd;
847 
848 			for (bd = 0; bd < MAX_BOARDS; bd++)
849 				dr_board_destroy(&softsp->boards[bd]);
850 		}
851 
852 		FREESTRUCT(softsp->boards, dr_board_t, MAX_BOARDS);
853 		mutex_destroy(&softsp->i_lock);
854 		ddi_soft_state_free(dr_g.softsp, instance);
855 
856 		rw_exit(&dr_grwlock);
857 		return (DDI_SUCCESS);
858 
859 	default:
860 		return (DDI_FAILURE);
861 	}
862 	/*NOTREACHED*/
863 }
864 
865 static int
866 dr_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
867 {
868 	_NOTE(ARGUNUSED(dip))
869 
870 	dev_t		dev = (dev_t)arg;
871 	int		instance, error;
872 	dr_softstate_t	*softsp;
873 
874 	*result = NULL;
875 	error = DDI_SUCCESS;
876 	instance = DR_MINOR2INST(getminor(dev));
877 
878 	switch (cmd) {
879 	case DDI_INFO_DEVT2DEVINFO:
880 		softsp = ddi_get_soft_state(dr_g.softsp, instance);
881 		if (softsp == NULL)
882 			return (DDI_FAILURE);
883 		*result = (void *)softsp->dip;
884 		break;
885 
886 	case DDI_INFO_DEVT2INSTANCE:
887 		*result = (void *)instance;
888 		break;
889 
890 	default:
891 		error = DDI_FAILURE;
892 		break;
893 	}
894 
895 	return (error);
896 }
897 
898 /*
899  * DR operations.
900  */
901 
902 static int
903 dr_copyin_iocmd(dr_handle_t *hp)
904 {
905 	static fn_t	f = "dr_copyin_iocmd";
906 	sbd_cmd_t	*scp = &hp->h_sbdcmd;
907 
908 	if (hp->h_iap == NULL)
909 		return (EINVAL);
910 
911 	bzero((caddr_t)scp, sizeof (sbd_cmd_t));
912 
913 #ifdef _MULTI_DATAMODEL
914 	if (ddi_model_convert_from(hp->h_mode & FMODELS) == DDI_MODEL_ILP32) {
915 		sbd_cmd32_t	scmd32;
916 
917 		bzero((caddr_t)&scmd32, sizeof (sbd_cmd32_t));
918 
919 		if (ddi_copyin((void *)hp->h_iap, (void *)&scmd32,
920 				sizeof (sbd_cmd32_t), hp->h_mode)) {
921 			cmn_err(CE_WARN,
922 				"%s: (32bit) failed to copyin "
923 					"sbdcmd-struct", f);
924 			return (EFAULT);
925 		}
926 		scp->cmd_cm.c_id.c_type = scmd32.cmd_cm.c_id.c_type;
927 		scp->cmd_cm.c_id.c_unit = scmd32.cmd_cm.c_id.c_unit;
928 		bcopy(&scmd32.cmd_cm.c_id.c_name[0],
929 			&scp->cmd_cm.c_id.c_name[0], OBP_MAXPROPNAME);
930 		scp->cmd_cm.c_flags = scmd32.cmd_cm.c_flags;
931 		scp->cmd_cm.c_len = scmd32.cmd_cm.c_len;
932 		scp->cmd_cm.c_opts = (caddr_t)scmd32.cmd_cm.c_opts;
933 
934 		switch (hp->h_cmd) {
935 		case SBD_CMD_STATUS:
936 			scp->cmd_stat.s_nbytes = scmd32.cmd_stat.s_nbytes;
937 			scp->cmd_stat.s_statp =
938 				(caddr_t)scmd32.cmd_stat.s_statp;
939 			break;
940 		default:
941 			break;
942 
943 		}
944 	} else
945 #endif /* _MULTI_DATAMODEL */
946 	if (ddi_copyin((void *)hp->h_iap, (void *)scp,
947 			sizeof (sbd_cmd_t), hp->h_mode) != 0) {
948 		cmn_err(CE_WARN,
949 			"%s: failed to copyin sbdcmd-struct", f);
950 		return (EFAULT);
951 	}
952 
953 	if ((hp->h_opts.size = scp->cmd_cm.c_len) != 0) {
954 		hp->h_opts.copts = GETSTRUCT(char, scp->cmd_cm.c_len + 1);
955 		++hp->h_opts.size;
956 		if (ddi_copyin((void *)scp->cmd_cm.c_opts,
957 			(void *)hp->h_opts.copts,
958 			scp->cmd_cm.c_len, hp->h_mode) != 0) {
959 			cmn_err(CE_WARN, "%s: failed to copyin options", f);
960 			return (EFAULT);
961 		}
962 	}
963 	return (0);
964 }
965 
966 static int
967 dr_copyout_iocmd(dr_handle_t *hp)
968 {
969 	static fn_t	f = "dr_copyout_iocmd";
970 	sbd_cmd_t	*scp = &hp->h_sbdcmd;
971 
972 	if (hp->h_iap == NULL)
973 		return (EINVAL);
974 
975 #ifdef _MULTI_DATAMODEL
976 	if (ddi_model_convert_from(hp->h_mode & FMODELS) == DDI_MODEL_ILP32) {
977 		sbd_cmd32_t	scmd32;
978 
979 		scmd32.cmd_cm.c_id.c_type = scp->cmd_cm.c_id.c_type;
980 		scmd32.cmd_cm.c_id.c_unit = scp->cmd_cm.c_id.c_unit;
981 		bcopy(&scp->cmd_cm.c_id.c_name[0],
982 			&scmd32.cmd_cm.c_id.c_name[0], OBP_MAXPROPNAME);
983 
984 		scmd32.cmd_cm.c_flags = scp->cmd_cm.c_flags;
985 		scmd32.cmd_cm.c_len = scp->cmd_cm.c_len;
986 		scmd32.cmd_cm.c_opts = (caddr32_t)scp->cmd_cm.c_opts;
987 
988 		switch (hp->h_cmd) {
989 		case SBD_CMD_GETNCM:
990 			scmd32.cmd_getncm.g_ncm = scp->cmd_getncm.g_ncm;
991 			break;
992 		default:
993 			break;
994 		}
995 
996 		if (ddi_copyout((void *)&scmd32, (void *)hp->h_iap,
997 				sizeof (sbd_cmd32_t), hp->h_mode)) {
998 			cmn_err(CE_WARN,
999 				"%s: (32bit) failed to copyout "
1000 					"sbdcmd-struct", f);
1001 			return (EFAULT);
1002 		}
1003 	} else
1004 #endif /* _MULTI_DATAMODEL */
1005 	if (ddi_copyout((void *)scp, (void *)hp->h_iap,
1006 			sizeof (sbd_cmd_t), hp->h_mode) != 0) {
1007 		cmn_err(CE_WARN,
1008 			"%s: failed to copyout sbdcmd-struct", f);
1009 		return (EFAULT);
1010 	}
1011 
1012 	return (0);
1013 }
1014 
1015 static int
1016 dr_copyout_errs(dr_handle_t *hp)
1017 {
1018 	static fn_t	f = "dr_copyout_errs";
1019 
1020 	if (hp->h_err == NULL)
1021 		return (0);
1022 
1023 	if (hp->h_err->e_code) {
1024 		PR_ALL("%s: error %d %s",
1025 			f, hp->h_err->e_code, hp->h_err->e_rsc);
1026 	}
1027 
1028 #ifdef _MULTI_DATAMODEL
1029 	if (ddi_model_convert_from(hp->h_mode & FMODELS) == DDI_MODEL_ILP32) {
1030 		sbd_error32_t	*serr32p;
1031 
1032 		serr32p = GETSTRUCT(sbd_error32_t, 1);
1033 
1034 		serr32p->e_code = hp->h_err->e_code;
1035 		bcopy(&hp->h_err->e_rsc[0], &serr32p->e_rsc[0],
1036 			MAXPATHLEN);
1037 		if (ddi_copyout((void *)serr32p,
1038 			(void *)&((sbd_ioctl_arg32_t *)hp->h_iap)->i_err,
1039 				sizeof (sbd_error32_t), hp->h_mode)) {
1040 			cmn_err(CE_WARN,
1041 				"%s: (32bit) failed to copyout", f);
1042 			return (EFAULT);
1043 		}
1044 		FREESTRUCT(serr32p, sbd_error32_t, 1);
1045 	} else
1046 #endif /* _MULTI_DATAMODEL */
1047 	if (ddi_copyout((void *)hp->h_err,
1048 		(void *)&hp->h_iap->i_err,
1049 			sizeof (sbd_error_t), hp->h_mode)) {
1050 		cmn_err(CE_WARN,
1051 			"%s: failed to copyout", f);
1052 		return (EFAULT);
1053 	}
1054 
1055 	sbd_err_clear(&hp->h_err);
1056 
1057 	return (0);
1058 
1059 }
1060 
1061 /*
1062  * pre-op entry point must sbd_err_set_c(), if needed.
1063  * Return value of non-zero indicates failure.
1064  */
1065 static int
1066 dr_pre_op(dr_handle_t *hp)
1067 {
1068 	int		rv = 0, t;
1069 	int		cmd, serr = 0;
1070 	dr_devset_t	devset;
1071 	dr_board_t	*bp = hp->h_bd;
1072 	dr_handle_t	*shp = hp;
1073 	static fn_t	f = "dr_pre_op";
1074 
1075 	cmd = hp->h_cmd;
1076 	devset = shp->h_devset;
1077 
1078 	PR_ALL("%s (cmd = %s)...\n", f, (uint_t)SBD_CMD_STR(cmd));
1079 
1080 	hp->h_err = drmach_pre_op(cmd, bp->b_id, &hp->h_opts);
1081 	if (hp->h_err != NULL) {
1082 		PR_ALL("drmach_pre_op failed for cmd %s(%d)\n",
1083 			SBD_CMD_STR(cmd), cmd);
1084 		return (-1);
1085 	}
1086 
1087 	/*
1088 	 * Check for valid state transitions.
1089 	 */
1090 	if ((t = CMD2INDEX(cmd)) != -1) {
1091 		struct dr_state_trans	*transp;
1092 		int			state_err;
1093 
1094 		transp = &dr_state_transition[t];
1095 		ASSERT(transp->x_cmd == cmd);
1096 
1097 		state_err = dr_check_transition(bp, &devset, transp, cmd);
1098 
1099 		if (state_err < 0) {
1100 			/*
1101 			 * Invalidate device.
1102 			 */
1103 			dr_op_err(CE_IGNORE, hp, ESBD_INVAL, NULL);
1104 			serr = -1;
1105 			PR_ALL("%s: invalid devset (0x%x)\n",
1106 				f, (uint_t)devset);
1107 		} else if (state_err != 0) {
1108 			/*
1109 			 * State transition is not a valid one.
1110 			 */
1111 			dr_op_err(CE_IGNORE, hp,
1112 				transp->x_op[state_err].x_err, NULL);
1113 
1114 			serr = transp->x_op[state_err].x_rv;
1115 
1116 			PR_ALL("%s: invalid state %s(%d) for cmd %s(%d)\n",
1117 				f, state_str[state_err], state_err,
1118 				SBD_CMD_STR(cmd), cmd);
1119 		} else {
1120 			shp->h_devset = devset;
1121 		}
1122 	}
1123 
1124 	if (serr) {
1125 		rv = -1;
1126 	}
1127 
1128 	return (rv);
1129 }
1130 
1131 static int
1132 dr_post_op(dr_handle_t *hp)
1133 {
1134 	int		rv = 0;
1135 	int		cmd;
1136 	dr_board_t	*bp = hp->h_bd;
1137 	static fn_t	f = "dr_post_op";
1138 
1139 	cmd = hp->h_cmd;
1140 
1141 	PR_ALL("%s (cmd = %s)...\n", f, (uint_t)SBD_CMD_STR(cmd));
1142 
1143 	/* errors should have been caught by now */
1144 	ASSERT(hp->h_err == NULL);
1145 
1146 	hp->h_err = drmach_post_op(cmd, bp->b_id, &hp->h_opts);
1147 	if (hp->h_err != NULL) {
1148 		PR_ALL("drmach_post_op failed for cmd %s(%d)\n",
1149 			SBD_CMD_STR(cmd), cmd);
1150 		return (-1);
1151 	}
1152 
1153 	switch (cmd) {
1154 	case SBD_CMD_CONFIGURE:
1155 	case SBD_CMD_UNCONFIGURE:
1156 	case SBD_CMD_CONNECT:
1157 	case SBD_CMD_DISCONNECT:
1158 	case SBD_CMD_GETNCM:
1159 	case SBD_CMD_STATUS:
1160 		break;
1161 
1162 	default:
1163 		break;
1164 	}
1165 
1166 	return (rv);
1167 }
1168 
1169 static int
1170 dr_exec_op(dr_handle_t *hp)
1171 {
1172 	int		rv = 0;
1173 	static fn_t	f = "dr_exec_op";
1174 
1175 	/* errors should have been caught by now */
1176 	ASSERT(hp->h_err == NULL);
1177 
1178 	switch (hp->h_cmd) {
1179 	case SBD_CMD_ASSIGN:
1180 		dr_assign_board(hp);
1181 		break;
1182 
1183 	case SBD_CMD_UNASSIGN:
1184 		dr_unassign_board(hp);
1185 		break;
1186 
1187 	case SBD_CMD_POWEROFF:
1188 		dr_poweroff_board(hp);
1189 		break;
1190 
1191 	case SBD_CMD_POWERON:
1192 		dr_poweron_board(hp);
1193 		break;
1194 
1195 	case SBD_CMD_TEST:
1196 		dr_test_board(hp);
1197 		break;
1198 
1199 	case SBD_CMD_CONNECT:
1200 		dr_connect(hp);
1201 		break;
1202 
1203 	case SBD_CMD_CONFIGURE:
1204 		dr_dev_configure(hp);
1205 		break;
1206 
1207 	case SBD_CMD_UNCONFIGURE:
1208 		dr_dev_release(hp);
1209 		if (hp->h_err == NULL)
1210 			rv = dr_dev_unconfigure(hp);
1211 		else
1212 			dr_dev_cancel(hp);
1213 		break;
1214 
1215 	case SBD_CMD_DISCONNECT:
1216 		rv = dr_disconnect(hp);
1217 		break;
1218 
1219 	case SBD_CMD_STATUS:
1220 		rv = dr_dev_status(hp);
1221 		break;
1222 
1223 	case SBD_CMD_GETNCM:
1224 		hp->h_sbdcmd.cmd_getncm.g_ncm = dr_get_ncm(hp);
1225 		rv = dr_copyout_iocmd(hp);
1226 		break;
1227 
1228 	case SBD_CMD_PASSTHRU:
1229 		rv = dr_pt_ioctl(hp);
1230 		break;
1231 
1232 	default:
1233 		cmn_err(CE_WARN,
1234 			"%s: unknown command (%d)",
1235 			f, hp->h_cmd);
1236 		break;
1237 	}
1238 
1239 	if (hp->h_err != NULL) {
1240 		rv = -1;
1241 	}
1242 
1243 	return (rv);
1244 }
1245 
1246 static void
1247 dr_assign_board(dr_handle_t *hp)
1248 {
1249 	dr_board_t *bp = hp->h_bd;
1250 
1251 	hp->h_err = drmach_board_assign(bp->b_num, &bp->b_id);
1252 	if (hp->h_err == NULL) {
1253 		bp->b_assigned = 1;
1254 	}
1255 }
1256 
1257 static void
1258 dr_unassign_board(dr_handle_t *hp)
1259 {
1260 	dr_board_t *bp = hp->h_bd;
1261 
1262 	/*
1263 	 * Block out status during unassign.
1264 	 * Not doing cv_wait_sig here as starfire SSP software
1265 	 * ignores unassign failure and removes board from
1266 	 * domain mask causing system panic.
1267 	 * TODO: Change cv_wait to cv_wait_sig when SSP software
1268 	 * handles unassign failure.
1269 	 */
1270 	dr_lock_status(bp);
1271 
1272 	hp->h_err = drmach_board_unassign(bp->b_id);
1273 	if (hp->h_err == NULL) {
1274 		/*
1275 		 * clear drmachid_t handle; not valid after board unassign
1276 		 */
1277 		bp->b_id = 0;
1278 		bp->b_assigned = 0;
1279 	}
1280 
1281 	dr_unlock_status(bp);
1282 }
1283 
1284 static void
1285 dr_poweron_board(dr_handle_t *hp)
1286 {
1287 	dr_board_t *bp = hp->h_bd;
1288 
1289 	hp->h_err = drmach_board_poweron(bp->b_id);
1290 }
1291 
1292 static void
1293 dr_poweroff_board(dr_handle_t *hp)
1294 {
1295 	dr_board_t *bp = hp->h_bd;
1296 
1297 	hp->h_err = drmach_board_poweroff(bp->b_id);
1298 }
1299 
1300 static void
1301 dr_test_board(dr_handle_t *hp)
1302 {
1303 	dr_board_t *bp = hp->h_bd;
1304 	hp->h_err = drmach_board_test(bp->b_id, &hp->h_opts,
1305 	    dr_cmd_flags(hp) & SBD_FLAG_FORCE);
1306 }
1307 
1308 /*
1309  * Create and populate the component nodes for a board.  Assumes that the
1310  * devlists for the board have been initialized.
1311  */
1312 static void
1313 dr_make_comp_nodes(dr_board_t *bp) {
1314 
1315 	int	i;
1316 
1317 	/*
1318 	 * Make nodes for the individual components on the board.
1319 	 * First we need to initialize memory unit data structures of board
1320 	 * structure.
1321 	 */
1322 	for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
1323 		dr_mem_unit_t *mp;
1324 
1325 		mp = dr_get_mem_unit(bp, i);
1326 		dr_init_mem_unit(mp);
1327 	}
1328 
1329 	/*
1330 	 * Initialize cpu unit data structures.
1331 	 */
1332 	for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
1333 		dr_cpu_unit_t *cp;
1334 
1335 		cp = dr_get_cpu_unit(bp, i);
1336 		dr_init_cpu_unit(cp);
1337 	}
1338 
1339 	/*
1340 	 * Initialize io unit data structures.
1341 	 */
1342 	for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
1343 		dr_io_unit_t *ip;
1344 
1345 		ip = dr_get_io_unit(bp, i);
1346 		dr_init_io_unit(ip);
1347 	}
1348 
1349 	dr_board_transition(bp, DR_STATE_CONNECTED);
1350 
1351 	bp->b_rstate = SBD_STAT_CONNECTED;
1352 	bp->b_ostate = SBD_STAT_UNCONFIGURED;
1353 	bp->b_cond = SBD_COND_OK;
1354 	(void) drv_getparm(TIME, (void *)&bp->b_time);
1355 
1356 }
1357 
1358 /*
1359  * Only do work if called to operate on an entire board
1360  * which doesn't already have components present.
1361  */
1362 static void
1363 dr_connect(dr_handle_t *hp)
1364 {
1365 	dr_board_t	*bp = hp->h_bd;
1366 	static fn_t	f = "dr_connect";
1367 
1368 	PR_ALL("%s...\n", f);
1369 
1370 	if (DR_DEVS_PRESENT(bp)) {
1371 		/*
1372 		 * Board already has devices present.
1373 		 */
1374 		PR_ALL("%s: devices already present (0x%x)\n",
1375 			f, DR_DEVS_PRESENT(bp));
1376 		return;
1377 	}
1378 
1379 	hp->h_err = drmach_board_connect(bp->b_id, &hp->h_opts);
1380 	if (hp->h_err)
1381 		return;
1382 
1383 	hp->h_err = dr_init_devlists(bp);
1384 	if (hp->h_err)
1385 		return;
1386 	else if (bp->b_ndev == 0) {
1387 		dr_op_err(CE_WARN, hp, ESBD_EMPTY_BD, bp->b_path);
1388 		return;
1389 	} else {
1390 		dr_make_comp_nodes(bp);
1391 		return;
1392 	}
1393 	/*NOTREACHED*/
1394 }
1395 
1396 static int
1397 dr_disconnect(dr_handle_t *hp)
1398 {
1399 	int		i;
1400 	dr_devset_t	devset;
1401 	dr_board_t	*bp = hp->h_bd;
1402 	static fn_t	f = "dr_disconnect";
1403 
1404 	PR_ALL("%s...\n", f);
1405 
1406 	/*
1407 	 * Only devices which are present, but
1408 	 * unattached can be disconnected.
1409 	 */
1410 	devset = hp->h_devset & DR_DEVS_PRESENT(bp) &
1411 			DR_DEVS_UNATTACHED(bp);
1412 
1413 	if ((devset == 0) && DR_DEVS_PRESENT(bp)) {
1414 		dr_op_err(CE_IGNORE, hp, ESBD_EMPTY_BD, bp->b_path);
1415 		return (0);
1416 	}
1417 
1418 	/*
1419 	 * Block out status during disconnect.
1420 	 */
1421 	mutex_enter(&bp->b_slock);
1422 	while (bp->b_sflags & DR_BSLOCK) {
1423 		if (cv_wait_sig(&bp->b_scv, &bp->b_slock) == 0) {
1424 			mutex_exit(&bp->b_slock);
1425 			return (EINTR);
1426 		}
1427 	}
1428 	bp->b_sflags |= DR_BSLOCK;
1429 	mutex_exit(&bp->b_slock);
1430 
1431 	hp->h_err = drmach_board_disconnect(bp->b_id, &hp->h_opts);
1432 
1433 	DR_DEVS_DISCONNECT(bp, devset);
1434 
1435 	ASSERT((DR_DEVS_ATTACHED(bp) & devset) == 0);
1436 
1437 	/*
1438 	 * Update per-device state transitions.
1439 	 */
1440 	for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
1441 		dr_cpu_unit_t *cp;
1442 
1443 		if (!DEVSET_IN_SET(devset, SBD_COMP_CPU, i))
1444 			continue;
1445 
1446 		cp = dr_get_cpu_unit(bp, i);
1447 		if (dr_disconnect_cpu(cp) == 0)
1448 			dr_device_transition(&cp->sbc_cm, DR_STATE_EMPTY);
1449 		else if (cp->sbc_cm.sbdev_error != NULL)
1450 			DRERR_SET_C(&hp->h_err, &cp->sbc_cm.sbdev_error);
1451 
1452 		ASSERT(cp->sbc_cm.sbdev_error == NULL);
1453 	}
1454 
1455 	for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
1456 		dr_mem_unit_t *mp;
1457 
1458 		if (!DEVSET_IN_SET(devset, SBD_COMP_MEM, i))
1459 			continue;
1460 
1461 		mp = dr_get_mem_unit(bp, i);
1462 		if (dr_disconnect_mem(mp) == 0)
1463 			dr_device_transition(&mp->sbm_cm, DR_STATE_EMPTY);
1464 		else if (mp->sbm_cm.sbdev_error != NULL)
1465 			DRERR_SET_C(&hp->h_err, &mp->sbm_cm.sbdev_error);
1466 
1467 		ASSERT(mp->sbm_cm.sbdev_error == NULL);
1468 	}
1469 
1470 	for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
1471 		dr_io_unit_t *ip;
1472 
1473 		if (!DEVSET_IN_SET(devset, SBD_COMP_IO, i))
1474 			continue;
1475 
1476 		ip = dr_get_io_unit(bp, i);
1477 		if (dr_disconnect_io(ip) == 0)
1478 			dr_device_transition(&ip->sbi_cm, DR_STATE_EMPTY);
1479 		else if (ip->sbi_cm.sbdev_error != NULL)
1480 			DRERR_SET_C(&hp->h_err, &ip->sbi_cm.sbdev_error);
1481 
1482 		ASSERT(ip->sbi_cm.sbdev_error == NULL);
1483 	}
1484 	if (hp->h_err) {
1485 		/*
1486 		 * For certain errors, drmach_board_disconnect will mark
1487 		 * the board as unusable; in these cases the devtree must
1488 		 * be purged so that status calls will succeed.
1489 		 * XXX
1490 		 * This implementation checks for discrete error codes -
1491 		 * someday, the i/f to drmach_board_disconnect should be
1492 		 * changed to avoid the e_code testing.
1493 		 */
1494 		if ((hp->h_err->e_code == ESTC_MBXRPLY) ||
1495 			(hp->h_err->e_code == ESTC_MBXRQST) ||
1496 			(hp->h_err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
1497 			(hp->h_err->e_code == ESTC_SMS_ERR_RECOVERABLE) ||
1498 			(hp->h_err->e_code == ESTC_DEPROBE)) {
1499 			bp->b_ostate = SBD_STAT_UNCONFIGURED;
1500 			bp->b_busy = 0;
1501 			(void) drv_getparm(TIME, (void *)&bp->b_time);
1502 
1503 			if (drmach_board_deprobe(bp->b_id))
1504 				goto disconnect_done;
1505 			else
1506 				bp->b_ndev = 0;
1507 		}
1508 
1509 		/*
1510 		 * If the disconnect failed in a recoverable way,
1511 		 * more work is required.
1512 		 * XXX
1513 		 * This implementation checks for discrete error codes -
1514 		 * someday, the i/f to drmach_board_disconnect should be
1515 		 * changed to avoid the e_code testing.
1516 		 */
1517 		if ((hp->h_err->e_code == ESTC_MBXRQST) ||
1518 		    (hp->h_err->e_code == ESTC_SMS_ERR_RECOVERABLE) ||
1519 		    (hp->h_err->e_code == ESTC_DEPROBE)) {
1520 			/*
1521 			 * With this failure, the board has been deprobed
1522 			 * by IKP, and reprobed.  We've already gotten rid
1523 			 * of the old devtree, now we need to reconstruct it
1524 			 * based on the new IKP probe
1525 			 */
1526 			if (dr_init_devlists(bp) || (bp->b_ndev == 0))
1527 				goto disconnect_done;
1528 
1529 			dr_make_comp_nodes(bp);
1530 		}
1531 	}
1532 	/*
1533 	 * Once all the components on a board have been disconnect
1534 	 * the board's state can transition to disconnected and
1535 	 * we can allow the deprobe to take place.
1536 	 */
1537 	if (hp->h_err == NULL && DR_DEVS_PRESENT(bp) == 0) {
1538 		dr_board_transition(bp, DR_STATE_OCCUPIED);
1539 		bp->b_rstate = SBD_STAT_DISCONNECTED;
1540 		bp->b_ostate = SBD_STAT_UNCONFIGURED;
1541 		bp->b_busy = 0;
1542 		(void) drv_getparm(TIME, (void *)&bp->b_time);
1543 
1544 		hp->h_err = drmach_board_deprobe(bp->b_id);
1545 
1546 		if (hp->h_err == NULL) {
1547 			bp->b_ndev = 0;
1548 			dr_board_transition(bp, DR_STATE_EMPTY);
1549 			bp->b_rstate = SBD_STAT_EMPTY;
1550 			(void) drv_getparm(TIME, (void *)&bp->b_time);
1551 		}
1552 	}
1553 
1554 disconnect_done:
1555 	dr_unlock_status(bp);
1556 
1557 	return (0);
1558 }
1559 
1560 /*
1561  * Check if a particular device is a valid target of the current
1562  * operation. Return 1 if it is a valid target, and 0 otherwise.
1563  */
1564 static int
1565 dr_dev_is_target(dr_dev_unit_t *dp, int present_only, uint_t uset)
1566 {
1567 	dr_common_unit_t *cp;
1568 	int		 is_present;
1569 	int		 is_attached;
1570 
1571 	cp = &dp->du_common;
1572 
1573 	/* check if the user requested this device */
1574 	if ((uset & (1 << cp->sbdev_unum)) == 0) {
1575 		return (0);
1576 	}
1577 
1578 	is_present = DR_DEV_IS_PRESENT(cp) ? 1 : 0;
1579 	is_attached = DR_DEV_IS_ATTACHED(cp) ? 1 : 0;
1580 
1581 	/*
1582 	 * If the present_only flag is set, a valid target
1583 	 * must be present but not attached. Otherwise, it
1584 	 * must be both present and attached.
1585 	 */
1586 	if (is_present && (present_only ^ is_attached)) {
1587 		/* sanity check */
1588 		ASSERT(cp->sbdev_id != (drmachid_t)0);
1589 
1590 		return (1);
1591 	}
1592 
1593 	return (0);
1594 }
1595 
1596 static void
1597 dr_dev_make_list(dr_handle_t *hp, sbd_comp_type_t type, int present_only,
1598 	dr_common_unit_t ***devlist, int *devnum)
1599 {
1600 	dr_board_t	*bp = hp->h_bd;
1601 	int		 unum;
1602 	int		 nunits;
1603 	uint_t		 uset;
1604 	int		 len;
1605 	dr_common_unit_t **list, **wp;
1606 
1607 	switch (type) {
1608 	case SBD_COMP_CPU:
1609 		nunits = MAX_CPU_UNITS_PER_BOARD;
1610 		break;
1611 	case SBD_COMP_MEM:
1612 		nunits = MAX_MEM_UNITS_PER_BOARD;
1613 		break;
1614 	case SBD_COMP_IO:
1615 		nunits = MAX_IO_UNITS_PER_BOARD;
1616 		break;
1617 	default:
1618 		/* catch this in debug kernels */
1619 		ASSERT(0);
1620 		break;
1621 	}
1622 
1623 	/* allocate list storage. */
1624 	len = sizeof (dr_common_unit_t *) * (nunits + 1);
1625 	list = kmem_zalloc(len, KM_SLEEP);
1626 
1627 	/* record length of storage in first element */
1628 	*list++ = (dr_common_unit_t *)len;
1629 
1630 	/* get bit array signifying which units are to be involved */
1631 	uset = DEVSET_GET_UNITSET(hp->h_devset, type);
1632 
1633 	/*
1634 	 * Adjust the loop count for CPU devices since all cores
1635 	 * in a CMP will be examined in a single iteration.
1636 	 */
1637 	if (type == SBD_COMP_CPU) {
1638 		nunits = MAX_CMP_UNITS_PER_BOARD;
1639 	}
1640 
1641 	/* populate list */
1642 	for (wp = list, unum = 0; unum < nunits; unum++) {
1643 
1644 		dr_dev_unit_t	*dp;
1645 		int		core;
1646 		int		cunum;
1647 
1648 		dp = DR_GET_BOARD_DEVUNIT(bp, type, unum);
1649 		if (dr_dev_is_target(dp, present_only, uset)) {
1650 			*wp++ = &dp->du_common;
1651 		}
1652 
1653 		/* further processing is only required for CPUs */
1654 		if (type != SBD_COMP_CPU) {
1655 			continue;
1656 		}
1657 
1658 		/*
1659 		 * Add any additional cores from the current CPU
1660 		 * device. This is to ensure that all the cores
1661 		 * are grouped together in the device list, and
1662 		 * consequently sequenced together during the actual
1663 		 * operation.
1664 		 */
1665 		for (core = 1; core < MAX_CORES_PER_CMP; core++) {
1666 
1667 			cunum = DR_CMP_CORE_UNUM(unum, core);
1668 			dp = DR_GET_BOARD_DEVUNIT(bp, type, cunum);
1669 
1670 			if (dr_dev_is_target(dp, present_only, uset)) {
1671 				*wp++ = &dp->du_common;
1672 			}
1673 		}
1674 	}
1675 
1676 	/* calculate number of units in list, return result and list pointer */
1677 	*devnum = wp - list;
1678 	*devlist = list;
1679 }
1680 
1681 static void
1682 dr_dev_clean_up(dr_handle_t *hp, dr_common_unit_t **list, int devnum)
1683 {
1684 	int len;
1685 	int n = 0;
1686 	dr_common_unit_t *cp, **rp = list;
1687 
1688 	/*
1689 	 * move first encountered unit error to handle if handle
1690 	 * does not yet have a recorded error.
1691 	 */
1692 	if (hp->h_err == NULL) {
1693 		while (n++ < devnum) {
1694 			cp = *rp++;
1695 			if (cp->sbdev_error != NULL) {
1696 				hp->h_err = cp->sbdev_error;
1697 				cp->sbdev_error = NULL;
1698 				break;
1699 			}
1700 		}
1701 	}
1702 
1703 	/* free remaining unit errors */
1704 	while (n++ < devnum) {
1705 		cp = *rp++;
1706 		if (cp->sbdev_error != NULL) {
1707 			sbd_err_clear(&cp->sbdev_error);
1708 			cp->sbdev_error = NULL;
1709 		}
1710 	}
1711 
1712 	/* free list */
1713 	list -= 1;
1714 	len = (int)list[0];
1715 	kmem_free(list, len);
1716 }
1717 
1718 static int
1719 dr_dev_walk(dr_handle_t *hp, sbd_comp_type_t type, int present_only,
1720 		int (*pre_op)(dr_handle_t *, dr_common_unit_t **, int),
1721 		void (*op)(dr_handle_t *, dr_common_unit_t *),
1722 		int (*post_op)(dr_handle_t *, dr_common_unit_t **, int),
1723 		void (*board_op)(dr_handle_t *, dr_common_unit_t **, int))
1724 {
1725 	int			  devnum, rv;
1726 	dr_common_unit_t	**devlist;
1727 
1728 	dr_dev_make_list(hp, type, present_only, &devlist, &devnum);
1729 
1730 	rv = 0;
1731 	if (devnum > 0) {
1732 		rv = (*pre_op)(hp, devlist, devnum);
1733 		if (rv == 0) {
1734 			int n;
1735 
1736 			for (n = 0; n < devnum; n++)
1737 				(*op)(hp, devlist[n]);
1738 
1739 			rv = (*post_op)(hp, devlist, devnum);
1740 
1741 			(*board_op)(hp, devlist, devnum);
1742 		}
1743 	}
1744 
1745 	dr_dev_clean_up(hp, devlist, devnum);
1746 	return (rv);
1747 }
1748 
1749 /*ARGSUSED*/
1750 static int
1751 dr_dev_noop(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
1752 {
1753 	return (0);
1754 }
1755 
1756 static void
1757 dr_attach_update_state(dr_handle_t *hp,
1758 	dr_common_unit_t **devlist, int devnum)
1759 {
1760 	dr_board_t	*bp = hp->h_bd;
1761 	int		i;
1762 	dr_devset_t	devs_unattached, devs_present;
1763 	static fn_t	f = "dr_post_attach_devlist";
1764 
1765 	for (i = 0; i < devnum; i++) {
1766 		dr_common_unit_t *cp = devlist[i];
1767 
1768 		if (dr_check_unit_attached(cp) == -1) {
1769 			PR_ALL("%s: ERROR %s not attached\n",
1770 				f, cp->sbdev_path);
1771 			continue;
1772 		}
1773 
1774 		DR_DEV_SET_ATTACHED(cp);
1775 
1776 		dr_device_transition(cp, DR_STATE_CONFIGURED);
1777 		cp->sbdev_cond = SBD_COND_OK;
1778 	}
1779 
1780 	devs_present = DR_DEVS_PRESENT(bp);
1781 	devs_unattached = DR_DEVS_UNATTACHED(bp);
1782 
1783 	switch (bp->b_state) {
1784 	case DR_STATE_CONNECTED:
1785 	case DR_STATE_UNCONFIGURED:
1786 		ASSERT(devs_present);
1787 
1788 		if (devs_unattached == 0) {
1789 			/*
1790 			 * All devices finally attached.
1791 			 */
1792 			dr_board_transition(bp, DR_STATE_CONFIGURED);
1793 			hp->h_bd->b_ostate = SBD_STAT_CONFIGURED;
1794 			hp->h_bd->b_rstate = SBD_STAT_CONNECTED;
1795 			hp->h_bd->b_cond = SBD_COND_OK;
1796 			hp->h_bd->b_busy = 0;
1797 			(void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
1798 		} else if (devs_present != devs_unattached) {
1799 			/*
1800 			 * Only some devices are fully attached.
1801 			 */
1802 			dr_board_transition(bp, DR_STATE_PARTIAL);
1803 			hp->h_bd->b_rstate = SBD_STAT_CONNECTED;
1804 			hp->h_bd->b_ostate = SBD_STAT_CONFIGURED;
1805 			(void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
1806 		}
1807 		break;
1808 
1809 	case DR_STATE_PARTIAL:
1810 		ASSERT(devs_present);
1811 		/*
1812 		 * All devices finally attached.
1813 		 */
1814 		if (devs_unattached == 0) {
1815 			dr_board_transition(bp, DR_STATE_CONFIGURED);
1816 			hp->h_bd->b_rstate = SBD_STAT_CONNECTED;
1817 			hp->h_bd->b_ostate = SBD_STAT_CONFIGURED;
1818 			hp->h_bd->b_cond = SBD_COND_OK;
1819 			hp->h_bd->b_busy = 0;
1820 			(void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
1821 		}
1822 		break;
1823 
1824 	default:
1825 		break;
1826 	}
1827 }
1828 
1829 static void
1830 dr_dev_configure(dr_handle_t *hp)
1831 {
1832 	int rv;
1833 
1834 	rv = dr_dev_walk(hp, SBD_COMP_CPU, 1,
1835 		dr_pre_attach_cpu,
1836 		dr_attach_cpu,
1837 		dr_post_attach_cpu,
1838 		dr_attach_update_state);
1839 
1840 	if (rv >= 0) {
1841 		rv = dr_dev_walk(hp, SBD_COMP_MEM, 1,
1842 			dr_pre_attach_mem,
1843 			dr_attach_mem,
1844 			dr_post_attach_mem,
1845 			dr_attach_update_state);
1846 	}
1847 
1848 	if (rv >= 0) {
1849 		(void) dr_dev_walk(hp, SBD_COMP_IO, 1,
1850 			dr_pre_attach_io,
1851 			dr_attach_io,
1852 			dr_post_attach_io,
1853 			dr_attach_update_state);
1854 	}
1855 }
1856 
1857 static void
1858 dr_release_update_state(dr_handle_t *hp,
1859 	dr_common_unit_t **devlist, int devnum)
1860 {
1861 	_NOTE(ARGUNUSED(devlist))
1862 	_NOTE(ARGUNUSED(devnum))
1863 
1864 	dr_board_t *bp = hp->h_bd;
1865 
1866 	/*
1867 	 * If the entire board was released and all components
1868 	 * unreferenced then transfer it to the UNREFERENCED state.
1869 	 */
1870 	if ((bp->b_state != DR_STATE_RELEASE) &&
1871 		(DR_DEVS_RELEASED(bp) == DR_DEVS_ATTACHED(bp))) {
1872 		dr_board_transition(bp, DR_STATE_RELEASE);
1873 		hp->h_bd->b_busy = 1;
1874 	}
1875 }
1876 
1877 /* called by dr_release_done [below] and dr_release_mem_done [dr_mem.c] */
1878 int
1879 dr_release_dev_done(dr_common_unit_t *cp)
1880 {
1881 	if (cp->sbdev_state == DR_STATE_RELEASE) {
1882 		ASSERT(DR_DEV_IS_RELEASED(cp));
1883 
1884 		DR_DEV_SET_UNREFERENCED(cp);
1885 
1886 		dr_device_transition(cp, DR_STATE_UNREFERENCED);
1887 
1888 		return (0);
1889 	} else {
1890 		return (-1);
1891 	}
1892 }
1893 
1894 static void
1895 dr_release_done(dr_handle_t *hp, dr_common_unit_t *cp)
1896 {
1897 	_NOTE(ARGUNUSED(hp))
1898 
1899 	dr_board_t		*bp;
1900 	static fn_t		f = "dr_release_done";
1901 
1902 	PR_ALL("%s...\n", f);
1903 
1904 	/* get board pointer & sanity check */
1905 	bp = cp->sbdev_bp;
1906 	ASSERT(bp == hp->h_bd);
1907 
1908 	/*
1909 	 * Transfer the device which just completed its release
1910 	 * to the UNREFERENCED state.
1911 	 */
1912 	switch (cp->sbdev_type) {
1913 	case SBD_COMP_MEM:
1914 		dr_release_mem_done(cp);
1915 		break;
1916 
1917 	default:
1918 		DR_DEV_SET_RELEASED(cp);
1919 
1920 		dr_device_transition(cp, DR_STATE_RELEASE);
1921 
1922 		(void) dr_release_dev_done(cp);
1923 		break;
1924 	}
1925 
1926 	/*
1927 	 * If we're not already in the RELEASE state for this
1928 	 * board and we now have released all that were previously
1929 	 * attached, then transfer the board to the RELEASE state.
1930 	 */
1931 	if ((bp->b_state == DR_STATE_RELEASE) &&
1932 		(DR_DEVS_RELEASED(bp) == DR_DEVS_UNREFERENCED(bp))) {
1933 		dr_board_transition(bp, DR_STATE_UNREFERENCED);
1934 		bp->b_busy = 1;
1935 		(void) drv_getparm(TIME, (void *)&bp->b_time);
1936 	}
1937 }
1938 
1939 static void
1940 dr_dev_release_mem(dr_handle_t *hp, dr_common_unit_t *dv)
1941 {
1942 	dr_release_mem(dv);
1943 	dr_release_done(hp, dv);
1944 }
1945 
1946 static void
1947 dr_dev_release(dr_handle_t *hp)
1948 {
1949 	int rv;
1950 
1951 	hp->h_bd->b_busy = 1;
1952 
1953 	rv = dr_dev_walk(hp, SBD_COMP_CPU, 0,
1954 		dr_pre_release_cpu,
1955 		dr_release_done,
1956 		dr_dev_noop,
1957 		dr_release_update_state);
1958 
1959 	if (rv >= 0) {
1960 		rv = dr_dev_walk(hp, SBD_COMP_MEM, 0,
1961 			dr_pre_release_mem,
1962 			dr_dev_release_mem,
1963 			dr_dev_noop,
1964 			dr_release_update_state);
1965 	}
1966 
1967 	if (rv >= 0) {
1968 		rv = dr_dev_walk(hp, SBD_COMP_IO, 0,
1969 			dr_pre_release_io,
1970 			dr_release_done,
1971 			dr_dev_noop,
1972 			dr_release_update_state);
1973 
1974 	}
1975 
1976 	if (rv < 0)
1977 		hp->h_bd->b_busy = 0;
1978 	/* else, b_busy will be cleared in dr_detach_update_state() */
1979 }
1980 
1981 static void
1982 dr_detach_update_state(dr_handle_t *hp,
1983 	dr_common_unit_t **devlist, int devnum)
1984 {
1985 	dr_board_t	*bp = hp->h_bd;
1986 	int		i;
1987 	dr_state_t	bstate;
1988 	static fn_t	f = "dr_detach_update_state";
1989 
1990 	for (i = 0; i < devnum; i++) {
1991 		dr_common_unit_t *cp = devlist[i];
1992 
1993 		if (dr_check_unit_attached(cp) >= 0) {
1994 			/*
1995 			 * Device is still attached probably due
1996 			 * to an error.  Need to keep track of it.
1997 			 */
1998 			PR_ALL("%s: ERROR %s not detached\n",
1999 				f, cp->sbdev_path);
2000 
2001 			continue;
2002 		}
2003 
2004 		DR_DEV_CLR_ATTACHED(cp);
2005 		DR_DEV_CLR_RELEASED(cp);
2006 		DR_DEV_CLR_UNREFERENCED(cp);
2007 		dr_device_transition(cp, DR_STATE_UNCONFIGURED);
2008 	}
2009 
2010 	bstate = bp->b_state;
2011 	if (bstate != DR_STATE_UNCONFIGURED) {
2012 		if (DR_DEVS_PRESENT(bp) == DR_DEVS_UNATTACHED(bp)) {
2013 			/*
2014 			 * All devices are finally detached.
2015 			 */
2016 			dr_board_transition(bp, DR_STATE_UNCONFIGURED);
2017 			hp->h_bd->b_ostate = SBD_STAT_UNCONFIGURED;
2018 			(void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
2019 		} else if ((bp->b_state != DR_STATE_PARTIAL) &&
2020 				(DR_DEVS_ATTACHED(bp) !=
2021 					DR_DEVS_PRESENT(bp))) {
2022 			/*
2023 			 * Some devices remain attached.
2024 			 */
2025 			dr_board_transition(bp, DR_STATE_PARTIAL);
2026 			(void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
2027 		}
2028 
2029 		if ((hp->h_devset & DR_DEVS_UNATTACHED(bp)) == hp->h_devset)
2030 			hp->h_bd->b_busy = 0;
2031 	}
2032 }
2033 
2034 static int
2035 dr_dev_unconfigure(dr_handle_t *hp)
2036 {
2037 	dr_board_t	*bp = hp->h_bd;
2038 
2039 	/*
2040 	 * Block out status during IO unconfig.
2041 	 */
2042 	mutex_enter(&bp->b_slock);
2043 	while (bp->b_sflags & DR_BSLOCK) {
2044 		if (cv_wait_sig(&bp->b_scv, &bp->b_slock) == 0) {
2045 			mutex_exit(&bp->b_slock);
2046 			return (EINTR);
2047 		}
2048 	}
2049 	bp->b_sflags |= DR_BSLOCK;
2050 	mutex_exit(&bp->b_slock);
2051 
2052 	(void) dr_dev_walk(hp, SBD_COMP_IO, 0,
2053 		dr_pre_detach_io,
2054 		dr_detach_io,
2055 		dr_post_detach_io,
2056 		dr_detach_update_state);
2057 
2058 	dr_unlock_status(bp);
2059 
2060 	(void) dr_dev_walk(hp, SBD_COMP_CPU, 0,
2061 		dr_pre_detach_cpu,
2062 		dr_detach_cpu,
2063 		dr_post_detach_cpu,
2064 		dr_detach_update_state);
2065 
2066 	(void) dr_dev_walk(hp, SBD_COMP_MEM, 0,
2067 		dr_pre_detach_mem,
2068 		dr_detach_mem,
2069 		dr_post_detach_mem,
2070 		dr_detach_update_state);
2071 
2072 	return (0);
2073 }
2074 
2075 static void
2076 dr_dev_cancel(dr_handle_t *hp)
2077 {
2078 	int		i;
2079 	dr_devset_t	devset;
2080 	dr_board_t	*bp = hp->h_bd;
2081 	static fn_t	f = "dr_dev_cancel";
2082 
2083 	PR_ALL("%s...\n", f);
2084 
2085 	/*
2086 	 * Only devices which have been "released" are
2087 	 * subject to cancellation.
2088 	 */
2089 	devset = hp->h_devset & DR_DEVS_RELEASED(bp);
2090 
2091 	/*
2092 	 * Nothing to do for CPUs or IO other than change back
2093 	 * their state.
2094 	 */
2095 	for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
2096 		dr_cpu_unit_t	*cp;
2097 		dr_state_t	nstate;
2098 
2099 		if (!DEVSET_IN_SET(devset, SBD_COMP_CPU, i))
2100 			continue;
2101 
2102 		cp = dr_get_cpu_unit(bp, i);
2103 		if (dr_cancel_cpu(cp) == 0)
2104 			nstate = DR_STATE_CONFIGURED;
2105 		else
2106 			nstate = DR_STATE_FATAL;
2107 
2108 		dr_device_transition(&cp->sbc_cm, nstate);
2109 	}
2110 
2111 	for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
2112 		dr_io_unit_t *ip;
2113 
2114 		if (!DEVSET_IN_SET(devset, SBD_COMP_IO, i))
2115 			continue;
2116 		ip = dr_get_io_unit(bp, i);
2117 		dr_device_transition(&ip->sbi_cm, DR_STATE_CONFIGURED);
2118 	}
2119 	for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
2120 		dr_mem_unit_t	*mp;
2121 		dr_state_t	nstate;
2122 
2123 		if (!DEVSET_IN_SET(devset, SBD_COMP_MEM, i))
2124 			continue;
2125 
2126 		mp = dr_get_mem_unit(bp, i);
2127 		if (dr_cancel_mem(mp) == 0)
2128 			nstate = DR_STATE_CONFIGURED;
2129 		else
2130 			nstate = DR_STATE_FATAL;
2131 
2132 		dr_device_transition(&mp->sbm_cm, nstate);
2133 	}
2134 
2135 	PR_ALL("%s: unreleasing devset (0x%x)\n", f, (uint_t)devset);
2136 
2137 	DR_DEVS_CANCEL(bp, devset);
2138 
2139 	if (DR_DEVS_RELEASED(bp) == 0) {
2140 		dr_state_t	new_state;
2141 		/*
2142 		 * If the board no longer has any released devices
2143 		 * than transfer it back to the CONFIG/PARTIAL state.
2144 		 */
2145 		if (DR_DEVS_ATTACHED(bp) == DR_DEVS_PRESENT(bp))
2146 			new_state = DR_STATE_CONFIGURED;
2147 		else
2148 			new_state = DR_STATE_PARTIAL;
2149 		if (bp->b_state != new_state) {
2150 			dr_board_transition(bp, new_state);
2151 		}
2152 		hp->h_bd->b_ostate = SBD_STAT_CONFIGURED;
2153 		hp->h_bd->b_busy = 0;
2154 		(void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
2155 	}
2156 }
2157 
2158 static int
2159 dr_dev_status(dr_handle_t *hp)
2160 {
2161 	int		nstat, mode, ncm, sz, pbsz, pnstat;
2162 	dr_handle_t	*shp;
2163 	dr_devset_t	devset = 0;
2164 	sbd_stat_t	*dstatp = NULL;
2165 	sbd_dev_stat_t	*devstatp;
2166 	dr_board_t	*bp;
2167 	drmach_status_t	 pstat;
2168 	int		rv = 0;
2169 
2170 #ifdef _MULTI_DATAMODEL
2171 	int sz32 = 0;
2172 #endif /* _MULTI_DATAMODEL */
2173 
2174 	static fn_t	f = "dr_status";
2175 
2176 	PR_ALL("%s...\n", f);
2177 
2178 	mode = hp->h_mode;
2179 	shp = hp;
2180 	devset = shp->h_devset;
2181 	bp = hp->h_bd;
2182 
2183 	/*
2184 	 * Block out disconnect, unassign, IO unconfigure and
2185 	 * devinfo branch creation during status.
2186 	 */
2187 	mutex_enter(&bp->b_slock);
2188 	while (bp->b_sflags & DR_BSLOCK) {
2189 		if (cv_wait_sig(&bp->b_scv, &bp->b_slock) == 0) {
2190 			mutex_exit(&bp->b_slock);
2191 			return (EINTR);
2192 		}
2193 	}
2194 	bp->b_sflags |= DR_BSLOCK;
2195 	mutex_exit(&bp->b_slock);
2196 
2197 	ncm = 1;
2198 	if (hp->h_sbdcmd.cmd_cm.c_id.c_type == SBD_COMP_NONE) {
2199 		if (dr_cmd_flags(hp) & SBD_FLAG_ALLCMP) {
2200 		/*
2201 		 * Calculate the maximum number of components possible
2202 		 * for a board.  This number will be used to size the
2203 		 * status scratch buffer used by board and component
2204 		 * status functions.
2205 		 * This buffer may differ in size from what is provided
2206 		 * by the plugin, since the known component set on the
2207 		 * board may change between the plugin's GETNCM call, and
2208 		 * the status call.  Sizing will be adjusted to the plugin's
2209 		 * receptacle buffer at copyout time.
2210 		 */
2211 			ncm = MAX_CPU_UNITS_PER_BOARD +
2212 				MAX_MEM_UNITS_PER_BOARD +
2213 				MAX_IO_UNITS_PER_BOARD;
2214 
2215 		} else {
2216 			/*
2217 			 * In the case of c_type == SBD_COMP_NONE, and
2218 			 * SBD_FLAG_ALLCMP not specified, only the board
2219 			 * info is to be returned, no components.
2220 			 */
2221 			ncm = 0;
2222 			devset = 0;
2223 		}
2224 	}
2225 
2226 	sz = sizeof (sbd_stat_t);
2227 	if (ncm > 1)
2228 		sz += sizeof (sbd_dev_stat_t) * (ncm - 1);
2229 
2230 
2231 	pbsz = (int)hp->h_sbdcmd.cmd_stat.s_nbytes;
2232 	pnstat = (pbsz - sizeof (sbd_stat_t))/sizeof (sbd_dev_stat_t);
2233 
2234 	/*
2235 	 * s_nbytes describes the size of the preallocated user
2236 	 * buffer into which the application is execting to
2237 	 * receive the sbd_stat_t and sbd_dev_stat_t structures.
2238 	 */
2239 
2240 #ifdef _MULTI_DATAMODEL
2241 
2242 	/*
2243 	 * More buffer space is required for the 64bit to 32bit
2244 	 * conversion of data structures.
2245 	 */
2246 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
2247 		sz32 = sizeof (sbd_stat32_t);
2248 		if (ncm > 1)
2249 			sz32  += sizeof (sbd_dev_stat32_t) * (ncm - 1);
2250 		pnstat = (pbsz - sizeof (sbd_stat32_t))/
2251 				sizeof (sbd_dev_stat32_t);
2252 	}
2253 
2254 	sz += sz32;
2255 #endif
2256 	/*
2257 	 * Since one sbd_dev_stat_t is included in the sbd_stat_t,
2258 	 * increment the plugin's nstat count.
2259 	 */
2260 	++pnstat;
2261 
2262 	if (bp->b_id == 0) {
2263 		bzero(&pstat, sizeof (pstat));
2264 	} else {
2265 		sbd_error_t *err;
2266 
2267 		err = drmach_status(bp->b_id, &pstat);
2268 		if (err) {
2269 			DRERR_SET_C(&hp->h_err, &err);
2270 			rv = EIO;
2271 			goto status_done;
2272 		}
2273 	}
2274 
2275 	dstatp = (sbd_stat_t *)GETSTRUCT(char, sz);
2276 
2277 	devstatp = &dstatp->s_stat[0];
2278 
2279 	dstatp->s_board = bp->b_num;
2280 
2281 	/*
2282 	 * Detect transitions between empty and disconnected.
2283 	 */
2284 	if (!pstat.empty && (bp->b_rstate == SBD_STAT_EMPTY))
2285 		bp->b_rstate = SBD_STAT_DISCONNECTED;
2286 	else if (pstat.empty && (bp->b_rstate == SBD_STAT_DISCONNECTED))
2287 		bp->b_rstate = SBD_STAT_EMPTY;
2288 
2289 	dstatp->s_rstate = bp->b_rstate;
2290 	dstatp->s_ostate = bp->b_ostate;
2291 	dstatp->s_cond = bp->b_cond = pstat.cond;
2292 	dstatp->s_busy = bp->b_busy | pstat.busy;
2293 	dstatp->s_time = bp->b_time;
2294 	dstatp->s_power = pstat.powered;
2295 	dstatp->s_assigned = bp->b_assigned = pstat.assigned;
2296 	dstatp->s_nstat = nstat = 0;
2297 	bcopy(&pstat.type[0], &dstatp->s_type[0], SBD_TYPE_LEN);
2298 	bcopy(&pstat.info[0], &dstatp->s_info[0], SBD_MAX_INFO);
2299 
2300 	devset &= DR_DEVS_PRESENT(bp);
2301 	if (devset == 0) {
2302 		/*
2303 		 * No device chosen.
2304 		 */
2305 		PR_ALL("%s: no device present\n", f);
2306 	}
2307 
2308 	if (DEVSET_IN_SET(devset, SBD_COMP_CPU, DEVSET_ANYUNIT))
2309 		if ((nstat = dr_cpu_status(hp, devset, devstatp)) > 0) {
2310 			dstatp->s_nstat += nstat;
2311 			devstatp += nstat;
2312 		}
2313 
2314 	if (DEVSET_IN_SET(devset, SBD_COMP_MEM, DEVSET_ANYUNIT))
2315 		if ((nstat = dr_mem_status(hp, devset, devstatp)) > 0) {
2316 			dstatp->s_nstat += nstat;
2317 			devstatp += nstat;
2318 		}
2319 
2320 	if (DEVSET_IN_SET(devset, SBD_COMP_IO, DEVSET_ANYUNIT))
2321 		if ((nstat = dr_io_status(hp, devset, devstatp)) > 0) {
2322 			dstatp->s_nstat += nstat;
2323 			devstatp += nstat;
2324 		}
2325 
2326 	/*
2327 	 * Due to a possible change in number of components between
2328 	 * the time of plugin's GETNCM call and now, there may be
2329 	 * more or less components than the plugin's buffer can
2330 	 * hold.  Adjust s_nstat accordingly.
2331 	 */
2332 
2333 	dstatp->s_nstat = dstatp->s_nstat > pnstat ? pnstat : dstatp->s_nstat;
2334 
2335 
2336 #ifdef _MULTI_DATAMODEL
2337 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
2338 		int		i, j;
2339 		sbd_stat32_t	*dstat32p;
2340 
2341 		dstat32p = (sbd_stat32_t *)devstatp;
2342 
2343 		/* Alignment Paranoia */
2344 		if ((ulong_t)dstat32p & 0x1) {
2345 			PR_ALL("%s: alignment: sz=0x%x dstat32p=0x%x\n",
2346 				f, sizeof (sbd_stat32_t), dstat32p);
2347 			DR_OP_INTERNAL_ERROR(hp);
2348 			rv = EINVAL;
2349 			goto status_done;
2350 		}
2351 
2352 		/* paranoia: detect buffer overrun */
2353 		if ((caddr_t)&dstat32p->s_stat[dstatp->s_nstat] >
2354 				((caddr_t)dstatp) + sz) {
2355 			DR_OP_INTERNAL_ERROR(hp);
2356 			rv = EINVAL;
2357 			goto status_done;
2358 		}
2359 
2360 		/* copy sbd_stat_t structure members */
2361 #define	_SBD_STAT(t, m) dstat32p->m = (t)dstatp->m
2362 		_SBD_STAT(int32_t, s_board);
2363 		_SBD_STAT(int32_t, s_rstate);
2364 		_SBD_STAT(int32_t, s_ostate);
2365 		_SBD_STAT(int32_t, s_cond);
2366 		_SBD_STAT(int32_t, s_busy);
2367 		_SBD_STAT(time32_t, s_time);
2368 		_SBD_STAT(uint32_t, s_power);
2369 		_SBD_STAT(uint32_t, s_assigned);
2370 		_SBD_STAT(int32_t, s_nstat);
2371 		bcopy(&dstatp->s_type[0], &dstat32p->s_type[0],
2372 			SBD_TYPE_LEN);
2373 		bcopy(&dstatp->s_info[0], &dstat32p->s_info[0],
2374 			SBD_MAX_INFO);
2375 #undef _SBD_STAT
2376 
2377 		for (i = 0; i < dstatp->s_nstat; i++) {
2378 			sbd_dev_stat_t		*dsp = &dstatp->s_stat[i];
2379 			sbd_dev_stat32_t	*ds32p = &dstat32p->s_stat[i];
2380 #define	_SBD_DEV_STAT(t, m) ds32p->m = (t)dsp->m
2381 
2382 			/* copy sbd_cm_stat_t structure members */
2383 			_SBD_DEV_STAT(int32_t, ds_type);
2384 			_SBD_DEV_STAT(int32_t, ds_unit);
2385 			_SBD_DEV_STAT(int32_t, ds_ostate);
2386 			_SBD_DEV_STAT(int32_t, ds_cond);
2387 			_SBD_DEV_STAT(int32_t, ds_busy);
2388 			_SBD_DEV_STAT(int32_t, ds_suspend);
2389 			_SBD_DEV_STAT(time32_t, ds_time);
2390 			bcopy(&dsp->ds_name[0], &ds32p->ds_name[0],
2391 			    OBP_MAXPROPNAME);
2392 
2393 			switch (dsp->ds_type) {
2394 			case SBD_COMP_CPU:
2395 				/* copy sbd_cpu_stat_t structure members */
2396 				_SBD_DEV_STAT(int32_t, d_cpu.cs_isbootproc);
2397 				_SBD_DEV_STAT(int32_t, d_cpu.cs_cpuid);
2398 				_SBD_DEV_STAT(int32_t, d_cpu.cs_speed);
2399 				_SBD_DEV_STAT(int32_t, d_cpu.cs_ecache);
2400 				break;
2401 
2402 			case SBD_COMP_MEM:
2403 				/* copy sbd_mem_stat_t structure members */
2404 				_SBD_DEV_STAT(int32_t, d_mem.ms_interleave);
2405 				_SBD_DEV_STAT(uint32_t, d_mem.ms_basepfn);
2406 				_SBD_DEV_STAT(uint32_t, d_mem.ms_totpages);
2407 				_SBD_DEV_STAT(uint32_t, d_mem.ms_detpages);
2408 				_SBD_DEV_STAT(int32_t, d_mem.ms_pageslost);
2409 				_SBD_DEV_STAT(uint32_t, d_mem.ms_managed_pages);
2410 				_SBD_DEV_STAT(uint32_t, d_mem.ms_noreloc_pages);
2411 				_SBD_DEV_STAT(uint32_t, d_mem.ms_noreloc_first);
2412 				_SBD_DEV_STAT(uint32_t, d_mem.ms_noreloc_last);
2413 				_SBD_DEV_STAT(int32_t, d_mem.ms_cage_enabled);
2414 				_SBD_DEV_STAT(int32_t, d_mem.ms_peer_is_target);
2415 				bcopy(&dsp->d_mem.ms_peer_ap_id[0],
2416 					&ds32p->d_mem.ms_peer_ap_id[0],
2417 					sizeof (ds32p->d_mem.ms_peer_ap_id));
2418 				break;
2419 
2420 			case SBD_COMP_IO:
2421 				/* copy sbd_io_stat_t structure members */
2422 				_SBD_DEV_STAT(int32_t, d_io.is_referenced);
2423 				_SBD_DEV_STAT(int32_t, d_io.is_unsafe_count);
2424 
2425 				for (j = 0; j < SBD_MAX_UNSAFE; j++)
2426 					_SBD_DEV_STAT(int32_t,
2427 						d_io.is_unsafe_list[j]);
2428 
2429 				bcopy(&dsp->d_io.is_pathname[0],
2430 				    &ds32p->d_io.is_pathname[0], MAXPATHLEN);
2431 				break;
2432 
2433 			case SBD_COMP_CMP:
2434 				/* copy sbd_cmp_stat_t structure members */
2435 				bcopy(&dsp->d_cmp.ps_cpuid[0],
2436 					&ds32p->d_cmp.ps_cpuid[0],
2437 					sizeof (ds32p->d_cmp.ps_cpuid));
2438 				_SBD_DEV_STAT(int32_t, d_cmp.ps_ncores);
2439 				_SBD_DEV_STAT(int32_t, d_cmp.ps_speed);
2440 				_SBD_DEV_STAT(int32_t, d_cmp.ps_ecache);
2441 				break;
2442 
2443 			default:
2444 				cmn_err(CE_WARN, "%s: unknown dev type (%d)",
2445 				    f, (int)dsp->ds_type);
2446 				rv = EFAULT;
2447 				goto status_done;
2448 			}
2449 #undef _SBD_DEV_STAT
2450 		}
2451 
2452 
2453 		if (ddi_copyout((void *)dstat32p,
2454 			hp->h_sbdcmd.cmd_stat.s_statp, pbsz, mode) != 0) {
2455 			cmn_err(CE_WARN,
2456 				"%s: failed to copyout status "
2457 				"for board %d", f, bp->b_num);
2458 			rv = EFAULT;
2459 			goto status_done;
2460 		}
2461 	} else
2462 #endif /* _MULTI_DATAMODEL */
2463 
2464 	if (ddi_copyout((void *)dstatp, hp->h_sbdcmd.cmd_stat.s_statp,
2465 		pbsz, mode) != 0) {
2466 		cmn_err(CE_WARN,
2467 			"%s: failed to copyout status for board %d",
2468 			f, bp->b_num);
2469 		rv = EFAULT;
2470 		goto status_done;
2471 	}
2472 
2473 status_done:
2474 	if (dstatp != NULL)
2475 		FREESTRUCT(dstatp, char, sz);
2476 
2477 	dr_unlock_status(bp);
2478 
2479 	return (rv);
2480 }
2481 
2482 static int
2483 dr_get_ncm(dr_handle_t *hp)
2484 {
2485 	int		i;
2486 	int		ncm = 0;
2487 	dr_devset_t	devset;
2488 
2489 	devset = DR_DEVS_PRESENT(hp->h_bd);
2490 	if (hp->h_sbdcmd.cmd_cm.c_id.c_type != SBD_COMP_NONE)
2491 		devset &= DEVSET(hp->h_sbdcmd.cmd_cm.c_id.c_type,
2492 				DEVSET_ANYUNIT);
2493 
2494 	/*
2495 	 * Handle CPUs first to deal with possible CMP
2496 	 * devices. If the CPU is a CMP, we need to only
2497 	 * increment ncm once even if there are multiple
2498 	 * cores for that CMP present in the devset.
2499 	 */
2500 	for (i = 0; i < MAX_CMP_UNITS_PER_BOARD; i++) {
2501 		if (devset & DEVSET(SBD_COMP_CMP, i)) {
2502 			ncm++;
2503 		}
2504 	}
2505 
2506 	/* eliminate the CPU information from the devset */
2507 	devset &= ~(DEVSET(SBD_COMP_CMP, DEVSET_ANYUNIT));
2508 
2509 	for (i = 0; i < (sizeof (dr_devset_t) * 8); i++) {
2510 		ncm += devset & 0x1;
2511 		devset >>= 1;
2512 	}
2513 
2514 	return (ncm);
2515 }
2516 
2517 /* used by dr_mem.c */
2518 /* TODO: eliminate dr_boardlist */
2519 dr_board_t *
2520 dr_lookup_board(int board_num)
2521 {
2522 	dr_board_t *bp;
2523 
2524 	ASSERT(board_num >= 0 && board_num < MAX_BOARDS);
2525 
2526 	bp = &dr_boardlist[board_num];
2527 	ASSERT(bp->b_num == board_num);
2528 
2529 	return (bp);
2530 }
2531 
2532 static dr_dev_unit_t *
2533 dr_get_dev_unit(dr_board_t *bp, sbd_comp_type_t nt, int unit_num)
2534 {
2535 	dr_dev_unit_t	*dp;
2536 
2537 	dp = DR_GET_BOARD_DEVUNIT(bp, nt, unit_num);
2538 	ASSERT(dp->du_common.sbdev_bp == bp);
2539 	ASSERT(dp->du_common.sbdev_unum == unit_num);
2540 	ASSERT(dp->du_common.sbdev_type == nt);
2541 
2542 	return (dp);
2543 }
2544 
2545 dr_cpu_unit_t *
2546 dr_get_cpu_unit(dr_board_t *bp, int unit_num)
2547 {
2548 	dr_dev_unit_t	*dp;
2549 
2550 	ASSERT(unit_num >= 0 && unit_num < MAX_CPU_UNITS_PER_BOARD);
2551 
2552 	dp = dr_get_dev_unit(bp, SBD_COMP_CPU, unit_num);
2553 	return (&dp->du_cpu);
2554 }
2555 
2556 dr_mem_unit_t *
2557 dr_get_mem_unit(dr_board_t *bp, int unit_num)
2558 {
2559 	dr_dev_unit_t	*dp;
2560 
2561 	ASSERT(unit_num >= 0 && unit_num < MAX_MEM_UNITS_PER_BOARD);
2562 
2563 	dp = dr_get_dev_unit(bp, SBD_COMP_MEM, unit_num);
2564 	return (&dp->du_mem);
2565 }
2566 
2567 dr_io_unit_t *
2568 dr_get_io_unit(dr_board_t *bp, int unit_num)
2569 {
2570 	dr_dev_unit_t	*dp;
2571 
2572 	ASSERT(unit_num >= 0 && unit_num < MAX_IO_UNITS_PER_BOARD);
2573 
2574 	dp = dr_get_dev_unit(bp, SBD_COMP_IO, unit_num);
2575 	return (&dp->du_io);
2576 }
2577 
2578 dr_common_unit_t *
2579 dr_get_common_unit(dr_board_t *bp, sbd_comp_type_t nt, int unum)
2580 {
2581 	dr_dev_unit_t	*dp;
2582 
2583 	dp = dr_get_dev_unit(bp, nt, unum);
2584 	return (&dp->du_common);
2585 }
2586 
2587 static dr_devset_t
2588 dr_dev2devset(sbd_comp_id_t *cid)
2589 {
2590 	static fn_t	f = "dr_dev2devset";
2591 
2592 	dr_devset_t	devset;
2593 	int		unit = cid->c_unit;
2594 
2595 	switch (cid->c_type) {
2596 		case SBD_COMP_NONE:
2597 			devset =  DEVSET(SBD_COMP_CPU, DEVSET_ANYUNIT);
2598 			devset |= DEVSET(SBD_COMP_MEM, DEVSET_ANYUNIT);
2599 			devset |= DEVSET(SBD_COMP_IO,  DEVSET_ANYUNIT);
2600 			PR_ALL("%s: COMP_NONE devset = 0x%x\n", f, devset);
2601 			break;
2602 
2603 		case SBD_COMP_CPU:
2604 			if ((unit > MAX_CPU_UNITS_PER_BOARD) || (unit < 0)) {
2605 				cmn_err(CE_WARN,
2606 					"%s: invalid cpu unit# = %d",
2607 					f, unit);
2608 				devset = 0;
2609 			} else {
2610 				/*
2611 				 * Generate a devset that includes all the
2612 				 * cores of a CMP device. If this is not a
2613 				 * CMP, the extra cores will be eliminated
2614 				 * later since they are not present. This is
2615 				 * also true for CMP devices that do not have
2616 				 * all cores active.
2617 				 */
2618 				devset = DEVSET(SBD_COMP_CMP, unit);
2619 			}
2620 
2621 			PR_ALL("%s: CPU devset = 0x%x\n", f, devset);
2622 			break;
2623 
2624 		case SBD_COMP_MEM:
2625 			if (unit == SBD_NULL_UNIT) {
2626 				unit = 0;
2627 				cid->c_unit = 0;
2628 			}
2629 
2630 			if ((unit > MAX_MEM_UNITS_PER_BOARD) || (unit < 0)) {
2631 				cmn_err(CE_WARN,
2632 					"%s: invalid mem unit# = %d",
2633 					f, unit);
2634 				devset = 0;
2635 			} else
2636 				devset = DEVSET(cid->c_type, unit);
2637 
2638 			PR_ALL("%s: MEM devset = 0x%x\n", f, devset);
2639 			break;
2640 
2641 		case SBD_COMP_IO:
2642 			if ((unit > MAX_IO_UNITS_PER_BOARD) || (unit < 0)) {
2643 				cmn_err(CE_WARN,
2644 					"%s: invalid io unit# = %d",
2645 					f, unit);
2646 				devset = 0;
2647 			} else
2648 				devset = DEVSET(cid->c_type, unit);
2649 
2650 			PR_ALL("%s: IO devset = 0x%x\n", f, devset);
2651 			break;
2652 
2653 		default:
2654 		case SBD_COMP_UNKNOWN:
2655 			devset = 0;
2656 			break;
2657 	}
2658 
2659 	return (devset);
2660 }
2661 
2662 /*
2663  * Converts a dynamic attachment point name to a SBD_COMP_* type.
2664  * Returns SDB_COMP_UNKNOWN if name is not recognized.
2665  */
2666 static int
2667 dr_dev_type_to_nt(char *type)
2668 {
2669 	int i;
2670 
2671 	for (i = 0; dr_devattr[i].s_nodetype != SBD_COMP_UNKNOWN; i++)
2672 		if (strcmp(dr_devattr[i].s_devtype, type) == 0)
2673 			break;
2674 
2675 	return (dr_devattr[i].s_nodetype);
2676 }
2677 
2678 /*
2679  * Converts a SBD_COMP_* type to a dynamic attachment point name.
2680  * Return NULL if SBD_COMP_ type is not recognized.
2681  */
2682 char *
2683 dr_nt_to_dev_type(int nt)
2684 {
2685 	int i;
2686 
2687 	for (i = 0; dr_devattr[i].s_nodetype != SBD_COMP_UNKNOWN; i++)
2688 		if (dr_devattr[i].s_nodetype == nt)
2689 			break;
2690 
2691 	return (dr_devattr[i].s_devtype);
2692 }
2693 
2694 
2695 /*
2696  * State transition policy is that if there is some component for which
2697  * the state transition is valid, then let it through. The exception is
2698  * SBD_CMD_DISCONNECT. On disconnect, the state transition must be valid
2699  * for ALL components.
2700  * Returns the state that is in error, if any.
2701  */
2702 static int
2703 dr_check_transition(dr_board_t *bp, dr_devset_t *devsetp,
2704 			struct dr_state_trans *transp, int cmd)
2705 {
2706 	int			s, ut;
2707 	int			state_err = 0;
2708 	dr_devset_t		devset;
2709 	dr_common_unit_t	*cp;
2710 	static fn_t		f = "dr_check_transition";
2711 
2712 	devset = *devsetp;
2713 
2714 	if (DEVSET_IN_SET(devset, SBD_COMP_CPU, DEVSET_ANYUNIT)) {
2715 		for (ut = 0; ut < MAX_CPU_UNITS_PER_BOARD; ut++) {
2716 			if (DEVSET_IN_SET(devset, SBD_COMP_CPU, ut) == 0)
2717 				continue;
2718 
2719 			cp = dr_get_common_unit(bp, SBD_COMP_CPU, ut);
2720 			s = (int)cp->sbdev_state;
2721 			if (!DR_DEV_IS_PRESENT(cp)) {
2722 				DEVSET_DEL(devset, SBD_COMP_CPU, ut);
2723 			} else {
2724 				if (transp->x_op[s].x_rv) {
2725 					if (!state_err)
2726 						state_err = s;
2727 					DEVSET_DEL(devset, SBD_COMP_CPU, ut);
2728 				}
2729 			}
2730 		}
2731 	}
2732 	if (DEVSET_IN_SET(devset, SBD_COMP_MEM, DEVSET_ANYUNIT)) {
2733 		for (ut = 0; ut < MAX_MEM_UNITS_PER_BOARD; ut++) {
2734 			if (DEVSET_IN_SET(devset, SBD_COMP_MEM, ut) == 0)
2735 				continue;
2736 
2737 			cp = dr_get_common_unit(bp, SBD_COMP_MEM, ut);
2738 			s = (int)cp->sbdev_state;
2739 			if (!DR_DEV_IS_PRESENT(cp)) {
2740 				DEVSET_DEL(devset, SBD_COMP_MEM, ut);
2741 			} else {
2742 				if (transp->x_op[s].x_rv) {
2743 					if (!state_err)
2744 						state_err = s;
2745 					DEVSET_DEL(devset, SBD_COMP_MEM, ut);
2746 				}
2747 			}
2748 		}
2749 	}
2750 	if (DEVSET_IN_SET(devset, SBD_COMP_IO, DEVSET_ANYUNIT)) {
2751 		for (ut = 0; ut < MAX_IO_UNITS_PER_BOARD; ut++) {
2752 			if (DEVSET_IN_SET(devset, SBD_COMP_IO, ut) == 0)
2753 				continue;
2754 
2755 			cp = dr_get_common_unit(bp, SBD_COMP_IO, ut);
2756 			s = (int)cp->sbdev_state;
2757 			if (!DR_DEV_IS_PRESENT(cp)) {
2758 				DEVSET_DEL(devset, SBD_COMP_IO, ut);
2759 			} else {
2760 				if (transp->x_op[s].x_rv) {
2761 					if (!state_err)
2762 						state_err = s;
2763 					DEVSET_DEL(devset, SBD_COMP_IO, ut);
2764 				}
2765 			}
2766 		}
2767 	}
2768 
2769 	PR_ALL("%s: requested devset = 0x%x, final devset = 0x%x\n",
2770 		f, (uint_t)*devsetp, (uint_t)devset);
2771 
2772 	*devsetp = devset;
2773 	/*
2774 	 * If there are some remaining components for which
2775 	 * this state transition is valid, then allow them
2776 	 * through, otherwise if none are left then return
2777 	 * the state error. The exception is SBD_CMD_DISCONNECT.
2778 	 * On disconnect, the state transition must be valid for ALL
2779 	 * components.
2780 	 */
2781 	if (cmd == SBD_CMD_DISCONNECT)
2782 		return (state_err);
2783 	return (devset ? 0 : state_err);
2784 }
2785 
2786 void
2787 dr_device_transition(dr_common_unit_t *cp, dr_state_t st)
2788 {
2789 	PR_STATE("%s STATE %s(%d) -> %s(%d)\n",
2790 		cp->sbdev_path,
2791 		state_str[cp->sbdev_state], cp->sbdev_state,
2792 		state_str[st], st);
2793 
2794 	cp->sbdev_state = st;
2795 	if (st == DR_STATE_CONFIGURED) {
2796 		cp->sbdev_ostate = SBD_STAT_CONFIGURED;
2797 		if (cp->sbdev_bp->b_ostate != SBD_STAT_CONFIGURED) {
2798 			cp->sbdev_bp->b_ostate = SBD_STAT_CONFIGURED;
2799 			(void) drv_getparm(TIME,
2800 				(void *) &cp->sbdev_bp->b_time);
2801 		}
2802 	} else
2803 		cp->sbdev_ostate = SBD_STAT_UNCONFIGURED;
2804 
2805 	(void) drv_getparm(TIME, (void *) &cp->sbdev_time);
2806 }
2807 
2808 static void
2809 dr_board_transition(dr_board_t *bp, dr_state_t st)
2810 {
2811 	PR_STATE("BOARD %d STATE: %s(%d) -> %s(%d)\n",
2812 		bp->b_num,
2813 		state_str[bp->b_state], bp->b_state,
2814 		state_str[st], st);
2815 
2816 	bp->b_state = st;
2817 }
2818 
2819 void
2820 dr_op_err(int ce, dr_handle_t *hp, int code, char *fmt, ...)
2821 {
2822 	sbd_error_t	*err;
2823 	va_list		args;
2824 
2825 	va_start(args, fmt);
2826 	err = drerr_new_v(code, fmt, args);
2827 	va_end(args);
2828 
2829 	if (ce != CE_IGNORE)
2830 		sbd_err_log(err, ce);
2831 
2832 	DRERR_SET_C(&hp->h_err, &err);
2833 }
2834 
2835 void
2836 dr_dev_err(int ce, dr_common_unit_t *cp, int code)
2837 {
2838 	sbd_error_t	*err;
2839 
2840 	err = drerr_new(0, code, cp->sbdev_path, NULL);
2841 
2842 	if (ce != CE_IGNORE)
2843 		sbd_err_log(err, ce);
2844 
2845 	DRERR_SET_C(&cp->sbdev_error, &err);
2846 }
2847 
2848 /*
2849  * A callback routine.  Called from the drmach layer as a result of
2850  * call to drmach_board_find_devices from dr_init_devlists.
2851  */
2852 static sbd_error_t *
2853 dr_dev_found(void *data, const char *name, int unum, drmachid_t id)
2854 {
2855 	dr_board_t	*bp = data;
2856 	dr_dev_unit_t	*dp;
2857 	int		 nt;
2858 	static fn_t	f = "dr_dev_found";
2859 
2860 	PR_ALL("%s (board = %d, name = %s, unum = %d, id = %p)...\n",
2861 		f, bp->b_num, name, unum, id);
2862 
2863 	nt = dr_dev_type_to_nt((char *)name);
2864 	if (nt == SBD_COMP_UNKNOWN) {
2865 		/*
2866 		 * this should not happen.  When it does, it indicates
2867 		 * a missmatch in devices supported by the drmach layer
2868 		 * vs devices supported by this layer.
2869 		 */
2870 		return (DR_INTERNAL_ERROR());
2871 	}
2872 
2873 	dp = DR_GET_BOARD_DEVUNIT(bp, nt, unum);
2874 
2875 	/* sanity check */
2876 	ASSERT(dp->du_common.sbdev_bp == bp);
2877 	ASSERT(dp->du_common.sbdev_unum == unum);
2878 	ASSERT(dp->du_common.sbdev_type == nt);
2879 
2880 	/* render dynamic attachment point path of this unit */
2881 	(void) snprintf(dp->du_common.sbdev_path,
2882 			sizeof (dp->du_common.sbdev_path),
2883 			(nt == SBD_COMP_MEM ? "%s::%s" : "%s::%s%d"),
2884 			bp->b_path, name, DR_UNUM2SBD_UNUM(unum));
2885 
2886 	dp->du_common.sbdev_id = id;
2887 	DR_DEV_SET_PRESENT(&dp->du_common);
2888 
2889 	bp->b_ndev++;
2890 
2891 	return (NULL);
2892 }
2893 
2894 static sbd_error_t *
2895 dr_init_devlists(dr_board_t *bp)
2896 {
2897 	int		i;
2898 	sbd_error_t	*err;
2899 	dr_dev_unit_t	*dp;
2900 	static fn_t	f = "dr_init_devlists";
2901 
2902 	PR_ALL("%s (%s)...\n", f, bp->b_path);
2903 
2904 	/* sanity check */
2905 	ASSERT(bp->b_ndev == 0);
2906 
2907 	DR_DEVS_DISCONNECT(bp, (uint_t)-1);
2908 
2909 	/*
2910 	 * This routine builds the board's devlist and initializes
2911 	 * the common portion of the unit data structures.
2912 	 * Note: because the common portion is considered
2913 	 * uninitialized, the dr_get_*_unit() routines can not
2914 	 * be used.
2915 	 */
2916 
2917 	/*
2918 	 * Clear out old entries, if any.
2919 	 */
2920 	for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
2921 		dp = DR_GET_BOARD_DEVUNIT(bp, SBD_COMP_CPU, i);
2922 
2923 		bzero(dp, sizeof (*dp));
2924 		dp->du_common.sbdev_bp = bp;
2925 		dp->du_common.sbdev_unum = i;
2926 		dp->du_common.sbdev_type = SBD_COMP_CPU;
2927 	}
2928 
2929 	for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
2930 		dp = DR_GET_BOARD_DEVUNIT(bp, SBD_COMP_MEM, i);
2931 
2932 		bzero(dp, sizeof (*dp));
2933 		dp->du_common.sbdev_bp = bp;
2934 		dp->du_common.sbdev_unum = i;
2935 		dp->du_common.sbdev_type = SBD_COMP_MEM;
2936 	}
2937 
2938 	for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
2939 		dp = DR_GET_BOARD_DEVUNIT(bp, SBD_COMP_IO, i);
2940 
2941 		bzero(dp, sizeof (*dp));
2942 		dp->du_common.sbdev_bp = bp;
2943 		dp->du_common.sbdev_unum = i;
2944 		dp->du_common.sbdev_type = SBD_COMP_IO;
2945 	}
2946 
2947 	err = NULL;
2948 	if (bp->b_id) {
2949 		/* find devices on this board */
2950 		err = drmach_board_find_devices(
2951 			bp->b_id, bp, dr_dev_found);
2952 	}
2953 
2954 	return (err);
2955 }
2956 
2957 /*
2958  * Return the unit number of the respective drmachid if
2959  * it's found to be attached.
2960  */
2961 static int
2962 dr_check_unit_attached(dr_common_unit_t *cp)
2963 {
2964 	int		rv = 0;
2965 	processorid_t	cpuid;
2966 	uint64_t	basepa, endpa;
2967 	struct memlist	*ml;
2968 	extern struct memlist	*phys_install;
2969 	sbd_error_t	*err;
2970 	int		yes;
2971 	static fn_t	f = "dr_check_unit_attached";
2972 
2973 	switch (cp->sbdev_type) {
2974 	case SBD_COMP_CPU:
2975 		err = drmach_cpu_get_id(cp->sbdev_id, &cpuid);
2976 		if (err) {
2977 			DRERR_SET_C(&cp->sbdev_error, &err);
2978 			rv = -1;
2979 			break;
2980 		}
2981 		mutex_enter(&cpu_lock);
2982 		if (cpu_get(cpuid) == NULL)
2983 			rv = -1;
2984 		mutex_exit(&cpu_lock);
2985 		break;
2986 
2987 	case SBD_COMP_MEM:
2988 		err = drmach_mem_get_base_physaddr(cp->sbdev_id, &basepa);
2989 		if (err) {
2990 			DRERR_SET_C(&cp->sbdev_error, &err);
2991 			rv = -1;
2992 			break;
2993 		}
2994 
2995 		/*
2996 		 * basepa may not be on a alignment boundary, make it so.
2997 		 */
2998 		err = drmach_mem_get_slice_size(cp->sbdev_id, &endpa);
2999 		if (err) {
3000 			DRERR_SET_C(&cp->sbdev_error, &err);
3001 			rv = -1;
3002 			break;
3003 		}
3004 
3005 		basepa &= ~(endpa - 1);
3006 		endpa += basepa;
3007 
3008 		/*
3009 		 * Check if base address is in phys_install.
3010 		 */
3011 		memlist_read_lock();
3012 		for (ml = phys_install; ml; ml = ml->next)
3013 			if ((endpa <= ml->address) ||
3014 					(basepa >= (ml->address + ml->size)))
3015 				continue;
3016 			else
3017 				break;
3018 		memlist_read_unlock();
3019 		if (ml == NULL)
3020 			rv = -1;
3021 		break;
3022 
3023 	case SBD_COMP_IO:
3024 		err = drmach_io_is_attached(cp->sbdev_id, &yes);
3025 		if (err) {
3026 			DRERR_SET_C(&cp->sbdev_error, &err);
3027 			rv = -1;
3028 			break;
3029 		} else if (!yes)
3030 			rv = -1;
3031 		break;
3032 
3033 	default:
3034 		PR_ALL("%s: unexpected nodetype(%d) for id 0x%x\n",
3035 			f, cp->sbdev_type, cp->sbdev_id);
3036 		rv = -1;
3037 		break;
3038 	}
3039 
3040 	return (rv);
3041 }
3042 
3043 /*
3044  * See if drmach recognizes the passthru command.  DRMACH expects the
3045  * id to identify the thing to which the command is being applied.  Using
3046  * nonsense SBD terms, that information has been perversely encoded in the
3047  * c_id member of the sbd_cmd_t structure.  This logic reads those tea
3048  * leaves, finds the associated drmach id, then calls drmach to process
3049  * the passthru command.
3050  */
3051 static int
3052 dr_pt_try_drmach(dr_handle_t *hp)
3053 {
3054 	dr_board_t	*bp = hp->h_bd;
3055 	sbd_comp_id_t	*comp_id = &hp->h_sbdcmd.cmd_cm.c_id;
3056 	drmachid_t	 id;
3057 
3058 	if (comp_id->c_type == SBD_COMP_NONE) {
3059 		id = bp->b_id;
3060 	} else {
3061 		sbd_comp_type_t	 nt;
3062 
3063 		nt = dr_dev_type_to_nt(comp_id->c_name);
3064 		if (nt == SBD_COMP_UNKNOWN) {
3065 			dr_op_err(CE_IGNORE, hp, ESBD_INVAL, comp_id->c_name);
3066 			id = 0;
3067 		} else {
3068 			/* pt command applied to dynamic attachment point */
3069 			dr_common_unit_t *cp;
3070 			cp = dr_get_common_unit(bp, nt, comp_id->c_unit);
3071 			id = cp->sbdev_id;
3072 		}
3073 	}
3074 
3075 	if (hp->h_err == NULL)
3076 		hp->h_err = drmach_passthru(id, &hp->h_opts);
3077 
3078 	return (hp->h_err == NULL ? 0 : -1);
3079 }
3080 
3081 static int
3082 dr_pt_ioctl(dr_handle_t *hp)
3083 {
3084 	int		cmd, rv, len;
3085 	int32_t		sz;
3086 	int		found;
3087 	char		*copts;
3088 	static fn_t	f = "dr_pt_ioctl";
3089 
3090 	PR_ALL("%s...\n", f);
3091 
3092 	sz = hp->h_opts.size;
3093 	copts = hp->h_opts.copts;
3094 
3095 	if (sz == 0 || copts == (char *)NULL) {
3096 		cmn_err(CE_WARN, "%s: invalid passthru args", f);
3097 		return (EINVAL);
3098 	}
3099 
3100 	found = 0;
3101 	for (cmd = 0; cmd < (sizeof (pt_arr) / sizeof (pt_arr[0])); cmd++) {
3102 		len = strlen(pt_arr[cmd].pt_name);
3103 		found = (strncmp(pt_arr[cmd].pt_name, copts, len) == 0);
3104 		if (found)
3105 			break;
3106 	}
3107 
3108 	if (found)
3109 		rv = (*pt_arr[cmd].pt_func)(hp);
3110 	else
3111 		rv = dr_pt_try_drmach(hp);
3112 
3113 	return (rv);
3114 }
3115 
3116 /*
3117  * Called at driver load time to determine the state and condition
3118  * of an existing board in the system.
3119  */
3120 static void
3121 dr_board_discovery(dr_board_t *bp)
3122 {
3123 	int			i;
3124 	dr_devset_t		devs_lost, devs_attached = 0;
3125 	dr_cpu_unit_t		*cp;
3126 	dr_mem_unit_t		*mp;
3127 	dr_io_unit_t		*ip;
3128 	static fn_t		f = "dr_board_discovery";
3129 
3130 	if (DR_DEVS_PRESENT(bp) == 0) {
3131 		PR_ALL("%s: board %d has no devices present\n",
3132 			f, bp->b_num);
3133 		return;
3134 	}
3135 
3136 	/*
3137 	 * Check for existence of cpus.
3138 	 */
3139 	for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
3140 		cp = dr_get_cpu_unit(bp, i);
3141 
3142 		if (!DR_DEV_IS_PRESENT(&cp->sbc_cm))
3143 			continue;
3144 
3145 		if (dr_check_unit_attached(&cp->sbc_cm) >= 0) {
3146 			DR_DEV_SET_ATTACHED(&cp->sbc_cm);
3147 			DEVSET_ADD(devs_attached, SBD_COMP_CPU, i);
3148 			PR_ALL("%s: board %d, cpu-unit %d - attached\n",
3149 				f, bp->b_num, i);
3150 		}
3151 		dr_init_cpu_unit(cp);
3152 	}
3153 
3154 	/*
3155 	 * Check for existence of memory.
3156 	 */
3157 	for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
3158 		mp = dr_get_mem_unit(bp, i);
3159 
3160 		if (!DR_DEV_IS_PRESENT(&mp->sbm_cm))
3161 			continue;
3162 
3163 		if (dr_check_unit_attached(&mp->sbm_cm) >= 0) {
3164 			DR_DEV_SET_ATTACHED(&mp->sbm_cm);
3165 			DEVSET_ADD(devs_attached, SBD_COMP_MEM, i);
3166 			PR_ALL("%s: board %d, mem-unit %d - attached\n",
3167 				f, bp->b_num, i);
3168 		}
3169 		dr_init_mem_unit(mp);
3170 	}
3171 
3172 	/*
3173 	 * Check for i/o state.
3174 	 */
3175 	for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
3176 		ip = dr_get_io_unit(bp, i);
3177 
3178 		if (!DR_DEV_IS_PRESENT(&ip->sbi_cm))
3179 			continue;
3180 
3181 		if (dr_check_unit_attached(&ip->sbi_cm) >= 0) {
3182 			/*
3183 			 * Found it!
3184 			 */
3185 			DR_DEV_SET_ATTACHED(&ip->sbi_cm);
3186 			DEVSET_ADD(devs_attached, SBD_COMP_IO, i);
3187 			PR_ALL("%s: board %d, io-unit %d - attached\n",
3188 				f, bp->b_num, i);
3189 		}
3190 		dr_init_io_unit(ip);
3191 	}
3192 
3193 	DR_DEVS_CONFIGURE(bp, devs_attached);
3194 	if (devs_attached && ((devs_lost = DR_DEVS_UNATTACHED(bp)) != 0)) {
3195 		int		ut;
3196 		/*
3197 		 * It is not legal on board discovery to have a
3198 		 * board that is only partially attached.  A board
3199 		 * is either all attached or all connected.  If a
3200 		 * board has at least one attached device, then
3201 		 * the the remaining devices, if any, must have
3202 		 * been lost or disconnected.  These devices can
3203 		 * only be recovered by a full attach from scratch.
3204 		 * Note that devices previously in the unreferenced
3205 		 * state are subsequently lost until the next full
3206 		 * attach.  This is necessary since the driver unload
3207 		 * that must have occurred would have wiped out the
3208 		 * information necessary to re-configure the device
3209 		 * back online, e.g. memlist.
3210 		 */
3211 		PR_ALL("%s: some devices LOST (0x%x)...\n", f, devs_lost);
3212 
3213 		for (ut = 0; ut < MAX_CPU_UNITS_PER_BOARD; ut++) {
3214 			if (!DEVSET_IN_SET(devs_lost, SBD_COMP_CPU, ut))
3215 				continue;
3216 
3217 			cp = dr_get_cpu_unit(bp, ut);
3218 			dr_device_transition(&cp->sbc_cm, DR_STATE_EMPTY);
3219 		}
3220 
3221 		for (ut = 0; ut < MAX_MEM_UNITS_PER_BOARD; ut++) {
3222 			if (!DEVSET_IN_SET(devs_lost, SBD_COMP_MEM, ut))
3223 				continue;
3224 
3225 			mp = dr_get_mem_unit(bp, ut);
3226 			dr_device_transition(&mp->sbm_cm, DR_STATE_EMPTY);
3227 		}
3228 
3229 		for (ut = 0; ut < MAX_IO_UNITS_PER_BOARD; ut++) {
3230 			if (!DEVSET_IN_SET(devs_lost, SBD_COMP_IO, ut))
3231 				continue;
3232 
3233 			ip = dr_get_io_unit(bp, ut);
3234 			dr_device_transition(&ip->sbi_cm, DR_STATE_EMPTY);
3235 		}
3236 
3237 		DR_DEVS_DISCONNECT(bp, devs_lost);
3238 	}
3239 }
3240 
3241 static int
3242 dr_board_init(dr_board_t *bp, dev_info_t *dip, int bd)
3243 {
3244 	sbd_error_t	*err;
3245 
3246 	mutex_init(&bp->b_lock, NULL, MUTEX_DRIVER, NULL);
3247 	mutex_init(&bp->b_slock, NULL, MUTEX_DRIVER, NULL);
3248 	cv_init(&bp->b_scv, NULL, CV_DRIVER, NULL);
3249 	bp->b_rstate = SBD_STAT_EMPTY;
3250 	bp->b_ostate = SBD_STAT_UNCONFIGURED;
3251 	bp->b_cond = SBD_COND_UNKNOWN;
3252 	(void) drv_getparm(TIME, (void *)&bp->b_time);
3253 
3254 	(void) drmach_board_lookup(bd, &bp->b_id);
3255 	bp->b_num = bd;
3256 	bp->b_dip = dip;
3257 
3258 	bp->b_dev[NIX(SBD_COMP_CPU)] = GETSTRUCT(dr_dev_unit_t,
3259 						MAX_CPU_UNITS_PER_BOARD);
3260 
3261 	bp->b_dev[NIX(SBD_COMP_MEM)] = GETSTRUCT(dr_dev_unit_t,
3262 						MAX_MEM_UNITS_PER_BOARD);
3263 
3264 	bp->b_dev[NIX(SBD_COMP_IO)] = GETSTRUCT(dr_dev_unit_t,
3265 						MAX_IO_UNITS_PER_BOARD);
3266 
3267 	/*
3268 	 * Initialize the devlists
3269 	 */
3270 	err = dr_init_devlists(bp);
3271 	if (err) {
3272 		sbd_err_clear(&err);
3273 		dr_board_destroy(bp);
3274 		return (-1);
3275 	} else if (bp->b_ndev == 0) {
3276 		dr_board_transition(bp, DR_STATE_EMPTY);
3277 	} else {
3278 		/*
3279 		 * Couldn't have made it down here without
3280 		 * having found at least one device.
3281 		 */
3282 		ASSERT(DR_DEVS_PRESENT(bp) != 0);
3283 		/*
3284 		 * Check the state of any possible devices on the
3285 		 * board.
3286 		 */
3287 		dr_board_discovery(bp);
3288 
3289 		bp->b_assigned = 1;
3290 
3291 		if (DR_DEVS_UNATTACHED(bp) == 0) {
3292 			/*
3293 			 * The board has no unattached devices, therefore
3294 			 * by reason of insanity it must be configured!
3295 			 */
3296 			dr_board_transition(bp, DR_STATE_CONFIGURED);
3297 			bp->b_ostate = SBD_STAT_CONFIGURED;
3298 			bp->b_rstate = SBD_STAT_CONNECTED;
3299 			bp->b_cond = SBD_COND_OK;
3300 			(void) drv_getparm(TIME, (void *)&bp->b_time);
3301 		} else if (DR_DEVS_ATTACHED(bp)) {
3302 			dr_board_transition(bp, DR_STATE_PARTIAL);
3303 			bp->b_ostate = SBD_STAT_CONFIGURED;
3304 			bp->b_rstate = SBD_STAT_CONNECTED;
3305 			bp->b_cond = SBD_COND_OK;
3306 			(void) drv_getparm(TIME, (void *)&bp->b_time);
3307 		} else {
3308 			dr_board_transition(bp, DR_STATE_CONNECTED);
3309 			bp->b_rstate = SBD_STAT_CONNECTED;
3310 			(void) drv_getparm(TIME, (void *)&bp->b_time);
3311 		}
3312 	}
3313 
3314 	return (0);
3315 }
3316 
3317 static void
3318 dr_board_destroy(dr_board_t *bp)
3319 {
3320 	PR_ALL("dr_board_destroy: num %d, path %s\n",
3321 		bp->b_num, bp->b_path);
3322 
3323 	dr_board_transition(bp, DR_STATE_EMPTY);
3324 	bp->b_rstate = SBD_STAT_EMPTY;
3325 	(void) drv_getparm(TIME, (void *)&bp->b_time);
3326 
3327 	/*
3328 	 * Free up MEM unit structs.
3329 	 */
3330 	FREESTRUCT(bp->b_dev[NIX(SBD_COMP_MEM)],
3331 			dr_dev_unit_t, MAX_MEM_UNITS_PER_BOARD);
3332 	bp->b_dev[NIX(SBD_COMP_MEM)] = NULL;
3333 	/*
3334 	 * Free up CPU unit structs.
3335 	 */
3336 	FREESTRUCT(bp->b_dev[NIX(SBD_COMP_CPU)],
3337 			dr_dev_unit_t, MAX_CPU_UNITS_PER_BOARD);
3338 	bp->b_dev[NIX(SBD_COMP_CPU)] = NULL;
3339 	/*
3340 	 * Free up IO unit structs.
3341 	 */
3342 	FREESTRUCT(bp->b_dev[NIX(SBD_COMP_IO)],
3343 			dr_dev_unit_t, MAX_IO_UNITS_PER_BOARD);
3344 	bp->b_dev[NIX(SBD_COMP_IO)] = NULL;
3345 
3346 	mutex_destroy(&bp->b_lock);
3347 	mutex_destroy(&bp->b_slock);
3348 	cv_destroy(&bp->b_scv);
3349 }
3350 
3351 void
3352 dr_lock_status(dr_board_t *bp)
3353 {
3354 	mutex_enter(&bp->b_slock);
3355 	while (bp->b_sflags & DR_BSLOCK)
3356 		cv_wait(&bp->b_scv, &bp->b_slock);
3357 	bp->b_sflags |= DR_BSLOCK;
3358 	mutex_exit(&bp->b_slock);
3359 }
3360 
3361 void
3362 dr_unlock_status(dr_board_t *bp)
3363 {
3364 	mutex_enter(&bp->b_slock);
3365 	bp->b_sflags &= ~DR_BSLOCK;
3366 	cv_signal(&bp->b_scv);
3367 	mutex_exit(&bp->b_slock);
3368 }
3369 
3370 /*
3371  * Extract flags passed via ioctl.
3372  */
3373 int
3374 dr_cmd_flags(dr_handle_t *hp)
3375 {
3376 	return (hp->h_sbdcmd.cmd_cm.c_flags);
3377 }
3378