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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" /* SVr4 */ 28 29 /* 30 * Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI"; 31 * 32 * Porting notes: 33 * 34 * OPROMU2P unsupported after SunOS 4.x. 35 * 36 * Only one of these devices per system is allowed. 37 */ 38 39 /* 40 * Openprom eeprom options/devinfo driver. 41 */ 42 43 #include <sys/types.h> 44 #include <sys/errno.h> 45 #include <sys/file.h> 46 #include <sys/cmn_err.h> 47 #include <sys/kmem.h> 48 #include <sys/openpromio.h> 49 #include <sys/conf.h> 50 #include <sys/stat.h> 51 #include <sys/modctl.h> 52 #include <sys/debug.h> 53 #include <sys/autoconf.h> 54 #include <sys/ddi.h> 55 #include <sys/sunddi.h> 56 #include <sys/promif.h> 57 #include <sys/sysmacros.h> /* offsetof */ 58 #include <sys/nvpair.h> 59 #include <sys/wanboot_impl.h> 60 #include <sys/zone.h> 61 62 #define MAX_OPENS 32 /* Up to this many simultaneous opens */ 63 64 #define IOC_IDLE 0 /* snapshot ioctl states */ 65 #define IOC_SNAP 1 /* snapshot in progress */ 66 #define IOC_DONE 2 /* snapshot done, but not copied out */ 67 #define IOC_COPY 3 /* copyout in progress */ 68 69 extern int plat_stdout_is_framebuffer(void); 70 extern int plat_stdin_is_keyboard(void); 71 72 /* 73 * XXX Make this dynamic.. or (better still) make the interface stateless 74 */ 75 static struct oprom_state { 76 dnode_t current_id; /* node we're fetching props from */ 77 int16_t already_open; /* if true, this instance is 'active' */ 78 int16_t ioc_state; /* snapshot ioctl state */ 79 char *snapshot; /* snapshot of all prom nodes */ 80 size_t size; /* size of snapshot */ 81 prom_generation_cookie_t tree_gen; 82 } oprom_state[MAX_OPENS]; 83 84 static kmutex_t oprom_lock; /* serialize instance assignment */ 85 86 static int opromopen(dev_t *, int, int, cred_t *); 87 static int opromioctl(dev_t, int, intptr_t, int, cred_t *, int *); 88 static int opromclose(dev_t, int, int, cred_t *); 89 90 static int opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 91 void **result); 92 static int opattach(dev_info_t *, ddi_attach_cmd_t cmd); 93 static int opdetach(dev_info_t *, ddi_detach_cmd_t cmd); 94 95 /* help functions */ 96 static int oprom_checknodeid(dnode_t, dnode_t); 97 static int oprom_copyinstr(intptr_t, char *, size_t, size_t); 98 static int oprom_copynode(dnode_t, uint_t, char **, size_t *); 99 static int oprom_snapshot(struct oprom_state *, intptr_t); 100 static int oprom_copyout(struct oprom_state *, intptr_t); 101 static int oprom_setstate(struct oprom_state *, int16_t); 102 103 static struct cb_ops openeepr_cb_ops = { 104 opromopen, /* open */ 105 opromclose, /* close */ 106 nodev, /* strategy */ 107 nodev, /* print */ 108 nodev, /* dump */ 109 nodev, /* read */ 110 nodev, /* write */ 111 opromioctl, /* ioctl */ 112 nodev, /* devmap */ 113 nodev, /* mmap */ 114 nodev, /* segmap */ 115 nochpoll, /* poll */ 116 ddi_prop_op, /* prop_op */ 117 NULL, /* streamtab */ 118 D_NEW | D_MP /* Driver compatibility flag */ 119 }; 120 121 static struct dev_ops openeepr_ops = { 122 DEVO_REV, /* devo_rev, */ 123 0, /* refcnt */ 124 opinfo, /* info */ 125 nulldev, /* identify */ 126 nulldev, /* probe */ 127 opattach, /* attach */ 128 opdetach, /* detach */ 129 nodev, /* reset */ 130 &openeepr_cb_ops, /* driver operations */ 131 NULL /* bus operations */ 132 }; 133 134 /* 135 * Module linkage information for the kernel. 136 */ 137 static struct modldrv modldrv = { 138 &mod_driverops, 139 "OPENPROM/NVRAM Driver v%I%", 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 dnode_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 = (dnode_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 struct opromioctl_args { 322 struct oprom_state *st; 323 int cmd; 324 intptr_t arg; 325 int mode; 326 }; 327 328 /*ARGSUSED*/ 329 static int 330 opromioctl_cb(void *avp, int has_changed) 331 { 332 struct opromioctl_args *argp = avp; 333 int cmd; 334 intptr_t arg; 335 int mode; 336 struct oprom_state *st; 337 struct openpromio *opp; 338 int valsize; 339 char *valbuf; 340 int error = 0; 341 uint_t userbufsize; 342 dnode_t node_id; 343 char propname[OBP_MAXPROPNAME]; 344 345 st = argp->st; 346 cmd = argp->cmd; 347 arg = argp->arg; 348 mode = argp->mode; 349 350 if (has_changed) { 351 /* 352 * The prom tree has changed since we last used current_id, 353 * so we need to check it. 354 */ 355 if ((st->current_id != OBP_NONODE) && 356 (st->current_id != OBP_BADNODE)) { 357 if (oprom_checknodeid(st->current_id, OBP_NONODE) == 0) 358 st->current_id = OBP_BADNODE; 359 } 360 } 361 362 /* 363 * Check permissions 364 * and weed out unsupported commands on x86 platform 365 */ 366 switch (cmd) { 367 #if !defined(__i386) && !defined(__amd64) 368 case OPROMLISTKEYSLEN: 369 valsize = prom_asr_list_keys_len(); 370 opp = (struct openpromio *)kmem_zalloc( 371 sizeof (uint_t) + 1, KM_SLEEP); 372 opp->oprom_size = valsize; 373 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0) 374 error = EFAULT; 375 kmem_free(opp, sizeof (uint_t) + 1); 376 break; 377 case OPROMLISTKEYS: 378 valsize = prom_asr_list_keys_len(); 379 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0) 380 return (EFAULT); 381 if (valsize > userbufsize) 382 return (EINVAL); 383 valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP); 384 if (prom_asr_list_keys((caddr_t)valbuf) == -1) { 385 kmem_free(valbuf, valsize + 1); 386 return (EFAULT); 387 } 388 opp = (struct openpromio *)kmem_zalloc( 389 valsize + sizeof (uint_t) + 1, KM_SLEEP); 390 opp->oprom_size = valsize; 391 bcopy(valbuf, opp->oprom_array, valsize); 392 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0) 393 error = EFAULT; 394 kmem_free(valbuf, valsize + 1); 395 kmem_free(opp, valsize + sizeof (uint_t) + 1); 396 break; 397 case OPROMEXPORT: 398 valsize = prom_asr_export_len(); 399 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0) 400 return (EFAULT); 401 if (valsize > userbufsize) 402 return (EINVAL); 403 valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP); 404 if (prom_asr_export((caddr_t)valbuf) == -1) { 405 kmem_free(valbuf, valsize + 1); 406 return (EFAULT); 407 } 408 opp = (struct openpromio *)kmem_zalloc( 409 valsize + sizeof (uint_t) + 1, KM_SLEEP); 410 opp->oprom_size = valsize; 411 bcopy(valbuf, opp->oprom_array, valsize); 412 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0) 413 error = EFAULT; 414 kmem_free(valbuf, valsize + 1); 415 kmem_free(opp, valsize + sizeof (uint_t) + 1); 416 break; 417 case OPROMEXPORTLEN: 418 valsize = prom_asr_export_len(); 419 opp = (struct openpromio *)kmem_zalloc( 420 sizeof (uint_t) + 1, KM_SLEEP); 421 opp->oprom_size = valsize; 422 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0) 423 error = EFAULT; 424 kmem_free(opp, sizeof (uint_t) + 1); 425 break; 426 #endif 427 case OPROMGETOPT: 428 case OPROMNXTOPT: 429 if ((mode & FREAD) == 0) { 430 return (EPERM); 431 } 432 node_id = options_nodeid; 433 break; 434 435 case OPROMSETOPT: 436 case OPROMSETOPT2: 437 #if !defined(__i386) && !defined(__amd64) 438 if (mode & FWRITE) { 439 node_id = options_nodeid; 440 break; 441 } 442 #endif /* !__i386 && !__amd64 */ 443 return (EPERM); 444 445 case OPROMNEXT: 446 case OPROMCHILD: 447 case OPROMGETPROP: 448 case OPROMGETPROPLEN: 449 case OPROMNXTPROP: 450 case OPROMSETNODEID: 451 if ((mode & FREAD) == 0) { 452 return (EPERM); 453 } 454 node_id = st->current_id; 455 break; 456 case OPROMCOPYOUT: 457 if (st->snapshot == NULL) 458 return (EINVAL); 459 /*FALLTHROUGH*/ 460 case OPROMSNAPSHOT: 461 case OPROMGETCONS: 462 case OPROMGETBOOTARGS: 463 case OPROMGETVERSION: 464 case OPROMPATH2DRV: 465 case OPROMPROM2DEVNAME: 466 #if !defined(__i386) && !defined(__amd64) 467 case OPROMGETFBNAME: 468 case OPROMDEV2PROMNAME: 469 case OPROMREADY64: 470 #endif /* !__i386 && !__amd64 */ 471 if ((mode & FREAD) == 0) { 472 return (EPERM); 473 } 474 break; 475 476 #if !defined(__i386) && !defined(__amd64) 477 case WANBOOT_SETKEY: 478 if (!(mode & FWRITE)) 479 return (EPERM); 480 break; 481 #endif /* !__i386 && !defined(__amd64) */ 482 483 default: 484 return (EINVAL); 485 } 486 487 /* 488 * Deal with SNAPSHOT and COPYOUT ioctls first 489 */ 490 switch (cmd) { 491 case OPROMCOPYOUT: 492 return (oprom_copyout(st, arg)); 493 494 case OPROMSNAPSHOT: 495 return (oprom_snapshot(st, arg)); 496 } 497 498 /* 499 * Copy in user argument length and allocation memory 500 * 501 * NB do not copyin the entire buffer we may not need 502 * to. userbufsize can be as big as 32 K. 503 */ 504 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0) 505 return (EFAULT); 506 507 if (userbufsize == 0 || userbufsize > OPROMMAXPARAM) 508 return (EINVAL); 509 510 opp = (struct openpromio *)kmem_zalloc( 511 userbufsize + sizeof (uint_t) + 1, KM_SLEEP); 512 513 /* 514 * Execute command 515 */ 516 switch (cmd) { 517 518 case OPROMGETOPT: 519 case OPROMGETPROP: 520 case OPROMGETPROPLEN: 521 522 if ((prom_is_openprom() == 0) || 523 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) { 524 error = EINVAL; 525 break; 526 } 527 528 /* 529 * The argument, a NULL terminated string, is a prop name. 530 */ 531 if ((error = oprom_copyinstr(arg, opp->oprom_array, 532 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) { 533 break; 534 } 535 (void) strcpy(propname, opp->oprom_array); 536 valsize = prom_getproplen(node_id, propname); 537 538 /* 539 * 4010173: 'name' is a property, but not an option. 540 */ 541 if ((cmd == OPROMGETOPT) && (strcmp("name", propname) == 0)) 542 valsize = -1; 543 544 if (cmd == OPROMGETPROPLEN) { 545 int proplen = valsize; 546 547 if (userbufsize < sizeof (int)) { 548 error = EINVAL; 549 break; 550 } 551 opp->oprom_size = valsize = sizeof (int); 552 bcopy(&proplen, opp->oprom_array, valsize); 553 } else if (valsize > 0 && valsize <= userbufsize) { 554 bzero(opp->oprom_array, valsize + 1); 555 (void) prom_getprop(node_id, propname, 556 opp->oprom_array); 557 opp->oprom_size = valsize; 558 if (valsize < userbufsize) 559 ++valsize; /* Forces NULL termination */ 560 /* If space permits */ 561 } else { 562 /* 563 * XXX: There is no error code if the buf is too small. 564 * which is consistent with the current behavior. 565 * 566 * NB: This clause also handles the non-error 567 * zero length (boolean) property value case. 568 */ 569 opp->oprom_size = 0; 570 (void) strcpy(opp->oprom_array, ""); 571 valsize = 1; 572 } 573 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0) 574 error = EFAULT; 575 break; 576 577 case OPROMNXTOPT: 578 case OPROMNXTPROP: 579 if ((prom_is_openprom() == 0) || 580 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) { 581 error = EINVAL; 582 break; 583 } 584 585 /* 586 * The argument, a NULL terminated string, is a prop name. 587 */ 588 if ((error = oprom_copyinstr(arg, opp->oprom_array, 589 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) { 590 break; 591 } 592 valbuf = (char *)prom_nextprop(node_id, opp->oprom_array, 593 propname); 594 valsize = strlen(valbuf); 595 596 /* 597 * 4010173: 'name' is a property, but it's not an option. 598 */ 599 if ((cmd == OPROMNXTOPT) && valsize && 600 (strcmp(valbuf, "name") == 0)) { 601 valbuf = (char *)prom_nextprop(node_id, "name", 602 propname); 603 valsize = strlen(valbuf); 604 } 605 606 if (valsize == 0) { 607 opp->oprom_size = 0; 608 } else if (++valsize <= userbufsize) { 609 opp->oprom_size = valsize; 610 bzero((caddr_t)opp->oprom_array, (size_t)valsize); 611 bcopy((caddr_t)valbuf, (caddr_t)opp->oprom_array, 612 (size_t)valsize); 613 } 614 615 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0) 616 error = EFAULT; 617 break; 618 619 case OPROMNEXT: 620 case OPROMCHILD: 621 case OPROMSETNODEID: 622 623 if (prom_is_openprom() == 0 || 624 userbufsize < sizeof (dnode_t)) { 625 error = EINVAL; 626 break; 627 } 628 629 /* 630 * The argument is a phandle. (aka dnode_t) 631 */ 632 if (copyin(((caddr_t)arg + sizeof (uint_t)), 633 opp->oprom_array, sizeof (dnode_t)) != 0) { 634 error = EFAULT; 635 break; 636 } 637 638 /* 639 * If dnode_t from userland is garbage, we 640 * could confuse the PROM. 641 */ 642 node_id = *(dnode_t *)opp->oprom_array; 643 if (oprom_checknodeid(node_id, st->current_id) == 0) { 644 cmn_err(CE_NOTE, "!nodeid 0x%x not found", 645 (int)node_id); 646 error = EINVAL; 647 break; 648 } 649 650 if (cmd == OPROMNEXT) 651 st->current_id = prom_nextnode(node_id); 652 else if (cmd == OPROMCHILD) 653 st->current_id = prom_childnode(node_id); 654 else { 655 /* OPROMSETNODEID */ 656 st->current_id = node_id; 657 break; 658 } 659 660 opp->oprom_size = sizeof (dnode_t); 661 *(dnode_t *)opp->oprom_array = st->current_id; 662 663 if (copyout(opp, (void *)arg, 664 sizeof (dnode_t) + sizeof (uint_t)) != 0) 665 error = EFAULT; 666 break; 667 668 case OPROMGETCONS: 669 /* 670 * Is openboot supported on this machine? 671 * This ioctl used to return the console device, 672 * information; this is now done via modctl() 673 * in libdevinfo. 674 */ 675 opp->oprom_size = sizeof (char); 676 677 opp->oprom_array[0] |= prom_is_openprom() ? 678 OPROMCONS_OPENPROM : 0; 679 680 /* 681 * The rest of the info is needed by Install to 682 * decide if graphics should be started. 683 */ 684 if ((getzoneid() == GLOBAL_ZONEID) && 685 plat_stdin_is_keyboard()) { 686 opp->oprom_array[0] |= OPROMCONS_STDIN_IS_KBD; 687 } 688 689 if ((getzoneid() == GLOBAL_ZONEID) && 690 plat_stdout_is_framebuffer()) { 691 opp->oprom_array[0] |= OPROMCONS_STDOUT_IS_FB; 692 } 693 694 if (copyout(opp, (void *)arg, 695 sizeof (char) + sizeof (uint_t)) != 0) 696 error = EFAULT; 697 break; 698 699 case OPROMGETBOOTARGS: { 700 extern char kern_bootargs[]; 701 702 valsize = strlen(kern_bootargs) + 1; 703 if (valsize > userbufsize) { 704 error = EINVAL; 705 break; 706 } 707 (void) strcpy(opp->oprom_array, kern_bootargs); 708 opp->oprom_size = valsize - 1; 709 710 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0) 711 error = EFAULT; 712 } break; 713 714 /* 715 * convert a prom device path to an equivalent devfs path 716 */ 717 case OPROMPROM2DEVNAME: { 718 char *dev_name; 719 720 /* 721 * The input argument, a pathname, is a NULL terminated string. 722 */ 723 if ((error = oprom_copyinstr(arg, opp->oprom_array, 724 (size_t)userbufsize, MAXPATHLEN)) != 0) { 725 break; 726 } 727 728 dev_name = kmem_alloc(MAXPATHLEN, KM_SLEEP); 729 730 error = i_promname_to_devname(opp->oprom_array, dev_name); 731 if (error != 0) { 732 kmem_free(dev_name, MAXPATHLEN); 733 break; 734 } 735 valsize = opp->oprom_size = strlen(dev_name); 736 if (++valsize > userbufsize) { 737 kmem_free(dev_name, MAXPATHLEN); 738 error = EINVAL; 739 break; 740 } 741 (void) strcpy(opp->oprom_array, dev_name); 742 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0) 743 error = EFAULT; 744 745 kmem_free(dev_name, MAXPATHLEN); 746 } break; 747 748 /* 749 * Convert a prom device path name to a driver name 750 */ 751 case OPROMPATH2DRV: { 752 char *drv_name; 753 major_t maj; 754 755 /* 756 * The input argument, a pathname, is a NULL terminated string. 757 */ 758 if ((error = oprom_copyinstr(arg, opp->oprom_array, 759 (size_t)userbufsize, MAXPATHLEN)) != 0) { 760 break; 761 } 762 763 /* 764 * convert path to a driver binding name 765 */ 766 maj = path_to_major((char *)opp->oprom_array); 767 if (maj == (major_t)-1) { 768 error = EINVAL; 769 break; 770 } 771 772 /* 773 * resolve any aliases 774 */ 775 if ((drv_name = ddi_major_to_name(maj)) == NULL) { 776 error = EINVAL; 777 break; 778 } 779 780 (void) strcpy(opp->oprom_array, drv_name); 781 opp->oprom_size = strlen(drv_name); 782 if (copyout(opp, (void *)arg, 783 sizeof (uint_t) + opp->oprom_size + 1) != 0) 784 error = EFAULT; 785 } break; 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 case OPROMSETOPT: 880 case OPROMSETOPT2: { 881 int namebuflen; 882 int valbuflen; 883 884 if ((prom_is_openprom() == 0) || 885 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) { 886 error = EINVAL; 887 break; 888 } 889 890 /* 891 * The arguments are a property name and a value. 892 * Copy in the entire user buffer. 893 */ 894 if (copyin(((caddr_t)arg + sizeof (uint_t)), 895 opp->oprom_array, userbufsize) != 0) { 896 error = EFAULT; 897 break; 898 } 899 900 /* 901 * The property name is the first string, value second 902 */ 903 namebuflen = strlen(opp->oprom_array); 904 valbuf = opp->oprom_array + namebuflen + 1; 905 valbuflen = strlen(valbuf); 906 907 if (cmd == OPROMSETOPT) { 908 valsize = valbuflen + 1; /* +1 for the '\0' */ 909 } else { 910 if ((namebuflen + 1 + valbuflen + 1) > userbufsize) { 911 error = EINVAL; 912 break; 913 } 914 valsize = (opp->oprom_array + userbufsize) - valbuf; 915 } 916 917 /* 918 * 4010173: 'name' is not an option, but it is a property. 919 */ 920 if (strcmp(opp->oprom_array, "name") == 0) 921 error = EINVAL; 922 else if (prom_setprop(node_id, opp->oprom_array, 923 valbuf, valsize) < 0) 924 error = EINVAL; 925 926 } break; 927 928 case OPROMREADY64: { 929 struct openprom_opr64 *opr = 930 (struct openprom_opr64 *)opp->oprom_array; 931 int i; 932 dnode_t id; 933 934 if (userbufsize < sizeof (*opr)) { 935 error = EINVAL; 936 break; 937 } 938 939 valsize = userbufsize - 940 offsetof(struct openprom_opr64, message); 941 942 i = prom_version_check(opr->message, valsize, &id); 943 opr->return_code = i; 944 opr->nodeid = (int)id; 945 946 valsize = offsetof(struct openprom_opr64, message); 947 valsize += strlen(opr->message) + 1; 948 949 /* 950 * copyout only the part of the user buffer we need to. 951 */ 952 if (copyout(opp, (void *)arg, 953 (size_t)(min((uint_t)valsize, userbufsize) + 954 sizeof (uint_t))) != 0) 955 error = EFAULT; 956 break; 957 958 } /* case OPROMREADY64 */ 959 960 case WANBOOT_SETKEY: { 961 struct wankeyio *wp; 962 int reslen; 963 int status; 964 int rv; 965 int i; 966 967 /* 968 * The argument is a struct wankeyio. Validate it as best 969 * we can. 970 */ 971 if (userbufsize != (sizeof (struct wankeyio))) { 972 error = EINVAL; 973 break; 974 } 975 if (copyin(((caddr_t)arg + sizeof (uint_t)), 976 opp->oprom_array, sizeof (struct wankeyio)) != 0) { 977 error = EFAULT; 978 break; 979 } 980 wp = (struct wankeyio *)opp->oprom_array; 981 982 /* check for key name and key size overflow */ 983 for (i = 0; i < WANBOOT_MAXKEYNAMELEN; i++) 984 if (wp->wk_keyname[i] == '\0') 985 break; 986 if ((i == WANBOOT_MAXKEYNAMELEN) || 987 (wp->wk_keysize > WANBOOT_MAXKEYLEN)) { 988 error = EINVAL; 989 break; 990 } 991 992 rv = prom_set_security_key(wp->wk_keyname, wp->wk_u.key, 993 wp->wk_keysize, &reslen, &status); 994 if (rv) 995 error = EIO; 996 else 997 switch (status) { 998 case 0: 999 error = 0; 1000 break; 1001 1002 case -2: /* out of key storage space */ 1003 error = ENOSPC; 1004 break; 1005 1006 case -3: /* key name or value too long */ 1007 error = EINVAL; 1008 break; 1009 1010 case -4: /* can't delete: no such key */ 1011 error = ENOENT; 1012 break; 1013 1014 case -1: /* unspecified error */ 1015 default: /* this should not happen */ 1016 error = EIO; 1017 break; 1018 } 1019 break; 1020 } /* case WANBOOT_SETKEY */ 1021 #endif /* !__i386 && !__amd64 */ 1022 } /* switch (cmd) */ 1023 1024 kmem_free(opp, userbufsize + sizeof (uint_t) + 1); 1025 return (error); 1026 } 1027 1028 /*ARGSUSED*/ 1029 static int 1030 opromioctl(dev_t dev, int cmd, intptr_t arg, int mode, 1031 cred_t *credp, int *rvalp) 1032 { 1033 struct oprom_state *st; 1034 struct opromioctl_args arg_block; 1035 1036 if (getminor(dev) >= MAX_OPENS) 1037 return (ENXIO); 1038 1039 st = &oprom_state[getminor(dev)]; 1040 ASSERT(st->already_open); 1041 arg_block.st = st; 1042 arg_block.cmd = cmd; 1043 arg_block.arg = arg; 1044 arg_block.mode = mode; 1045 return (prom_tree_access(opromioctl_cb, &arg_block, &st->tree_gen)); 1046 } 1047 1048 /* 1049 * Copyin string and verify the actual string length is less than maxsize 1050 * specified by the caller. 1051 * 1052 * Currently, maxsize is either OBP_MAXPROPNAME for property names 1053 * or MAXPATHLEN for device path names. userbufsize is specified 1054 * by the userland caller. 1055 */ 1056 static int 1057 oprom_copyinstr(intptr_t arg, char *buf, size_t bufsize, size_t maxsize) 1058 { 1059 int error; 1060 size_t actual_len; 1061 1062 if ((error = copyinstr(((caddr_t)arg + sizeof (uint_t)), 1063 buf, bufsize, &actual_len)) != 0) { 1064 return (error); 1065 } 1066 if ((actual_len == 0) || (actual_len > maxsize)) { 1067 return (EINVAL); 1068 } 1069 1070 return (0); 1071 } 1072 1073 /* 1074 * Check dnode_t passed in from userland 1075 */ 1076 static int 1077 oprom_checknodeid(dnode_t node_id, dnode_t current_id) 1078 { 1079 int depth; 1080 dnode_t id[OBP_STACKDEPTH]; 1081 1082 /* 1083 * optimized path 1084 */ 1085 if (node_id == 0) { 1086 return (1); 1087 } 1088 if (node_id == OBP_BADNODE) { 1089 return (0); 1090 } 1091 if ((current_id != OBP_BADNODE) && ((node_id == current_id) || 1092 (node_id == prom_nextnode(current_id)) || 1093 (node_id == prom_childnode(current_id)))) { 1094 return (1); 1095 } 1096 1097 /* 1098 * long path: walk from root till we find node_id 1099 */ 1100 depth = 1; 1101 id[0] = prom_nextnode((dnode_t)0); 1102 1103 while (depth) { 1104 if (id[depth - 1] == node_id) 1105 return (1); /* node_id found */ 1106 1107 if (id[depth] = prom_childnode(id[depth - 1])) { 1108 depth++; 1109 continue; 1110 } 1111 1112 while (depth && 1113 ((id[depth - 1] = prom_nextnode(id[depth - 1])) == 0)) 1114 depth--; 1115 } 1116 return (0); /* node_id not found */ 1117 } 1118 1119 static int 1120 oprom_copytree(struct oprom_state *st, uint_t flag) 1121 { 1122 ASSERT(st->snapshot == NULL && st->size == 0); 1123 return (oprom_copynode( 1124 prom_nextnode(0), flag, &st->snapshot, &st->size)); 1125 } 1126 1127 static int 1128 oprom_snapshot(struct oprom_state *st, intptr_t arg) 1129 { 1130 uint_t flag; 1131 1132 if (oprom_setstate(st, IOC_SNAP) == -1) 1133 return (EBUSY); 1134 1135 /* copyin flag and create snapshot */ 1136 if ((copyin((void *)arg, &flag, sizeof (uint_t)) != 0) || 1137 (oprom_copytree(st, flag) != 0)) { 1138 (void) oprom_setstate(st, IOC_IDLE); 1139 return (EFAULT); 1140 } 1141 1142 1143 /* copyout the size of the snapshot */ 1144 flag = (uint_t)st->size; 1145 if (copyout(&flag, (void *)arg, sizeof (uint_t)) != 0) { 1146 kmem_free(st->snapshot, st->size); 1147 st->snapshot = NULL; 1148 st->size = 0; 1149 (void) oprom_setstate(st, IOC_IDLE); 1150 return (EFAULT); 1151 } 1152 1153 (void) oprom_setstate(st, IOC_DONE); 1154 return (0); 1155 } 1156 1157 static int 1158 oprom_copyout(struct oprom_state *st, intptr_t arg) 1159 { 1160 int error = 0; 1161 uint_t size; 1162 1163 if (oprom_setstate(st, IOC_COPY) == -1) 1164 return (EBUSY); 1165 1166 /* copyin size and copyout snapshot */ 1167 if (copyin((void *)arg, &size, sizeof (uint_t)) != 0) 1168 error = EFAULT; 1169 else if (size < st->size) 1170 error = EINVAL; 1171 else if (copyout(st->snapshot, (void *)arg, st->size) != 0) 1172 error = EFAULT; 1173 1174 if (error) { 1175 /* 1176 * on error keep the snapshot until a successful 1177 * copyout or when the driver is closed. 1178 */ 1179 (void) oprom_setstate(st, IOC_DONE); 1180 return (error); 1181 } 1182 1183 kmem_free(st->snapshot, st->size); 1184 st->snapshot = NULL; 1185 st->size = 0; 1186 (void) oprom_setstate(st, IOC_IDLE); 1187 return (0); 1188 } 1189 1190 /* 1191 * Copy all properties of nodeid into a single packed nvlist 1192 */ 1193 static int 1194 oprom_copyprop(dnode_t nodeid, uint_t flag, nvlist_t *nvl) 1195 { 1196 int proplen; 1197 char *propname, *propval, *buf1, *buf2; 1198 1199 ASSERT(nvl != NULL); 1200 1201 /* 1202 * non verbose mode, get the "name" property only 1203 */ 1204 if (flag == 0) { 1205 proplen = prom_getproplen(nodeid, "name"); 1206 if (proplen <= 0) { 1207 cmn_err(CE_WARN, 1208 "failed to get the name of openprom node 0x%x", 1209 nodeid); 1210 (void) nvlist_add_string(nvl, "name", ""); 1211 return (0); 1212 } 1213 propval = kmem_zalloc(proplen + 1, KM_SLEEP); 1214 (void) prom_getprop(nodeid, "name", propval); 1215 (void) nvlist_add_string(nvl, "name", propval); 1216 kmem_free(propval, proplen + 1); 1217 return (0); 1218 } 1219 1220 /* 1221 * Ask for first property by passing a NULL string 1222 */ 1223 buf1 = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP); 1224 buf2 = kmem_zalloc(OBP_MAXPROPNAME, KM_SLEEP); 1225 buf1[0] = '\0'; 1226 while (propname = (char *)prom_nextprop(nodeid, buf1, buf2)) { 1227 if (strlen(propname) == 0) 1228 break; /* end of prop list */ 1229 (void) strcpy(buf1, propname); 1230 1231 proplen = prom_getproplen(nodeid, propname); 1232 if (proplen == 0) { 1233 /* boolean property */ 1234 (void) nvlist_add_boolean(nvl, propname); 1235 continue; 1236 } 1237 /* add 1 for null termination in case of a string */ 1238 propval = kmem_zalloc(proplen + 1, KM_SLEEP); 1239 (void) prom_getprop(nodeid, propname, propval); 1240 (void) nvlist_add_byte_array(nvl, propname, 1241 (uchar_t *)propval, proplen + 1); 1242 kmem_free(propval, proplen + 1); 1243 bzero(buf2, OBP_MAXPROPNAME); 1244 } 1245 1246 kmem_free(buf1, OBP_MAXPROPNAME); 1247 kmem_free(buf2, OBP_MAXPROPNAME); 1248 1249 return (0); 1250 } 1251 1252 /* 1253 * Copy all children and descendents into a a packed nvlist 1254 */ 1255 static int 1256 oprom_copychild(dnode_t nodeid, uint_t flag, char **buf, size_t *size) 1257 { 1258 nvlist_t *nvl; 1259 dnode_t child = prom_childnode(nodeid); 1260 1261 if (child == 0) 1262 return (0); 1263 1264 (void) nvlist_alloc(&nvl, 0, KM_SLEEP); 1265 while (child != 0) { 1266 char *nodebuf = NULL; 1267 size_t nodesize = 0; 1268 if (oprom_copynode(child, flag, &nodebuf, &nodesize)) { 1269 nvlist_free(nvl); 1270 cmn_err(CE_WARN, "failed to copy nodeid 0x%x", child); 1271 return (-1); 1272 } 1273 (void) nvlist_add_byte_array(nvl, "node", 1274 (uchar_t *)nodebuf, nodesize); 1275 kmem_free(nodebuf, nodesize); 1276 child = prom_nextnode(child); 1277 } 1278 1279 (void) nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP); 1280 nvlist_free(nvl); 1281 return (0); 1282 } 1283 1284 /* 1285 * Copy a node into a packed nvlist 1286 */ 1287 static int 1288 oprom_copynode(dnode_t nodeid, uint_t flag, char **buf, size_t *size) 1289 { 1290 int error = 0; 1291 nvlist_t *nvl; 1292 char *childlist = NULL; 1293 size_t childsize = 0; 1294 1295 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 1296 ASSERT(nvl != NULL); 1297 1298 /* @nodeid -- @ is not a legal char in a 1275 property name */ 1299 (void) nvlist_add_int32(nvl, "@nodeid", (int32_t)nodeid); 1300 1301 /* properties */ 1302 if (error = oprom_copyprop(nodeid, flag, nvl)) 1303 goto fail; 1304 1305 /* children */ 1306 error = oprom_copychild(nodeid, flag, &childlist, &childsize); 1307 if (error != 0) 1308 goto fail; 1309 if (childlist != NULL) { 1310 (void) nvlist_add_byte_array(nvl, "@child", 1311 (uchar_t *)childlist, (uint_t)childsize); 1312 kmem_free(childlist, childsize); 1313 } 1314 1315 /* pack into contiguous buffer */ 1316 error = nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP); 1317 1318 fail: 1319 nvlist_free(nvl); 1320 return (error); 1321 } 1322 1323 /* 1324 * The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT. 1325 * This function encapsulates the state machine: 1326 * 1327 * -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY -> 1328 * | SNAPSHOT COPYOUT | 1329 * -------------------------------------------------- 1330 * 1331 * Returns 0 on success and -1 on failure 1332 */ 1333 static int 1334 oprom_setstate(struct oprom_state *st, int16_t new_state) 1335 { 1336 int ret = 0; 1337 1338 mutex_enter(&oprom_lock); 1339 switch (new_state) { 1340 case IOC_IDLE: 1341 case IOC_DONE: 1342 break; 1343 case IOC_SNAP: 1344 if (st->ioc_state != IOC_IDLE) 1345 ret = -1; 1346 break; 1347 case IOC_COPY: 1348 if (st->ioc_state != IOC_DONE) 1349 ret = -1; 1350 break; 1351 default: 1352 ret = -1; 1353 } 1354 1355 if (ret == 0) 1356 st->ioc_state = new_state; 1357 else 1358 cmn_err(CE_NOTE, "incorrect state transition from %d to %d", 1359 st->ioc_state, new_state); 1360 mutex_exit(&oprom_lock); 1361 return (ret); 1362 } 1363