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