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