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