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