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