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 } 1322