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