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  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI";
  28  *
  29  * Porting notes:
  30  *
  31  * OPROMU2P unsupported after SunOS 4.x.
  32  *
  33  * Only one of these devices per system is allowed.
  34  */
  35 
  36 /*
  37  * Openprom eeprom options/devinfo driver.
  38  */
  39 
  40 #include <sys/types.h>
  41 #include <sys/errno.h>
  42 #include <sys/file.h>
  43 #include <sys/cmn_err.h>
  44 #include <sys/kmem.h>
  45 #include <sys/openpromio.h>
  46 #include <sys/conf.h>
  47 #include <sys/stat.h>
  48 #include <sys/modctl.h>
  49 #include <sys/debug.h>
  50 #include <sys/autoconf.h>
  51 #include <sys/ddi.h>
  52 #include <sys/sunddi.h>
  53 #include <sys/promif.h>
  54 #include <sys/sysmacros.h>        /* offsetof */
  55 #include <sys/nvpair.h>
  56 #include <sys/wanboot_impl.h>
  57 #include <sys/zone.h>
  58 #include <sys/consplat.h>
  59 #include <sys/bootconf.h>
  60 #include <sys/systm.h>
  61 #include <sys/bootprops.h>
  62 
  63 #define MAX_OPENS       32      /* Up to this many simultaneous opens */
  64 
  65 #define IOC_IDLE        0       /* snapshot ioctl states */
  66 #define IOC_SNAP        1       /* snapshot in progress */
  67 #define IOC_DONE        2       /* snapshot done, but not copied out */
  68 #define IOC_COPY        3       /* copyout in progress */
  69 
  70 /*
  71  * XXX  Make this dynamic.. or (better still) make the interface stateless
  72  */
  73 static struct oprom_state {
  74         pnode_t current_id;     /* node we're fetching props from */
  75         int16_t already_open;   /* if true, this instance is 'active' */
  76         int16_t ioc_state;      /* snapshot ioctl state */
  77         char    *snapshot;      /* snapshot of all prom nodes */
  78         size_t  size;           /* size of snapshot */
  79         prom_generation_cookie_t tree_gen;
  80 } oprom_state[MAX_OPENS];
  81 
  82 static kmutex_t oprom_lock;     /* serialize instance assignment */
  83 
  84 static int opromopen(dev_t *, int, int, cred_t *);
  85 static int opromioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  86 static int opromclose(dev_t, int, int, cred_t *);
  87 
  88 static int opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
  89                 void **result);
  90 static int opattach(dev_info_t *, ddi_attach_cmd_t cmd);
  91 static int opdetach(dev_info_t *, ddi_detach_cmd_t cmd);
  92 
  93 /* help functions */
  94 static int oprom_checknodeid(pnode_t, pnode_t);
  95 static int oprom_copyinstr(intptr_t, char *, size_t, size_t);
  96 static int oprom_copynode(pnode_t, uint_t, char **, size_t *);
  97 static int oprom_snapshot(struct oprom_state *, intptr_t);
  98 static int oprom_copyout(struct oprom_state *, intptr_t);
  99 static int oprom_setstate(struct oprom_state *, int16_t);
 100 
 101 static struct cb_ops openeepr_cb_ops = {
 102         opromopen,              /* open */
 103         opromclose,             /* close */
 104         nodev,                  /* strategy */
 105         nodev,                  /* print */
 106         nodev,                  /* dump */
 107         nodev,                  /* read */
 108         nodev,                  /* write */
 109         opromioctl,             /* ioctl */
 110         nodev,                  /* devmap */
 111         nodev,                  /* mmap */
 112         nodev,                  /* segmap */
 113         nochpoll,               /* poll */
 114         ddi_prop_op,            /* prop_op */
 115         NULL,                   /* streamtab  */
 116         D_NEW | D_MP            /* Driver compatibility flag */
 117 };
 118 
 119 static struct dev_ops openeepr_ops = {
 120         DEVO_REV,               /* devo_rev, */
 121         0,                      /* refcnt  */
 122         opinfo,                 /* info */
 123         nulldev,                /* identify */
 124         nulldev,                /* probe */
 125         opattach,               /* attach */
 126         opdetach,               /* detach */
 127         nodev,                  /* reset */
 128         &openeepr_cb_ops,   /* driver operations */
 129         NULL,                   /* bus operations */
 130         NULL,                   /* power */
 131         ddi_quiesce_not_needed,         /* quiesce */
 132 };
 133 
 134 /*
 135  * Module linkage information for the kernel.
 136  */
 137 static struct modldrv modldrv = {
 138         &mod_driverops,
 139         "OPENPROM/NVRAM Driver",
 140         &openeepr_ops
 141 };
 142 
 143 static struct modlinkage modlinkage = {
 144         MODREV_1,
 145         &modldrv,
 146         NULL
 147 };
 148 
 149 int
 150 _init(void)
 151 {
 152         int     error;
 153 
 154         mutex_init(&oprom_lock, NULL, MUTEX_DRIVER, NULL);
 155 
 156         error = mod_install(&modlinkage);
 157         if (error != 0) {
 158                 mutex_destroy(&oprom_lock);
 159                 return (error);
 160         }
 161 
 162         return (0);
 163 }
 164 
 165 int
 166 _info(struct modinfo *modinfop)
 167 {
 168         return (mod_info(&modlinkage, modinfop));
 169 }
 170 
 171 int
 172 _fini(void)
 173 {
 174         int     error;
 175 
 176         error = mod_remove(&modlinkage);
 177         if (error != 0)
 178                 return (error);
 179 
 180         mutex_destroy(&oprom_lock);
 181         return (0);
 182 }
 183 
 184 static dev_info_t *opdip;
 185 static pnode_t options_nodeid;
 186 
 187 /*ARGSUSED*/
 188 static int
 189 opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 190 {
 191         int error = DDI_FAILURE;
 192 
 193         switch (infocmd) {
 194         case DDI_INFO_DEVT2DEVINFO:
 195                 *result = (void *)opdip;
 196                 error = DDI_SUCCESS;
 197                 break;
 198         case DDI_INFO_DEVT2INSTANCE:
 199                 /* All dev_t's map to the same, single instance */
 200                 *result = (void *)0;
 201                 error = DDI_SUCCESS;
 202                 break;
 203         default:
 204                 break;
 205         }
 206 
 207         return (error);
 208 }
 209 
 210 static int
 211 opattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 212 {
 213         switch (cmd) {
 214 
 215         case DDI_ATTACH:
 216                 if (prom_is_openprom()) {
 217                         options_nodeid = prom_optionsnode();
 218                 } else {
 219                         options_nodeid = OBP_BADNODE;
 220                 }
 221 
 222                 opdip = dip;
 223 
 224                 if (ddi_create_minor_node(dip, "openprom", S_IFCHR,
 225                     0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 226                         return (DDI_FAILURE);
 227                 }
 228 
 229                 return (DDI_SUCCESS);
 230 
 231         default:
 232                 return (DDI_FAILURE);
 233         }
 234 }
 235 
 236 static int
 237 opdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 238 {
 239         if (cmd != DDI_DETACH)
 240                 return (DDI_FAILURE);
 241 
 242         ddi_remove_minor_node(dip, NULL);
 243         opdip = NULL;
 244 
 245         return (DDI_SUCCESS);
 246 }
 247 
 248 /*
 249  * Allow multiple opens by tweaking the dev_t such that it looks like each
 250  * open is getting a different minor device.  Each minor gets a separate
 251  * entry in the oprom_state[] table.
 252  */
 253 /*ARGSUSED*/
 254 static int
 255 opromopen(dev_t *devp, int flag, int otyp, cred_t *credp)
 256 {
 257         int m;
 258         struct oprom_state *st = oprom_state;
 259 
 260         if (getminor(*devp) != 0)
 261                 return (ENXIO);
 262 
 263         mutex_enter(&oprom_lock);
 264         for (m = 0; m < MAX_OPENS; m++)
 265                 if (st->already_open)
 266                         st++;
 267                 else {
 268                         st->already_open = 1;
 269                         /*
 270                          * It's ours.
 271                          */
 272                         st->current_id = (pnode_t)0;
 273                         ASSERT(st->snapshot == NULL && st->size == 0);
 274                         ASSERT(st->ioc_state == IOC_IDLE);
 275                         break;
 276                 }
 277         mutex_exit(&oprom_lock);
 278 
 279         if (m == MAX_OPENS)  {
 280                 /*
 281                  * "Thank you for calling, but all our lines are
 282                  * busy at the moment.."
 283                  *
 284                  * We could get sophisticated here, and go into a
 285                  * sleep-retry loop .. but hey, I just can't see
 286                  * that many processes sitting in this driver.
 287                  *
 288                  * (And if it does become possible, then we should
 289                  * change the interface so that the 'state' is held
 290                  * external to the driver)
 291                  */
 292                 return (EAGAIN);
 293         }
 294 
 295         *devp = makedevice(getmajor(*devp), (minor_t)m);
 296 
 297         return (0);
 298 }
 299 
 300 /*ARGSUSED*/
 301 static int
 302 opromclose(dev_t dev, int flag, int otype, cred_t *cred_p)
 303 {
 304         struct oprom_state *st;
 305 
 306         st = &oprom_state[getminor(dev)];
 307         ASSERT(getminor(dev) < MAX_OPENS && st->already_open != 0);
 308         if (st->snapshot) {
 309                 kmem_free(st->snapshot, st->size);
 310                 st->snapshot = NULL;
 311                 st->size = 0;
 312                 st->ioc_state = IOC_IDLE;
 313         }
 314         mutex_enter(&oprom_lock);
 315         st->already_open = 0;
 316         mutex_exit(&oprom_lock);
 317 
 318         return (0);
 319 }
 320 
 321 #ifdef __sparc
 322 static int
 323 get_bootpath_prop(char *bootpath)
 324 {
 325         if (root_is_ramdisk) {
 326                 if (BOP_GETPROP(bootops, "bootarchive", bootpath) == -1)
 327                         return (-1);
 328                 (void) strlcat(bootpath, ":a", BO_MAXOBJNAME);
 329         } else {
 330                 if ((BOP_GETPROP(bootops, "bootpath", bootpath) == -1) ||
 331                     strlen(bootpath) == 0) {
 332                         if (BOP_GETPROP(bootops,
 333                             "boot-path", bootpath) == -1)
 334                                 return (-1);
 335                 }
 336                 if (memcmp(bootpath, BP_ISCSI_DISK,
 337                     strlen(BP_ISCSI_DISK)) == 0) {
 338                         get_iscsi_bootpath_vhci(bootpath);
 339                 }
 340         }
 341         return (0);
 342 }
 343 #endif
 344 
 345 struct opromioctl_args {
 346         struct oprom_state *st;
 347         int cmd;
 348         intptr_t arg;
 349         int mode;
 350 };
 351 
 352 /*ARGSUSED*/
 353 static int
 354 opromioctl_cb(void *avp, int has_changed)
 355 {
 356         struct opromioctl_args *argp = avp;
 357         int cmd;
 358         intptr_t arg;
 359         int mode;
 360         struct oprom_state *st;
 361         struct openpromio *opp;
 362         int valsize;
 363         char *valbuf;
 364         int error = 0;
 365         uint_t userbufsize;
 366         pnode_t node_id;
 367         char propname[OBP_MAXPROPNAME];
 368 
 369         st = argp->st;
 370         cmd = argp->cmd;
 371         arg = argp->arg;
 372         mode = argp->mode;
 373 
 374         if (has_changed) {
 375                 /*
 376                  * The prom tree has changed since we last used current_id,
 377                  * so we need to check it.
 378                  */
 379                 if ((st->current_id != OBP_NONODE) &&
 380                     (st->current_id != OBP_BADNODE)) {
 381                         if (oprom_checknodeid(st->current_id, OBP_NONODE) == 0)
 382                                 st->current_id = OBP_BADNODE;
 383                 }
 384         }
 385 
 386         /*
 387          * Check permissions
 388          * and weed out unsupported commands on x86 platform
 389          */
 390         switch (cmd) {
 391 #if !defined(__i386) && !defined(__amd64)
 392         case OPROMLISTKEYSLEN:
 393                 valsize = prom_asr_list_keys_len();
 394                 opp = (struct openpromio *)kmem_zalloc(
 395                     sizeof (uint_t) + 1, KM_SLEEP);
 396                 opp->oprom_size = valsize;
 397                 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0)
 398                         error = EFAULT;
 399                 kmem_free(opp, sizeof (uint_t) + 1);
 400                 break;
 401         case OPROMLISTKEYS:
 402                 valsize = prom_asr_list_keys_len();
 403                 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
 404                         return (EFAULT);
 405                 if (valsize > userbufsize)
 406                         return (EINVAL);
 407                 valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP);
 408                 if (prom_asr_list_keys((caddr_t)valbuf) == -1) {
 409                         kmem_free(valbuf, valsize + 1);
 410                         return (EFAULT);
 411                 }
 412                 opp = (struct openpromio *)kmem_zalloc(
 413                     valsize + sizeof (uint_t) + 1, KM_SLEEP);
 414                 opp->oprom_size = valsize;
 415                 bcopy(valbuf, opp->oprom_array, valsize);
 416                 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
 417                         error = EFAULT;
 418                 kmem_free(valbuf, valsize + 1);
 419                 kmem_free(opp, valsize + sizeof (uint_t) + 1);
 420                 break;
 421         case OPROMEXPORT:
 422                 valsize = prom_asr_export_len();
 423                 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
 424                         return (EFAULT);
 425                 if (valsize > userbufsize)
 426                         return (EINVAL);
 427                 valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP);
 428                 if (prom_asr_export((caddr_t)valbuf) == -1) {
 429                         kmem_free(valbuf, valsize + 1);
 430                         return (EFAULT);
 431                 }
 432                 opp = (struct openpromio *)kmem_zalloc(
 433                     valsize + sizeof (uint_t) + 1, KM_SLEEP);
 434                 opp->oprom_size = valsize;
 435                 bcopy(valbuf, opp->oprom_array, valsize);
 436                 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
 437                         error = EFAULT;
 438                 kmem_free(valbuf, valsize + 1);
 439                 kmem_free(opp, valsize + sizeof (uint_t) + 1);
 440                 break;
 441         case OPROMEXPORTLEN:
 442                 valsize = prom_asr_export_len();
 443                 opp = (struct openpromio *)kmem_zalloc(
 444                     sizeof (uint_t) + 1, KM_SLEEP);
 445                 opp->oprom_size = valsize;
 446                 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0)
 447                         error = EFAULT;
 448                 kmem_free(opp, sizeof (uint_t) + 1);
 449                 break;
 450 #endif
 451         case OPROMGETOPT:
 452         case OPROMNXTOPT:
 453                 if ((mode & FREAD) == 0) {
 454                         return (EPERM);
 455                 }
 456                 node_id = options_nodeid;
 457                 break;
 458 
 459         case OPROMSETOPT:
 460         case OPROMSETOPT2:
 461 #if !defined(__i386) && !defined(__amd64)
 462                 if (mode & FWRITE) {
 463                         node_id = options_nodeid;
 464                         break;
 465                 }
 466 #endif /* !__i386 && !__amd64 */
 467                 return (EPERM);
 468 
 469         case OPROMNEXT:
 470         case OPROMCHILD:
 471         case OPROMGETPROP:
 472         case OPROMGETPROPLEN:
 473         case OPROMNXTPROP:
 474         case OPROMSETNODEID:
 475                 if ((mode & FREAD) == 0) {
 476                         return (EPERM);
 477                 }
 478                 node_id = st->current_id;
 479                 break;
 480         case OPROMCOPYOUT:
 481                 if (st->snapshot == NULL)
 482                         return (EINVAL);
 483                 /*FALLTHROUGH*/
 484         case OPROMSNAPSHOT:
 485         case OPROMGETCONS:
 486         case OPROMGETBOOTARGS:
 487         case OPROMGETBOOTPATH:
 488         case OPROMGETVERSION:
 489         case OPROMPATH2DRV:
 490         case OPROMPROM2DEVNAME:
 491 #if !defined(__i386) && !defined(__amd64)
 492         case OPROMGETFBNAME:
 493         case OPROMDEV2PROMNAME:
 494         case OPROMREADY64:
 495 #endif  /* !__i386 && !__amd64 */
 496                 if ((mode & FREAD) == 0) {
 497                         return (EPERM);
 498                 }
 499                 break;
 500 
 501 #if !defined(__i386) && !defined(__amd64)
 502         case WANBOOT_SETKEY:
 503                 if (!(mode & FWRITE))
 504                         return (EPERM);
 505                 break;
 506 #endif  /* !__i386 && !defined(__amd64) */
 507 
 508         default:
 509                 return (EINVAL);
 510         }
 511 
 512         /*
 513          * Deal with SNAPSHOT and COPYOUT ioctls first
 514          */
 515         switch (cmd) {
 516         case OPROMCOPYOUT:
 517                 return (oprom_copyout(st, arg));
 518 
 519         case OPROMSNAPSHOT:
 520                 return (oprom_snapshot(st, arg));
 521         }
 522 
 523         /*
 524          * Copy in user argument length and allocation memory
 525          *
 526          * NB do not copyin the entire buffer we may not need
 527          *      to. userbufsize can be as big as 32 K.
 528          */
 529         if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
 530                 return (EFAULT);
 531 
 532         if (userbufsize == 0 || userbufsize > OPROMMAXPARAM)
 533                 return (EINVAL);
 534 
 535         opp = (struct openpromio *)kmem_zalloc(
 536             userbufsize + sizeof (uint_t) + 1, KM_SLEEP);
 537 
 538         /*
 539          * Execute command
 540          */
 541         switch (cmd) {
 542 
 543         case OPROMGETOPT:
 544         case OPROMGETPROP:
 545         case OPROMGETPROPLEN:
 546 
 547                 if ((prom_is_openprom() == 0) ||
 548                     (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
 549                         error = EINVAL;
 550                         break;
 551                 }
 552 
 553                 /*
 554                  * The argument, a NULL terminated string, is a prop name.
 555                  */
 556                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 557                     (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
 558                         break;
 559                 }
 560                 (void) strcpy(propname, opp->oprom_array);
 561                 valsize = prom_getproplen(node_id, propname);
 562 
 563                 /*
 564                  * 4010173: 'name' is a property, but not an option.
 565                  */
 566                 if ((cmd == OPROMGETOPT) && (strcmp("name", propname) == 0))
 567                         valsize = -1;
 568 
 569                 if (cmd == OPROMGETPROPLEN)  {
 570                         int proplen = valsize;
 571 
 572                         if (userbufsize < sizeof (int)) {
 573                                 error = EINVAL;
 574                                 break;
 575                         }
 576                         opp->oprom_size = valsize = sizeof (int);
 577                         bcopy(&proplen, opp->oprom_array, valsize);
 578                 } else if (valsize > 0 && valsize <= userbufsize) {
 579                         bzero(opp->oprom_array, valsize + 1);
 580                         (void) prom_getprop(node_id, propname,
 581                             opp->oprom_array);
 582                         opp->oprom_size = valsize;
 583                         if (valsize < userbufsize)
 584                                 ++valsize;      /* Forces NULL termination */
 585                                                 /* If space permits */
 586                 } else {
 587                         /*
 588                          * XXX: There is no error code if the buf is too small.
 589                          * which is consistent with the current behavior.
 590                          *
 591                          * NB: This clause also handles the non-error
 592                          * zero length (boolean) property value case.
 593                          */
 594                         opp->oprom_size = 0;
 595                         (void) strcpy(opp->oprom_array, "");
 596                         valsize = 1;
 597                 }
 598                 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
 599                         error = EFAULT;
 600                 break;
 601 
 602         case OPROMNXTOPT:
 603         case OPROMNXTPROP:
 604                 if ((prom_is_openprom() == 0) ||
 605                     (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
 606                         error = EINVAL;
 607                         break;
 608                 }
 609 
 610                 /*
 611                  * The argument, a NULL terminated string, is a prop name.
 612                  */
 613                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 614                     (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
 615                         break;
 616                 }
 617                 valbuf = (char *)prom_nextprop(node_id, opp->oprom_array,
 618                     propname);
 619                 valsize = strlen(valbuf);
 620 
 621                 /*
 622                  * 4010173: 'name' is a property, but it's not an option.
 623                  */
 624                 if ((cmd == OPROMNXTOPT) && valsize &&
 625                     (strcmp(valbuf, "name") == 0)) {
 626                         valbuf = (char *)prom_nextprop(node_id, "name",
 627                             propname);
 628                         valsize = strlen(valbuf);
 629                 }
 630 
 631                 if (valsize == 0) {
 632                         opp->oprom_size = 0;
 633                 } else if (++valsize <= userbufsize) {
 634                         opp->oprom_size = valsize;
 635                         bzero((caddr_t)opp->oprom_array, (size_t)valsize);
 636                         bcopy((caddr_t)valbuf, (caddr_t)opp->oprom_array,
 637                             (size_t)valsize);
 638                 }
 639 
 640                 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
 641                         error = EFAULT;
 642                 break;
 643 
 644         case OPROMNEXT:
 645         case OPROMCHILD:
 646         case OPROMSETNODEID:
 647 
 648                 if (prom_is_openprom() == 0 ||
 649                     userbufsize < sizeof (pnode_t)) {
 650                         error = EINVAL;
 651                         break;
 652                 }
 653 
 654                 /*
 655                  * The argument is a phandle. (aka pnode_t)
 656                  */
 657                 if (copyin(((caddr_t)arg + sizeof (uint_t)),
 658                     opp->oprom_array, sizeof (pnode_t)) != 0) {
 659                         error = EFAULT;
 660                         break;
 661                 }
 662 
 663                 /*
 664                  * If pnode_t from userland is garbage, we
 665                  * could confuse the PROM.
 666                  */
 667                 node_id = *(pnode_t *)opp->oprom_array;
 668                 if (oprom_checknodeid(node_id, st->current_id) == 0) {
 669                         cmn_err(CE_NOTE, "!nodeid 0x%x not found",
 670                             (int)node_id);
 671                         error = EINVAL;
 672                         break;
 673                 }
 674 
 675                 if (cmd == OPROMNEXT)
 676                         st->current_id = prom_nextnode(node_id);
 677                 else if (cmd == OPROMCHILD)
 678                         st->current_id = prom_childnode(node_id);
 679                 else {
 680                         /* OPROMSETNODEID */
 681                         st->current_id = node_id;
 682                         break;
 683                 }
 684 
 685                 opp->oprom_size = sizeof (pnode_t);
 686                 *(pnode_t *)opp->oprom_array = st->current_id;
 687 
 688                 if (copyout(opp, (void *)arg,
 689                     sizeof (pnode_t) + sizeof (uint_t)) != 0)
 690                         error = EFAULT;
 691                 break;
 692 
 693         case OPROMGETCONS:
 694                 /*
 695                  * Is openboot supported on this machine?
 696                  * This ioctl used to return the console device,
 697                  * information; this is now done via modctl()
 698                  * in libdevinfo.
 699                  */
 700                 opp->oprom_size = sizeof (char);
 701 
 702                 opp->oprom_array[0] |= prom_is_openprom() ?
 703                     OPROMCONS_OPENPROM : 0;
 704 
 705                 /*
 706                  * The rest of the info is needed by Install to
 707                  * decide if graphics should be started.
 708                  */
 709                 if ((getzoneid() == GLOBAL_ZONEID) &&
 710                     plat_stdin_is_keyboard()) {
 711                         opp->oprom_array[0] |= OPROMCONS_STDIN_IS_KBD;
 712                 }
 713 
 714                 if ((getzoneid() == GLOBAL_ZONEID) &&
 715                     plat_stdout_is_framebuffer()) {
 716                         opp->oprom_array[0] |= OPROMCONS_STDOUT_IS_FB;
 717                 }
 718 
 719                 if (copyout(opp, (void *)arg,
 720                     sizeof (char) + sizeof (uint_t)) != 0)
 721                         error = EFAULT;
 722                 break;
 723 
 724         case OPROMGETBOOTARGS: {
 725                 extern char kern_bootargs[];
 726 
 727                 valsize = strlen(kern_bootargs) + 1;
 728                 if (valsize > userbufsize) {
 729                         error = EINVAL;
 730                         break;
 731                 }
 732                 (void) strcpy(opp->oprom_array, kern_bootargs);
 733                 opp->oprom_size = valsize - 1;
 734 
 735                 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
 736                         error = EFAULT;
 737                 break;
 738         }
 739 
 740         case OPROMGETBOOTPATH: {
 741 #if defined(__sparc) && defined(_OBP)
 742 
 743                 char bpath[OBP_MAXPATHLEN];
 744                 if (get_bootpath_prop(bpath) != 0) {
 745                         error = EINVAL;
 746                         break;
 747                 }
 748                 valsize = strlen(bpath) + 1;
 749                 if (valsize > userbufsize) {
 750                         error = EINVAL;
 751                         break;
 752                 }
 753                 (void) strcpy(opp->oprom_array, bpath);
 754 
 755 #elif defined(__i386) || defined(__amd64)
 756 
 757                 extern char saved_cmdline[];
 758                 valsize = strlen(saved_cmdline) + 1;
 759                 if (valsize > userbufsize) {
 760                         error = EINVAL;
 761                         break;
 762                 }
 763                 (void) strcpy(opp->oprom_array, saved_cmdline);
 764 #endif
 765                 opp->oprom_size = valsize - 1;
 766                 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
 767                         error = EFAULT;
 768                 break;
 769         }
 770 
 771         /*
 772          * convert a prom device path to an equivalent devfs path
 773          */
 774         case OPROMPROM2DEVNAME: {
 775                 char *dev_name;
 776 
 777                 /*
 778                  * The input argument, a pathname, is a NULL terminated string.
 779                  */
 780                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 781                     (size_t)userbufsize, MAXPATHLEN)) != 0) {
 782                         break;
 783                 }
 784 
 785                 dev_name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 786 
 787                 error = i_promname_to_devname(opp->oprom_array, dev_name);
 788                 if (error != 0) {
 789                         kmem_free(dev_name, MAXPATHLEN);
 790                         break;
 791                 }
 792                 valsize = opp->oprom_size = strlen(dev_name);
 793                 if (++valsize > userbufsize) {
 794                         kmem_free(dev_name, MAXPATHLEN);
 795                         error = EINVAL;
 796                         break;
 797                 }
 798                 (void) strcpy(opp->oprom_array, dev_name);
 799                 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
 800                         error = EFAULT;
 801 
 802                 kmem_free(dev_name, MAXPATHLEN);
 803                 break;
 804         }
 805 
 806         /*
 807          * Convert a prom device path name to a driver name
 808          */
 809         case OPROMPATH2DRV: {
 810                 char *drv_name;
 811                 major_t maj;
 812 
 813                 /*
 814                  * The input argument, a pathname, is a NULL terminated string.
 815                  */
 816                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 817                     (size_t)userbufsize, MAXPATHLEN)) != 0) {
 818                         break;
 819                 }
 820 
 821                 /*
 822                  * convert path to a driver binding name
 823                  */
 824                 maj = path_to_major((char *)opp->oprom_array);
 825                 if (maj == DDI_MAJOR_T_NONE) {
 826                         error = EINVAL;
 827                         break;
 828                 }
 829 
 830                 /*
 831                  * resolve any aliases
 832                  */
 833                 if ((drv_name = ddi_major_to_name(maj)) == NULL) {
 834                         error = EINVAL;
 835                         break;
 836                 }
 837 
 838                 (void) strcpy(opp->oprom_array, drv_name);
 839                 opp->oprom_size = strlen(drv_name);
 840                 if (copyout(opp, (void *)arg,
 841                     sizeof (uint_t) + opp->oprom_size + 1) != 0)
 842                         error = EFAULT;
 843                 break;
 844         }
 845 
 846         case OPROMGETVERSION:
 847                 /*
 848                  * Get a string representing the running version of the
 849                  * prom. How to create such a string is platform dependent,
 850                  * so we just defer to a promif function. If no such
 851                  * association exists, the promif implementation
 852                  * may copy the string "unknown" into the given buffer,
 853                  * and return its length (incl. NULL terminator).
 854                  *
 855                  * We expect prom_version_name to return the actual
 856                  * length of the string, but copy at most userbufsize
 857                  * bytes into the given buffer, including NULL termination.
 858                  */
 859 
 860                 valsize = prom_version_name(opp->oprom_array, userbufsize);
 861                 if (valsize < 0) {
 862                         error = EINVAL;
 863                         break;
 864                 }
 865 
 866                 /*
 867                  * copyout only the part of the user buffer we need to.
 868                  */
 869                 if (copyout(opp, (void *)arg,
 870                     (size_t)(min((uint_t)valsize, userbufsize) +
 871                     sizeof (uint_t))) != 0)
 872                         error = EFAULT;
 873                 break;
 874 
 875 #if !defined(__i386) && !defined(__amd64)
 876         case OPROMGETFBNAME:
 877                 /*
 878                  * Return stdoutpath, if it's a frame buffer.
 879                  * Yes, we are comparing a possibly longer string against
 880                  * the size we're really going to copy, but so what?
 881                  */
 882                 if ((getzoneid() == GLOBAL_ZONEID) &&
 883                     (prom_stdout_is_framebuffer() != 0) &&
 884                     (userbufsize > strlen(prom_stdoutpath()))) {
 885                         prom_strip_options(prom_stdoutpath(),
 886                             opp->oprom_array);       /* strip options and copy */
 887                         valsize = opp->oprom_size = strlen(opp->oprom_array);
 888                         if (copyout(opp, (void *)arg,
 889                             valsize + 1 + sizeof (uint_t)) != 0)
 890                                 error = EFAULT;
 891                 } else
 892                         error = EINVAL;
 893                 break;
 894 
 895         /*
 896          * Convert a logical or physical device path to prom device path
 897          */
 898         case OPROMDEV2PROMNAME: {
 899                 char *prom_name;
 900 
 901                 /*
 902                  * The input argument, a pathname, is a NULL terminated string.
 903                  */
 904                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 905                     (size_t)userbufsize, MAXPATHLEN)) != 0) {
 906                         break;
 907                 }
 908 
 909                 prom_name = kmem_alloc(userbufsize, KM_SLEEP);
 910 
 911                 /*
 912                  * convert the devfs path to an equivalent prom path
 913                  */
 914                 error = i_devname_to_promname(opp->oprom_array, prom_name,
 915                     userbufsize);
 916 
 917                 if (error != 0) {
 918                         kmem_free(prom_name, userbufsize);
 919                         break;
 920                 }
 921 
 922                 for (valsize = 0; valsize < userbufsize; valsize++) {
 923                         opp->oprom_array[valsize] = prom_name[valsize];
 924 
 925                         if ((valsize > 0) && (prom_name[valsize] == '\0') &&
 926                             (prom_name[valsize-1] == '\0')) {
 927                                 break;
 928                         }
 929                 }
 930                 opp->oprom_size = valsize;
 931 
 932                 kmem_free(prom_name, userbufsize);
 933                 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
 934                         error = EFAULT;
 935 
 936                 break;
 937         }
 938 
 939         case OPROMSETOPT:
 940         case OPROMSETOPT2: {
 941                 int namebuflen;
 942                 int valbuflen;
 943 
 944                 if ((prom_is_openprom() == 0) ||
 945                     (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
 946                         error = EINVAL;
 947                         break;
 948                 }
 949 
 950                 /*
 951                  * The arguments are a property name and a value.
 952                  * Copy in the entire user buffer.
 953                  */
 954                 if (copyin(((caddr_t)arg + sizeof (uint_t)),
 955                     opp->oprom_array, userbufsize) != 0) {
 956                         error = EFAULT;
 957                         break;
 958                 }
 959 
 960                 /*
 961                  * The property name is the first string, value second
 962                  */
 963                 namebuflen = strlen(opp->oprom_array);
 964                 valbuf = opp->oprom_array + namebuflen + 1;
 965                 valbuflen = strlen(valbuf);
 966 
 967                 if (cmd == OPROMSETOPT) {
 968                         valsize = valbuflen + 1;  /* +1 for the '\0' */
 969                 } else {
 970                         if ((namebuflen + 1 + valbuflen + 1) > userbufsize) {
 971                                 error = EINVAL;
 972                                 break;
 973                         }
 974                         valsize = (opp->oprom_array + userbufsize) - valbuf;
 975                 }
 976 
 977                 /*
 978                  * 4010173: 'name' is not an option, but it is a property.
 979                  */
 980                 if (strcmp(opp->oprom_array, "name") == 0)
 981                         error = EINVAL;
 982                 else if (prom_setprop(node_id, opp->oprom_array,
 983                     valbuf, valsize) < 0)
 984                         error = EINVAL;
 985 
 986                 break;
 987         }
 988 
 989         case OPROMREADY64: {
 990                 struct openprom_opr64 *opr =
 991                     (struct openprom_opr64 *)opp->oprom_array;
 992                 int i;
 993                 pnode_t id;
 994 
 995                 if (userbufsize < sizeof (*opr)) {
 996                         error = EINVAL;
 997                         break;
 998                 }
 999 
1000                 valsize = userbufsize -
1001                     offsetof(struct openprom_opr64, message);
1002 
1003                 i = prom_version_check(opr->message, valsize, &id);
1004                 opr->return_code = i;
1005                 opr->nodeid = (int)id;
1006 
1007                 valsize = offsetof(struct openprom_opr64, message);
1008                 valsize += strlen(opr->message) + 1;
1009 
1010                 /*
1011                  * copyout only the part of the user buffer we need to.
1012                  */
1013                 if (copyout(opp, (void *)arg,
1014                     (size_t)(min((uint_t)valsize, userbufsize) +
1015                     sizeof (uint_t))) != 0)
1016                         error = EFAULT;
1017                 break;
1018 
1019         }       /* case OPROMREADY64 */
1020 
1021         case WANBOOT_SETKEY: {
1022                 struct wankeyio *wp;
1023                 int reslen;
1024                 int status;
1025                 int rv;
1026                 int i;
1027 
1028                 /*
1029                  * The argument is a struct wankeyio.  Validate it as best
1030                  * we can.
1031                  */
1032                 if (userbufsize != (sizeof (struct wankeyio))) {
1033                         error = EINVAL;
1034                         break;
1035                 }
1036                 if (copyin(((caddr_t)arg + sizeof (uint_t)),
1037                     opp->oprom_array, sizeof (struct wankeyio)) != 0) {
1038                         error = EFAULT;
1039                         break;
1040                 }
1041                 wp = (struct wankeyio *)opp->oprom_array;
1042 
1043                 /* check for key name and key size overflow */
1044                 for (i = 0; i < WANBOOT_MAXKEYNAMELEN; i++)
1045                         if (wp->wk_keyname[i] == '\0')
1046                                 break;
1047                 if ((i == WANBOOT_MAXKEYNAMELEN) ||
1048                     (wp->wk_keysize > WANBOOT_MAXKEYLEN)) {
1049                         error = EINVAL;
1050                         break;
1051                 }
1052 
1053                 rv = prom_set_security_key(wp->wk_keyname, wp->wk_u.key,
1054                     wp->wk_keysize, &reslen, &status);
1055                 if (rv)
1056                         error = EIO;
1057                 else
1058                         switch (status) {
1059                                 case 0:
1060                                         error = 0;
1061                                         break;
1062 
1063                                 case -2:        /* out of key storage space */
1064                                         error = ENOSPC;
1065                                         break;
1066 
1067                                 case -3:        /* key name or value too long */
1068                                         error = EINVAL;
1069                                         break;
1070 
1071                                 case -4:        /* can't delete:  no such key */
1072                                         error = ENOENT;
1073                                         break;
1074 
1075                                 case -1:        /* unspecified error */
1076                                 default:        /* this should not happen */
1077                                         error = EIO;
1078                                         break;
1079                         }
1080                 break;
1081         }       /* case WANBOOT_SETKEY */
1082 #endif  /* !__i386 && !__amd64 */
1083         }       /* switch (cmd) */
1084 
1085         kmem_free(opp, userbufsize + sizeof (uint_t) + 1);
1086         return (error);
1087 }
1088 
1089 /*ARGSUSED*/
1090 static int
1091 opromioctl(dev_t dev, int cmd, intptr_t arg, int mode,
1092         cred_t *credp, int *rvalp)
1093 {
1094         struct oprom_state *st;
1095         struct opromioctl_args arg_block;
1096 
1097         if (getminor(dev) >= MAX_OPENS)
1098                 return (ENXIO);
1099 
1100         st = &oprom_state[getminor(dev)];
1101         ASSERT(st->already_open);
1102         arg_block.st = st;
1103         arg_block.cmd = cmd;
1104         arg_block.arg = arg;
1105         arg_block.mode = mode;
1106         return (prom_tree_access(opromioctl_cb, &arg_block, &st->tree_gen));
1107 }
1108 
1109 /*
1110  * Copyin string and verify the actual string length is less than maxsize
1111  * specified by the caller.
1112  *
1113  * Currently, maxsize is either OBP_MAXPROPNAME for property names
1114  * or MAXPATHLEN for device path names. userbufsize is specified
1115  * by the userland caller.
1116  */
1117 static int
1118 oprom_copyinstr(intptr_t arg, char *buf, size_t bufsize, size_t maxsize)
1119 {
1120         int error;
1121         size_t actual_len;
1122 
1123         if ((error = copyinstr(((caddr_t)arg + sizeof (uint_t)),
1124             buf, bufsize, &actual_len)) != 0) {
1125                 return (error);
1126         }
1127         if ((actual_len == 0) || (actual_len > maxsize)) {
1128                 return (EINVAL);
1129         }
1130 
1131         return (0);
1132 }
1133 
1134 /*
1135  * Check pnode_t passed in from userland
1136  */
1137 static int
1138 oprom_checknodeid(pnode_t node_id, pnode_t current_id)
1139 {
1140         int depth;
1141         pnode_t id[OBP_STACKDEPTH];
1142 
1143         /*
1144          * optimized path
1145          */
1146         if (node_id == 0) {
1147                 return (1);
1148         }
1149         if (node_id == OBP_BADNODE) {
1150                 return (0);
1151         }
1152         if ((current_id != OBP_BADNODE) && ((node_id == current_id) ||
1153             (node_id == prom_nextnode(current_id)) ||
1154             (node_id == prom_childnode(current_id)))) {
1155                 return (1);
1156         }
1157 
1158         /*
1159          * long path: walk from root till we find node_id
1160          */
1161         depth = 1;
1162         id[0] = prom_nextnode((pnode_t)0);
1163 
1164         while (depth) {
1165                 if (id[depth - 1] == node_id)
1166                         return (1);     /* node_id found */
1167 
1168                 if (id[depth] = prom_childnode(id[depth - 1])) {
1169                         depth++;
1170                         continue;
1171                 }
1172 
1173                 while (depth &&
1174                     ((id[depth - 1] = prom_nextnode(id[depth - 1])) == 0))
1175                         depth--;
1176         }
1177         return (0);     /* node_id not found */
1178 }
1179 
1180 static int
1181 oprom_copytree(struct oprom_state *st, uint_t flag)
1182 {
1183         ASSERT(st->snapshot == NULL && st->size == 0);
1184         return (oprom_copynode(
1185             prom_nextnode(0), flag, &st->snapshot, &st->size));
1186 }
1187 
1188 static int
1189 oprom_snapshot(struct oprom_state *st, intptr_t arg)
1190 {
1191         uint_t flag;
1192 
1193         if (oprom_setstate(st, IOC_SNAP) == -1)
1194                 return (EBUSY);
1195 
1196         /* copyin flag and create snapshot */
1197         if ((copyin((void *)arg, &flag, sizeof (uint_t)) != 0) ||
1198             (oprom_copytree(st, flag) != 0)) {
1199                 (void) oprom_setstate(st, IOC_IDLE);
1200                 return (EFAULT);
1201         }
1202 
1203 
1204         /* copyout the size of the snapshot */
1205         flag = (uint_t)st->size;
1206         if (copyout(&flag, (void *)arg, sizeof (uint_t)) != 0) {
1207                 kmem_free(st->snapshot, st->size);
1208                 st->snapshot = NULL;
1209                 st->size = 0;
1210                 (void) oprom_setstate(st, IOC_IDLE);
1211                 return (EFAULT);
1212         }
1213 
1214         (void) oprom_setstate(st, IOC_DONE);
1215         return (0);
1216 }
1217 
1218 static int
1219 oprom_copyout(struct oprom_state *st, intptr_t arg)
1220 {
1221         int error = 0;
1222         uint_t size;
1223 
1224         if (oprom_setstate(st, IOC_COPY) == -1)
1225                 return (EBUSY);
1226 
1227         /* copyin size and copyout snapshot */
1228         if (copyin((void *)arg, &size, sizeof (uint_t)) != 0)
1229                 error = EFAULT;
1230         else if (size < st->size)
1231                 error = EINVAL;
1232         else if (copyout(st->snapshot, (void *)arg, st->size) != 0)
1233                 error = EFAULT;
1234 
1235         if (error) {
1236                 /*
1237                  * on error keep the snapshot until a successful
1238                  * copyout or when the driver is closed.
1239                  */
1240                 (void) oprom_setstate(st, IOC_DONE);
1241                 return (error);
1242         }
1243 
1244         kmem_free(st->snapshot, st->size);
1245         st->snapshot = NULL;
1246         st->size = 0;
1247         (void) oprom_setstate(st, IOC_IDLE);
1248         return (0);
1249 }
1250 
1251 /*
1252  * Copy all properties of nodeid into a single packed nvlist
1253  */
1254 static int
1255 oprom_copyprop(pnode_t nodeid, uint_t flag, nvlist_t *nvl)
1256 {
1257         int proplen;
1258         char *propname, *propval, *buf1, *buf2;
1259 
1260         ASSERT(nvl != NULL);
1261 
1262         /*
1263          * non verbose mode, get the "name" property only
1264          */
1265         if (flag == 0) {
1266                 proplen = prom_getproplen(nodeid, "name");
1267                 if (proplen <= 0) {
1268                         cmn_err(CE_WARN,
1269                             "failed to get the name of openprom node 0x%x",
1270                             nodeid);
1271                         (void) nvlist_add_string(nvl, "name", "");
1272                         return (0);
1273                 }
1274                 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1275                 (void) prom_getprop(nodeid, "name", propval);
1276                 (void) nvlist_add_string(nvl, "name", propval);
1277                 kmem_free(propval, proplen + 1);
1278                 return (0);
1279         }
1280 
1281         /*
1282          * Ask for first property by passing a NULL string
1283          */
1284         buf1 = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP);
1285         buf2 = kmem_zalloc(OBP_MAXPROPNAME, KM_SLEEP);
1286         buf1[0] = '\0';
1287         while (propname = (char *)prom_nextprop(nodeid, buf1, buf2)) {
1288                 if (strlen(propname) == 0)
1289                         break;          /* end of prop list */
1290                 (void) strcpy(buf1, propname);
1291 
1292                 proplen = prom_getproplen(nodeid, propname);
1293                 if (proplen == 0) {
1294                         /* boolean property */
1295                         (void) nvlist_add_boolean(nvl, propname);
1296                         continue;
1297                 }
1298                 /* add 1 for null termination in case of a string */
1299                 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1300                 (void) prom_getprop(nodeid, propname, propval);
1301                 (void) nvlist_add_byte_array(nvl, propname,
1302                     (uchar_t *)propval, proplen + 1);
1303                 kmem_free(propval, proplen + 1);
1304                 bzero(buf2, OBP_MAXPROPNAME);
1305         }
1306 
1307         kmem_free(buf1, OBP_MAXPROPNAME);
1308         kmem_free(buf2, OBP_MAXPROPNAME);
1309 
1310         return (0);
1311 }
1312 
1313 /*
1314  * Copy all children and descendents into a a packed nvlist
1315  */
1316 static int
1317 oprom_copychild(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1318 {
1319         nvlist_t *nvl;
1320         pnode_t child = prom_childnode(nodeid);
1321 
1322         if (child == 0)
1323                 return (0);
1324 
1325         (void) nvlist_alloc(&nvl, 0, KM_SLEEP);
1326         while (child != 0) {
1327                 char *nodebuf = NULL;
1328                 size_t nodesize = 0;
1329                 if (oprom_copynode(child, flag, &nodebuf, &nodesize)) {
1330                         nvlist_free(nvl);
1331                         cmn_err(CE_WARN, "failed to copy nodeid 0x%x", child);
1332                         return (-1);
1333                 }
1334                 (void) nvlist_add_byte_array(nvl, "node",
1335                     (uchar_t *)nodebuf, nodesize);
1336                 kmem_free(nodebuf, nodesize);
1337                 child = prom_nextnode(child);
1338         }
1339 
1340         (void) nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1341         nvlist_free(nvl);
1342         return (0);
1343 }
1344 
1345 /*
1346  * Copy a node into a packed nvlist
1347  */
1348 static int
1349 oprom_copynode(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1350 {
1351         int error = 0;
1352         nvlist_t *nvl;
1353         char *childlist = NULL;
1354         size_t childsize = 0;
1355 
1356         (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
1357         ASSERT(nvl != NULL);
1358 
1359         /* @nodeid -- @ is not a legal char in a 1275 property name */
1360         (void) nvlist_add_int32(nvl, "@nodeid", (int32_t)nodeid);
1361 
1362         /* properties */
1363         if (error = oprom_copyprop(nodeid, flag, nvl))
1364                 goto fail;
1365 
1366         /* children */
1367         error = oprom_copychild(nodeid, flag, &childlist, &childsize);
1368         if (error != 0)
1369                 goto fail;
1370         if (childlist != NULL) {
1371                 (void) nvlist_add_byte_array(nvl, "@child",
1372                     (uchar_t *)childlist, (uint_t)childsize);
1373                 kmem_free(childlist, childsize);
1374         }
1375 
1376         /* pack into contiguous buffer */
1377         error = nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1378 
1379 fail:
1380         nvlist_free(nvl);
1381         return (error);
1382 }
1383 
1384 /*
1385  * The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT.
1386  * This function encapsulates the state machine:
1387  *
1388  *      -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY ->
1389  *      |               SNAPSHOT                COPYOUT  |
1390  *      --------------------------------------------------
1391  *
1392  * Returns 0 on success and -1 on failure
1393  */
1394 static int
1395 oprom_setstate(struct oprom_state *st, int16_t new_state)
1396 {
1397         int ret = 0;
1398 
1399         mutex_enter(&oprom_lock);
1400         switch (new_state) {
1401         case IOC_IDLE:
1402         case IOC_DONE:
1403                 break;
1404         case IOC_SNAP:
1405                 if (st->ioc_state != IOC_IDLE)
1406                         ret = -1;
1407                 break;
1408         case IOC_COPY:
1409                 if (st->ioc_state != IOC_DONE)
1410                         ret = -1;
1411                 break;
1412         default:
1413                 ret = -1;
1414         }
1415 
1416         if (ret == 0)
1417                 st->ioc_state = new_state;
1418         else
1419                 cmn_err(CE_NOTE, "incorrect state transition from %d to %d",
1420                     st->ioc_state, new_state);
1421         mutex_exit(&oprom_lock);
1422         return (ret);
1423 }