openprom.c revision 7656:2621e50fdf4a
1193323Sed/* 2193323Sed * CDDL HEADER START 3193323Sed * 4193323Sed * The contents of this file are subject to the terms of the 5193323Sed * Common Development and Distribution License (the "License"). 6193323Sed * You may not use this file except in compliance with the License. 7193323Sed * 8193323Sed * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9193323Sed * or http://www.opensolaris.org/os/licensing. 10193323Sed * See the License for the specific language governing permissions 11193323Sed * and limitations under the License. 12193323Sed * 13193323Sed * When distributing Covered Code, include this CDDL HEADER in each 14193323Sed * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15193323Sed * If applicable, add the following below this CDDL HEADER, with the 16193323Sed * fields enclosed by brackets "[]" replaced with your own identifying 17193323Sed * information: Portions Copyright [yyyy] [name of copyright owner] 18193323Sed * 19193323Sed * CDDL HEADER END 20193323Sed */ 21193323Sed/* 22193323Sed * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23193323Sed * Use is subject to license terms. 24193323Sed */ 25193323Sed 26198090Srdivacky/* 27193323Sed * Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI"; 28193323Sed * 29193323Sed * Porting notes: 30193323Sed * 31193323Sed * OPROMU2P unsupported after SunOS 4.x. 32193323Sed * 33198090Srdivacky * Only one of these devices per system is allowed. 34198090Srdivacky */ 35198090Srdivacky 36198090Srdivacky/* 37198090Srdivacky * Openprom eeprom options/devinfo driver. 38198090Srdivacky */ 39193323Sed 40193323Sed#include <sys/types.h> 41193323Sed#include <sys/errno.h> 42193323Sed#include <sys/file.h> 43193323Sed#include <sys/cmn_err.h> 44193323Sed#include <sys/kmem.h> 45193323Sed#include <sys/openpromio.h> 46193323Sed#include <sys/conf.h> 47198090Srdivacky#include <sys/stat.h> 48193323Sed#include <sys/modctl.h> 49193323Sed#include <sys/debug.h> 50193323Sed#include <sys/autoconf.h> 51193323Sed#include <sys/ddi.h> 52193323Sed#include <sys/sunddi.h> 53193323Sed#include <sys/promif.h> 54193323Sed#include <sys/sysmacros.h> /* offsetof */ 55193323Sed#include <sys/nvpair.h> 56193323Sed#include <sys/wanboot_impl.h> 57193323Sed#include <sys/zone.h> 58193323Sed#include <sys/consplat.h> 59193323Sed 60193323Sed#define MAX_OPENS 32 /* Up to this many simultaneous opens */ 61193323Sed 62193323Sed#define IOC_IDLE 0 /* snapshot ioctl states */ 63193323Sed#define IOC_SNAP 1 /* snapshot in progress */ 64193323Sed#define IOC_DONE 2 /* snapshot done, but not copied out */ 65193323Sed#define IOC_COPY 3 /* copyout in progress */ 66193323Sed 67193323Sed/* 68193323Sed * XXX Make this dynamic.. or (better still) make the interface stateless 69193323Sed */ 70193323Sedstatic struct oprom_state { 71193323Sed pnode_t current_id; /* node we're fetching props from */ 72193323Sed int16_t already_open; /* if true, this instance is 'active' */ 73193323Sed int16_t ioc_state; /* snapshot ioctl state */ 74193323Sed char *snapshot; /* snapshot of all prom nodes */ 75193323Sed size_t size; /* size of snapshot */ 76193323Sed prom_generation_cookie_t tree_gen; 77193323Sed} oprom_state[MAX_OPENS]; 78193323Sed 79193323Sedstatic kmutex_t oprom_lock; /* serialize instance assignment */ 80193323Sed 81198090Srdivackystatic int opromopen(dev_t *, int, int, cred_t *); 82198090Srdivackystatic int opromioctl(dev_t, int, intptr_t, int, cred_t *, int *); 83193323Sedstatic int opromclose(dev_t, int, int, cred_t *); 84193323Sed 85193323Sedstatic int opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 86193323Sed void **result); 87193323Sedstatic int opattach(dev_info_t *, ddi_attach_cmd_t cmd); 88193323Sedstatic int opdetach(dev_info_t *, ddi_detach_cmd_t cmd); 89193323Sed 90193323Sed/* help functions */ 91193323Sedstatic int oprom_checknodeid(pnode_t, pnode_t); 92193323Sedstatic int oprom_copyinstr(intptr_t, char *, size_t, size_t); 93193323Sedstatic int oprom_copynode(pnode_t, uint_t, char **, size_t *); 94193323Sedstatic int oprom_snapshot(struct oprom_state *, intptr_t); 95193323Sedstatic int oprom_copyout(struct oprom_state *, intptr_t); 96193323Sedstatic int oprom_setstate(struct oprom_state *, int16_t); 97193323Sed 98193323Sedstatic struct cb_ops openeepr_cb_ops = { 99193323Sed opromopen, /* open */ 100193323Sed opromclose, /* close */ 101193323Sed nodev, /* strategy */ 102193323Sed nodev, /* print */ 103193323Sed nodev, /* dump */ 104193323Sed nodev, /* read */ 105193323Sed nodev, /* write */ 106193323Sed opromioctl, /* ioctl */ 107193323Sed nodev, /* devmap */ 108193323Sed nodev, /* mmap */ 109193323Sed nodev, /* segmap */ 110193323Sed nochpoll, /* poll */ 111193323Sed ddi_prop_op, /* prop_op */ 112193323Sed NULL, /* streamtab */ 113193323Sed D_NEW | D_MP /* Driver compatibility flag */ 114193323Sed}; 115193323Sed 116193323Sedstatic struct dev_ops openeepr_ops = { 117193323Sed DEVO_REV, /* devo_rev, */ 118193323Sed 0, /* refcnt */ 119193323Sed opinfo, /* info */ 120193323Sed nulldev, /* identify */ 121193323Sed nulldev, /* probe */ 122193323Sed opattach, /* attach */ 123193323Sed opdetach, /* detach */ 124193323Sed nodev, /* reset */ 125193323Sed &openeepr_cb_ops, /* driver operations */ 126193323Sed NULL, /* bus operations */ 127193323Sed NULL, /* power */ 128194612Sed ddi_quiesce_not_needed, /* quiesce */ 129193323Sed}; 130194612Sed 131195340Sed/* 132193323Sed * Module linkage information for the kernel. 133193323Sed */ 134193323Sedstatic struct modldrv modldrv = { 135193323Sed &mod_driverops, 136193323Sed "OPENPROM/NVRAM Driver", 137193323Sed &openeepr_ops 138193323Sed}; 139193323Sed 140194612Sedstatic struct modlinkage modlinkage = { 141193323Sed MODREV_1, 142193323Sed &modldrv, 143193323Sed NULL 144198090Srdivacky}; 145195340Sed 146193323Sedint 147193323Sed_init(void) 148193323Sed{ 149193323Sed int error; 150194612Sed 151193323Sed mutex_init(&oprom_lock, NULL, MUTEX_DRIVER, NULL); 152193323Sed 153193323Sed error = mod_install(&modlinkage); 154194612Sed if (error != 0) { 155194612Sed mutex_destroy(&oprom_lock); 156194612Sed return (error); 157193323Sed } 158193323Sed 159193323Sed return (0); 160193323Sed} 161193323Sed 162193323Sedint 163193323Sed_info(struct modinfo *modinfop) 164193323Sed{ 165193323Sed return (mod_info(&modlinkage, modinfop)); 166193323Sed} 167193323Sed 168193323Sedint 169193323Sed_fini(void) 170193323Sed{ 171195340Sed int error; 172193323Sed 173195340Sed error = mod_remove(&modlinkage); 174198090Srdivacky if (error != 0) 175193323Sed return (error); 176195340Sed 177195340Sed mutex_destroy(&oprom_lock); 178195340Sed return (0); 179193323Sed} 180193323Sed 181193323Sedstatic dev_info_t *opdip; 182193323Sedstatic pnode_t options_nodeid; 183193323Sed 184193323Sed/*ARGSUSED*/ 185193323Sedstatic int 186193323Sedopinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 187193323Sed{ 188193323Sed int error = DDI_FAILURE; 189193323Sed 190193323Sed switch (infocmd) { 191193323Sed case DDI_INFO_DEVT2DEVINFO: 192193323Sed *result = (void *)opdip; 193193323Sed error = DDI_SUCCESS; 194193323Sed break; 195193323Sed case DDI_INFO_DEVT2INSTANCE: 196193323Sed /* All dev_t's map to the same, single instance */ 197193323Sed *result = (void *)0; 198193323Sed error = DDI_SUCCESS; 199193323Sed break; 200193323Sed default: 201193323Sed break; 202193323Sed } 203193323Sed 204193323Sed return (error); 205193323Sed} 206193323Sed 207193323Sedstatic int 208193323Sedopattach(dev_info_t *dip, ddi_attach_cmd_t cmd) 209193323Sed{ 210193323Sed switch (cmd) { 211193323Sed 212193323Sed case DDI_ATTACH: 213193323Sed if (prom_is_openprom()) { 214193323Sed options_nodeid = prom_optionsnode(); 215193323Sed } else { 216198090Srdivacky options_nodeid = OBP_BADNODE; 217198090Srdivacky } 218198090Srdivacky 219193323Sed opdip = dip; 220193323Sed 221193323Sed if (ddi_create_minor_node(dip, "openprom", S_IFCHR, 222193323Sed 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 223193323Sed return (DDI_FAILURE); 224193323Sed } 225193323Sed 226193323Sed return (DDI_SUCCESS); 227193323Sed 228193323Sed default: 229193323Sed return (DDI_FAILURE); 230193323Sed } 231193323Sed} 232193323Sed 233193323Sedstatic int 234193323Sedopdetach(dev_info_t *dip, ddi_detach_cmd_t cmd) 235193323Sed{ 236193323Sed if (cmd != DDI_DETACH) 237193323Sed return (DDI_FAILURE); 238193323Sed 239193323Sed ddi_remove_minor_node(dip, NULL); 240193323Sed opdip = NULL; 241193323Sed 242193323Sed return (DDI_SUCCESS); 243193323Sed} 244193323Sed 245193323Sed/* 246193323Sed * Allow multiple opens by tweaking the dev_t such that it looks like each 247193323Sed * open is getting a different minor device. Each minor gets a separate 248193323Sed * entry in the oprom_state[] table. 249193323Sed */ 250193323Sed/*ARGSUSED*/ 251193323Sedstatic int 252193323Sedopromopen(dev_t *devp, int flag, int otyp, cred_t *credp) 253193323Sed{ 254193323Sed int m; 255193323Sed struct oprom_state *st = oprom_state; 256195340Sed 257193323Sed if (getminor(*devp) != 0) 258193323Sed return (ENXIO); 259193323Sed 260193323Sed mutex_enter(&oprom_lock); 261195340Sed for (m = 0; m < MAX_OPENS; m++) 262193323Sed if (st->already_open) 263193323Sed st++; 264193323Sed else { 265195340Sed st->already_open = 1; 266193323Sed /* 267195340Sed * It's ours. 268193323Sed */ 269195340Sed st->current_id = (pnode_t)0; 270193323Sed ASSERT(st->snapshot == NULL && st->size == 0); 271193323Sed ASSERT(st->ioc_state == IOC_IDLE); 272195340Sed break; 273193323Sed } 274193323Sed mutex_exit(&oprom_lock); 275193323Sed 276193323Sed if (m == MAX_OPENS) { 277193323Sed /* 278 * "Thank you for calling, but all our lines are 279 * busy at the moment.." 280 * 281 * We could get sophisticated here, and go into a 282 * sleep-retry loop .. but hey, I just can't see 283 * that many processes sitting in this driver. 284 * 285 * (And if it does become possible, then we should 286 * change the interface so that the 'state' is held 287 * external to the driver) 288 */ 289 return (EAGAIN); 290 } 291 292 *devp = makedevice(getmajor(*devp), (minor_t)m); 293 294 return (0); 295} 296 297/*ARGSUSED*/ 298static int 299opromclose(dev_t dev, int flag, int otype, cred_t *cred_p) 300{ 301 struct oprom_state *st; 302 303 st = &oprom_state[getminor(dev)]; 304 ASSERT(getminor(dev) < MAX_OPENS && st->already_open != 0); 305 if (st->snapshot) { 306 kmem_free(st->snapshot, st->size); 307 st->snapshot = NULL; 308 st->size = 0; 309 st->ioc_state = IOC_IDLE; 310 } 311 mutex_enter(&oprom_lock); 312 st->already_open = 0; 313 mutex_exit(&oprom_lock); 314 315 return (0); 316} 317 318struct opromioctl_args { 319 struct oprom_state *st; 320 int cmd; 321 intptr_t arg; 322 int mode; 323}; 324 325/*ARGSUSED*/ 326static int 327opromioctl_cb(void *avp, int has_changed) 328{ 329 struct opromioctl_args *argp = avp; 330 int cmd; 331 intptr_t arg; 332 int mode; 333 struct oprom_state *st; 334 struct openpromio *opp; 335 int valsize; 336 char *valbuf; 337 int error = 0; 338 uint_t userbufsize; 339 pnode_t node_id; 340 char propname[OBP_MAXPROPNAME]; 341 342 st = argp->st; 343 cmd = argp->cmd; 344 arg = argp->arg; 345 mode = argp->mode; 346 347 if (has_changed) { 348 /* 349 * The prom tree has changed since we last used current_id, 350 * so we need to check it. 351 */ 352 if ((st->current_id != OBP_NONODE) && 353 (st->current_id != OBP_BADNODE)) { 354 if (oprom_checknodeid(st->current_id, OBP_NONODE) == 0) 355 st->current_id = OBP_BADNODE; 356 } 357 } 358 359 /* 360 * Check permissions 361 * and weed out unsupported commands on x86 platform 362 */ 363 switch (cmd) { 364#if !defined(__i386) && !defined(__amd64) 365 case OPROMLISTKEYSLEN: 366 valsize = prom_asr_list_keys_len(); 367 opp = (struct openpromio *)kmem_zalloc( 368 sizeof (uint_t) + 1, KM_SLEEP); 369 opp->oprom_size = valsize; 370 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0) 371 error = EFAULT; 372 kmem_free(opp, sizeof (uint_t) + 1); 373 break; 374 case OPROMLISTKEYS: 375 valsize = prom_asr_list_keys_len(); 376 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0) 377 return (EFAULT); 378 if (valsize > userbufsize) 379 return (EINVAL); 380 valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP); 381 if (prom_asr_list_keys((caddr_t)valbuf) == -1) { 382 kmem_free(valbuf, valsize + 1); 383 return (EFAULT); 384 } 385 opp = (struct openpromio *)kmem_zalloc( 386 valsize + sizeof (uint_t) + 1, KM_SLEEP); 387 opp->oprom_size = valsize; 388 bcopy(valbuf, opp->oprom_array, valsize); 389 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0) 390 error = EFAULT; 391 kmem_free(valbuf, valsize + 1); 392 kmem_free(opp, valsize + sizeof (uint_t) + 1); 393 break; 394 case OPROMEXPORT: 395 valsize = prom_asr_export_len(); 396 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0) 397 return (EFAULT); 398 if (valsize > userbufsize) 399 return (EINVAL); 400 valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP); 401 if (prom_asr_export((caddr_t)valbuf) == -1) { 402 kmem_free(valbuf, valsize + 1); 403 return (EFAULT); 404 } 405 opp = (struct openpromio *)kmem_zalloc( 406 valsize + sizeof (uint_t) + 1, KM_SLEEP); 407 opp->oprom_size = valsize; 408 bcopy(valbuf, opp->oprom_array, valsize); 409 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0) 410 error = EFAULT; 411 kmem_free(valbuf, valsize + 1); 412 kmem_free(opp, valsize + sizeof (uint_t) + 1); 413 break; 414 case OPROMEXPORTLEN: 415 valsize = prom_asr_export_len(); 416 opp = (struct openpromio *)kmem_zalloc( 417 sizeof (uint_t) + 1, KM_SLEEP); 418 opp->oprom_size = valsize; 419 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0) 420 error = EFAULT; 421 kmem_free(opp, sizeof (uint_t) + 1); 422 break; 423#endif 424 case OPROMGETOPT: 425 case OPROMNXTOPT: 426 if ((mode & FREAD) == 0) { 427 return (EPERM); 428 } 429 node_id = options_nodeid; 430 break; 431 432 case OPROMSETOPT: 433 case OPROMSETOPT2: 434#if !defined(__i386) && !defined(__amd64) 435 if (mode & FWRITE) { 436 node_id = options_nodeid; 437 break; 438 } 439#endif /* !__i386 && !__amd64 */ 440 return (EPERM); 441 442 case OPROMNEXT: 443 case OPROMCHILD: 444 case OPROMGETPROP: 445 case OPROMGETPROPLEN: 446 case OPROMNXTPROP: 447 case OPROMSETNODEID: 448 if ((mode & FREAD) == 0) { 449 return (EPERM); 450 } 451 node_id = st->current_id; 452 break; 453 case OPROMCOPYOUT: 454 if (st->snapshot == NULL) 455 return (EINVAL); 456 /*FALLTHROUGH*/ 457 case OPROMSNAPSHOT: 458 case OPROMGETCONS: 459 case OPROMGETBOOTARGS: 460 case OPROMGETVERSION: 461 case OPROMPATH2DRV: 462 case OPROMPROM2DEVNAME: 463#if !defined(__i386) && !defined(__amd64) 464 case OPROMGETFBNAME: 465 case OPROMDEV2PROMNAME: 466 case OPROMREADY64: 467#endif /* !__i386 && !__amd64 */ 468 if ((mode & FREAD) == 0) { 469 return (EPERM); 470 } 471 break; 472 473#if !defined(__i386) && !defined(__amd64) 474 case WANBOOT_SETKEY: 475 if (!(mode & FWRITE)) 476 return (EPERM); 477 break; 478#endif /* !__i386 && !defined(__amd64) */ 479 480 default: 481 return (EINVAL); 482 } 483 484 /* 485 * Deal with SNAPSHOT and COPYOUT ioctls first 486 */ 487 switch (cmd) { 488 case OPROMCOPYOUT: 489 return (oprom_copyout(st, arg)); 490 491 case OPROMSNAPSHOT: 492 return (oprom_snapshot(st, arg)); 493 } 494 495 /* 496 * Copy in user argument length and allocation memory 497 * 498 * NB do not copyin the entire buffer we may not need 499 * to. userbufsize can be as big as 32 K. 500 */ 501 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0) 502 return (EFAULT); 503 504 if (userbufsize == 0 || userbufsize > OPROMMAXPARAM) 505 return (EINVAL); 506 507 opp = (struct openpromio *)kmem_zalloc( 508 userbufsize + sizeof (uint_t) + 1, KM_SLEEP); 509 510 /* 511 * Execute command 512 */ 513 switch (cmd) { 514 515 case OPROMGETOPT: 516 case OPROMGETPROP: 517 case OPROMGETPROPLEN: 518 519 if ((prom_is_openprom() == 0) || 520 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) { 521 error = EINVAL; 522 break; 523 } 524 525 /* 526 * The argument, a NULL terminated string, is a prop name. 527 */ 528 if ((error = oprom_copyinstr(arg, opp->oprom_array, 529 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) { 530 break; 531 } 532 (void) strcpy(propname, opp->oprom_array); 533 valsize = prom_getproplen(node_id, propname); 534 535 /* 536 * 4010173: 'name' is a property, but not an option. 537 */ 538 if ((cmd == OPROMGETOPT) && (strcmp("name", propname) == 0)) 539 valsize = -1; 540 541 if (cmd == OPROMGETPROPLEN) { 542 int proplen = valsize; 543 544 if (userbufsize < sizeof (int)) { 545 error = EINVAL; 546 break; 547 } 548 opp->oprom_size = valsize = sizeof (int); 549 bcopy(&proplen, opp->oprom_array, valsize); 550 } else if (valsize > 0 && valsize <= userbufsize) { 551 bzero(opp->oprom_array, valsize + 1); 552 (void) prom_getprop(node_id, propname, 553 opp->oprom_array); 554 opp->oprom_size = valsize; 555 if (valsize < userbufsize) 556 ++valsize; /* Forces NULL termination */ 557 /* If space permits */ 558 } else { 559 /* 560 * XXX: There is no error code if the buf is too small. 561 * which is consistent with the current behavior. 562 * 563 * NB: This clause also handles the non-error 564 * zero length (boolean) property value case. 565 */ 566 opp->oprom_size = 0; 567 (void) strcpy(opp->oprom_array, ""); 568 valsize = 1; 569 } 570 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0) 571 error = EFAULT; 572 break; 573 574 case OPROMNXTOPT: 575 case OPROMNXTPROP: 576 if ((prom_is_openprom() == 0) || 577 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) { 578 error = EINVAL; 579 break; 580 } 581 582 /* 583 * The argument, a NULL terminated string, is a prop name. 584 */ 585 if ((error = oprom_copyinstr(arg, opp->oprom_array, 586 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) { 587 break; 588 } 589 valbuf = (char *)prom_nextprop(node_id, opp->oprom_array, 590 propname); 591 valsize = strlen(valbuf); 592 593 /* 594 * 4010173: 'name' is a property, but it's not an option. 595 */ 596 if ((cmd == OPROMNXTOPT) && valsize && 597 (strcmp(valbuf, "name") == 0)) { 598 valbuf = (char *)prom_nextprop(node_id, "name", 599 propname); 600 valsize = strlen(valbuf); 601 } 602 603 if (valsize == 0) { 604 opp->oprom_size = 0; 605 } else if (++valsize <= userbufsize) { 606 opp->oprom_size = valsize; 607 bzero((caddr_t)opp->oprom_array, (size_t)valsize); 608 bcopy((caddr_t)valbuf, (caddr_t)opp->oprom_array, 609 (size_t)valsize); 610 } 611 612 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0) 613 error = EFAULT; 614 break; 615 616 case OPROMNEXT: 617 case OPROMCHILD: 618 case OPROMSETNODEID: 619 620 if (prom_is_openprom() == 0 || 621 userbufsize < sizeof (pnode_t)) { 622 error = EINVAL; 623 break; 624 } 625 626 /* 627 * The argument is a phandle. (aka pnode_t) 628 */ 629 if (copyin(((caddr_t)arg + sizeof (uint_t)), 630 opp->oprom_array, sizeof (pnode_t)) != 0) { 631 error = EFAULT; 632 break; 633 } 634 635 /* 636 * If pnode_t from userland is garbage, we 637 * could confuse the PROM. 638 */ 639 node_id = *(pnode_t *)opp->oprom_array; 640 if (oprom_checknodeid(node_id, st->current_id) == 0) { 641 cmn_err(CE_NOTE, "!nodeid 0x%x not found", 642 (int)node_id); 643 error = EINVAL; 644 break; 645 } 646 647 if (cmd == OPROMNEXT) 648 st->current_id = prom_nextnode(node_id); 649 else if (cmd == OPROMCHILD) 650 st->current_id = prom_childnode(node_id); 651 else { 652 /* OPROMSETNODEID */ 653 st->current_id = node_id; 654 break; 655 } 656 657 opp->oprom_size = sizeof (pnode_t); 658 *(pnode_t *)opp->oprom_array = st->current_id; 659 660 if (copyout(opp, (void *)arg, 661 sizeof (pnode_t) + sizeof (uint_t)) != 0) 662 error = EFAULT; 663 break; 664 665 case OPROMGETCONS: 666 /* 667 * Is openboot supported on this machine? 668 * This ioctl used to return the console device, 669 * information; this is now done via modctl() 670 * in libdevinfo. 671 */ 672 opp->oprom_size = sizeof (char); 673 674 opp->oprom_array[0] |= prom_is_openprom() ? 675 OPROMCONS_OPENPROM : 0; 676 677 /* 678 * The rest of the info is needed by Install to 679 * decide if graphics should be started. 680 */ 681 if ((getzoneid() == GLOBAL_ZONEID) && 682 plat_stdin_is_keyboard()) { 683 opp->oprom_array[0] |= OPROMCONS_STDIN_IS_KBD; 684 } 685 686 if ((getzoneid() == GLOBAL_ZONEID) && 687 plat_stdout_is_framebuffer()) { 688 opp->oprom_array[0] |= OPROMCONS_STDOUT_IS_FB; 689 } 690 691 if (copyout(opp, (void *)arg, 692 sizeof (char) + sizeof (uint_t)) != 0) 693 error = EFAULT; 694 break; 695 696 case OPROMGETBOOTARGS: { 697 extern char kern_bootargs[]; 698 699 valsize = strlen(kern_bootargs) + 1; 700 if (valsize > userbufsize) { 701 error = EINVAL; 702 break; 703 } 704 (void) strcpy(opp->oprom_array, kern_bootargs); 705 opp->oprom_size = valsize - 1; 706 707 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0) 708 error = EFAULT; 709 break; 710 } 711 712 /* 713 * convert a prom device path to an equivalent devfs path 714 */ 715 case OPROMPROM2DEVNAME: { 716 char *dev_name; 717 718 /* 719 * The input argument, a pathname, is a NULL terminated string. 720 */ 721 if ((error = oprom_copyinstr(arg, opp->oprom_array, 722 (size_t)userbufsize, MAXPATHLEN)) != 0) { 723 break; 724 } 725 726 dev_name = kmem_alloc(MAXPATHLEN, KM_SLEEP); 727 728 error = i_promname_to_devname(opp->oprom_array, dev_name); 729 if (error != 0) { 730 kmem_free(dev_name, MAXPATHLEN); 731 break; 732 } 733 valsize = opp->oprom_size = strlen(dev_name); 734 if (++valsize > userbufsize) { 735 kmem_free(dev_name, MAXPATHLEN); 736 error = EINVAL; 737 break; 738 } 739 (void) strcpy(opp->oprom_array, dev_name); 740 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0) 741 error = EFAULT; 742 743 kmem_free(dev_name, MAXPATHLEN); 744 break; 745 } 746 747 /* 748 * Convert a prom device path name to a driver name 749 */ 750 case OPROMPATH2DRV: { 751 char *drv_name; 752 major_t maj; 753 754 /* 755 * The input argument, a pathname, is a NULL terminated string. 756 */ 757 if ((error = oprom_copyinstr(arg, opp->oprom_array, 758 (size_t)userbufsize, MAXPATHLEN)) != 0) { 759 break; 760 } 761 762 /* 763 * convert path to a driver binding name 764 */ 765 maj = path_to_major((char *)opp->oprom_array); 766 if (maj == DDI_MAJOR_T_NONE) { 767 error = EINVAL; 768 break; 769 } 770 771 /* 772 * resolve any aliases 773 */ 774 if ((drv_name = ddi_major_to_name(maj)) == NULL) { 775 error = EINVAL; 776 break; 777 } 778 779 (void) strcpy(opp->oprom_array, drv_name); 780 opp->oprom_size = strlen(drv_name); 781 if (copyout(opp, (void *)arg, 782 sizeof (uint_t) + opp->oprom_size + 1) != 0) 783 error = EFAULT; 784 break; 785 } 786 787 case OPROMGETVERSION: 788 /* 789 * Get a string representing the running version of the 790 * prom. How to create such a string is platform dependent, 791 * so we just defer to a promif function. If no such 792 * association exists, the promif implementation 793 * may copy the string "unknown" into the given buffer, 794 * and return its length (incl. NULL terminator). 795 * 796 * We expect prom_version_name to return the actual 797 * length of the string, but copy at most userbufsize 798 * bytes into the given buffer, including NULL termination. 799 */ 800 801 valsize = prom_version_name(opp->oprom_array, userbufsize); 802 if (valsize < 0) { 803 error = EINVAL; 804 break; 805 } 806 807 /* 808 * copyout only the part of the user buffer we need to. 809 */ 810 if (copyout(opp, (void *)arg, 811 (size_t)(min((uint_t)valsize, userbufsize) + 812 sizeof (uint_t))) != 0) 813 error = EFAULT; 814 break; 815 816#if !defined(__i386) && !defined(__amd64) 817 case OPROMGETFBNAME: 818 /* 819 * Return stdoutpath, if it's a frame buffer. 820 * Yes, we are comparing a possibly longer string against 821 * the size we're really going to copy, but so what? 822 */ 823 if ((getzoneid() == GLOBAL_ZONEID) && 824 (prom_stdout_is_framebuffer() != 0) && 825 (userbufsize > strlen(prom_stdoutpath()))) { 826 prom_strip_options(prom_stdoutpath(), 827 opp->oprom_array); /* strip options and copy */ 828 valsize = opp->oprom_size = strlen(opp->oprom_array); 829 if (copyout(opp, (void *)arg, 830 valsize + 1 + sizeof (uint_t)) != 0) 831 error = EFAULT; 832 } else 833 error = EINVAL; 834 break; 835 836 /* 837 * Convert a logical or physical device path to prom device path 838 */ 839 case OPROMDEV2PROMNAME: { 840 char *prom_name; 841 842 /* 843 * The input argument, a pathname, is a NULL terminated string. 844 */ 845 if ((error = oprom_copyinstr(arg, opp->oprom_array, 846 (size_t)userbufsize, MAXPATHLEN)) != 0) { 847 break; 848 } 849 850 prom_name = kmem_alloc(userbufsize, KM_SLEEP); 851 852 /* 853 * convert the devfs path to an equivalent prom path 854 */ 855 error = i_devname_to_promname(opp->oprom_array, prom_name, 856 userbufsize); 857 858 if (error != 0) { 859 kmem_free(prom_name, userbufsize); 860 break; 861 } 862 863 for (valsize = 0; valsize < userbufsize; valsize++) { 864 opp->oprom_array[valsize] = prom_name[valsize]; 865 866 if ((valsize > 0) && (prom_name[valsize] == '\0') && 867 (prom_name[valsize-1] == '\0')) { 868 break; 869 } 870 } 871 opp->oprom_size = valsize; 872 873 kmem_free(prom_name, userbufsize); 874 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0) 875 error = EFAULT; 876 877 break; 878 } 879 880 case OPROMSETOPT: 881 case OPROMSETOPT2: { 882 int namebuflen; 883 int valbuflen; 884 885 if ((prom_is_openprom() == 0) || 886 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) { 887 error = EINVAL; 888 break; 889 } 890 891 /* 892 * The arguments are a property name and a value. 893 * Copy in the entire user buffer. 894 */ 895 if (copyin(((caddr_t)arg + sizeof (uint_t)), 896 opp->oprom_array, userbufsize) != 0) { 897 error = EFAULT; 898 break; 899 } 900 901 /* 902 * The property name is the first string, value second 903 */ 904 namebuflen = strlen(opp->oprom_array); 905 valbuf = opp->oprom_array + namebuflen + 1; 906 valbuflen = strlen(valbuf); 907 908 if (cmd == OPROMSETOPT) { 909 valsize = valbuflen + 1; /* +1 for the '\0' */ 910 } else { 911 if ((namebuflen + 1 + valbuflen + 1) > userbufsize) { 912 error = EINVAL; 913 break; 914 } 915 valsize = (opp->oprom_array + userbufsize) - valbuf; 916 } 917 918 /* 919 * 4010173: 'name' is not an option, but it is a property. 920 */ 921 if (strcmp(opp->oprom_array, "name") == 0) 922 error = EINVAL; 923 else if (prom_setprop(node_id, opp->oprom_array, 924 valbuf, valsize) < 0) 925 error = EINVAL; 926 927 break; 928 } 929 930 case OPROMREADY64: { 931 struct openprom_opr64 *opr = 932 (struct openprom_opr64 *)opp->oprom_array; 933 int i; 934 pnode_t id; 935 936 if (userbufsize < sizeof (*opr)) { 937 error = EINVAL; 938 break; 939 } 940 941 valsize = userbufsize - 942 offsetof(struct openprom_opr64, message); 943 944 i = prom_version_check(opr->message, valsize, &id); 945 opr->return_code = i; 946 opr->nodeid = (int)id; 947 948 valsize = offsetof(struct openprom_opr64, message); 949 valsize += strlen(opr->message) + 1; 950 951 /* 952 * copyout only the part of the user buffer we need to. 953 */ 954 if (copyout(opp, (void *)arg, 955 (size_t)(min((uint_t)valsize, userbufsize) + 956 sizeof (uint_t))) != 0) 957 error = EFAULT; 958 break; 959 960 } /* case OPROMREADY64 */ 961 962 case WANBOOT_SETKEY: { 963 struct wankeyio *wp; 964 int reslen; 965 int status; 966 int rv; 967 int i; 968 969 /* 970 * The argument is a struct wankeyio. Validate it as best 971 * we can. 972 */ 973 if (userbufsize != (sizeof (struct wankeyio))) { 974 error = EINVAL; 975 break; 976 } 977 if (copyin(((caddr_t)arg + sizeof (uint_t)), 978 opp->oprom_array, sizeof (struct wankeyio)) != 0) { 979 error = EFAULT; 980 break; 981 } 982 wp = (struct wankeyio *)opp->oprom_array; 983 984 /* check for key name and key size overflow */ 985 for (i = 0; i < WANBOOT_MAXKEYNAMELEN; i++) 986 if (wp->wk_keyname[i] == '\0') 987 break; 988 if ((i == WANBOOT_MAXKEYNAMELEN) || 989 (wp->wk_keysize > WANBOOT_MAXKEYLEN)) { 990 error = EINVAL; 991 break; 992 } 993 994 rv = prom_set_security_key(wp->wk_keyname, wp->wk_u.key, 995 wp->wk_keysize, &reslen, &status); 996 if (rv) 997 error = EIO; 998 else 999 switch (status) { 1000 case 0: 1001 error = 0; 1002 break; 1003 1004 case -2: /* out of key storage space */ 1005 error = ENOSPC; 1006 break; 1007 1008 case -3: /* key name or value too long */ 1009 error = EINVAL; 1010 break; 1011 1012 case -4: /* can't delete: no such key */ 1013 error = ENOENT; 1014 break; 1015 1016 case -1: /* unspecified error */ 1017 default: /* this should not happen */ 1018 error = EIO; 1019 break; 1020 } 1021 break; 1022 } /* case WANBOOT_SETKEY */ 1023#endif /* !__i386 && !__amd64 */ 1024 } /* switch (cmd) */ 1025 1026 kmem_free(opp, userbufsize + sizeof (uint_t) + 1); 1027 return (error); 1028} 1029 1030/*ARGSUSED*/ 1031static int 1032opromioctl(dev_t dev, int cmd, intptr_t arg, int mode, 1033 cred_t *credp, int *rvalp) 1034{ 1035 struct oprom_state *st; 1036 struct opromioctl_args arg_block; 1037 1038 if (getminor(dev) >= MAX_OPENS) 1039 return (ENXIO); 1040 1041 st = &oprom_state[getminor(dev)]; 1042 ASSERT(st->already_open); 1043 arg_block.st = st; 1044 arg_block.cmd = cmd; 1045 arg_block.arg = arg; 1046 arg_block.mode = mode; 1047 return (prom_tree_access(opromioctl_cb, &arg_block, &st->tree_gen)); 1048} 1049 1050/* 1051 * Copyin string and verify the actual string length is less than maxsize 1052 * specified by the caller. 1053 * 1054 * Currently, maxsize is either OBP_MAXPROPNAME for property names 1055 * or MAXPATHLEN for device path names. userbufsize is specified 1056 * by the userland caller. 1057 */ 1058static int 1059oprom_copyinstr(intptr_t arg, char *buf, size_t bufsize, size_t maxsize) 1060{ 1061 int error; 1062 size_t actual_len; 1063 1064 if ((error = copyinstr(((caddr_t)arg + sizeof (uint_t)), 1065 buf, bufsize, &actual_len)) != 0) { 1066 return (error); 1067 } 1068 if ((actual_len == 0) || (actual_len > maxsize)) { 1069 return (EINVAL); 1070 } 1071 1072 return (0); 1073} 1074 1075/* 1076 * Check pnode_t passed in from userland 1077 */ 1078static int 1079oprom_checknodeid(pnode_t node_id, pnode_t current_id) 1080{ 1081 int depth; 1082 pnode_t id[OBP_STACKDEPTH]; 1083 1084 /* 1085 * optimized path 1086 */ 1087 if (node_id == 0) { 1088 return (1); 1089 } 1090 if (node_id == OBP_BADNODE) { 1091 return (0); 1092 } 1093 if ((current_id != OBP_BADNODE) && ((node_id == current_id) || 1094 (node_id == prom_nextnode(current_id)) || 1095 (node_id == prom_childnode(current_id)))) { 1096 return (1); 1097 } 1098 1099 /* 1100 * long path: walk from root till we find node_id 1101 */ 1102 depth = 1; 1103 id[0] = prom_nextnode((pnode_t)0); 1104 1105 while (depth) { 1106 if (id[depth - 1] == node_id) 1107 return (1); /* node_id found */ 1108 1109 if (id[depth] = prom_childnode(id[depth - 1])) { 1110 depth++; 1111 continue; 1112 } 1113 1114 while (depth && 1115 ((id[depth - 1] = prom_nextnode(id[depth - 1])) == 0)) 1116 depth--; 1117 } 1118 return (0); /* node_id not found */ 1119} 1120 1121static int 1122oprom_copytree(struct oprom_state *st, uint_t flag) 1123{ 1124 ASSERT(st->snapshot == NULL && st->size == 0); 1125 return (oprom_copynode( 1126 prom_nextnode(0), flag, &st->snapshot, &st->size)); 1127} 1128 1129static int 1130oprom_snapshot(struct oprom_state *st, intptr_t arg) 1131{ 1132 uint_t flag; 1133 1134 if (oprom_setstate(st, IOC_SNAP) == -1) 1135 return (EBUSY); 1136 1137 /* copyin flag and create snapshot */ 1138 if ((copyin((void *)arg, &flag, sizeof (uint_t)) != 0) || 1139 (oprom_copytree(st, flag) != 0)) { 1140 (void) oprom_setstate(st, IOC_IDLE); 1141 return (EFAULT); 1142 } 1143 1144 1145 /* copyout the size of the snapshot */ 1146 flag = (uint_t)st->size; 1147 if (copyout(&flag, (void *)arg, sizeof (uint_t)) != 0) { 1148 kmem_free(st->snapshot, st->size); 1149 st->snapshot = NULL; 1150 st->size = 0; 1151 (void) oprom_setstate(st, IOC_IDLE); 1152 return (EFAULT); 1153 } 1154 1155 (void) oprom_setstate(st, IOC_DONE); 1156 return (0); 1157} 1158 1159static int 1160oprom_copyout(struct oprom_state *st, intptr_t arg) 1161{ 1162 int error = 0; 1163 uint_t size; 1164 1165 if (oprom_setstate(st, IOC_COPY) == -1) 1166 return (EBUSY); 1167 1168 /* copyin size and copyout snapshot */ 1169 if (copyin((void *)arg, &size, sizeof (uint_t)) != 0) 1170 error = EFAULT; 1171 else if (size < st->size) 1172 error = EINVAL; 1173 else if (copyout(st->snapshot, (void *)arg, st->size) != 0) 1174 error = EFAULT; 1175 1176 if (error) { 1177 /* 1178 * on error keep the snapshot until a successful 1179 * copyout or when the driver is closed. 1180 */ 1181 (void) oprom_setstate(st, IOC_DONE); 1182 return (error); 1183 } 1184 1185 kmem_free(st->snapshot, st->size); 1186 st->snapshot = NULL; 1187 st->size = 0; 1188 (void) oprom_setstate(st, IOC_IDLE); 1189 return (0); 1190} 1191 1192/* 1193 * Copy all properties of nodeid into a single packed nvlist 1194 */ 1195static int 1196oprom_copyprop(pnode_t nodeid, uint_t flag, nvlist_t *nvl) 1197{ 1198 int proplen; 1199 char *propname, *propval, *buf1, *buf2; 1200 1201 ASSERT(nvl != NULL); 1202 1203 /* 1204 * non verbose mode, get the "name" property only 1205 */ 1206 if (flag == 0) { 1207 proplen = prom_getproplen(nodeid, "name"); 1208 if (proplen <= 0) { 1209 cmn_err(CE_WARN, 1210 "failed to get the name of openprom node 0x%x", 1211 nodeid); 1212 (void) nvlist_add_string(nvl, "name", ""); 1213 return (0); 1214 } 1215 propval = kmem_zalloc(proplen + 1, KM_SLEEP); 1216 (void) prom_getprop(nodeid, "name", propval); 1217 (void) nvlist_add_string(nvl, "name", propval); 1218 kmem_free(propval, proplen + 1); 1219 return (0); 1220 } 1221 1222 /* 1223 * Ask for first property by passing a NULL string 1224 */ 1225 buf1 = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP); 1226 buf2 = kmem_zalloc(OBP_MAXPROPNAME, KM_SLEEP); 1227 buf1[0] = '\0'; 1228 while (propname = (char *)prom_nextprop(nodeid, buf1, buf2)) { 1229 if (strlen(propname) == 0) 1230 break; /* end of prop list */ 1231 (void) strcpy(buf1, propname); 1232 1233 proplen = prom_getproplen(nodeid, propname); 1234 if (proplen == 0) { 1235 /* boolean property */ 1236 (void) nvlist_add_boolean(nvl, propname); 1237 continue; 1238 } 1239 /* add 1 for null termination in case of a string */ 1240 propval = kmem_zalloc(proplen + 1, KM_SLEEP); 1241 (void) prom_getprop(nodeid, propname, propval); 1242 (void) nvlist_add_byte_array(nvl, propname, 1243 (uchar_t *)propval, proplen + 1); 1244 kmem_free(propval, proplen + 1); 1245 bzero(buf2, OBP_MAXPROPNAME); 1246 } 1247 1248 kmem_free(buf1, OBP_MAXPROPNAME); 1249 kmem_free(buf2, OBP_MAXPROPNAME); 1250 1251 return (0); 1252} 1253 1254/* 1255 * Copy all children and descendents into a a packed nvlist 1256 */ 1257static int 1258oprom_copychild(pnode_t nodeid, uint_t flag, char **buf, size_t *size) 1259{ 1260 nvlist_t *nvl; 1261 pnode_t child = prom_childnode(nodeid); 1262 1263 if (child == 0) 1264 return (0); 1265 1266 (void) nvlist_alloc(&nvl, 0, KM_SLEEP); 1267 while (child != 0) { 1268 char *nodebuf = NULL; 1269 size_t nodesize = 0; 1270 if (oprom_copynode(child, flag, &nodebuf, &nodesize)) { 1271 nvlist_free(nvl); 1272 cmn_err(CE_WARN, "failed to copy nodeid 0x%x", child); 1273 return (-1); 1274 } 1275 (void) nvlist_add_byte_array(nvl, "node", 1276 (uchar_t *)nodebuf, nodesize); 1277 kmem_free(nodebuf, nodesize); 1278 child = prom_nextnode(child); 1279 } 1280 1281 (void) nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP); 1282 nvlist_free(nvl); 1283 return (0); 1284} 1285 1286/* 1287 * Copy a node into a packed nvlist 1288 */ 1289static int 1290oprom_copynode(pnode_t nodeid, uint_t flag, char **buf, size_t *size) 1291{ 1292 int error = 0; 1293 nvlist_t *nvl; 1294 char *childlist = NULL; 1295 size_t childsize = 0; 1296 1297 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 1298 ASSERT(nvl != NULL); 1299 1300 /* @nodeid -- @ is not a legal char in a 1275 property name */ 1301 (void) nvlist_add_int32(nvl, "@nodeid", (int32_t)nodeid); 1302 1303 /* properties */ 1304 if (error = oprom_copyprop(nodeid, flag, nvl)) 1305 goto fail; 1306 1307 /* children */ 1308 error = oprom_copychild(nodeid, flag, &childlist, &childsize); 1309 if (error != 0) 1310 goto fail; 1311 if (childlist != NULL) { 1312 (void) nvlist_add_byte_array(nvl, "@child", 1313 (uchar_t *)childlist, (uint_t)childsize); 1314 kmem_free(childlist, childsize); 1315 } 1316 1317 /* pack into contiguous buffer */ 1318 error = nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP); 1319 1320fail: 1321 nvlist_free(nvl); 1322 return (error); 1323} 1324 1325/* 1326 * The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT. 1327 * This function encapsulates the state machine: 1328 * 1329 * -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY -> 1330 * | SNAPSHOT COPYOUT | 1331 * -------------------------------------------------- 1332 * 1333 * Returns 0 on success and -1 on failure 1334 */ 1335static int 1336oprom_setstate(struct oprom_state *st, int16_t new_state) 1337{ 1338 int ret = 0; 1339 1340 mutex_enter(&oprom_lock); 1341 switch (new_state) { 1342 case IOC_IDLE: 1343 case IOC_DONE: 1344 break; 1345 case IOC_SNAP: 1346 if (st->ioc_state != IOC_IDLE) 1347 ret = -1; 1348 break; 1349 case IOC_COPY: 1350 if (st->ioc_state != IOC_DONE) 1351 ret = -1; 1352 break; 1353 default: 1354 ret = -1; 1355 } 1356 1357 if (ret == 0) 1358 st->ioc_state = new_state; 1359 else 1360 cmn_err(CE_NOTE, "incorrect state transition from %d to %d", 1361 st->ioc_state, new_state); 1362 mutex_exit(&oprom_lock); 1363 return (ret); 1364} 1365