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