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/zone.h>
  57 #include <sys/consplat.h>
  58 #include <sys/bootconf.h>
  59 #include <sys/systm.h>
  60 #include <sys/bootprops.h>
  61 
  62 #define MAX_OPENS       32      /* Up to this many simultaneous opens */
  63 
  64 #define IOC_IDLE        0       /* snapshot ioctl states */
  65 #define IOC_SNAP        1       /* snapshot in progress */
  66 #define IOC_DONE        2       /* snapshot done, but not copied out */
  67 #define IOC_COPY        3       /* copyout in progress */
  68 
  69 /*
  70  * XXX  Make this dynamic.. or (better still) make the interface stateless
  71  */
  72 static struct oprom_state {
  73         pnode_t current_id;     /* node we're fetching props from */
  74         int16_t already_open;   /* if true, this instance is 'active' */
  75         int16_t ioc_state;      /* snapshot ioctl state */
  76         char    *snapshot;      /* snapshot of all prom nodes */
  77         size_t  size;           /* size of snapshot */
  78         prom_generation_cookie_t tree_gen;
  79 } oprom_state[MAX_OPENS];
  80 
  81 static kmutex_t oprom_lock;     /* serialize instance assignment */
  82 
  83 static int opromopen(dev_t *, int, int, cred_t *);
  84 static int opromioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  85 static int opromclose(dev_t, int, int, cred_t *);
  86 
  87 static int opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
  88                 void **result);
  89 static int opattach(dev_info_t *, ddi_attach_cmd_t cmd);
  90 static int opdetach(dev_info_t *, ddi_detach_cmd_t cmd);
  91 
  92 /* help functions */
  93 static int oprom_checknodeid(pnode_t, pnode_t);
  94 static int oprom_copyinstr(intptr_t, char *, size_t, size_t);
  95 static int oprom_copynode(pnode_t, uint_t, char **, size_t *);
  96 static int oprom_snapshot(struct oprom_state *, intptr_t);
  97 static int oprom_copyout(struct oprom_state *, intptr_t);
  98 static int oprom_setstate(struct oprom_state *, int16_t);
  99 
 100 static struct cb_ops openeepr_cb_ops = {
 101         opromopen,              /* open */
 102         opromclose,             /* close */
 103         nodev,                  /* strategy */
 104         nodev,                  /* print */
 105         nodev,                  /* dump */
 106         nodev,                  /* read */
 107         nodev,                  /* write */
 108         opromioctl,             /* ioctl */
 109         nodev,                  /* devmap */
 110         nodev,                  /* mmap */
 111         nodev,                  /* segmap */
 112         nochpoll,               /* poll */
 113         ddi_prop_op,            /* prop_op */
 114         NULL,                   /* streamtab  */
 115         D_NEW | D_MP            /* Driver compatibility flag */
 116 };
 117 
 118 static struct dev_ops openeepr_ops = {
 119         DEVO_REV,               /* devo_rev, */
 120         0,                      /* refcnt  */
 121         opinfo,                 /* info */
 122         nulldev,                /* identify */
 123         nulldev,                /* probe */
 124         opattach,               /* attach */
 125         opdetach,               /* detach */
 126         nodev,                  /* reset */
 127         &openeepr_cb_ops,   /* driver operations */
 128         NULL,                   /* bus operations */
 129         NULL,                   /* power */
 130         ddi_quiesce_not_needed,         /* quiesce */
 131 };
 132 
 133 /*
 134  * Module linkage information for the kernel.
 135  */
 136 static struct modldrv modldrv = {
 137         &mod_driverops,
 138         "OPENPROM/NVRAM Driver",
 139         &openeepr_ops
 140 };
 141 
 142 static struct modlinkage modlinkage = {
 143         MODREV_1,
 144         &modldrv,
 145         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, 0) == 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         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 
 981         case OPROMREADY64: {
 982                 struct openprom_opr64 *opr =
 983                     (struct openprom_opr64 *)opp->oprom_array;
 984                 int i;
 985                 pnode_t id;
 986 
 987                 if (userbufsize < sizeof (*opr)) {
 988                         error = EINVAL;
 989                         break;
 990                 }
 991 
 992                 valsize = userbufsize -
 993                     offsetof(struct openprom_opr64, message);
 994 
 995                 i = prom_version_check(opr->message, valsize, &id);
 996                 opr->return_code = i;
 997                 opr->nodeid = (int)id;
 998 
 999                 valsize = offsetof(struct openprom_opr64, message);
1000                 valsize += strlen(opr->message) + 1;
1001 
1002                 /*
1003                  * copyout only the part of the user buffer we need to.
1004                  */
1005                 if (copyout(opp, (void *)arg,
1006                     (size_t)(min((uint_t)valsize, userbufsize) +
1007                     sizeof (uint_t))) != 0)
1008                         error = EFAULT;
1009                 break;
1010 
1011         }       /* case OPROMREADY64 */
1012 #endif  /* !__i386 && !__amd64 */
1013         }       /* switch (cmd) */
1014 
1015         kmem_free(opp, userbufsize + sizeof (uint_t) + 1);
1016         return (error);
1017 }
1018 
1019 /*ARGSUSED*/
1020 static int
1021 opromioctl(dev_t dev, int cmd, intptr_t arg, int mode,
1022     cred_t *credp, int *rvalp)
1023 {
1024         struct oprom_state *st;
1025         struct opromioctl_args arg_block;
1026 
1027         if (getminor(dev) >= MAX_OPENS)
1028                 return (ENXIO);
1029 
1030         st = &oprom_state[getminor(dev)];
1031         ASSERT(st->already_open);
1032         arg_block.st = st;
1033         arg_block.cmd = cmd;
1034         arg_block.arg = arg;
1035         arg_block.mode = mode;
1036         return (prom_tree_access(opromioctl_cb, &arg_block, &st->tree_gen));
1037 }
1038 
1039 /*
1040  * Copyin string and verify the actual string length is less than maxsize
1041  * specified by the caller.
1042  *
1043  * Currently, maxsize is either OBP_MAXPROPNAME for property names
1044  * or MAXPATHLEN for device path names. userbufsize is specified
1045  * by the userland caller.
1046  */
1047 static int
1048 oprom_copyinstr(intptr_t arg, char *buf, size_t bufsize, size_t maxsize)
1049 {
1050         int error;
1051         size_t actual_len;
1052 
1053         if ((error = copyinstr(((caddr_t)arg + sizeof (uint_t)),
1054             buf, bufsize, &actual_len)) != 0) {
1055                 return (error);
1056         }
1057         if ((actual_len == 0) || (actual_len > maxsize)) {
1058                 return (EINVAL);
1059         }
1060 
1061         return (0);
1062 }
1063 
1064 /*
1065  * Check pnode_t passed in from userland
1066  */
1067 static int
1068 oprom_checknodeid(pnode_t node_id, pnode_t current_id)
1069 {
1070         int depth;
1071         pnode_t id[OBP_STACKDEPTH];
1072 
1073         /*
1074          * optimized path
1075          */
1076         if (node_id == 0) {
1077                 return (1);
1078         }
1079         if (node_id == OBP_BADNODE) {
1080                 return (0);
1081         }
1082         if ((current_id != OBP_BADNODE) && ((node_id == current_id) ||
1083             (node_id == prom_nextnode(current_id)) ||
1084             (node_id == prom_childnode(current_id)))) {
1085                 return (1);
1086         }
1087 
1088         /*
1089          * long path: walk from root till we find node_id
1090          */
1091         depth = 1;
1092         id[0] = prom_nextnode((pnode_t)0);
1093 
1094         while (depth) {
1095                 if (id[depth - 1] == node_id)
1096                         return (1);     /* node_id found */
1097 
1098                 if (id[depth] = prom_childnode(id[depth - 1])) {
1099                         depth++;
1100                         continue;
1101                 }
1102 
1103                 while (depth &&
1104                     ((id[depth - 1] = prom_nextnode(id[depth - 1])) == 0))
1105                         depth--;
1106         }
1107         return (0);     /* node_id not found */
1108 }
1109 
1110 static int
1111 oprom_copytree(struct oprom_state *st, uint_t flag)
1112 {
1113         ASSERT(st->snapshot == NULL && st->size == 0);
1114         return (oprom_copynode(
1115             prom_nextnode(0), flag, &st->snapshot, &st->size));
1116 }
1117 
1118 static int
1119 oprom_snapshot(struct oprom_state *st, intptr_t arg)
1120 {
1121         uint_t flag;
1122 
1123         if (oprom_setstate(st, IOC_SNAP) == -1)
1124                 return (EBUSY);
1125 
1126         /* copyin flag and create snapshot */
1127         if ((copyin((void *)arg, &flag, sizeof (uint_t)) != 0) ||
1128             (oprom_copytree(st, flag) != 0)) {
1129                 (void) oprom_setstate(st, IOC_IDLE);
1130                 return (EFAULT);
1131         }
1132 
1133 
1134         /* copyout the size of the snapshot */
1135         flag = (uint_t)st->size;
1136         if (copyout(&flag, (void *)arg, sizeof (uint_t)) != 0) {
1137                 kmem_free(st->snapshot, st->size);
1138                 st->snapshot = NULL;
1139                 st->size = 0;
1140                 (void) oprom_setstate(st, IOC_IDLE);
1141                 return (EFAULT);
1142         }
1143 
1144         (void) oprom_setstate(st, IOC_DONE);
1145         return (0);
1146 }
1147 
1148 static int
1149 oprom_copyout(struct oprom_state *st, intptr_t arg)
1150 {
1151         int error = 0;
1152         uint_t size;
1153 
1154         if (oprom_setstate(st, IOC_COPY) == -1)
1155                 return (EBUSY);
1156 
1157         /* copyin size and copyout snapshot */
1158         if (copyin((void *)arg, &size, sizeof (uint_t)) != 0)
1159                 error = EFAULT;
1160         else if (size < st->size)
1161                 error = EINVAL;
1162         else if (copyout(st->snapshot, (void *)arg, st->size) != 0)
1163                 error = EFAULT;
1164 
1165         if (error) {
1166                 /*
1167                  * on error keep the snapshot until a successful
1168                  * copyout or when the driver is closed.
1169                  */
1170                 (void) oprom_setstate(st, IOC_DONE);
1171                 return (error);
1172         }
1173 
1174         kmem_free(st->snapshot, st->size);
1175         st->snapshot = NULL;
1176         st->size = 0;
1177         (void) oprom_setstate(st, IOC_IDLE);
1178         return (0);
1179 }
1180 
1181 /*
1182  * Copy all properties of nodeid into a single packed nvlist
1183  */
1184 static int
1185 oprom_copyprop(pnode_t nodeid, uint_t flag, nvlist_t *nvl)
1186 {
1187         int proplen;
1188         char *propname, *propval, *buf1, *buf2;
1189 
1190         ASSERT(nvl != NULL);
1191 
1192         /*
1193          * non verbose mode, get the "name" property only
1194          */
1195         if (flag == 0) {
1196                 proplen = prom_getproplen(nodeid, "name");
1197                 if (proplen <= 0) {
1198                         cmn_err(CE_WARN,
1199                             "failed to get the name of openprom node 0x%x",
1200                             nodeid);
1201                         (void) nvlist_add_string(nvl, "name", "");
1202                         return (0);
1203                 }
1204                 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1205                 (void) prom_getprop(nodeid, "name", propval);
1206                 (void) nvlist_add_string(nvl, "name", propval);
1207                 kmem_free(propval, proplen + 1);
1208                 return (0);
1209         }
1210 
1211         /*
1212          * Ask for first property by passing a NULL string
1213          */
1214         buf1 = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP);
1215         buf2 = kmem_zalloc(OBP_MAXPROPNAME, KM_SLEEP);
1216         buf1[0] = '\0';
1217         while (propname = (char *)prom_nextprop(nodeid, buf1, buf2)) {
1218                 if (strlen(propname) == 0)
1219                         break;          /* end of prop list */
1220                 (void) strcpy(buf1, propname);
1221 
1222                 proplen = prom_getproplen(nodeid, propname);
1223                 if (proplen == 0) {
1224                         /* boolean property */
1225                         (void) nvlist_add_boolean(nvl, propname);
1226                         continue;
1227                 }
1228                 /* add 1 for null termination in case of a string */
1229                 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1230                 (void) prom_getprop(nodeid, propname, propval);
1231                 (void) nvlist_add_byte_array(nvl, propname,
1232                     (uchar_t *)propval, proplen + 1);
1233                 kmem_free(propval, proplen + 1);
1234                 bzero(buf2, OBP_MAXPROPNAME);
1235         }
1236 
1237         kmem_free(buf1, OBP_MAXPROPNAME);
1238         kmem_free(buf2, OBP_MAXPROPNAME);
1239 
1240         return (0);
1241 }
1242 
1243 /*
1244  * Copy all children and descendents into a a packed nvlist
1245  */
1246 static int
1247 oprom_copychild(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1248 {
1249         nvlist_t *nvl;
1250         pnode_t child = prom_childnode(nodeid);
1251 
1252         if (child == 0)
1253                 return (0);
1254 
1255         (void) nvlist_alloc(&nvl, 0, KM_SLEEP);
1256         while (child != 0) {
1257                 char *nodebuf = NULL;
1258                 size_t nodesize = 0;
1259                 if (oprom_copynode(child, flag, &nodebuf, &nodesize)) {
1260                         nvlist_free(nvl);
1261                         cmn_err(CE_WARN, "failed to copy nodeid 0x%x", child);
1262                         return (-1);
1263                 }
1264                 (void) nvlist_add_byte_array(nvl, "node",
1265                     (uchar_t *)nodebuf, nodesize);
1266                 kmem_free(nodebuf, nodesize);
1267                 child = prom_nextnode(child);
1268         }
1269 
1270         (void) nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1271         nvlist_free(nvl);
1272         return (0);
1273 }
1274 
1275 /*
1276  * Copy a node into a packed nvlist
1277  */
1278 static int
1279 oprom_copynode(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1280 {
1281         int error = 0;
1282         nvlist_t *nvl;
1283         char *childlist = NULL;
1284         size_t childsize = 0;
1285 
1286         (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
1287         ASSERT(nvl != NULL);
1288 
1289         /* @nodeid -- @ is not a legal char in a 1275 property name */
1290         (void) nvlist_add_int32(nvl, "@nodeid", (int32_t)nodeid);
1291 
1292         /* properties */
1293         if (error = oprom_copyprop(nodeid, flag, nvl))
1294                 goto fail;
1295 
1296         /* children */
1297         error = oprom_copychild(nodeid, flag, &childlist, &childsize);
1298         if (error != 0)
1299                 goto fail;
1300         if (childlist != NULL) {
1301                 (void) nvlist_add_byte_array(nvl, "@child",
1302                     (uchar_t *)childlist, (uint_t)childsize);
1303                 kmem_free(childlist, childsize);
1304         }
1305 
1306         /* pack into contiguous buffer */
1307         error = nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1308 
1309 fail:
1310         nvlist_free(nvl);
1311         return (error);
1312 }
1313 
1314 /*
1315  * The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT.
1316  * This function encapsulates the state machine:
1317  *
1318  *      -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY ->
1319  *      |               SNAPSHOT                COPYOUT  |
1320  *      --------------------------------------------------
1321  *
1322  * Returns 0 on success and -1 on failure
1323  */
1324 static int
1325 oprom_setstate(struct oprom_state *st, int16_t new_state)
1326 {
1327         int ret = 0;
1328 
1329         mutex_enter(&oprom_lock);
1330         switch (new_state) {
1331         case IOC_IDLE:
1332         case IOC_DONE:
1333                 break;
1334         case IOC_SNAP:
1335                 if (st->ioc_state != IOC_IDLE)
1336                         ret = -1;
1337                 break;
1338         case IOC_COPY:
1339                 if (st->ioc_state != IOC_DONE)
1340                         ret = -1;
1341                 break;
1342         default:
1343                 ret = -1;
1344         }
1345 
1346         if (ret == 0)
1347                 st->ioc_state = new_state;
1348         else
1349                 cmn_err(CE_NOTE, "incorrect state transition from %d to %d",
1350                     st->ioc_state, new_state);
1351         mutex_exit(&oprom_lock);
1352         return (ret);
1353 }