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, NULL);
 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                 /* catch this in debug kernels */
1574                 ASSERT(0);
1575                 break;
1576         }
1577 
1578         /* allocate list storage. */
1579         len = sizeof (dr_common_unit_t *) * (nunits + 1);
1580         list = kmem_zalloc(len, KM_SLEEP);
1581 
1582         /* record length of storage in first element */
1583         *list++ = (dr_common_unit_t *)(uintptr_t)len;
1584 
1585         /* get bit array signifying which units are to be involved */
1586         uset = DEVSET_GET_UNITSET(hp->h_devset, type);
1587 
1588         /*
1589          * Adjust the loop count for CPU devices since all cores
1590          * in a CMP will be examined in a single iteration.
1591          */
1592         if (type == SBD_COMP_CPU) {
1593                 nunits = MAX_CMP_UNITS_PER_BOARD;
1594         }
1595 
1596         /* populate list */
1597         for (wp = list, unum = 0; unum < nunits; unum++) {
1598                 dr_dev_unit_t   *dp;
1599                 int             core;
1600                 int             cunum;
1601 
1602                 dp = DR_GET_BOARD_DEVUNIT(bp, type, unum);
1603                 if (dr_dev_is_target(dp, present_only, uset)) {
1604                         *wp++ = &dp->du_common;
1605                 }
1606 
1607                 /* further processing is only required for CPUs */
1608                 if (type != SBD_COMP_CPU) {
1609                         continue;
1610                 }
1611 
1612                 /*
1613                  * Add any additional cores from the current CPU
1614                  * device. This is to ensure that all the cores
1615                  * are grouped together in the device list, and
1616                  * consequently sequenced together during the actual
1617                  * operation.
1618                  */
1619                 for (core = 1; core < MAX_CORES_PER_CMP; core++) {
1620                         cunum = DR_CMP_CORE_UNUM(unum, core);
1621                         dp = DR_GET_BOARD_DEVUNIT(bp, type, cunum);
1622 
1623                         if (dr_dev_is_target(dp, present_only, uset)) {
1624                                 *wp++ = &dp->du_common;
1625                         }
1626                 }
1627         }
1628 
1629         /* calculate number of units in list, return result and list pointer */
1630         *devnum = wp - list;
1631         *devlist = list;
1632 }
1633 
1634 static void
1635 dr_dev_clean_up(dr_handle_t *hp, dr_common_unit_t **list, int devnum)
1636 {
1637         int len;
1638         int n = 0;
1639         dr_common_unit_t *cp, **rp = list;
1640 
1641         /*
1642          * move first encountered unit error to handle if handle
1643          * does not yet have a recorded error.
1644          */
1645         if (hp->h_err == NULL) {
1646                 while (n++ < devnum) {
1647                         cp = *rp++;
1648                         if (cp->sbdev_error != NULL) {
1649                                 hp->h_err = cp->sbdev_error;
1650                                 cp->sbdev_error = NULL;
1651                                 break;
1652                         }
1653                 }
1654         }
1655 
1656         /* free remaining unit errors */
1657         while (n++ < devnum) {
1658                 cp = *rp++;
1659                 if (cp->sbdev_error != NULL) {
1660                         sbd_err_clear(&cp->sbdev_error);
1661                         cp->sbdev_error = NULL;
1662                 }
1663         }
1664 
1665         /* free list */
1666         list -= 1;
1667         len = (int)(uintptr_t)list[0];
1668         kmem_free(list, len);
1669 }
1670 
1671 static int
1672 dr_dev_walk(dr_handle_t *hp, sbd_comp_type_t type, int present_only,
1673                 int (*pre_op)(dr_handle_t *, dr_common_unit_t **, int),
1674                 void (*op)(dr_handle_t *, dr_common_unit_t *),
1675                 int (*post_op)(dr_handle_t *, dr_common_unit_t **, int),
1676                 void (*board_op)(dr_handle_t *, dr_common_unit_t **, int))
1677 {
1678         int                       devnum, rv;
1679         dr_common_unit_t        **devlist;
1680 
1681         dr_dev_make_list(hp, type, present_only, &devlist, &devnum);
1682 
1683         rv = 0;
1684         if (devnum > 0) {
1685                 rv = (*pre_op)(hp, devlist, devnum);
1686                 if (rv == 0) {
1687                         int n;
1688 
1689                         for (n = 0; n < devnum; n++)
1690                                 (*op)(hp, devlist[n]);
1691 
1692                         rv = (*post_op)(hp, devlist, devnum);
1693 
1694                         (*board_op)(hp, devlist, devnum);
1695                 }
1696         }
1697 
1698         dr_dev_clean_up(hp, devlist, devnum);
1699         return (rv);
1700 }
1701 
1702 /*ARGSUSED*/
1703 static int
1704 dr_dev_noop(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
1705 {
1706         return (0);
1707 }
1708 
1709 static void
1710 dr_attach_update_state(dr_handle_t *hp,
1711         dr_common_unit_t **devlist, int devnum)
1712 {
1713         dr_board_t      *bp = hp->h_bd;
1714         int             i;
1715         dr_devset_t     devs_unattached, devs_present;
1716         static fn_t     f = "dr_attach_update_state";
1717 
1718         for (i = 0; i < devnum; i++) {
1719                 dr_common_unit_t *cp = devlist[i];
1720 
1721                 if (dr_check_unit_attached(cp) == -1) {
1722                         PR_ALL("%s: ERROR %s not attached\n",
1723                             f, cp->sbdev_path);
1724                         continue;
1725                 }
1726 
1727                 DR_DEV_SET_ATTACHED(cp);
1728 
1729                 dr_device_transition(cp, DR_STATE_CONFIGURED);
1730                 cp->sbdev_cond = SBD_COND_OK;
1731         }
1732 
1733         devs_present = DR_DEVS_PRESENT(bp);
1734         devs_unattached = DR_DEVS_UNATTACHED(bp);
1735 
1736         switch (bp->b_state) {
1737         case DR_STATE_CONNECTED:
1738         case DR_STATE_UNCONFIGURED:
1739                 ASSERT(devs_present);
1740 
1741                 if (devs_unattached == 0) {
1742                         /*
1743                          * All devices finally attached.
1744                          */
1745                         dr_board_transition(bp, DR_STATE_CONFIGURED);
1746                         hp->h_bd->b_ostate = SBD_STAT_CONFIGURED;
1747                         hp->h_bd->b_rstate = SBD_STAT_CONNECTED;
1748                         hp->h_bd->b_cond = SBD_COND_OK;
1749                         hp->h_bd->b_busy = 0;
1750                         (void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
1751                 } else if (devs_present != devs_unattached) {
1752                         /*
1753                          * Only some devices are fully attached.
1754                          */
1755                         dr_board_transition(bp, DR_STATE_PARTIAL);
1756                         hp->h_bd->b_rstate = SBD_STAT_CONNECTED;
1757                         hp->h_bd->b_ostate = SBD_STAT_CONFIGURED;
1758                         (void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
1759                 }
1760                 break;
1761 
1762         case DR_STATE_PARTIAL:
1763                 ASSERT(devs_present);
1764                 /*
1765                  * All devices finally attached.
1766                  */
1767                 if (devs_unattached == 0) {
1768                         dr_board_transition(bp, DR_STATE_CONFIGURED);
1769                         hp->h_bd->b_rstate = SBD_STAT_CONNECTED;
1770                         hp->h_bd->b_ostate = SBD_STAT_CONFIGURED;
1771                         hp->h_bd->b_cond = SBD_COND_OK;
1772                         hp->h_bd->b_busy = 0;
1773                         (void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
1774                 }
1775                 break;
1776 
1777         default:
1778                 break;
1779         }
1780 }
1781 
1782 static void
1783 dr_dev_configure(dr_handle_t *hp)
1784 {
1785         int rv;
1786 
1787         rv = dr_dev_walk(hp, SBD_COMP_CPU, 1,
1788             dr_pre_attach_cpu,
1789             dr_attach_cpu,
1790             dr_post_attach_cpu,
1791             dr_attach_update_state);
1792 
1793         if (rv >= 0) {
1794                 rv = dr_dev_walk(hp, SBD_COMP_MEM, 1,
1795                     dr_pre_attach_mem,
1796                     dr_attach_mem,
1797                     dr_post_attach_mem,
1798                     dr_attach_update_state);
1799         }
1800 
1801         if (rv >= 0) {
1802                 (void) dr_dev_walk(hp, SBD_COMP_IO, 1,
1803                     dr_pre_attach_io,
1804                     dr_attach_io,
1805                     dr_post_attach_io,
1806                     dr_attach_update_state);
1807         }
1808 }
1809 
1810 static void
1811 dr_release_update_state(dr_handle_t *hp,
1812         dr_common_unit_t **devlist, int devnum)
1813 {
1814         _NOTE(ARGUNUSED(devlist))
1815         _NOTE(ARGUNUSED(devnum))
1816 
1817         dr_board_t *bp = hp->h_bd;
1818 
1819         /*
1820          * If the entire board was released and all components
1821          * unreferenced then transfer it to the UNREFERENCED state.
1822          */
1823         if ((bp->b_state != DR_STATE_RELEASE) &&
1824             (DR_DEVS_RELEASED(bp) == DR_DEVS_ATTACHED(bp))) {
1825                 dr_board_transition(bp, DR_STATE_RELEASE);
1826                 hp->h_bd->b_busy = 1;
1827         }
1828 }
1829 
1830 /* called by dr_release_done [below] and dr_release_mem_done [dr_mem.c] */
1831 int
1832 dr_release_dev_done(dr_common_unit_t *cp)
1833 {
1834         if (cp->sbdev_state == DR_STATE_RELEASE) {
1835                 ASSERT(DR_DEV_IS_RELEASED(cp));
1836 
1837                 DR_DEV_SET_UNREFERENCED(cp);
1838 
1839                 dr_device_transition(cp, DR_STATE_UNREFERENCED);
1840 
1841                 return (0);
1842         } else {
1843                 return (-1);
1844         }
1845 }
1846 
1847 static void
1848 dr_release_done(dr_handle_t *hp, dr_common_unit_t *cp)
1849 {
1850         _NOTE(ARGUNUSED(hp))
1851 
1852         dr_board_t              *bp;
1853         static fn_t             f = "dr_release_done";
1854 
1855         PR_ALL("%s...\n", f);
1856 
1857         /* get board pointer & sanity check */
1858         bp = cp->sbdev_bp;
1859         ASSERT(bp == hp->h_bd);
1860 
1861         /*
1862          * Transfer the device which just completed its release
1863          * to the UNREFERENCED state.
1864          */
1865         switch (cp->sbdev_type) {
1866         case SBD_COMP_MEM:
1867                 dr_release_mem_done(cp);
1868                 break;
1869 
1870         default:
1871                 DR_DEV_SET_RELEASED(cp);
1872 
1873                 dr_device_transition(cp, DR_STATE_RELEASE);
1874 
1875                 (void) dr_release_dev_done(cp);
1876                 break;
1877         }
1878 
1879         /*
1880          * If we're not already in the RELEASE state for this
1881          * board and we now have released all that were previously
1882          * attached, then transfer the board to the RELEASE state.
1883          */
1884         if ((bp->b_state == DR_STATE_RELEASE) &&
1885             (DR_DEVS_RELEASED(bp) == DR_DEVS_UNREFERENCED(bp))) {
1886                 dr_board_transition(bp, DR_STATE_UNREFERENCED);
1887                 bp->b_busy = 1;
1888                 (void) drv_getparm(TIME, (void *)&bp->b_time);
1889         }
1890 }
1891 
1892 static void
1893 dr_dev_release_mem(dr_handle_t *hp, dr_common_unit_t *dv)
1894 {
1895         dr_release_mem(dv);
1896         dr_release_done(hp, dv);
1897 }
1898 
1899 static void
1900 dr_dev_release(dr_handle_t *hp)
1901 {
1902         int rv;
1903 
1904         hp->h_bd->b_busy = 1;
1905 
1906         rv = dr_dev_walk(hp, SBD_COMP_CPU, 0,
1907             dr_pre_release_cpu,
1908             dr_release_done,
1909             dr_dev_noop,
1910             dr_release_update_state);
1911 
1912         if (rv >= 0) {
1913                 rv = dr_dev_walk(hp, SBD_COMP_MEM, 0,
1914                     dr_pre_release_mem,
1915                     dr_dev_release_mem,
1916                     dr_dev_noop,
1917                     dr_release_update_state);
1918         }
1919 
1920         if (rv >= 0) {
1921                 rv = dr_dev_walk(hp, SBD_COMP_IO, 0,
1922                     dr_pre_release_io,
1923                     dr_release_done,
1924                     dr_dev_noop,
1925                     dr_release_update_state);
1926 
1927         }
1928 
1929         if (rv < 0)
1930                 hp->h_bd->b_busy = 0;
1931         /* else, b_busy will be cleared in dr_detach_update_state() */
1932 }
1933 
1934 static void
1935 dr_detach_update_state(dr_handle_t *hp,
1936         dr_common_unit_t **devlist, int devnum)
1937 {
1938         dr_board_t      *bp = hp->h_bd;
1939         int             i;
1940         dr_state_t      bstate;
1941         static fn_t     f = "dr_detach_update_state";
1942 
1943         for (i = 0; i < devnum; i++) {
1944                 dr_common_unit_t *cp = devlist[i];
1945 
1946                 if (dr_check_unit_attached(cp) >= 0) {
1947                         /*
1948                          * Device is still attached probably due
1949                          * to an error.  Need to keep track of it.
1950                          */
1951                         PR_ALL("%s: ERROR %s not detached\n",
1952                             f, cp->sbdev_path);
1953 
1954                         continue;
1955                 }
1956 
1957                 DR_DEV_CLR_ATTACHED(cp);
1958                 DR_DEV_CLR_RELEASED(cp);
1959                 DR_DEV_CLR_UNREFERENCED(cp);
1960                 dr_device_transition(cp, DR_STATE_UNCONFIGURED);
1961         }
1962 
1963         bstate = bp->b_state;
1964         if (bstate != DR_STATE_UNCONFIGURED) {
1965                 if (DR_DEVS_PRESENT(bp) == DR_DEVS_UNATTACHED(bp)) {
1966                         /*
1967                          * All devices are finally detached.
1968                          */
1969                         dr_board_transition(bp, DR_STATE_UNCONFIGURED);
1970                         hp->h_bd->b_ostate = SBD_STAT_UNCONFIGURED;
1971                         (void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
1972                 } else if ((bp->b_state != DR_STATE_PARTIAL) &&
1973                     (DR_DEVS_ATTACHED(bp) !=
1974                     DR_DEVS_PRESENT(bp))) {
1975                         /*
1976                          * Some devices remain attached.
1977                          */
1978                         dr_board_transition(bp, DR_STATE_PARTIAL);
1979                         (void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
1980                 }
1981 
1982                 if ((hp->h_devset & DR_DEVS_UNATTACHED(bp)) == hp->h_devset)
1983                         hp->h_bd->b_busy = 0;
1984         }
1985 }
1986 
1987 static int
1988 dr_dev_unconfigure(dr_handle_t *hp)
1989 {
1990         dr_board_t      *bp = hp->h_bd;
1991 
1992         /*
1993          * Block out status during IO unconfig.
1994          */
1995         mutex_enter(&bp->b_slock);
1996         while (bp->b_sflags & DR_BSLOCK) {
1997                 if (cv_wait_sig(&bp->b_scv, &bp->b_slock) == 0) {
1998                         mutex_exit(&bp->b_slock);
1999                         return (EINTR);
2000                 }
2001         }
2002         bp->b_sflags |= DR_BSLOCK;
2003         mutex_exit(&bp->b_slock);
2004 
2005         (void) dr_dev_walk(hp, SBD_COMP_IO, 0,
2006             dr_pre_detach_io,
2007             dr_detach_io,
2008             dr_post_detach_io,
2009             dr_detach_update_state);
2010 
2011         dr_unlock_status(bp);
2012 
2013         (void) dr_dev_walk(hp, SBD_COMP_CPU, 0,
2014             dr_pre_detach_cpu,
2015             dr_detach_cpu,
2016             dr_post_detach_cpu,
2017             dr_detach_update_state);
2018 
2019         (void) dr_dev_walk(hp, SBD_COMP_MEM, 0,
2020             dr_pre_detach_mem,
2021             dr_detach_mem,
2022             dr_post_detach_mem,
2023             dr_detach_update_state);
2024 
2025         return (0);
2026 }
2027 
2028 static void
2029 dr_dev_cancel(dr_handle_t *hp)
2030 {
2031         int             i;
2032         dr_devset_t     devset;
2033         dr_board_t      *bp = hp->h_bd;
2034         static fn_t     f = "dr_dev_cancel";
2035 
2036         PR_ALL("%s...\n", f);
2037 
2038         /*
2039          * Only devices which have been "released" are
2040          * subject to cancellation.
2041          */
2042         devset = hp->h_devset & DR_DEVS_RELEASED(bp);
2043 
2044         /*
2045          * Nothing to do for CPUs or IO other than change back
2046          * their state.
2047          */
2048         for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
2049                 dr_cpu_unit_t   *cp;
2050                 dr_state_t      nstate;
2051 
2052                 if (!DEVSET_IN_SET(devset, SBD_COMP_CPU, i))
2053                         continue;
2054 
2055                 cp = dr_get_cpu_unit(bp, i);
2056                 if (dr_cancel_cpu(cp) == 0)
2057                         nstate = DR_STATE_CONFIGURED;
2058                 else
2059                         nstate = DR_STATE_FATAL;
2060 
2061                 dr_device_transition(&cp->sbc_cm, nstate);
2062         }
2063 
2064         for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
2065                 dr_io_unit_t *ip;
2066 
2067                 if (!DEVSET_IN_SET(devset, SBD_COMP_IO, i))
2068                         continue;
2069                 ip = dr_get_io_unit(bp, i);
2070                 dr_device_transition(&ip->sbi_cm, DR_STATE_CONFIGURED);
2071         }
2072         for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
2073                 dr_mem_unit_t   *mp;
2074                 dr_state_t      nstate;
2075 
2076                 if (!DEVSET_IN_SET(devset, SBD_COMP_MEM, i))
2077                         continue;
2078 
2079                 mp = dr_get_mem_unit(bp, i);
2080                 if (dr_cancel_mem(mp) == 0)
2081                         nstate = DR_STATE_CONFIGURED;
2082                 else
2083                         nstate = DR_STATE_FATAL;
2084 
2085                 dr_device_transition(&mp->sbm_cm, nstate);
2086         }
2087 
2088         PR_ALL("%s: unreleasing devset (0x%x)\n", f, (uint_t)devset);
2089 
2090         DR_DEVS_CANCEL(bp, devset);
2091 
2092         if (DR_DEVS_RELEASED(bp) == 0) {
2093                 dr_state_t      new_state;
2094                 /*
2095                  * If the board no longer has any released devices
2096                  * than transfer it back to the CONFIG/PARTIAL state.
2097                  */
2098                 if (DR_DEVS_ATTACHED(bp) == DR_DEVS_PRESENT(bp))
2099                         new_state = DR_STATE_CONFIGURED;
2100                 else
2101                         new_state = DR_STATE_PARTIAL;
2102                 if (bp->b_state != new_state) {
2103                         dr_board_transition(bp, new_state);
2104                 }
2105                 hp->h_bd->b_ostate = SBD_STAT_CONFIGURED;
2106                 hp->h_bd->b_busy = 0;
2107                 (void) drv_getparm(TIME, (void *)&hp->h_bd->b_time);
2108         }
2109 }
2110 
2111 static int
2112 dr_dev_status(dr_handle_t *hp)
2113 {
2114         int             nstat, mode, ncm, sz, pbsz, pnstat;
2115         dr_handle_t     *shp;
2116         dr_devset_t     devset = 0;
2117         sbd_stat_t      *dstatp = NULL;
2118         sbd_dev_stat_t  *devstatp;
2119         dr_board_t      *bp;
2120         drmach_status_t  pstat;
2121         int             rv = 0;
2122 
2123 #ifdef _MULTI_DATAMODEL
2124         int sz32 = 0;
2125 #endif /* _MULTI_DATAMODEL */
2126 
2127         static fn_t     f = "dr_dev_status";
2128 
2129         PR_ALL("%s...\n", f);
2130 
2131         mode = hp->h_mode;
2132         shp = hp;
2133         devset = shp->h_devset;
2134         bp = hp->h_bd;
2135 
2136         /*
2137          * Block out disconnect, unassign, IO unconfigure and
2138          * devinfo branch creation during status.
2139          */
2140         mutex_enter(&bp->b_slock);
2141         while (bp->b_sflags & DR_BSLOCK) {
2142                 if (cv_wait_sig(&bp->b_scv, &bp->b_slock) == 0) {
2143                         mutex_exit(&bp->b_slock);
2144                         return (EINTR);
2145                 }
2146         }
2147         bp->b_sflags |= DR_BSLOCK;
2148         mutex_exit(&bp->b_slock);
2149 
2150         ncm = 1;
2151         if (hp->h_sbdcmd.cmd_cm.c_id.c_type == SBD_COMP_NONE) {
2152                 if (dr_cmd_flags(hp) & SBD_FLAG_ALLCMP) {
2153                 /*
2154                  * Calculate the maximum number of components possible
2155                  * for a board.  This number will be used to size the
2156                  * status scratch buffer used by board and component
2157                  * status functions.
2158                  * This buffer may differ in size from what is provided
2159                  * by the plugin, since the known component set on the
2160                  * board may change between the plugin's GETNCM call, and
2161                  * the status call.  Sizing will be adjusted to the plugin's
2162                  * receptacle buffer at copyout time.
2163                  */
2164                         ncm = MAX_CPU_UNITS_PER_BOARD +
2165                             MAX_MEM_UNITS_PER_BOARD +
2166                             MAX_IO_UNITS_PER_BOARD;
2167 
2168                 } else {
2169                         /*
2170                          * In the case of c_type == SBD_COMP_NONE, and
2171                          * SBD_FLAG_ALLCMP not specified, only the board
2172                          * info is to be returned, no components.
2173                          */
2174                         ncm = 0;
2175                         devset = 0;
2176                 }
2177         }
2178 
2179         sz = sizeof (sbd_stat_t);
2180         if (ncm > 1)
2181                 sz += sizeof (sbd_dev_stat_t) * (ncm - 1);
2182 
2183 
2184         pbsz = (int)hp->h_sbdcmd.cmd_stat.s_nbytes;
2185         pnstat = (pbsz - sizeof (sbd_stat_t)) / sizeof (sbd_dev_stat_t);
2186 
2187         /*
2188          * s_nbytes describes the size of the preallocated user
2189          * buffer into which the application is execting to
2190          * receive the sbd_stat_t and sbd_dev_stat_t structures.
2191          */
2192 
2193 #ifdef _MULTI_DATAMODEL
2194 
2195         /*
2196          * More buffer space is required for the 64bit to 32bit
2197          * conversion of data structures.
2198          */
2199         if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
2200                 sz32 = sizeof (sbd_stat32_t);
2201                 if (ncm > 1)
2202                         sz32  += sizeof (sbd_dev_stat32_t) * (ncm - 1);
2203                 pnstat = (pbsz - sizeof (sbd_stat32_t))/
2204                     sizeof (sbd_dev_stat32_t);
2205         }
2206 
2207         sz += sz32;
2208 #endif
2209         /*
2210          * Since one sbd_dev_stat_t is included in the sbd_stat_t,
2211          * increment the plugin's nstat count.
2212          */
2213         ++pnstat;
2214 
2215         if (bp->b_id == 0) {
2216                 bzero(&pstat, sizeof (pstat));
2217         } else {
2218                 sbd_error_t *err;
2219 
2220                 err = drmach_status(bp->b_id, &pstat);
2221                 if (err) {
2222                         DRERR_SET_C(&hp->h_err, &err);
2223                         rv = EIO;
2224                         goto status_done;
2225                 }
2226         }
2227 
2228         dstatp = (sbd_stat_t *)(void *)GETSTRUCT(char, sz);
2229 
2230         devstatp = &dstatp->s_stat[0];
2231 
2232         dstatp->s_board = bp->b_num;
2233 
2234         /*
2235          * Detect transitions between empty and disconnected.
2236          */
2237         if (!pstat.empty && (bp->b_rstate == SBD_STAT_EMPTY))
2238                 bp->b_rstate = SBD_STAT_DISCONNECTED;
2239         else if (pstat.empty && (bp->b_rstate == SBD_STAT_DISCONNECTED))
2240                 bp->b_rstate = SBD_STAT_EMPTY;
2241 
2242         dstatp->s_rstate = bp->b_rstate;
2243         dstatp->s_ostate = bp->b_ostate;
2244         dstatp->s_cond = bp->b_cond = pstat.cond;
2245         dstatp->s_busy = bp->b_busy | pstat.busy;
2246         dstatp->s_time = bp->b_time;
2247         dstatp->s_power = pstat.powered;
2248         dstatp->s_assigned = bp->b_assigned = pstat.assigned;
2249         dstatp->s_nstat = nstat = 0;
2250         bcopy(&pstat.type[0], &dstatp->s_type[0], SBD_TYPE_LEN);
2251         bcopy(&pstat.info[0], &dstatp->s_info[0], SBD_MAX_INFO);
2252 
2253         devset &= DR_DEVS_PRESENT(bp);
2254         if (devset == 0) {
2255                 /*
2256                  * No device chosen.
2257                  */
2258                 PR_ALL("%s: no device present\n", f);
2259         }
2260 
2261         if (DEVSET_IN_SET(devset, SBD_COMP_CPU, DEVSET_ANYUNIT))
2262                 if ((nstat = dr_cpu_status(hp, devset, devstatp)) > 0) {
2263                         dstatp->s_nstat += nstat;
2264                         devstatp += nstat;
2265                 }
2266 
2267         if (DEVSET_IN_SET(devset, SBD_COMP_MEM, DEVSET_ANYUNIT))
2268                 if ((nstat = dr_mem_status(hp, devset, devstatp)) > 0) {
2269                         dstatp->s_nstat += nstat;
2270                         devstatp += nstat;
2271                 }
2272 
2273         if (DEVSET_IN_SET(devset, SBD_COMP_IO, DEVSET_ANYUNIT))
2274                 if ((nstat = dr_io_status(hp, devset, devstatp)) > 0) {
2275                         dstatp->s_nstat += nstat;
2276                         devstatp += nstat;
2277                 }
2278 
2279         /*
2280          * Due to a possible change in number of components between
2281          * the time of plugin's GETNCM call and now, there may be
2282          * more or less components than the plugin's buffer can
2283          * hold.  Adjust s_nstat accordingly.
2284          */
2285 
2286         dstatp->s_nstat = dstatp->s_nstat > pnstat ? pnstat : dstatp->s_nstat;
2287 
2288 #ifdef _MULTI_DATAMODEL
2289         if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
2290                 int             i, j;
2291                 sbd_stat32_t    *dstat32p;
2292 
2293                 dstat32p = (sbd_stat32_t *)devstatp;
2294 
2295                 /* Alignment Paranoia */
2296                 if ((ulong_t)dstat32p & 0x1) {
2297                         PR_ALL("%s: alignment: sz=0x%lx dstat32p=0x%p\n",
2298                             f, sizeof (sbd_stat32_t), (void *)dstat32p);
2299                         DR_OP_INTERNAL_ERROR(hp);
2300                         rv = EINVAL;
2301                         goto status_done;
2302                 }
2303 
2304                 /* paranoia: detect buffer overrun */
2305                 if ((caddr_t)&dstat32p->s_stat[dstatp->s_nstat] >
2306                     ((caddr_t)dstatp) + sz) {
2307                         DR_OP_INTERNAL_ERROR(hp);
2308                         rv = EINVAL;
2309                         goto status_done;
2310                 }
2311 
2312                 /* copy sbd_stat_t structure members */
2313 #define _SBD_STAT(t, m) dstat32p->m = (t)dstatp->m
2314                 _SBD_STAT(int32_t, s_board);
2315                 _SBD_STAT(int32_t, s_rstate);
2316                 _SBD_STAT(int32_t, s_ostate);
2317                 _SBD_STAT(int32_t, s_cond);
2318                 _SBD_STAT(int32_t, s_busy);
2319                 _SBD_STAT(time32_t, s_time);
2320                 _SBD_STAT(uint32_t, s_power);
2321                 _SBD_STAT(uint32_t, s_assigned);
2322                 _SBD_STAT(int32_t, s_nstat);
2323                 bcopy(&dstatp->s_type[0], &dstat32p->s_type[0],
2324                     SBD_TYPE_LEN);
2325                 bcopy(&dstatp->s_info[0], &dstat32p->s_info[0],
2326                     SBD_MAX_INFO);
2327 #undef _SBD_STAT
2328 
2329                 for (i = 0; i < dstatp->s_nstat; i++) {
2330                         sbd_dev_stat_t          *dsp = &dstatp->s_stat[i];
2331                         sbd_dev_stat32_t        *ds32p = &dstat32p->s_stat[i];
2332 #define _SBD_DEV_STAT(t, m) ds32p->m = (t)dsp->m
2333 
2334                         /* copy sbd_cm_stat_t structure members */
2335                         _SBD_DEV_STAT(int32_t, ds_type);
2336                         _SBD_DEV_STAT(int32_t, ds_unit);
2337                         _SBD_DEV_STAT(int32_t, ds_ostate);
2338                         _SBD_DEV_STAT(int32_t, ds_cond);
2339                         _SBD_DEV_STAT(int32_t, ds_busy);
2340                         _SBD_DEV_STAT(int32_t, ds_suspend);
2341                         _SBD_DEV_STAT(time32_t, ds_time);
2342                         bcopy(&dsp->ds_name[0], &ds32p->ds_name[0],
2343                             OBP_MAXPROPNAME);
2344 
2345                         switch (dsp->ds_type) {
2346                         case SBD_COMP_CPU:
2347                                 /* copy sbd_cpu_stat_t structure members */
2348                                 _SBD_DEV_STAT(int32_t, d_cpu.cs_isbootproc);
2349                                 _SBD_DEV_STAT(int32_t, d_cpu.cs_cpuid);
2350                                 _SBD_DEV_STAT(int32_t, d_cpu.cs_speed);
2351                                 _SBD_DEV_STAT(int32_t, d_cpu.cs_ecache);
2352                                 break;
2353 
2354                         case SBD_COMP_MEM:
2355                                 /* copy sbd_mem_stat_t structure members */
2356                                 _SBD_DEV_STAT(int32_t, d_mem.ms_interleave);
2357                                 _SBD_DEV_STAT(uint32_t, d_mem.ms_basepfn);
2358                                 _SBD_DEV_STAT(uint32_t, d_mem.ms_totpages);
2359                                 _SBD_DEV_STAT(uint32_t, d_mem.ms_detpages);
2360                                 _SBD_DEV_STAT(int32_t, d_mem.ms_pageslost);
2361                                 _SBD_DEV_STAT(uint32_t, d_mem.ms_managed_pages);
2362                                 _SBD_DEV_STAT(uint32_t, d_mem.ms_noreloc_pages);
2363                                 _SBD_DEV_STAT(uint32_t, d_mem.ms_noreloc_first);
2364                                 _SBD_DEV_STAT(uint32_t, d_mem.ms_noreloc_last);
2365                                 _SBD_DEV_STAT(int32_t, d_mem.ms_cage_enabled);
2366                                 _SBD_DEV_STAT(int32_t, d_mem.ms_peer_is_target);
2367                                 bcopy(&dsp->d_mem.ms_peer_ap_id[0],
2368                                     &ds32p->d_mem.ms_peer_ap_id[0],
2369                                     sizeof (ds32p->d_mem.ms_peer_ap_id));
2370                                 break;
2371 
2372                         case SBD_COMP_IO:
2373                                 /* copy sbd_io_stat_t structure members */
2374                                 _SBD_DEV_STAT(int32_t, d_io.is_referenced);
2375                                 _SBD_DEV_STAT(int32_t, d_io.is_unsafe_count);
2376 
2377                                 for (j = 0; j < SBD_MAX_UNSAFE; j++)
2378                                         _SBD_DEV_STAT(int32_t,
2379                                             d_io.is_unsafe_list[j]);
2380 
2381                                 bcopy(&dsp->d_io.is_pathname[0],
2382                                     &ds32p->d_io.is_pathname[0], MAXPATHLEN);
2383                                 break;
2384 
2385                         case SBD_COMP_CMP:
2386                                 /* copy sbd_cmp_stat_t structure members */
2387                                 bcopy(&dsp->d_cmp.ps_cpuid[0],
2388                                     &ds32p->d_cmp.ps_cpuid[0],
2389                                     sizeof (ds32p->d_cmp.ps_cpuid));
2390                                 _SBD_DEV_STAT(int32_t, d_cmp.ps_ncores);
2391                                 _SBD_DEV_STAT(int32_t, d_cmp.ps_speed);
2392                                 _SBD_DEV_STAT(int32_t, d_cmp.ps_ecache);
2393                                 break;
2394 
2395                         default:
2396                                 cmn_err(CE_WARN, "%s: unknown dev type (%d)",
2397                                     f, (int)dsp->ds_type);
2398                                 rv = EFAULT;
2399                                 goto status_done;
2400                         }
2401 #undef _SBD_DEV_STAT
2402                 }
2403 
2404 
2405                 if (ddi_copyout((void *)dstat32p,
2406                     hp->h_sbdcmd.cmd_stat.s_statp, pbsz, mode) != 0) {
2407                         cmn_err(CE_WARN,
2408                             "%s: failed to copyout status "
2409                             "for board %d", f, bp->b_num);
2410                         rv = EFAULT;
2411                         goto status_done;
2412                 }
2413         } else
2414 #endif /* _MULTI_DATAMODEL */
2415 
2416         if (ddi_copyout((void *)dstatp, hp->h_sbdcmd.cmd_stat.s_statp,
2417             pbsz, mode) != 0) {
2418                 cmn_err(CE_WARN,
2419                     "%s: failed to copyout status for board %d",
2420                     f, bp->b_num);
2421                 rv = EFAULT;
2422                 goto status_done;
2423         }
2424 
2425 status_done:
2426         if (dstatp != NULL)
2427                 FREESTRUCT(dstatp, char, sz);
2428 
2429         dr_unlock_status(bp);
2430 
2431         return (rv);
2432 }
2433 
2434 static int
2435 dr_get_ncm(dr_handle_t *hp)
2436 {
2437         int             i;
2438         int             ncm = 0;
2439         dr_devset_t     devset;
2440 
2441         devset = DR_DEVS_PRESENT(hp->h_bd);
2442         if (hp->h_sbdcmd.cmd_cm.c_id.c_type != SBD_COMP_NONE)
2443                 devset &= DEVSET(hp->h_sbdcmd.cmd_cm.c_id.c_type,
2444                     DEVSET_ANYUNIT);
2445 
2446         /*
2447          * Handle CPUs first to deal with possible CMP
2448          * devices. If the CPU is a CMP, we need to only
2449          * increment ncm once even if there are multiple
2450          * cores for that CMP present in the devset.
2451          */
2452         for (i = 0; i < MAX_CMP_UNITS_PER_BOARD; i++) {
2453                 if (devset & DEVSET(SBD_COMP_CMP, i)) {
2454                         ncm++;
2455                 }
2456         }
2457 
2458         /* eliminate the CPU information from the devset */
2459         devset &= ~(DEVSET(SBD_COMP_CMP, DEVSET_ANYUNIT));
2460 
2461         for (i = 0; i < (sizeof (dr_devset_t) * 8); i++) {
2462                 ncm += devset & 0x1;
2463                 devset >>= 1;
2464         }
2465 
2466         return (ncm);
2467 }
2468 
2469 /* used by dr_mem.c */
2470 /* TODO: eliminate dr_boardlist */
2471 dr_board_t *
2472 dr_lookup_board(int board_num)
2473 {
2474         dr_board_t *bp;
2475 
2476         ASSERT(board_num >= 0 && board_num < MAX_BOARDS);
2477 
2478         bp = &dr_boardlist[board_num];
2479         ASSERT(bp->b_num == board_num);
2480 
2481         return (bp);
2482 }
2483 
2484 static dr_dev_unit_t *
2485 dr_get_dev_unit(dr_board_t *bp, sbd_comp_type_t nt, int unit_num)
2486 {
2487         dr_dev_unit_t   *dp;
2488 
2489         dp = DR_GET_BOARD_DEVUNIT(bp, nt, unit_num);
2490         ASSERT(dp->du_common.sbdev_bp == bp);
2491         ASSERT(dp->du_common.sbdev_unum == unit_num);
2492         ASSERT(dp->du_common.sbdev_type == nt);
2493 
2494         return (dp);
2495 }
2496 
2497 dr_cpu_unit_t *
2498 dr_get_cpu_unit(dr_board_t *bp, int unit_num)
2499 {
2500         dr_dev_unit_t   *dp;
2501 
2502         ASSERT(unit_num >= 0 && unit_num < MAX_CPU_UNITS_PER_BOARD);
2503 
2504         dp = dr_get_dev_unit(bp, SBD_COMP_CPU, unit_num);
2505         return (&dp->du_cpu);
2506 }
2507 
2508 dr_mem_unit_t *
2509 dr_get_mem_unit(dr_board_t *bp, int unit_num)
2510 {
2511         dr_dev_unit_t   *dp;
2512 
2513         ASSERT(unit_num >= 0 && unit_num < MAX_MEM_UNITS_PER_BOARD);
2514 
2515         dp = dr_get_dev_unit(bp, SBD_COMP_MEM, unit_num);
2516         return (&dp->du_mem);
2517 }
2518 
2519 dr_io_unit_t *
2520 dr_get_io_unit(dr_board_t *bp, int unit_num)
2521 {
2522         dr_dev_unit_t   *dp;
2523 
2524         ASSERT(unit_num >= 0 && unit_num < MAX_IO_UNITS_PER_BOARD);
2525 
2526         dp = dr_get_dev_unit(bp, SBD_COMP_IO, unit_num);
2527         return (&dp->du_io);
2528 }
2529 
2530 dr_common_unit_t *
2531 dr_get_common_unit(dr_board_t *bp, sbd_comp_type_t nt, int unum)
2532 {
2533         dr_dev_unit_t   *dp;
2534 
2535         dp = dr_get_dev_unit(bp, nt, unum);
2536         return (&dp->du_common);
2537 }
2538 
2539 static dr_devset_t
2540 dr_dev2devset(sbd_comp_id_t *cid)
2541 {
2542         static fn_t     f = "dr_dev2devset";
2543 
2544         dr_devset_t     devset;
2545         int             unit = cid->c_unit;
2546 
2547         switch (cid->c_type) {
2548                 case SBD_COMP_NONE:
2549                         devset =  DEVSET(SBD_COMP_CPU, DEVSET_ANYUNIT);
2550                         devset |= DEVSET(SBD_COMP_MEM, DEVSET_ANYUNIT);
2551                         devset |= DEVSET(SBD_COMP_IO,  DEVSET_ANYUNIT);
2552                         PR_ALL("%s: COMP_NONE devset = " DEVSET_FMT_STR "\n",
2553                             f, DEVSET_FMT_ARG(devset));
2554                         break;
2555 
2556                 case SBD_COMP_CPU:
2557                         if ((unit > MAX_CPU_UNITS_PER_BOARD) || (unit < 0)) {
2558                                 cmn_err(CE_WARN,
2559                                     "%s: invalid cpu unit# = %d",
2560                                     f, unit);
2561                                 devset = 0;
2562                         } else {
2563                                 /*
2564                                  * Generate a devset that includes all the
2565                                  * cores of a CMP device. If this is not a
2566                                  * CMP, the extra cores will be eliminated
2567                                  * later since they are not present. This is
2568                                  * also true for CMP devices that do not have
2569                                  * all cores active.
2570                                  */
2571                                 devset = DEVSET(SBD_COMP_CMP, unit);
2572                         }
2573 
2574                         PR_ALL("%s: CPU devset = " DEVSET_FMT_STR "\n",
2575                             f, DEVSET_FMT_ARG(devset));
2576                         break;
2577 
2578                 case SBD_COMP_MEM:
2579                         if (unit == SBD_NULL_UNIT) {
2580                                 unit = 0;
2581                                 cid->c_unit = 0;
2582                         }
2583 
2584                         if ((unit > MAX_MEM_UNITS_PER_BOARD) || (unit < 0)) {
2585                                 cmn_err(CE_WARN,
2586                                     "%s: invalid mem unit# = %d",
2587                                     f, unit);
2588                                 devset = 0;
2589                         } else
2590                                 devset = DEVSET(cid->c_type, unit);
2591 
2592                         PR_ALL("%s: MEM devset = " DEVSET_FMT_STR "\n",
2593                             f, DEVSET_FMT_ARG(devset));
2594                         break;
2595 
2596                 case SBD_COMP_IO:
2597                         if ((unit > MAX_IO_UNITS_PER_BOARD) || (unit < 0)) {
2598                                 cmn_err(CE_WARN,
2599                                     "%s: invalid io unit# = %d",
2600                                     f, unit);
2601                                 devset = 0;
2602                         } else
2603                                 devset = DEVSET(cid->c_type, unit);
2604 
2605                         PR_ALL("%s: IO devset = " DEVSET_FMT_STR "\n",
2606                             f, DEVSET_FMT_ARG(devset));
2607                         break;
2608 
2609                 default:
2610                 case SBD_COMP_UNKNOWN:
2611                         devset = 0;
2612                         break;
2613         }
2614 
2615         return (devset);
2616 }
2617 
2618 /*
2619  * Converts a dynamic attachment point name to a SBD_COMP_* type.
2620  * Returns SDB_COMP_UNKNOWN if name is not recognized.
2621  */
2622 static int
2623 dr_dev_type_to_nt(char *type)
2624 {
2625         int i;
2626 
2627         for (i = 0; dr_devattr[i].s_nodetype != SBD_COMP_UNKNOWN; i++)
2628                 if (strcmp(dr_devattr[i].s_devtype, type) == 0)
2629                         break;
2630 
2631         return (dr_devattr[i].s_nodetype);
2632 }
2633 
2634 /*
2635  * Converts a SBD_COMP_* type to a dynamic attachment point name.
2636  * Return NULL if SBD_COMP_ type is not recognized.
2637  */
2638 char *
2639 dr_nt_to_dev_type(int nt)
2640 {
2641         int i;
2642 
2643         for (i = 0; dr_devattr[i].s_nodetype != SBD_COMP_UNKNOWN; i++)
2644                 if (dr_devattr[i].s_nodetype == nt)
2645                         break;
2646 
2647         return (dr_devattr[i].s_devtype);
2648 }
2649 
2650 /*
2651  * State transition policy is that if there is some component for which
2652  * the state transition is valid, then let it through. The exception is
2653  * SBD_CMD_DISCONNECT. On disconnect, the state transition must be valid
2654  * for ALL components.
2655  * Returns the state that is in error, if any.
2656  */
2657 static int
2658 dr_check_transition(dr_board_t *bp, dr_devset_t *devsetp,
2659                         struct dr_state_trans *transp, int cmd)
2660 {
2661         int                     s, ut;
2662         int                     state_err = 0;
2663         dr_devset_t             devset;
2664         dr_common_unit_t        *cp;
2665         static fn_t             f = "dr_check_transition";
2666 
2667         devset = *devsetp;
2668 
2669         if (DEVSET_IN_SET(devset, SBD_COMP_CPU, DEVSET_ANYUNIT)) {
2670                 for (ut = 0; ut < MAX_CPU_UNITS_PER_BOARD; ut++) {
2671                         if (DEVSET_IN_SET(devset, SBD_COMP_CPU, ut) == 0)
2672                                 continue;
2673 
2674                         cp = dr_get_common_unit(bp, SBD_COMP_CPU, ut);
2675                         s = (int)cp->sbdev_state;
2676                         if (!DR_DEV_IS_PRESENT(cp)) {
2677                                 DEVSET_DEL(devset, SBD_COMP_CPU, ut);
2678                         } else {
2679                                 if (transp->x_op[s].x_rv) {
2680                                         if (!state_err)
2681                                                 state_err = s;
2682                                         DEVSET_DEL(devset, SBD_COMP_CPU, ut);
2683                                 }
2684                         }
2685                 }
2686         }
2687         if (DEVSET_IN_SET(devset, SBD_COMP_MEM, DEVSET_ANYUNIT)) {
2688                 for (ut = 0; ut < MAX_MEM_UNITS_PER_BOARD; ut++) {
2689                         if (DEVSET_IN_SET(devset, SBD_COMP_MEM, ut) == 0)
2690                                 continue;
2691 
2692                         cp = dr_get_common_unit(bp, SBD_COMP_MEM, ut);
2693                         s = (int)cp->sbdev_state;
2694                         if (!DR_DEV_IS_PRESENT(cp)) {
2695                                 DEVSET_DEL(devset, SBD_COMP_MEM, ut);
2696                         } else {
2697                                 if (transp->x_op[s].x_rv) {
2698                                         if (!state_err)
2699                                                 state_err = s;
2700                                         DEVSET_DEL(devset, SBD_COMP_MEM, ut);
2701                                 }
2702                         }
2703                 }
2704         }
2705         if (DEVSET_IN_SET(devset, SBD_COMP_IO, DEVSET_ANYUNIT)) {
2706                 for (ut = 0; ut < MAX_IO_UNITS_PER_BOARD; ut++) {
2707                         if (DEVSET_IN_SET(devset, SBD_COMP_IO, ut) == 0)
2708                                 continue;
2709 
2710                         cp = dr_get_common_unit(bp, SBD_COMP_IO, ut);
2711                         s = (int)cp->sbdev_state;
2712                         if (!DR_DEV_IS_PRESENT(cp)) {
2713                                 DEVSET_DEL(devset, SBD_COMP_IO, ut);
2714                         } else {
2715                                 if (transp->x_op[s].x_rv) {
2716                                         if (!state_err)
2717                                                 state_err = s;
2718                                         DEVSET_DEL(devset, SBD_COMP_IO, ut);
2719                                 }
2720                         }
2721                 }
2722         }
2723 
2724         PR_ALL("%s: requested devset = 0x%x, final devset = 0x%x\n",
2725             f, (uint_t)*devsetp, (uint_t)devset);
2726 
2727         *devsetp = devset;
2728         /*
2729          * If there are some remaining components for which
2730          * this state transition is valid, then allow them
2731          * through, otherwise if none are left then return
2732          * the state error. The exception is SBD_CMD_DISCONNECT.
2733          * On disconnect, the state transition must be valid for ALL
2734          * components.
2735          */
2736         if (cmd == SBD_CMD_DISCONNECT)
2737                 return (state_err);
2738         return (devset ? 0 : state_err);
2739 }
2740 
2741 void
2742 dr_device_transition(dr_common_unit_t *cp, dr_state_t st)
2743 {
2744         PR_STATE("%s STATE %s(%d) -> %s(%d)\n",
2745             cp->sbdev_path,
2746             state_str[cp->sbdev_state], cp->sbdev_state,
2747             state_str[st], st);
2748 
2749         cp->sbdev_state = st;
2750         if (st == DR_STATE_CONFIGURED) {
2751                 cp->sbdev_ostate = SBD_STAT_CONFIGURED;
2752                 if (cp->sbdev_bp->b_ostate != SBD_STAT_CONFIGURED) {
2753                         cp->sbdev_bp->b_ostate = SBD_STAT_CONFIGURED;
2754                         (void) drv_getparm(TIME,
2755                             (void *) &cp->sbdev_bp->b_time);
2756                 }
2757         } else
2758                 cp->sbdev_ostate = SBD_STAT_UNCONFIGURED;
2759 
2760         (void) drv_getparm(TIME, (void *) &cp->sbdev_time);
2761 }
2762 
2763 static void
2764 dr_board_transition(dr_board_t *bp, dr_state_t st)
2765 {
2766         PR_STATE("BOARD %d STATE: %s(%d) -> %s(%d)\n",
2767             bp->b_num,
2768             state_str[bp->b_state], bp->b_state,
2769             state_str[st], st);
2770 
2771         bp->b_state = st;
2772 }
2773 
2774 void
2775 dr_op_err(int ce, dr_handle_t *hp, int code, char *fmt, ...)
2776 {
2777         sbd_error_t     *err;
2778         va_list         args;
2779 
2780         va_start(args, fmt);
2781         err = drerr_new_v(code, fmt, args);
2782         va_end(args);
2783 
2784         if (ce != CE_IGNORE)
2785                 sbd_err_log(err, ce);
2786 
2787         DRERR_SET_C(&hp->h_err, &err);
2788 }
2789 
2790 void
2791 dr_dev_err(int ce, dr_common_unit_t *cp, int code)
2792 {
2793         sbd_error_t     *err;
2794 
2795         err = drerr_new(0, code, cp->sbdev_path, NULL);
2796 
2797         if (ce != CE_IGNORE)
2798                 sbd_err_log(err, ce);
2799 
2800         DRERR_SET_C(&cp->sbdev_error, &err);
2801 }
2802 
2803 /*
2804  * A callback routine.  Called from the drmach layer as a result of
2805  * call to drmach_board_find_devices from dr_init_devlists.
2806  */
2807 static sbd_error_t *
2808 dr_dev_found(void *data, const char *name, int unum, drmachid_t id)
2809 {
2810         dr_board_t      *bp = data;
2811         dr_dev_unit_t   *dp;
2812         int              nt;
2813         static fn_t     f = "dr_dev_found";
2814 
2815         PR_ALL("%s (board = %d, name = %s, unum = %d, id = %p)...\n",
2816             f, bp->b_num, name, unum, id);
2817 
2818         nt = dr_dev_type_to_nt((char *)name);
2819         if (nt == SBD_COMP_UNKNOWN) {
2820                 /*
2821                  * this should not happen.  When it does, it indicates
2822                  * a missmatch in devices supported by the drmach layer
2823                  * vs devices supported by this layer.
2824                  */
2825                 return (DR_INTERNAL_ERROR());
2826         }
2827 
2828         dp = DR_GET_BOARD_DEVUNIT(bp, nt, unum);
2829 
2830         /* sanity check */
2831         ASSERT(dp->du_common.sbdev_bp == bp);
2832         ASSERT(dp->du_common.sbdev_unum == unum);
2833         ASSERT(dp->du_common.sbdev_type == nt);
2834 
2835         /* render dynamic attachment point path of this unit */
2836         (void) snprintf(dp->du_common.sbdev_path,
2837             sizeof (dp->du_common.sbdev_path), "%s::%s%d",
2838             bp->b_path, name, DR_UNUM2SBD_UNUM(unum, nt));
2839 
2840         dp->du_common.sbdev_id = id;
2841         DR_DEV_SET_PRESENT(&dp->du_common);
2842 
2843         bp->b_ndev++;
2844 
2845         return (NULL);
2846 }
2847 
2848 static sbd_error_t *
2849 dr_init_devlists(dr_board_t *bp)
2850 {
2851         int             i;
2852         sbd_error_t     *err;
2853         dr_dev_unit_t   *dp;
2854         static fn_t     f = "dr_init_devlists";
2855 
2856         PR_ALL("%s (%s)...\n", f, bp->b_path);
2857 
2858         /* sanity check */
2859         ASSERT(bp->b_ndev == 0);
2860 
2861         DR_DEVS_DISCONNECT(bp, (uint_t)-1);
2862 
2863         /*
2864          * This routine builds the board's devlist and initializes
2865          * the common portion of the unit data structures.
2866          * Note: because the common portion is considered
2867          * uninitialized, the dr_get_*_unit() routines can not
2868          * be used.
2869          */
2870 
2871         /*
2872          * Clear out old entries, if any.
2873          */
2874         for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
2875                 dp = DR_GET_BOARD_DEVUNIT(bp, SBD_COMP_CPU, i);
2876 
2877                 bzero(dp, sizeof (*dp));
2878                 dp->du_common.sbdev_bp = bp;
2879                 dp->du_common.sbdev_unum = i;
2880                 dp->du_common.sbdev_type = SBD_COMP_CPU;
2881         }
2882 
2883         for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
2884                 dp = DR_GET_BOARD_DEVUNIT(bp, SBD_COMP_MEM, i);
2885 
2886                 bzero(dp, sizeof (*dp));
2887                 dp->du_common.sbdev_bp = bp;
2888                 dp->du_common.sbdev_unum = i;
2889                 dp->du_common.sbdev_type = SBD_COMP_MEM;
2890         }
2891 
2892         for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
2893                 dp = DR_GET_BOARD_DEVUNIT(bp, SBD_COMP_IO, i);
2894 
2895                 bzero(dp, sizeof (*dp));
2896                 dp->du_common.sbdev_bp = bp;
2897                 dp->du_common.sbdev_unum = i;
2898                 dp->du_common.sbdev_type = SBD_COMP_IO;
2899         }
2900 
2901         err = NULL;
2902         if (bp->b_id) {
2903                 /* find devices on this board */
2904                 err = drmach_board_find_devices(
2905                     bp->b_id, bp, dr_dev_found);
2906         }
2907 
2908         return (err);
2909 }
2910 
2911 /*
2912  * Return the unit number of the respective drmachid if
2913  * it's found to be attached.
2914  */
2915 static int
2916 dr_check_unit_attached(dr_common_unit_t *cp)
2917 {
2918         int             rv = 0;
2919         processorid_t   cpuid;
2920         uint64_t        basepa, endpa;
2921         struct memlist  *ml;
2922         extern struct memlist   *phys_install;
2923         sbd_error_t     *err;
2924         int             yes;
2925         static fn_t     f = "dr_check_unit_attached";
2926 
2927         switch (cp->sbdev_type) {
2928         case SBD_COMP_CPU:
2929                 err = drmach_cpu_get_id(cp->sbdev_id, &cpuid);
2930                 if (err) {
2931                         DRERR_SET_C(&cp->sbdev_error, &err);
2932                         rv = -1;
2933                         break;
2934                 }
2935                 mutex_enter(&cpu_lock);
2936                 if (cpu_get(cpuid) == NULL)
2937                         rv = -1;
2938                 mutex_exit(&cpu_lock);
2939                 break;
2940 
2941         case SBD_COMP_MEM:
2942                 err = drmach_mem_get_slice_info(cp->sbdev_id,
2943                     &basepa, &endpa, NULL);
2944                 if (err) {
2945                         DRERR_SET_C(&cp->sbdev_error, &err);
2946                         rv = -1;
2947                         break;
2948                 }
2949 
2950                 /*
2951                  * Check if base address is in phys_install.
2952                  */
2953                 memlist_read_lock();
2954                 for (ml = phys_install; ml; ml = ml->ml_next)
2955                         if ((endpa <= ml->ml_address) ||
2956                             (basepa >= (ml->ml_address + ml->ml_size)))
2957                                 continue;
2958                         else
2959                                 break;
2960                 memlist_read_unlock();
2961                 if (ml == NULL)
2962                         rv = -1;
2963                 break;
2964 
2965         case SBD_COMP_IO:
2966                 err = drmach_io_is_attached(cp->sbdev_id, &yes);
2967                 if (err) {
2968                         DRERR_SET_C(&cp->sbdev_error, &err);
2969                         rv = -1;
2970                         break;
2971                 } else if (!yes)
2972                         rv = -1;
2973                 break;
2974 
2975         default:
2976                 PR_ALL("%s: unexpected nodetype(%d) for id 0x%p\n",
2977                     f, cp->sbdev_type, cp->sbdev_id);
2978                 rv = -1;
2979                 break;
2980         }
2981 
2982         return (rv);
2983 }
2984 
2985 /*
2986  * See if drmach recognizes the passthru command.  DRMACH expects the
2987  * id to identify the thing to which the command is being applied.  Using
2988  * nonsense SBD terms, that information has been perversely encoded in the
2989  * c_id member of the sbd_cmd_t structure.  This logic reads those tea
2990  * leaves, finds the associated drmach id, then calls drmach to process
2991  * the passthru command.
2992  */
2993 static int
2994 dr_pt_try_drmach(dr_handle_t *hp)
2995 {
2996         dr_board_t      *bp = hp->h_bd;
2997         sbd_comp_id_t   *comp_id = &hp->h_sbdcmd.cmd_cm.c_id;
2998         drmachid_t       id;
2999 
3000         if (comp_id->c_type == SBD_COMP_NONE) {
3001                 id = bp->b_id;
3002         } else {
3003                 sbd_comp_type_t  nt;
3004 
3005                 nt = dr_dev_type_to_nt(comp_id->c_name);
3006                 if (nt == SBD_COMP_UNKNOWN) {
3007                         dr_op_err(CE_IGNORE, hp, ESBD_INVAL, comp_id->c_name);
3008                         id = 0;
3009                 } else {
3010                         /* pt command applied to dynamic attachment point */
3011                         dr_common_unit_t *cp;
3012                         cp = dr_get_common_unit(bp, nt, comp_id->c_unit);
3013                         id = cp->sbdev_id;
3014                 }
3015         }
3016 
3017         if (hp->h_err == NULL)
3018                 hp->h_err = drmach_passthru(id, &hp->h_opts);
3019 
3020         return (hp->h_err == NULL ? 0 : -1);
3021 }
3022 
3023 static int
3024 dr_pt_ioctl(dr_handle_t *hp)
3025 {
3026         int             cmd, rv, len;
3027         int32_t         sz;
3028         int             found;
3029         char            *copts;
3030         static fn_t     f = "dr_pt_ioctl";
3031 
3032         PR_ALL("%s...\n", f);
3033 
3034         sz = hp->h_opts.size;
3035         copts = hp->h_opts.copts;
3036 
3037         if (sz == 0 || copts == (char *)NULL) {
3038                 cmn_err(CE_WARN, "%s: invalid passthru args", f);
3039                 return (EINVAL);
3040         }
3041 
3042         found = 0;
3043         for (cmd = 0; cmd < (sizeof (pt_arr) / sizeof (pt_arr[0])); cmd++) {
3044                 len = strlen(pt_arr[cmd].pt_name);
3045                 found = (strncmp(pt_arr[cmd].pt_name, copts, len) == 0);
3046                 if (found)
3047                         break;
3048         }
3049 
3050         if (found)
3051                 rv = (*pt_arr[cmd].pt_func)(hp);
3052         else
3053                 rv = dr_pt_try_drmach(hp);
3054 
3055         return (rv);
3056 }
3057 
3058 /*
3059  * Called at driver load time to determine the state and condition
3060  * of an existing board in the system.
3061  */
3062 static void
3063 dr_board_discovery(dr_board_t *bp)
3064 {
3065         int                     i;
3066         dr_devset_t             devs_lost, devs_attached = 0;
3067         dr_cpu_unit_t           *cp;
3068         dr_mem_unit_t           *mp;
3069         dr_io_unit_t            *ip;
3070         static fn_t             f = "dr_board_discovery";
3071 
3072         if (DR_DEVS_PRESENT(bp) == 0) {
3073                 PR_ALL("%s: board %d has no devices present\n",
3074                     f, bp->b_num);
3075                 return;
3076         }
3077 
3078         /*
3079          * Check for existence of cpus.
3080          */
3081         for (i = 0; i < MAX_CPU_UNITS_PER_BOARD; i++) {
3082                 cp = dr_get_cpu_unit(bp, i);
3083 
3084                 if (!DR_DEV_IS_PRESENT(&cp->sbc_cm))
3085                         continue;
3086 
3087                 if (dr_check_unit_attached(&cp->sbc_cm) >= 0) {
3088                         DR_DEV_SET_ATTACHED(&cp->sbc_cm);
3089                         DEVSET_ADD(devs_attached, SBD_COMP_CPU, i);
3090                         PR_ALL("%s: board %d, cpu-unit %d - attached\n",
3091                             f, bp->b_num, i);
3092                 }
3093                 dr_init_cpu_unit(cp);
3094         }
3095 
3096         /*
3097          * Check for existence of memory.
3098          */
3099         for (i = 0; i < MAX_MEM_UNITS_PER_BOARD; i++) {
3100                 mp = dr_get_mem_unit(bp, i);
3101 
3102                 if (!DR_DEV_IS_PRESENT(&mp->sbm_cm))
3103                         continue;
3104 
3105                 if (dr_check_unit_attached(&mp->sbm_cm) >= 0) {
3106                         DR_DEV_SET_ATTACHED(&mp->sbm_cm);
3107                         DEVSET_ADD(devs_attached, SBD_COMP_MEM, i);
3108                         PR_ALL("%s: board %d, mem-unit %d - attached\n",
3109                             f, bp->b_num, i);
3110                 }
3111                 dr_init_mem_unit(mp);
3112         }
3113 
3114         /*
3115          * Check for i/o state.
3116          */
3117         for (i = 0; i < MAX_IO_UNITS_PER_BOARD; i++) {
3118                 ip = dr_get_io_unit(bp, i);
3119 
3120                 if (!DR_DEV_IS_PRESENT(&ip->sbi_cm))
3121                         continue;
3122 
3123                 if (dr_check_unit_attached(&ip->sbi_cm) >= 0) {
3124                         /*
3125                          * Found it!
3126                          */
3127                         DR_DEV_SET_ATTACHED(&ip->sbi_cm);
3128                         DEVSET_ADD(devs_attached, SBD_COMP_IO, i);
3129                         PR_ALL("%s: board %d, io-unit %d - attached\n",
3130                             f, bp->b_num, i);
3131                 }
3132                 dr_init_io_unit(ip);
3133         }
3134 
3135         DR_DEVS_CONFIGURE(bp, devs_attached);
3136         if (devs_attached && ((devs_lost = DR_DEVS_UNATTACHED(bp)) != 0)) {
3137                 int             ut;
3138 
3139                 /*
3140                  * It is not legal on board discovery to have a
3141                  * board that is only partially attached.  A board
3142                  * is either all attached or all connected.  If a
3143                  * board has at least one attached device, then
3144                  * the the remaining devices, if any, must have
3145                  * been lost or disconnected.  These devices can
3146                  * only be recovered by a full attach from scratch.
3147                  * Note that devices previously in the unreferenced
3148                  * state are subsequently lost until the next full
3149                  * attach.  This is necessary since the driver unload
3150                  * that must have occurred would have wiped out the
3151                  * information necessary to re-configure the device
3152                  * back online, e.g. memlist.
3153                  */
3154                 PR_ALL("%s: some devices LOST (" DEVSET_FMT_STR ")...\n",
3155                     f, DEVSET_FMT_ARG(devs_lost));
3156 
3157                 for (ut = 0; ut < MAX_CPU_UNITS_PER_BOARD; ut++) {
3158                         if (!DEVSET_IN_SET(devs_lost, SBD_COMP_CPU, ut))
3159                                 continue;
3160 
3161                         cp = dr_get_cpu_unit(bp, ut);
3162                         dr_device_transition(&cp->sbc_cm, DR_STATE_EMPTY);
3163                 }
3164 
3165                 for (ut = 0; ut < MAX_MEM_UNITS_PER_BOARD; ut++) {
3166                         if (!DEVSET_IN_SET(devs_lost, SBD_COMP_MEM, ut))
3167                                 continue;
3168 
3169                         mp = dr_get_mem_unit(bp, ut);
3170                         dr_device_transition(&mp->sbm_cm, DR_STATE_EMPTY);
3171                 }
3172 
3173                 for (ut = 0; ut < MAX_IO_UNITS_PER_BOARD; ut++) {
3174                         if (!DEVSET_IN_SET(devs_lost, SBD_COMP_IO, ut))
3175                                 continue;
3176 
3177                         ip = dr_get_io_unit(bp, ut);
3178                         dr_device_transition(&ip->sbi_cm, DR_STATE_EMPTY);
3179                 }
3180 
3181                 DR_DEVS_DISCONNECT(bp, devs_lost);
3182         }
3183 }
3184 
3185 static int
3186 dr_board_init(dr_board_t *bp, dev_info_t *dip, int bd)
3187 {
3188         sbd_error_t     *err;
3189 
3190         mutex_init(&bp->b_lock, NULL, MUTEX_DRIVER, NULL);
3191         mutex_init(&bp->b_slock, NULL, MUTEX_DRIVER, NULL);
3192         cv_init(&bp->b_scv, NULL, CV_DRIVER, NULL);
3193         bp->b_rstate = SBD_STAT_EMPTY;
3194         bp->b_ostate = SBD_STAT_UNCONFIGURED;
3195         bp->b_cond = SBD_COND_UNKNOWN;
3196         (void) drv_getparm(TIME, (void *)&bp->b_time);
3197 
3198         (void) drmach_board_lookup(bd, &bp->b_id);
3199         bp->b_num = bd;
3200         bp->b_dip = dip;
3201 
3202         bp->b_dev[DEVSET_NIX(SBD_COMP_CPU)] = GETSTRUCT(dr_dev_unit_t,
3203             MAX_CPU_UNITS_PER_BOARD);
3204 
3205         bp->b_dev[DEVSET_NIX(SBD_COMP_MEM)] = GETSTRUCT(dr_dev_unit_t,
3206             MAX_MEM_UNITS_PER_BOARD);
3207 
3208         bp->b_dev[DEVSET_NIX(SBD_COMP_IO)] = GETSTRUCT(dr_dev_unit_t,
3209             MAX_IO_UNITS_PER_BOARD);
3210 
3211         /*
3212          * Initialize the devlists
3213          */
3214         err = dr_init_devlists(bp);
3215         if (err) {
3216                 sbd_err_clear(&err);
3217                 dr_board_destroy(bp);
3218                 return (-1);
3219         } else if (bp->b_ndev == 0) {
3220                 dr_board_transition(bp, DR_STATE_EMPTY);
3221         } else {
3222                 /*
3223                  * Couldn't have made it down here without
3224                  * having found at least one device.
3225                  */
3226                 ASSERT(DR_DEVS_PRESENT(bp) != 0);
3227                 /*
3228                  * Check the state of any possible devices on the
3229                  * board.
3230                  */
3231                 dr_board_discovery(bp);
3232 
3233                 bp->b_assigned = 1;
3234 
3235                 if (DR_DEVS_UNATTACHED(bp) == 0) {
3236                         /*
3237                          * The board has no unattached devices, therefore
3238                          * by reason of insanity it must be configured!
3239                          */
3240                         dr_board_transition(bp, DR_STATE_CONFIGURED);
3241                         bp->b_ostate = SBD_STAT_CONFIGURED;
3242                         bp->b_rstate = SBD_STAT_CONNECTED;
3243                         bp->b_cond = SBD_COND_OK;
3244                         (void) drv_getparm(TIME, (void *)&bp->b_time);
3245                 } else if (DR_DEVS_ATTACHED(bp)) {
3246                         dr_board_transition(bp, DR_STATE_PARTIAL);
3247                         bp->b_ostate = SBD_STAT_CONFIGURED;
3248                         bp->b_rstate = SBD_STAT_CONNECTED;
3249                         bp->b_cond = SBD_COND_OK;
3250                         (void) drv_getparm(TIME, (void *)&bp->b_time);
3251                 } else {
3252                         dr_board_transition(bp, DR_STATE_CONNECTED);
3253                         bp->b_rstate = SBD_STAT_CONNECTED;
3254                         (void) drv_getparm(TIME, (void *)&bp->b_time);
3255                 }
3256         }
3257 
3258         return (0);
3259 }
3260 
3261 static void
3262 dr_board_destroy(dr_board_t *bp)
3263 {
3264         PR_ALL("dr_board_destroy: num %d, path %s\n",
3265             bp->b_num, bp->b_path);
3266 
3267         dr_board_transition(bp, DR_STATE_EMPTY);
3268         bp->b_rstate = SBD_STAT_EMPTY;
3269         (void) drv_getparm(TIME, (void *)&bp->b_time);
3270 
3271         /*
3272          * Free up MEM unit structs.
3273          */
3274         FREESTRUCT(bp->b_dev[DEVSET_NIX(SBD_COMP_MEM)],
3275             dr_dev_unit_t, MAX_MEM_UNITS_PER_BOARD);
3276         bp->b_dev[DEVSET_NIX(SBD_COMP_MEM)] = NULL;
3277         /*
3278          * Free up CPU unit structs.
3279          */
3280         FREESTRUCT(bp->b_dev[DEVSET_NIX(SBD_COMP_CPU)],
3281             dr_dev_unit_t, MAX_CPU_UNITS_PER_BOARD);
3282         bp->b_dev[DEVSET_NIX(SBD_COMP_CPU)] = NULL;
3283         /*
3284          * Free up IO unit structs.
3285          */
3286         FREESTRUCT(bp->b_dev[DEVSET_NIX(SBD_COMP_IO)],
3287             dr_dev_unit_t, MAX_IO_UNITS_PER_BOARD);
3288         bp->b_dev[DEVSET_NIX(SBD_COMP_IO)] = NULL;
3289 
3290         mutex_destroy(&bp->b_lock);
3291         mutex_destroy(&bp->b_slock);
3292         cv_destroy(&bp->b_scv);
3293 
3294         /*
3295          * Reset the board structure to its initial state, otherwise it will
3296          * cause trouble on the next call to dr_board_init() for the same board.
3297          * dr_board_init() may be called multiple times for the same board
3298          * if DR driver fails to initialize some boards.
3299          */
3300         bzero(bp, sizeof (*bp));
3301 }
3302 
3303 void
3304 dr_lock_status(dr_board_t *bp)
3305 {
3306         mutex_enter(&bp->b_slock);
3307         while (bp->b_sflags & DR_BSLOCK)
3308                 cv_wait(&bp->b_scv, &bp->b_slock);
3309         bp->b_sflags |= DR_BSLOCK;
3310         mutex_exit(&bp->b_slock);
3311 }
3312 
3313 void
3314 dr_unlock_status(dr_board_t *bp)
3315 {
3316         mutex_enter(&bp->b_slock);
3317         bp->b_sflags &= ~DR_BSLOCK;
3318         cv_signal(&bp->b_scv);
3319         mutex_exit(&bp->b_slock);
3320 }
3321 
3322 /*
3323  * Extract flags passed via ioctl.
3324  */
3325 int
3326 dr_cmd_flags(dr_handle_t *hp)
3327 {
3328         return (hp->h_sbdcmd.cmd_cm.c_flags);
3329 }