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