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