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