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