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  * Copyright 2019 Peter Tribble.
  25  */
  26 
  27 /*
  28  * Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI";
  29  *
  30  * Porting notes:
  31  *
  32  * OPROMU2P unsupported after SunOS 4.x.
  33  *
  34  * Only one of these devices per system is allowed.
  35  */
  36 
  37 /*
  38  * Openprom eeprom options/devinfo driver.
  39  */
  40 
  41 #include <sys/types.h>
  42 #include <sys/errno.h>
  43 #include <sys/file.h>
  44 #include <sys/cmn_err.h>
  45 #include <sys/kmem.h>
  46 #include <sys/openpromio.h>
  47 #include <sys/conf.h>
  48 #include <sys/stat.h>
  49 #include <sys/modctl.h>
  50 #include <sys/debug.h>
  51 #include <sys/autoconf.h>
  52 #include <sys/ddi.h>
  53 #include <sys/sunddi.h>
  54 #include <sys/promif.h>
  55 #include <sys/sysmacros.h>        /* offsetof */
  56 #include <sys/nvpair.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, 0) == 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 #endif  /* !__i386 && !__amd64 */
 495                 if ((mode & FREAD) == 0) {
 496                         return (EPERM);
 497                 }
 498                 break;
 499 
 500         default:
 501                 return (EINVAL);
 502         }
 503 
 504         /*
 505          * Deal with SNAPSHOT and COPYOUT ioctls first
 506          */
 507         switch (cmd) {
 508         case OPROMCOPYOUT:
 509                 return (oprom_copyout(st, arg));
 510 
 511         case OPROMSNAPSHOT:
 512                 return (oprom_snapshot(st, arg));
 513         }
 514 
 515         /*
 516          * Copy in user argument length and allocation memory
 517          *
 518          * NB do not copyin the entire buffer we may not need
 519          *      to. userbufsize can be as big as 32 K.
 520          */
 521         if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
 522                 return (EFAULT);
 523 
 524         if (userbufsize == 0 || userbufsize > OPROMMAXPARAM)
 525                 return (EINVAL);
 526 
 527         opp = (struct openpromio *)kmem_zalloc(
 528             userbufsize + sizeof (uint_t) + 1, KM_SLEEP);
 529 
 530         /*
 531          * Execute command
 532          */
 533         switch (cmd) {
 534 
 535         case OPROMGETOPT:
 536         case OPROMGETPROP:
 537         case OPROMGETPROPLEN:
 538 
 539                 if ((prom_is_openprom() == 0) ||
 540                     (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
 541                         error = EINVAL;
 542                         break;
 543                 }
 544 
 545                 /*
 546                  * The argument, a NULL terminated string, is a prop name.
 547                  */
 548                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 549                     (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
 550                         break;
 551                 }
 552                 (void) strcpy(propname, opp->oprom_array);
 553                 valsize = prom_getproplen(node_id, propname);
 554 
 555                 /*
 556                  * 4010173: 'name' is a property, but not an option.
 557                  */
 558                 if ((cmd == OPROMGETOPT) && (strcmp("name", propname) == 0))
 559                         valsize = -1;
 560 
 561                 if (cmd == OPROMGETPROPLEN)  {
 562                         int proplen = valsize;
 563 
 564                         if (userbufsize < sizeof (int)) {
 565                                 error = EINVAL;
 566                                 break;
 567                         }
 568                         opp->oprom_size = valsize = sizeof (int);
 569                         bcopy(&proplen, opp->oprom_array, valsize);
 570                 } else if (valsize > 0 && valsize <= userbufsize) {
 571                         bzero(opp->oprom_array, valsize + 1);
 572                         (void) prom_getprop(node_id, propname,
 573                             opp->oprom_array);
 574                         opp->oprom_size = valsize;
 575                         if (valsize < userbufsize)
 576                                 ++valsize;      /* Forces NULL termination */
 577                                                 /* If space permits */
 578                 } else {
 579                         /*
 580                          * XXX: There is no error code if the buf is too small.
 581                          * which is consistent with the current behavior.
 582                          *
 583                          * NB: This clause also handles the non-error
 584                          * zero length (boolean) property value case.
 585                          */
 586                         opp->oprom_size = 0;
 587                         (void) strcpy(opp->oprom_array, "");
 588                         valsize = 1;
 589                 }
 590                 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
 591                         error = EFAULT;
 592                 break;
 593 
 594         case OPROMNXTOPT:
 595         case OPROMNXTPROP:
 596                 if ((prom_is_openprom() == 0) ||
 597                     (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
 598                         error = EINVAL;
 599                         break;
 600                 }
 601 
 602                 /*
 603                  * The argument, a NULL terminated string, is a prop name.
 604                  */
 605                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 606                     (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
 607                         break;
 608                 }
 609                 valbuf = (char *)prom_nextprop(node_id, opp->oprom_array,
 610                     propname);
 611                 valsize = strlen(valbuf);
 612 
 613                 /*
 614                  * 4010173: 'name' is a property, but it's not an option.
 615                  */
 616                 if ((cmd == OPROMNXTOPT) && valsize &&
 617                     (strcmp(valbuf, "name") == 0)) {
 618                         valbuf = (char *)prom_nextprop(node_id, "name",
 619                             propname);
 620                         valsize = strlen(valbuf);
 621                 }
 622 
 623                 if (valsize == 0) {
 624                         opp->oprom_size = 0;
 625                 } else if (++valsize <= userbufsize) {
 626                         opp->oprom_size = valsize;
 627                         bzero((caddr_t)opp->oprom_array, (size_t)valsize);
 628                         bcopy((caddr_t)valbuf, (caddr_t)opp->oprom_array,
 629                             (size_t)valsize);
 630                 }
 631 
 632                 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
 633                         error = EFAULT;
 634                 break;
 635 
 636         case OPROMNEXT:
 637         case OPROMCHILD:
 638         case OPROMSETNODEID:
 639 
 640                 if (prom_is_openprom() == 0 ||
 641                     userbufsize < sizeof (pnode_t)) {
 642                         error = EINVAL;
 643                         break;
 644                 }
 645 
 646                 /*
 647                  * The argument is a phandle. (aka pnode_t)
 648                  */
 649                 if (copyin(((caddr_t)arg + sizeof (uint_t)),
 650                     opp->oprom_array, sizeof (pnode_t)) != 0) {
 651                         error = EFAULT;
 652                         break;
 653                 }
 654 
 655                 /*
 656                  * If pnode_t from userland is garbage, we
 657                  * could confuse the PROM.
 658                  */
 659                 node_id = *(pnode_t *)opp->oprom_array;
 660                 if (oprom_checknodeid(node_id, st->current_id) == 0) {
 661                         cmn_err(CE_NOTE, "!nodeid 0x%x not found",
 662                             (int)node_id);
 663                         error = EINVAL;
 664                         break;
 665                 }
 666 
 667                 if (cmd == OPROMNEXT)
 668                         st->current_id = prom_nextnode(node_id);
 669                 else if (cmd == OPROMCHILD)
 670                         st->current_id = prom_childnode(node_id);
 671                 else {
 672                         /* OPROMSETNODEID */
 673                         st->current_id = node_id;
 674                         break;
 675                 }
 676 
 677                 opp->oprom_size = sizeof (pnode_t);
 678                 *(pnode_t *)opp->oprom_array = st->current_id;
 679 
 680                 if (copyout(opp, (void *)arg,
 681                     sizeof (pnode_t) + sizeof (uint_t)) != 0)
 682                         error = EFAULT;
 683                 break;
 684 
 685         case OPROMGETCONS:
 686                 /*
 687                  * Is openboot supported on this machine?
 688                  * This ioctl used to return the console device,
 689                  * information; this is now done via modctl()
 690                  * in libdevinfo.
 691                  */
 692                 opp->oprom_size = sizeof (char);
 693 
 694                 opp->oprom_array[0] |= prom_is_openprom() ?
 695                     OPROMCONS_OPENPROM : 0;
 696 
 697                 /*
 698                  * The rest of the info is needed by Install to
 699                  * decide if graphics should be started.
 700                  */
 701                 if ((getzoneid() == GLOBAL_ZONEID) &&
 702                     plat_stdin_is_keyboard()) {
 703                         opp->oprom_array[0] |= OPROMCONS_STDIN_IS_KBD;
 704                 }
 705 
 706                 if ((getzoneid() == GLOBAL_ZONEID) &&
 707                     plat_stdout_is_framebuffer()) {
 708                         opp->oprom_array[0] |= OPROMCONS_STDOUT_IS_FB;
 709                 }
 710 
 711                 if (copyout(opp, (void *)arg,
 712                     sizeof (char) + sizeof (uint_t)) != 0)
 713                         error = EFAULT;
 714                 break;
 715 
 716         case OPROMGETBOOTARGS: {
 717                 extern char kern_bootargs[];
 718 
 719                 valsize = strlen(kern_bootargs) + 1;
 720                 if (valsize > userbufsize) {
 721                         error = EINVAL;
 722                         break;
 723                 }
 724                 (void) strcpy(opp->oprom_array, kern_bootargs);
 725                 opp->oprom_size = valsize - 1;
 726 
 727                 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
 728                         error = EFAULT;
 729                 break;
 730         }
 731 
 732         case OPROMGETBOOTPATH: {
 733 #if defined(__sparc) && defined(_OBP)
 734 
 735                 char bpath[OBP_MAXPATHLEN];
 736                 if (get_bootpath_prop(bpath) != 0) {
 737                         error = EINVAL;
 738                         break;
 739                 }
 740                 valsize = strlen(bpath) + 1;
 741                 if (valsize > userbufsize) {
 742                         error = EINVAL;
 743                         break;
 744                 }
 745                 (void) strcpy(opp->oprom_array, bpath);
 746 
 747 #elif defined(__i386) || defined(__amd64)
 748 
 749                 extern char saved_cmdline[];
 750                 valsize = strlen(saved_cmdline) + 1;
 751                 if (valsize > userbufsize) {
 752                         error = EINVAL;
 753                         break;
 754                 }
 755                 (void) strcpy(opp->oprom_array, saved_cmdline);
 756 #endif
 757                 opp->oprom_size = valsize - 1;
 758                 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
 759                         error = EFAULT;
 760                 break;
 761         }
 762 
 763         /*
 764          * convert a prom device path to an equivalent devfs path
 765          */
 766         case OPROMPROM2DEVNAME: {
 767                 char *dev_name;
 768 
 769                 /*
 770                  * The input argument, a pathname, is a NULL terminated string.
 771                  */
 772                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 773                     (size_t)userbufsize, MAXPATHLEN)) != 0) {
 774                         break;
 775                 }
 776 
 777                 dev_name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 778 
 779                 error = i_promname_to_devname(opp->oprom_array, dev_name);
 780                 if (error != 0) {
 781                         kmem_free(dev_name, MAXPATHLEN);
 782                         break;
 783                 }
 784                 valsize = opp->oprom_size = strlen(dev_name);
 785                 if (++valsize > userbufsize) {
 786                         kmem_free(dev_name, MAXPATHLEN);
 787                         error = EINVAL;
 788                         break;
 789                 }
 790                 (void) strcpy(opp->oprom_array, dev_name);
 791                 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
 792                         error = EFAULT;
 793 
 794                 kmem_free(dev_name, MAXPATHLEN);
 795                 break;
 796         }
 797 
 798         /*
 799          * Convert a prom device path name to a driver name
 800          */
 801         case OPROMPATH2DRV: {
 802                 char *drv_name;
 803                 major_t maj;
 804 
 805                 /*
 806                  * The input argument, a pathname, is a NULL terminated string.
 807                  */
 808                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 809                     (size_t)userbufsize, MAXPATHLEN)) != 0) {
 810                         break;
 811                 }
 812 
 813                 /*
 814                  * convert path to a driver binding name
 815                  */
 816                 maj = path_to_major((char *)opp->oprom_array);
 817                 if (maj == DDI_MAJOR_T_NONE) {
 818                         error = EINVAL;
 819                         break;
 820                 }
 821 
 822                 /*
 823                  * resolve any aliases
 824                  */
 825                 if ((drv_name = ddi_major_to_name(maj)) == NULL) {
 826                         error = EINVAL;
 827                         break;
 828                 }
 829 
 830                 (void) strcpy(opp->oprom_array, drv_name);
 831                 opp->oprom_size = strlen(drv_name);
 832                 if (copyout(opp, (void *)arg,
 833                     sizeof (uint_t) + opp->oprom_size + 1) != 0)
 834                         error = EFAULT;
 835                 break;
 836         }
 837 
 838         case OPROMGETVERSION:
 839                 /*
 840                  * Get a string representing the running version of the
 841                  * prom. How to create such a string is platform dependent,
 842                  * so we just defer to a promif function. If no such
 843                  * association exists, the promif implementation
 844                  * may copy the string "unknown" into the given buffer,
 845                  * and return its length (incl. NULL terminator).
 846                  *
 847                  * We expect prom_version_name to return the actual
 848                  * length of the string, but copy at most userbufsize
 849                  * bytes into the given buffer, including NULL termination.
 850                  */
 851 
 852                 valsize = prom_version_name(opp->oprom_array, userbufsize);
 853                 if (valsize < 0) {
 854                         error = EINVAL;
 855                         break;
 856                 }
 857 
 858                 /*
 859                  * copyout only the part of the user buffer we need to.
 860                  */
 861                 if (copyout(opp, (void *)arg,
 862                     (size_t)(min((uint_t)valsize, userbufsize) +
 863                     sizeof (uint_t))) != 0)
 864                         error = EFAULT;
 865                 break;
 866 
 867 #if !defined(__i386) && !defined(__amd64)
 868         case OPROMGETFBNAME:
 869                 /*
 870                  * Return stdoutpath, if it's a frame buffer.
 871                  * Yes, we are comparing a possibly longer string against
 872                  * the size we're really going to copy, but so what?
 873                  */
 874                 if ((getzoneid() == GLOBAL_ZONEID) &&
 875                     (prom_stdout_is_framebuffer() != 0) &&
 876                     (userbufsize > strlen(prom_stdoutpath()))) {
 877                         prom_strip_options(prom_stdoutpath(),
 878                             opp->oprom_array);       /* strip options and copy */
 879                         valsize = opp->oprom_size = strlen(opp->oprom_array);
 880                         if (copyout(opp, (void *)arg,
 881                             valsize + 1 + sizeof (uint_t)) != 0)
 882                                 error = EFAULT;
 883                 } else
 884                         error = EINVAL;
 885                 break;
 886 
 887         /*
 888          * Convert a logical or physical device path to prom device path
 889          */
 890         case OPROMDEV2PROMNAME: {
 891                 char *prom_name;
 892 
 893                 /*
 894                  * The input argument, a pathname, is a NULL terminated string.
 895                  */
 896                 if ((error = oprom_copyinstr(arg, opp->oprom_array,
 897                     (size_t)userbufsize, MAXPATHLEN)) != 0) {
 898                         break;
 899                 }
 900 
 901                 prom_name = kmem_alloc(userbufsize, KM_SLEEP);
 902 
 903                 /*
 904                  * convert the devfs path to an equivalent prom path
 905                  */
 906                 error = i_devname_to_promname(opp->oprom_array, prom_name,
 907                     userbufsize);
 908 
 909                 if (error != 0) {
 910                         kmem_free(prom_name, userbufsize);
 911                         break;
 912                 }
 913 
 914                 for (valsize = 0; valsize < userbufsize; valsize++) {
 915                         opp->oprom_array[valsize] = prom_name[valsize];
 916 
 917                         if ((valsize > 0) && (prom_name[valsize] == '\0') &&
 918                             (prom_name[valsize-1] == '\0')) {
 919                                 break;
 920                         }
 921                 }
 922                 opp->oprom_size = valsize;
 923 
 924                 kmem_free(prom_name, userbufsize);
 925                 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
 926                         error = EFAULT;
 927 
 928                 break;
 929         }
 930 
 931         case OPROMSETOPT:
 932         case OPROMSETOPT2: {
 933                 int namebuflen;
 934                 int valbuflen;
 935 
 936                 if ((prom_is_openprom() == 0) ||
 937                     (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
 938                         error = EINVAL;
 939                         break;
 940                 }
 941 
 942                 /*
 943                  * The arguments are a property name and a value.
 944                  * Copy in the entire user buffer.
 945                  */
 946                 if (copyin(((caddr_t)arg + sizeof (uint_t)),
 947                     opp->oprom_array, userbufsize) != 0) {
 948                         error = EFAULT;
 949                         break;
 950                 }
 951 
 952                 /*
 953                  * The property name is the first string, value second
 954                  */
 955                 namebuflen = strlen(opp->oprom_array);
 956                 valbuf = opp->oprom_array + namebuflen + 1;
 957                 valbuflen = strlen(valbuf);
 958 
 959                 if (cmd == OPROMSETOPT) {
 960                         valsize = valbuflen + 1;  /* +1 for the '\0' */
 961                 } else {
 962                         if ((namebuflen + 1 + valbuflen + 1) > userbufsize) {
 963                                 error = EINVAL;
 964                                 break;
 965                         }
 966                         valsize = (opp->oprom_array + userbufsize) - valbuf;
 967                 }
 968 
 969                 /*
 970                  * 4010173: 'name' is not an option, but it is a property.
 971                  */
 972                 if (strcmp(opp->oprom_array, "name") == 0)
 973                         error = EINVAL;
 974                 else if (prom_setprop(node_id, opp->oprom_array,
 975                     valbuf, valsize) < 0)
 976                         error = EINVAL;
 977 
 978                 break;
 979         }
 980 #endif  /* !__i386 && !__amd64 */
 981         }       /* switch (cmd) */
 982 
 983         kmem_free(opp, userbufsize + sizeof (uint_t) + 1);
 984         return (error);
 985 }
 986 
 987 /*ARGSUSED*/
 988 static int
 989 opromioctl(dev_t dev, int cmd, intptr_t arg, int mode,
 990     cred_t *credp, int *rvalp)
 991 {
 992         struct oprom_state *st;
 993         struct opromioctl_args arg_block;
 994 
 995         if (getminor(dev) >= MAX_OPENS)
 996                 return (ENXIO);
 997 
 998         st = &oprom_state[getminor(dev)];
 999         ASSERT(st->already_open);
1000         arg_block.st = st;
1001         arg_block.cmd = cmd;
1002         arg_block.arg = arg;
1003         arg_block.mode = mode;
1004         return (prom_tree_access(opromioctl_cb, &arg_block, &st->tree_gen));
1005 }
1006 
1007 /*
1008  * Copyin string and verify the actual string length is less than maxsize
1009  * specified by the caller.
1010  *
1011  * Currently, maxsize is either OBP_MAXPROPNAME for property names
1012  * or MAXPATHLEN for device path names. userbufsize is specified
1013  * by the userland caller.
1014  */
1015 static int
1016 oprom_copyinstr(intptr_t arg, char *buf, size_t bufsize, size_t maxsize)
1017 {
1018         int error;
1019         size_t actual_len;
1020 
1021         if ((error = copyinstr(((caddr_t)arg + sizeof (uint_t)),
1022             buf, bufsize, &actual_len)) != 0) {
1023                 return (error);
1024         }
1025         if ((actual_len == 0) || (actual_len > maxsize)) {
1026                 return (EINVAL);
1027         }
1028 
1029         return (0);
1030 }
1031 
1032 /*
1033  * Check pnode_t passed in from userland
1034  */
1035 static int
1036 oprom_checknodeid(pnode_t node_id, pnode_t current_id)
1037 {
1038         int depth;
1039         pnode_t id[OBP_STACKDEPTH];
1040 
1041         /*
1042          * optimized path
1043          */
1044         if (node_id == 0) {
1045                 return (1);
1046         }
1047         if (node_id == OBP_BADNODE) {
1048                 return (0);
1049         }
1050         if ((current_id != OBP_BADNODE) && ((node_id == current_id) ||
1051             (node_id == prom_nextnode(current_id)) ||
1052             (node_id == prom_childnode(current_id)))) {
1053                 return (1);
1054         }
1055 
1056         /*
1057          * long path: walk from root till we find node_id
1058          */
1059         depth = 1;
1060         id[0] = prom_nextnode((pnode_t)0);
1061 
1062         while (depth) {
1063                 if (id[depth - 1] == node_id)
1064                         return (1);     /* node_id found */
1065 
1066                 if (id[depth] = prom_childnode(id[depth - 1])) {
1067                         depth++;
1068                         continue;
1069                 }
1070 
1071                 while (depth &&
1072                     ((id[depth - 1] = prom_nextnode(id[depth - 1])) == 0))
1073                         depth--;
1074         }
1075         return (0);     /* node_id not found */
1076 }
1077 
1078 static int
1079 oprom_copytree(struct oprom_state *st, uint_t flag)
1080 {
1081         ASSERT(st->snapshot == NULL && st->size == 0);
1082         return (oprom_copynode(
1083             prom_nextnode(0), flag, &st->snapshot, &st->size));
1084 }
1085 
1086 static int
1087 oprom_snapshot(struct oprom_state *st, intptr_t arg)
1088 {
1089         uint_t flag;
1090 
1091         if (oprom_setstate(st, IOC_SNAP) == -1)
1092                 return (EBUSY);
1093 
1094         /* copyin flag and create snapshot */
1095         if ((copyin((void *)arg, &flag, sizeof (uint_t)) != 0) ||
1096             (oprom_copytree(st, flag) != 0)) {
1097                 (void) oprom_setstate(st, IOC_IDLE);
1098                 return (EFAULT);
1099         }
1100 
1101 
1102         /* copyout the size of the snapshot */
1103         flag = (uint_t)st->size;
1104         if (copyout(&flag, (void *)arg, sizeof (uint_t)) != 0) {
1105                 kmem_free(st->snapshot, st->size);
1106                 st->snapshot = NULL;
1107                 st->size = 0;
1108                 (void) oprom_setstate(st, IOC_IDLE);
1109                 return (EFAULT);
1110         }
1111 
1112         (void) oprom_setstate(st, IOC_DONE);
1113         return (0);
1114 }
1115 
1116 static int
1117 oprom_copyout(struct oprom_state *st, intptr_t arg)
1118 {
1119         int error = 0;
1120         uint_t size;
1121 
1122         if (oprom_setstate(st, IOC_COPY) == -1)
1123                 return (EBUSY);
1124 
1125         /* copyin size and copyout snapshot */
1126         if (copyin((void *)arg, &size, sizeof (uint_t)) != 0)
1127                 error = EFAULT;
1128         else if (size < st->size)
1129                 error = EINVAL;
1130         else if (copyout(st->snapshot, (void *)arg, st->size) != 0)
1131                 error = EFAULT;
1132 
1133         if (error) {
1134                 /*
1135                  * on error keep the snapshot until a successful
1136                  * copyout or when the driver is closed.
1137                  */
1138                 (void) oprom_setstate(st, IOC_DONE);
1139                 return (error);
1140         }
1141 
1142         kmem_free(st->snapshot, st->size);
1143         st->snapshot = NULL;
1144         st->size = 0;
1145         (void) oprom_setstate(st, IOC_IDLE);
1146         return (0);
1147 }
1148 
1149 /*
1150  * Copy all properties of nodeid into a single packed nvlist
1151  */
1152 static int
1153 oprom_copyprop(pnode_t nodeid, uint_t flag, nvlist_t *nvl)
1154 {
1155         int proplen;
1156         char *propname, *propval, *buf1, *buf2;
1157 
1158         ASSERT(nvl != NULL);
1159 
1160         /*
1161          * non verbose mode, get the "name" property only
1162          */
1163         if (flag == 0) {
1164                 proplen = prom_getproplen(nodeid, "name");
1165                 if (proplen <= 0) {
1166                         cmn_err(CE_WARN,
1167                             "failed to get the name of openprom node 0x%x",
1168                             nodeid);
1169                         (void) nvlist_add_string(nvl, "name", "");
1170                         return (0);
1171                 }
1172                 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1173                 (void) prom_getprop(nodeid, "name", propval);
1174                 (void) nvlist_add_string(nvl, "name", propval);
1175                 kmem_free(propval, proplen + 1);
1176                 return (0);
1177         }
1178 
1179         /*
1180          * Ask for first property by passing a NULL string
1181          */
1182         buf1 = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP);
1183         buf2 = kmem_zalloc(OBP_MAXPROPNAME, KM_SLEEP);
1184         buf1[0] = '\0';
1185         while (propname = (char *)prom_nextprop(nodeid, buf1, buf2)) {
1186                 if (strlen(propname) == 0)
1187                         break;          /* end of prop list */
1188                 (void) strcpy(buf1, propname);
1189 
1190                 proplen = prom_getproplen(nodeid, propname);
1191                 if (proplen == 0) {
1192                         /* boolean property */
1193                         (void) nvlist_add_boolean(nvl, propname);
1194                         continue;
1195                 }
1196                 /* add 1 for null termination in case of a string */
1197                 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1198                 (void) prom_getprop(nodeid, propname, propval);
1199                 (void) nvlist_add_byte_array(nvl, propname,
1200                     (uchar_t *)propval, proplen + 1);
1201                 kmem_free(propval, proplen + 1);
1202                 bzero(buf2, OBP_MAXPROPNAME);
1203         }
1204 
1205         kmem_free(buf1, OBP_MAXPROPNAME);
1206         kmem_free(buf2, OBP_MAXPROPNAME);
1207 
1208         return (0);
1209 }
1210 
1211 /*
1212  * Copy all children and descendents into a a packed nvlist
1213  */
1214 static int
1215 oprom_copychild(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1216 {
1217         nvlist_t *nvl;
1218         pnode_t child = prom_childnode(nodeid);
1219 
1220         if (child == 0)
1221                 return (0);
1222 
1223         (void) nvlist_alloc(&nvl, 0, KM_SLEEP);
1224         while (child != 0) {
1225                 char *nodebuf = NULL;
1226                 size_t nodesize = 0;
1227                 if (oprom_copynode(child, flag, &nodebuf, &nodesize)) {
1228                         nvlist_free(nvl);
1229                         cmn_err(CE_WARN, "failed to copy nodeid 0x%x", child);
1230                         return (-1);
1231                 }
1232                 (void) nvlist_add_byte_array(nvl, "node",
1233                     (uchar_t *)nodebuf, nodesize);
1234                 kmem_free(nodebuf, nodesize);
1235                 child = prom_nextnode(child);
1236         }
1237 
1238         (void) nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1239         nvlist_free(nvl);
1240         return (0);
1241 }
1242 
1243 /*
1244  * Copy a node into a packed nvlist
1245  */
1246 static int
1247 oprom_copynode(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1248 {
1249         int error = 0;
1250         nvlist_t *nvl;
1251         char *childlist = NULL;
1252         size_t childsize = 0;
1253 
1254         (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
1255         ASSERT(nvl != NULL);
1256 
1257         /* @nodeid -- @ is not a legal char in a 1275 property name */
1258         (void) nvlist_add_int32(nvl, "@nodeid", (int32_t)nodeid);
1259 
1260         /* properties */
1261         if (error = oprom_copyprop(nodeid, flag, nvl))
1262                 goto fail;
1263 
1264         /* children */
1265         error = oprom_copychild(nodeid, flag, &childlist, &childsize);
1266         if (error != 0)
1267                 goto fail;
1268         if (childlist != NULL) {
1269                 (void) nvlist_add_byte_array(nvl, "@child",
1270                     (uchar_t *)childlist, (uint_t)childsize);
1271                 kmem_free(childlist, childsize);
1272         }
1273 
1274         /* pack into contiguous buffer */
1275         error = nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1276 
1277 fail:
1278         nvlist_free(nvl);
1279         return (error);
1280 }
1281 
1282 /*
1283  * The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT.
1284  * This function encapsulates the state machine:
1285  *
1286  *      -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY ->
1287  *      |               SNAPSHOT                COPYOUT  |
1288  *      --------------------------------------------------
1289  *
1290  * Returns 0 on success and -1 on failure
1291  */
1292 static int
1293 oprom_setstate(struct oprom_state *st, int16_t new_state)
1294 {
1295         int ret = 0;
1296 
1297         mutex_enter(&oprom_lock);
1298         switch (new_state) {
1299         case IOC_IDLE:
1300         case IOC_DONE:
1301                 break;
1302         case IOC_SNAP:
1303                 if (st->ioc_state != IOC_IDLE)
1304                         ret = -1;
1305                 break;
1306         case IOC_COPY:
1307                 if (st->ioc_state != IOC_DONE)
1308                         ret = -1;
1309                 break;
1310         default:
1311                 ret = -1;
1312         }
1313 
1314         if (ret == 0)
1315                 st->ioc_state = new_state;
1316         else
1317                 cmn_err(CE_NOTE, "incorrect state transition from %d to %d",
1318                     st->ioc_state, new_state);
1319         mutex_exit(&oprom_lock);
1320         return (ret);
1321 }