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 }