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