1 /* 2 * Linux/SPARC PROM Configuration Driver 3 * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) 4 * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) 5 * 6 * This character device driver allows user programs to access the 7 * PROM device tree. It is compatible with the SunOS /dev/openprom 8 * driver and the NetBSD /dev/openprom driver. The SunOS eeprom 9 * utility works without any modifications. 10 * 11 * The driver uses a minor number under the misc device major. The 12 * file read/write mode determines the type of access to the PROM. 13 * Interrupts are disabled whenever the driver calls into the PROM for 14 * sanity's sake. 15 */ 16 17 /* This program is free software; you can redistribute it and/or 18 * modify it under the terms of the GNU General Public License as 19 * published by the Free Software Foundation; either version 2 of the 20 * License, or (at your option) any later version. 21 * 22 * This program is distributed in the hope that it will be useful, but 23 * WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 * General Public License for more details. 26 * 27 * You should have received a copy of the GNU General Public License 28 * along with this program; if not, write to the Free Software 29 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 30 */ 31 32 #include <linux/module.h> 33 #include <linux/kernel.h> 34 #include <linux/sched.h> 35 #include <linux/errno.h> 36 #include <linux/slab.h> 37 #include <linux/string.h> 38 #include <linux/miscdevice.h> 39 #include <linux/init.h> 40 #include <linux/fs.h> 41 #include <asm/oplib.h> 42 #include <asm/prom.h> 43 #include <asm/system.h> 44 #include <asm/uaccess.h> 45 #include <asm/openpromio.h> 46 #ifdef CONFIG_PCI 47 #include <linux/pci.h> 48 #include <asm/pbm.h> 49 #endif 50 51 MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost (ecd@skynet.be)"); 52 MODULE_DESCRIPTION("OPENPROM Configuration Driver"); 53 MODULE_LICENSE("GPL"); 54 MODULE_VERSION("1.0"); 55 56 /* Private data kept by the driver for each descriptor. */ 57 typedef struct openprom_private_data 58 { 59 struct device_node *current_node; /* Current node for SunOS ioctls. */ 60 struct device_node *lastnode; /* Last valid node used by BSD ioctls. */ 61 } DATA; 62 63 /* ID of the PROM node containing all of the EEPROM options. */ 64 static struct device_node *options_node; 65 66 /* 67 * Copy an openpromio structure into kernel space from user space. 68 * This routine does error checking to make sure that all memory 69 * accesses are within bounds. A pointer to the allocated openpromio 70 * structure will be placed in "*opp_p". Return value is the length 71 * of the user supplied buffer. 72 */ 73 static int copyin(struct openpromio __user *info, struct openpromio **opp_p) 74 { 75 unsigned int bufsize; 76 77 if (!info || !opp_p) 78 return -EFAULT; 79 80 if (get_user(bufsize, &info->oprom_size)) 81 return -EFAULT; 82 83 if (bufsize == 0) 84 return -EINVAL; 85 86 /* If the bufsize is too large, just limit it. 87 * Fix from Jason Rappleye. 88 */ 89 if (bufsize > OPROMMAXPARAM) 90 bufsize = OPROMMAXPARAM; 91 92 if (!(*opp_p = kzalloc(sizeof(int) + bufsize + 1, GFP_KERNEL))) 93 return -ENOMEM; 94 95 if (copy_from_user(&(*opp_p)->oprom_array, 96 &info->oprom_array, bufsize)) { 97 kfree(*opp_p); 98 return -EFAULT; 99 } 100 return bufsize; 101 } 102 103 static int getstrings(struct openpromio __user *info, struct openpromio **opp_p) 104 { 105 int n, bufsize; 106 char c; 107 108 if (!info || !opp_p) 109 return -EFAULT; 110 111 if (!(*opp_p = kzalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL))) 112 return -ENOMEM; 113 114 (*opp_p)->oprom_size = 0; 115 116 n = bufsize = 0; 117 while ((n < 2) && (bufsize < OPROMMAXPARAM)) { 118 if (get_user(c, &info->oprom_array[bufsize])) { 119 kfree(*opp_p); 120 return -EFAULT; 121 } 122 if (c == '\0') 123 n++; 124 (*opp_p)->oprom_array[bufsize++] = c; 125 } 126 if (!n) { 127 kfree(*opp_p); 128 return -EINVAL; 129 } 130 return bufsize; 131 } 132 133 /* 134 * Copy an openpromio structure in kernel space back to user space. 135 */ 136 static int copyout(void __user *info, struct openpromio *opp, int len) 137 { 138 if (copy_to_user(info, opp, len)) 139 return -EFAULT; 140 return 0; 141 } 142 143 static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize) 144 { 145 void *pval; 146 int len; 147 148 pval = of_get_property(dp, op->oprom_array, &len); 149 if (!pval || len <= 0 || len > bufsize) 150 return copyout(argp, op, sizeof(int)); 151 152 memcpy(op->oprom_array, pval, len); 153 op->oprom_array[len] = '\0'; 154 op->oprom_size = len; 155 156 return copyout(argp, op, sizeof(int) + bufsize); 157 } 158 159 static int opromnxtprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize) 160 { 161 struct property *prop; 162 int len; 163 164 if (op->oprom_array[0] == '\0') { 165 prop = dp->properties; 166 if (!prop) 167 return copyout(argp, op, sizeof(int)); 168 len = strlen(prop->name); 169 } else { 170 prop = of_find_property(dp, op->oprom_array, NULL); 171 172 if (!prop || 173 !prop->next || 174 (len = strlen(prop->next->name)) + 1 > bufsize) 175 return copyout(argp, op, sizeof(int)); 176 177 prop = prop->next; 178 } 179 180 memcpy(op->oprom_array, prop->name, len); 181 op->oprom_array[len] = '\0'; 182 op->oprom_size = ++len; 183 184 return copyout(argp, op, sizeof(int) + bufsize); 185 } 186 187 static int opromsetopt(struct device_node *dp, struct openpromio *op, int bufsize) 188 { 189 char *buf = op->oprom_array + strlen(op->oprom_array) + 1; 190 int len = op->oprom_array + bufsize - buf; 191 192 return of_set_property(options_node, op->oprom_array, buf, len); 193 } 194 195 static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data) 196 { 197 phandle ph; 198 199 BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); 200 201 if (bufsize < sizeof(phandle)) 202 return -EINVAL; 203 204 ph = *((int *) op->oprom_array); 205 if (ph) { 206 dp = of_find_node_by_phandle(ph); 207 if (!dp) 208 return -EINVAL; 209 210 switch (cmd) { 211 case OPROMNEXT: 212 dp = dp->sibling; 213 break; 214 215 case OPROMCHILD: 216 dp = dp->child; 217 break; 218 219 case OPROMSETCUR: 220 default: 221 break; 222 }; 223 } else { 224 /* Sibling of node zero is the root node. */ 225 if (cmd != OPROMNEXT) 226 return -EINVAL; 227 228 dp = of_find_node_by_path("/"); 229 } 230 231 ph = 0; 232 if (dp) 233 ph = dp->node; 234 235 data->current_node = dp; 236 *((int *) op->oprom_array) = ph; 237 op->oprom_size = sizeof(phandle); 238 239 return copyout(argp, op, bufsize + sizeof(int)); 240 } 241 242 static int oprompci2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data) 243 { 244 int err = -EINVAL; 245 246 if (bufsize >= 2*sizeof(int)) { 247 #ifdef CONFIG_PCI 248 struct pci_dev *pdev; 249 struct pcidev_cookie *pcp; 250 pdev = pci_find_slot (((int *) op->oprom_array)[0], 251 ((int *) op->oprom_array)[1]); 252 253 pcp = pdev->sysdata; 254 if (pcp != NULL) { 255 dp = pcp->prom_node; 256 data->current_node = dp; 257 *((int *)op->oprom_array) = dp->node; 258 op->oprom_size = sizeof(int); 259 err = copyout(argp, op, bufsize + sizeof(int)); 260 } 261 #endif 262 } 263 264 return err; 265 } 266 267 static int oprompath2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data) 268 { 269 dp = of_find_node_by_path(op->oprom_array); 270 data->current_node = dp; 271 *((int *)op->oprom_array) = dp->node; 272 op->oprom_size = sizeof(int); 273 274 return copyout(argp, op, bufsize + sizeof(int)); 275 } 276 277 static int opromgetbootargs(void __user *argp, struct openpromio *op, int bufsize) 278 { 279 char *buf = saved_command_line; 280 int len = strlen(buf); 281 282 if (len > bufsize) 283 return -EINVAL; 284 285 strcpy(op->oprom_array, buf); 286 op->oprom_size = len; 287 288 return copyout(argp, op, bufsize + sizeof(int)); 289 } 290 291 /* 292 * SunOS and Solaris /dev/openprom ioctl calls. 293 */ 294 static int openprom_sunos_ioctl(struct inode * inode, struct file * file, 295 unsigned int cmd, unsigned long arg, 296 struct device_node *dp) 297 { 298 DATA *data = file->private_data; 299 struct openpromio *opp; 300 int bufsize, error = 0; 301 static int cnt; 302 void __user *argp = (void __user *)arg; 303 304 if (cmd == OPROMSETOPT) 305 bufsize = getstrings(argp, &opp); 306 else 307 bufsize = copyin(argp, &opp); 308 309 if (bufsize < 0) 310 return bufsize; 311 312 switch (cmd) { 313 case OPROMGETOPT: 314 case OPROMGETPROP: 315 error = opromgetprop(argp, dp, opp, bufsize); 316 break; 317 318 case OPROMNXTOPT: 319 case OPROMNXTPROP: 320 error = opromnxtprop(argp, dp, opp, bufsize); 321 break; 322 323 case OPROMSETOPT: 324 case OPROMSETOPT2: 325 error = opromsetopt(dp, opp, bufsize); 326 break; 327 328 case OPROMNEXT: 329 case OPROMCHILD: 330 case OPROMSETCUR: 331 error = opromnext(argp, cmd, dp, opp, bufsize, data); 332 break; 333 334 case OPROMPCI2NODE: 335 error = oprompci2node(argp, dp, opp, bufsize, data); 336 break; 337 338 case OPROMPATH2NODE: 339 error = oprompath2node(argp, dp, opp, bufsize, data); 340 break; 341 342 case OPROMGETBOOTARGS: 343 error = opromgetbootargs(argp, opp, bufsize); 344 break; 345 346 case OPROMU2P: 347 case OPROMGETCONS: 348 case OPROMGETFBNAME: 349 if (cnt++ < 10) 350 printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n"); 351 error = -EINVAL; 352 break; 353 default: 354 if (cnt++ < 10) 355 printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); 356 error = -EINVAL; 357 break; 358 } 359 360 kfree(opp); 361 return error; 362 } 363 364 static struct device_node *get_node(phandle n, DATA *data) 365 { 366 struct device_node *dp = of_find_node_by_phandle(n); 367 368 if (dp) 369 data->lastnode = dp; 370 371 return dp; 372 } 373 374 /* Copy in a whole string from userspace into kernelspace. */ 375 static int copyin_string(char __user *user, size_t len, char **ptr) 376 { 377 char *tmp; 378 379 if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0) 380 return -EINVAL; 381 382 tmp = kmalloc(len + 1, GFP_KERNEL); 383 if (!tmp) 384 return -ENOMEM; 385 386 if (copy_from_user(tmp, user, len)) { 387 kfree(tmp); 388 return -EFAULT; 389 } 390 391 tmp[len] = '\0'; 392 393 *ptr = tmp; 394 395 return 0; 396 } 397 398 /* 399 * NetBSD /dev/openprom ioctl calls. 400 */ 401 static int opiocget(void __user *argp, DATA *data) 402 { 403 struct opiocdesc op; 404 struct device_node *dp; 405 char *str; 406 void *pval; 407 int err, len; 408 409 if (copy_from_user(&op, argp, sizeof(op))) 410 return -EFAULT; 411 412 dp = get_node(op.op_nodeid, data); 413 414 err = copyin_string(op.op_name, op.op_namelen, &str); 415 if (err) 416 return err; 417 418 pval = of_get_property(dp, str, &len); 419 err = 0; 420 if (!pval || len > op.op_buflen) { 421 err = -EINVAL; 422 } else { 423 op.op_buflen = len; 424 if (copy_to_user(argp, &op, sizeof(op)) || 425 copy_to_user(op.op_buf, pval, len)) 426 err = -EFAULT; 427 } 428 kfree(str); 429 430 return err; 431 } 432 433 static int opiocnextprop(void __user *argp, DATA *data) 434 { 435 struct opiocdesc op; 436 struct device_node *dp; 437 struct property *prop; 438 char *str; 439 int err, len; 440 441 if (copy_from_user(&op, argp, sizeof(op))) 442 return -EFAULT; 443 444 dp = get_node(op.op_nodeid, data); 445 if (!dp) 446 return -EINVAL; 447 448 err = copyin_string(op.op_name, op.op_namelen, &str); 449 if (err) 450 return err; 451 452 if (str[0] == '\0') { 453 prop = dp->properties; 454 } else { 455 prop = of_find_property(dp, str, NULL); 456 if (prop) 457 prop = prop->next; 458 } 459 kfree(str); 460 461 if (!prop) 462 len = 0; 463 else 464 len = prop->length; 465 466 if (len > op.op_buflen) 467 len = op.op_buflen; 468 469 if (copy_to_user(argp, &op, sizeof(op))) 470 return -EFAULT; 471 472 if (len && 473 copy_to_user(op.op_buf, prop->value, len)) 474 return -EFAULT; 475 476 return 0; 477 } 478 479 static int opiocset(void __user *argp, DATA *data) 480 { 481 struct opiocdesc op; 482 struct device_node *dp; 483 char *str, *tmp; 484 int err; 485 486 if (copy_from_user(&op, argp, sizeof(op))) 487 return -EFAULT; 488 489 dp = get_node(op.op_nodeid, data); 490 if (!dp) 491 return -EINVAL; 492 493 err = copyin_string(op.op_name, op.op_namelen, &str); 494 if (err) 495 return err; 496 497 err = copyin_string(op.op_buf, op.op_buflen, &tmp); 498 if (err) { 499 kfree(str); 500 return err; 501 } 502 503 err = of_set_property(dp, str, tmp, op.op_buflen); 504 505 kfree(str); 506 kfree(tmp); 507 508 return err; 509 } 510 511 static int opiocgetnext(unsigned int cmd, void __user *argp) 512 { 513 struct device_node *dp; 514 phandle nd; 515 516 BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); 517 518 if (copy_from_user(&nd, argp, sizeof(phandle))) 519 return -EFAULT; 520 521 if (nd == 0) { 522 if (cmd != OPIOCGETNEXT) 523 return -EINVAL; 524 dp = of_find_node_by_path("/"); 525 } else { 526 dp = of_find_node_by_phandle(nd); 527 nd = 0; 528 if (dp) { 529 if (cmd == OPIOCGETNEXT) 530 dp = dp->sibling; 531 else 532 dp = dp->child; 533 } 534 } 535 if (dp) 536 nd = dp->node; 537 if (copy_to_user(argp, &nd, sizeof(phandle))) 538 return -EFAULT; 539 540 return 0; 541 } 542 543 static int openprom_bsd_ioctl(struct inode * inode, struct file * file, 544 unsigned int cmd, unsigned long arg) 545 { 546 DATA *data = (DATA *) file->private_data; 547 void __user *argp = (void __user *)arg; 548 int err; 549 550 switch (cmd) { 551 case OPIOCGET: 552 err = opiocget(argp, data); 553 break; 554 555 case OPIOCNEXTPROP: 556 err = opiocnextprop(argp, data); 557 break; 558 559 case OPIOCSET: 560 err = opiocset(argp, data); 561 break; 562 563 case OPIOCGETOPTNODE: 564 BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); 565 566 if (copy_to_user(argp, &options_node->node, sizeof(phandle))) 567 return -EFAULT; 568 569 return 0; 570 571 case OPIOCGETNEXT: 572 case OPIOCGETCHILD: 573 err = opiocgetnext(cmd, argp); 574 break; 575 576 default: 577 return -EINVAL; 578 579 }; 580 581 return err; 582 } 583 584 585 /* 586 * Handoff control to the correct ioctl handler. 587 */ 588 static int openprom_ioctl(struct inode * inode, struct file * file, 589 unsigned int cmd, unsigned long arg) 590 { 591 DATA *data = (DATA *) file->private_data; 592 593 switch (cmd) { 594 case OPROMGETOPT: 595 case OPROMNXTOPT: 596 if ((file->f_mode & FMODE_READ) == 0) 597 return -EPERM; 598 return openprom_sunos_ioctl(inode, file, cmd, arg, 599 options_node); 600 601 case OPROMSETOPT: 602 case OPROMSETOPT2: 603 if ((file->f_mode & FMODE_WRITE) == 0) 604 return -EPERM; 605 return openprom_sunos_ioctl(inode, file, cmd, arg, 606 options_node); 607 608 case OPROMNEXT: 609 case OPROMCHILD: 610 case OPROMGETPROP: 611 case OPROMNXTPROP: 612 if ((file->f_mode & FMODE_READ) == 0) 613 return -EPERM; 614 return openprom_sunos_ioctl(inode, file, cmd, arg, 615 data->current_node); 616 617 case OPROMU2P: 618 case OPROMGETCONS: 619 case OPROMGETFBNAME: 620 case OPROMGETBOOTARGS: 621 case OPROMSETCUR: 622 case OPROMPCI2NODE: 623 case OPROMPATH2NODE: 624 if ((file->f_mode & FMODE_READ) == 0) 625 return -EPERM; 626 return openprom_sunos_ioctl(inode, file, cmd, arg, 0); 627 628 case OPIOCGET: 629 case OPIOCNEXTPROP: 630 case OPIOCGETOPTNODE: 631 case OPIOCGETNEXT: 632 case OPIOCGETCHILD: 633 if ((file->f_mode & FMODE_READ) == 0) 634 return -EBADF; 635 return openprom_bsd_ioctl(inode,file,cmd,arg); 636 637 case OPIOCSET: 638 if ((file->f_mode & FMODE_WRITE) == 0) 639 return -EBADF; 640 return openprom_bsd_ioctl(inode,file,cmd,arg); 641 642 default: 643 return -EINVAL; 644 }; 645 } 646 647 static long openprom_compat_ioctl(struct file *file, unsigned int cmd, 648 unsigned long arg) 649 { 650 long rval = -ENOTTY; 651 652 /* 653 * SunOS/Solaris only, the NetBSD one's have embedded pointers in 654 * the arg which we'd need to clean up... 655 */ 656 switch (cmd) { 657 case OPROMGETOPT: 658 case OPROMSETOPT: 659 case OPROMNXTOPT: 660 case OPROMSETOPT2: 661 case OPROMNEXT: 662 case OPROMCHILD: 663 case OPROMGETPROP: 664 case OPROMNXTPROP: 665 case OPROMU2P: 666 case OPROMGETCONS: 667 case OPROMGETFBNAME: 668 case OPROMGETBOOTARGS: 669 case OPROMSETCUR: 670 case OPROMPCI2NODE: 671 case OPROMPATH2NODE: 672 rval = openprom_ioctl(file->f_dentry->d_inode, file, cmd, arg); 673 break; 674 } 675 676 return rval; 677 } 678 679 static int openprom_open(struct inode * inode, struct file * file) 680 { 681 DATA *data; 682 683 data = kmalloc(sizeof(DATA), GFP_KERNEL); 684 if (!data) 685 return -ENOMEM; 686 687 data->current_node = of_find_node_by_path("/"); 688 data->lastnode = data->current_node; 689 file->private_data = (void *) data; 690 691 return 0; 692 } 693 694 static int openprom_release(struct inode * inode, struct file * file) 695 { 696 kfree(file->private_data); 697 return 0; 698 } 699 700 static struct file_operations openprom_fops = { 701 .owner = THIS_MODULE, 702 .llseek = no_llseek, 703 .ioctl = openprom_ioctl, 704 .compat_ioctl = openprom_compat_ioctl, 705 .open = openprom_open, 706 .release = openprom_release, 707 }; 708 709 static struct miscdevice openprom_dev = { 710 .minor = SUN_OPENPROM_MINOR, 711 .name = "openprom", 712 .fops = &openprom_fops, 713 }; 714 715 static int __init openprom_init(void) 716 { 717 struct device_node *dp; 718 int err; 719 720 err = misc_register(&openprom_dev); 721 if (err) 722 return err; 723 724 dp = of_find_node_by_path("/"); 725 dp = dp->child; 726 while (dp) { 727 if (!strcmp(dp->name, "options")) 728 break; 729 dp = dp->sibling; 730 } 731 options_node = dp; 732 733 if (!options_node) { 734 misc_deregister(&openprom_dev); 735 return -EIO; 736 } 737 738 return 0; 739 } 740 741 static void __exit openprom_cleanup(void) 742 { 743 misc_deregister(&openprom_dev); 744 } 745 746 module_init(openprom_init); 747 module_exit(openprom_cleanup); 748