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_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->phandle;
 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->phandle;
 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 long openprom_sunos_ioctl(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        mutex_lock(&openprom_mutex);
 320
 321        switch (cmd) {
 322        case OPROMGETOPT:
 323        case OPROMGETPROP:
 324                error = opromgetprop(argp, dp, opp, bufsize);
 325                break;
 326
 327        case OPROMNXTOPT:
 328        case OPROMNXTPROP:
 329                error = opromnxtprop(argp, dp, opp, bufsize);
 330                break;
 331
 332        case OPROMSETOPT:
 333        case OPROMSETOPT2:
 334                error = opromsetopt(dp, opp, bufsize);
 335                break;
 336
 337        case OPROMNEXT:
 338        case OPROMCHILD:
 339        case OPROMSETCUR:
 340                error = opromnext(argp, cmd, dp, opp, bufsize, data);
 341                break;
 342
 343        case OPROMPCI2NODE:
 344                error = oprompci2node(argp, dp, opp, bufsize, data);
 345                break;
 346
 347        case OPROMPATH2NODE:
 348                error = oprompath2node(argp, dp, opp, bufsize, data);
 349                break;
 350
 351        case OPROMGETBOOTARGS:
 352                error = opromgetbootargs(argp, opp, bufsize);
 353                break;
 354
 355        case OPROMU2P:
 356        case OPROMGETCONS:
 357        case OPROMGETFBNAME:
 358                if (cnt++ < 10)
 359                        printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
 360                error = -EINVAL;
 361                break;
 362        default:
 363                if (cnt++ < 10)
 364                        printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
 365                error = -EINVAL;
 366                break;
 367        }
 368
 369        kfree(opp);
 370        mutex_unlock(&openprom_mutex);
 371
 372        return error;
 373}
 374
 375static struct device_node *get_node(phandle n, DATA *data)
 376{
 377        struct device_node *dp = of_find_node_by_phandle(n);
 378
 379        if (dp)
 380                data->lastnode = dp;
 381
 382        return dp;
 383}
 384
 385/* Copy in a whole string from userspace into kernelspace. */
 386static char * copyin_string(char __user *user, size_t len)
 387{
 388        if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
 389                return ERR_PTR(-EINVAL);
 390
 391        return memdup_user_nul(user, len);
 392}
 393
 394/*
 395 *      NetBSD /dev/openprom ioctl calls.
 396 */
 397static int opiocget(void __user *argp, DATA *data)
 398{
 399        struct opiocdesc op;
 400        struct device_node *dp;
 401        char *str;
 402        const void *pval;
 403        int err, len;
 404
 405        if (copy_from_user(&op, argp, sizeof(op)))
 406                return -EFAULT;
 407
 408        dp = get_node(op.op_nodeid, data);
 409
 410        str = copyin_string(op.op_name, op.op_namelen);
 411        if (IS_ERR(str))
 412                return PTR_ERR(str);
 413
 414        pval = of_get_property(dp, str, &len);
 415        err = 0;
 416        if (!pval || len > op.op_buflen) {
 417                err = -EINVAL;
 418        } else {
 419                op.op_buflen = len;
 420                if (copy_to_user(argp, &op, sizeof(op)) ||
 421                    copy_to_user(op.op_buf, pval, len))
 422                        err = -EFAULT;
 423        }
 424        kfree(str);
 425
 426        return err;
 427}
 428
 429static int opiocnextprop(void __user *argp, DATA *data)
 430{
 431        struct opiocdesc op;
 432        struct device_node *dp;
 433        struct property *prop;
 434        char *str;
 435        int len;
 436
 437        if (copy_from_user(&op, argp, sizeof(op)))
 438                return -EFAULT;
 439
 440        dp = get_node(op.op_nodeid, data);
 441        if (!dp)
 442                return -EINVAL;
 443
 444        str = copyin_string(op.op_name, op.op_namelen);
 445        if (IS_ERR(str))
 446                return PTR_ERR(str);
 447
 448        if (str[0] == '\0') {
 449                prop = dp->properties;
 450        } else {
 451                prop = of_find_property(dp, str, NULL);
 452                if (prop)
 453                        prop = prop->next;
 454        }
 455        kfree(str);
 456
 457        if (!prop)
 458                len = 0;
 459        else
 460                len = prop->length;
 461
 462        if (len > op.op_buflen)
 463                len = op.op_buflen;
 464
 465        if (copy_to_user(argp, &op, sizeof(op)))
 466                return -EFAULT;
 467
 468        if (len &&
 469            copy_to_user(op.op_buf, prop->value, len))
 470                return -EFAULT;
 471
 472        return 0;
 473}
 474
 475static int opiocset(void __user *argp, DATA *data)
 476{
 477        struct opiocdesc op;
 478        struct device_node *dp;
 479        char *str, *tmp;
 480        int err;
 481
 482        if (copy_from_user(&op, argp, sizeof(op)))
 483                return -EFAULT;
 484
 485        dp = get_node(op.op_nodeid, data);
 486        if (!dp)
 487                return -EINVAL;
 488
 489        str = copyin_string(op.op_name, op.op_namelen);
 490        if (IS_ERR(str))
 491                return PTR_ERR(str);
 492
 493        tmp = copyin_string(op.op_buf, op.op_buflen);
 494        if (IS_ERR(tmp)) {
 495                kfree(str);
 496                return PTR_ERR(tmp);
 497        }
 498
 499        err = of_set_property(dp, str, tmp, op.op_buflen);
 500
 501        kfree(str);
 502        kfree(tmp);
 503
 504        return err;
 505}
 506
 507static int opiocgetnext(unsigned int cmd, void __user *argp)
 508{
 509        struct device_node *dp;
 510        phandle nd;
 511
 512        BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
 513
 514        if (copy_from_user(&nd, argp, sizeof(phandle)))
 515                return -EFAULT;
 516
 517        if (nd == 0) {
 518                if (cmd != OPIOCGETNEXT)
 519                        return -EINVAL;
 520                dp = of_find_node_by_path("/");
 521        } else {
 522                dp = of_find_node_by_phandle(nd);
 523                nd = 0;
 524                if (dp) {
 525                        if (cmd == OPIOCGETNEXT)
 526                                dp = dp->sibling;
 527                        else
 528                                dp = dp->child;
 529                }
 530        }
 531        if (dp)
 532                nd = dp->phandle;
 533        if (copy_to_user(argp, &nd, sizeof(phandle)))
 534                return -EFAULT;
 535
 536        return 0;
 537}
 538
 539static int openprom_bsd_ioctl(struct file * file,
 540                              unsigned int cmd, unsigned long arg)
 541{
 542        DATA *data = file->private_data;
 543        void __user *argp = (void __user *)arg;
 544        int err;
 545
 546        mutex_lock(&openprom_mutex);
 547        switch (cmd) {
 548        case OPIOCGET:
 549                err = opiocget(argp, data);
 550                break;
 551
 552        case OPIOCNEXTPROP:
 553                err = opiocnextprop(argp, data);
 554                break;
 555
 556        case OPIOCSET:
 557                err = opiocset(argp, data);
 558                break;
 559
 560        case OPIOCGETOPTNODE:
 561                BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
 562
 563                err = 0;
 564                if (copy_to_user(argp, &options_node->phandle, sizeof(phandle)))
 565                        err = -EFAULT;
 566                break;
 567
 568        case OPIOCGETNEXT:
 569        case OPIOCGETCHILD:
 570                err = opiocgetnext(cmd, argp);
 571                break;
 572
 573        default:
 574                err = -EINVAL;
 575                break;
 576        }
 577        mutex_unlock(&openprom_mutex);
 578
 579        return err;
 580}
 581
 582
 583/*
 584 *      Handoff control to the correct ioctl handler.
 585 */
 586static long openprom_ioctl(struct file * file,
 587                           unsigned int cmd, unsigned long arg)
 588{
 589        DATA *data = file->private_data;
 590
 591        switch (cmd) {
 592        case OPROMGETOPT:
 593        case OPROMNXTOPT:
 594                if ((file->f_mode & FMODE_READ) == 0)
 595                        return -EPERM;
 596                return openprom_sunos_ioctl(file, cmd, arg,
 597                                            options_node);
 598
 599        case OPROMSETOPT:
 600        case OPROMSETOPT2:
 601                if ((file->f_mode & FMODE_WRITE) == 0)
 602                        return -EPERM;
 603                return openprom_sunos_ioctl(file, cmd, arg,
 604                                            options_node);
 605
 606        case OPROMNEXT:
 607        case OPROMCHILD:
 608        case OPROMGETPROP:
 609        case OPROMNXTPROP:
 610                if ((file->f_mode & FMODE_READ) == 0)
 611                        return -EPERM;
 612                return openprom_sunos_ioctl(file, cmd, arg,
 613                                            data->current_node);
 614
 615        case OPROMU2P:
 616        case OPROMGETCONS:
 617        case OPROMGETFBNAME:
 618        case OPROMGETBOOTARGS:
 619        case OPROMSETCUR:
 620        case OPROMPCI2NODE:
 621        case OPROMPATH2NODE:
 622                if ((file->f_mode & FMODE_READ) == 0)
 623                        return -EPERM;
 624                return openprom_sunos_ioctl(file, cmd, arg, NULL);
 625
 626        case OPIOCGET:
 627        case OPIOCNEXTPROP:
 628        case OPIOCGETOPTNODE:
 629        case OPIOCGETNEXT:
 630        case OPIOCGETCHILD:
 631                if ((file->f_mode & FMODE_READ) == 0)
 632                        return -EBADF;
 633                return openprom_bsd_ioctl(file,cmd,arg);
 634
 635        case OPIOCSET:
 636                if ((file->f_mode & FMODE_WRITE) == 0)
 637                        return -EBADF;
 638                return openprom_bsd_ioctl(file,cmd,arg);
 639
 640        default:
 641                return -EINVAL;
 642        };
 643}
 644
 645static long openprom_compat_ioctl(struct file *file, unsigned int cmd,
 646                unsigned long arg)
 647{
 648        long rval = -ENOTTY;
 649
 650        /*
 651         * SunOS/Solaris only, the NetBSD one's have embedded pointers in
 652         * the arg which we'd need to clean up...
 653         */
 654        switch (cmd) {
 655        case OPROMGETOPT:
 656        case OPROMSETOPT:
 657        case OPROMNXTOPT:
 658        case OPROMSETOPT2:
 659        case OPROMNEXT:
 660        case OPROMCHILD:
 661        case OPROMGETPROP:
 662        case OPROMNXTPROP:
 663        case OPROMU2P:
 664        case OPROMGETCONS:
 665        case OPROMGETFBNAME:
 666        case OPROMGETBOOTARGS:
 667        case OPROMSETCUR:
 668        case OPROMPCI2NODE:
 669        case OPROMPATH2NODE:
 670                rval = openprom_ioctl(file, cmd, arg);
 671                break;
 672        }
 673
 674        return rval;
 675}
 676
 677static int openprom_open(struct inode * inode, struct file * file)
 678{
 679        DATA *data;
 680
 681        data = kmalloc(sizeof(DATA), GFP_KERNEL);
 682        if (!data)
 683                return -ENOMEM;
 684
 685        mutex_lock(&openprom_mutex);
 686        data->current_node = of_find_node_by_path("/");
 687        data->lastnode = data->current_node;
 688        file->private_data = (void *) data;
 689        mutex_unlock(&openprom_mutex);
 690
 691        return 0;
 692}
 693
 694static int openprom_release(struct inode * inode, struct file * file)
 695{
 696        kfree(file->private_data);
 697        return 0;
 698}
 699
 700static const struct file_operations openprom_fops = {
 701        .owner =        THIS_MODULE,
 702        .llseek =       no_llseek,
 703        .unlocked_ioctl = openprom_ioctl,
 704        .compat_ioctl = openprom_compat_ioctl,
 705        .open =         openprom_open,
 706        .release =      openprom_release,
 707};
 708
 709static struct miscdevice openprom_dev = {
 710        .minor          = SUN_OPENPROM_MINOR,
 711        .name           = "openprom",
 712        .fops           = &openprom_fops,
 713};
 714
 715static 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
 741static void __exit openprom_cleanup(void)
 742{
 743        misc_deregister(&openprom_dev);
 744}
 745
 746module_init(openprom_init);
 747module_exit(openprom_cleanup);
 748