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