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 <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");
  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 int copyin_string(char __user *user, size_t len, char **ptr)
 387{
 388        char *tmp;
 389
 390        if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
 391                return -EINVAL;
 392
 393        tmp = kmalloc(len + 1, GFP_KERNEL);
 394        if (!tmp)
 395                return -ENOMEM;
 396
 397        if (copy_from_user(tmp, user, len)) {
 398                kfree(tmp);
 399                return -EFAULT;
 400        }
 401
 402        tmp[len] = '\0';
 403
 404        *ptr = tmp;
 405
 406        return 0;
 407}
 408
 409/*
 410 *      NetBSD /dev/openprom ioctl calls.
 411 */
 412static int opiocget(void __user *argp, DATA *data)
 413{
 414        struct opiocdesc op;
 415        struct device_node *dp;
 416        char *str;
 417        const void *pval;
 418        int err, len;
 419
 420        if (copy_from_user(&op, argp, sizeof(op)))
 421                return -EFAULT;
 422
 423        dp = get_node(op.op_nodeid, data);
 424
 425        err = copyin_string(op.op_name, op.op_namelen, &str);
 426        if (err)
 427                return err;
 428
 429        pval = of_get_property(dp, str, &len);
 430        err = 0;
 431        if (!pval || len > op.op_buflen) {
 432                err = -EINVAL;
 433        } else {
 434                op.op_buflen = len;
 435                if (copy_to_user(argp, &op, sizeof(op)) ||
 436                    copy_to_user(op.op_buf, pval, len))
 437                        err = -EFAULT;
 438        }
 439        kfree(str);
 440
 441        return err;
 442}
 443
 444static int opiocnextprop(void __user *argp, DATA *data)
 445{
 446        struct opiocdesc op;
 447        struct device_node *dp;
 448        struct property *prop;
 449        char *str;
 450        int err, len;
 451
 452        if (copy_from_user(&op, argp, sizeof(op)))
 453                return -EFAULT;
 454
 455        dp = get_node(op.op_nodeid, data);
 456        if (!dp)
 457                return -EINVAL;
 458
 459        err = copyin_string(op.op_name, op.op_namelen, &str);
 460        if (err)
 461                return err;
 462
 463        if (str[0] == '\0') {
 464                prop = dp->properties;
 465        } else {
 466                prop = of_find_property(dp, str, NULL);
 467                if (prop)
 468                        prop = prop->next;
 469        }
 470        kfree(str);
 471
 472        if (!prop)
 473                len = 0;
 474        else
 475                len = prop->length;
 476
 477        if (len > op.op_buflen)
 478                len = op.op_buflen;
 479
 480        if (copy_to_user(argp, &op, sizeof(op)))
 481                return -EFAULT;
 482
 483        if (len &&
 484            copy_to_user(op.op_buf, prop->value, len))
 485                return -EFAULT;
 486
 487        return 0;
 488}
 489
 490static int opiocset(void __user *argp, DATA *data)
 491{
 492        struct opiocdesc op;
 493        struct device_node *dp;
 494        char *str, *tmp;
 495        int err;
 496
 497        if (copy_from_user(&op, argp, sizeof(op)))
 498                return -EFAULT;
 499
 500        dp = get_node(op.op_nodeid, data);
 501        if (!dp)
 502                return -EINVAL;
 503
 504        err = copyin_string(op.op_name, op.op_namelen, &str);
 505        if (err)
 506                return err;
 507
 508        err = copyin_string(op.op_buf, op.op_buflen, &tmp);
 509        if (err) {
 510                kfree(str);
 511                return err;
 512        }
 513
 514        err = of_set_property(dp, str, tmp, op.op_buflen);
 515
 516        kfree(str);
 517        kfree(tmp);
 518
 519        return err;
 520}
 521
 522static int opiocgetnext(unsigned int cmd, void __user *argp)
 523{
 524        struct device_node *dp;
 525        phandle nd;
 526
 527        BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
 528
 529        if (copy_from_user(&nd, argp, sizeof(phandle)))
 530                return -EFAULT;
 531
 532        if (nd == 0) {
 533                if (cmd != OPIOCGETNEXT)
 534                        return -EINVAL;
 535                dp = of_find_node_by_path("/");
 536        } else {
 537                dp = of_find_node_by_phandle(nd);
 538                nd = 0;
 539                if (dp) {
 540                        if (cmd == OPIOCGETNEXT)
 541                                dp = dp->sibling;
 542                        else
 543                                dp = dp->child;
 544                }
 545        }
 546        if (dp)
 547                nd = dp->phandle;
 548        if (copy_to_user(argp, &nd, sizeof(phandle)))
 549                return -EFAULT;
 550
 551        return 0;
 552}
 553
 554static int openprom_bsd_ioctl(struct file * file,
 555                              unsigned int cmd, unsigned long arg)
 556{
 557        DATA *data = file->private_data;
 558        void __user *argp = (void __user *)arg;
 559        int err;
 560
 561        mutex_lock(&openprom_mutex);
 562        switch (cmd) {
 563        case OPIOCGET:
 564                err = opiocget(argp, data);
 565                break;
 566
 567        case OPIOCNEXTPROP:
 568                err = opiocnextprop(argp, data);
 569                break;
 570
 571        case OPIOCSET:
 572                err = opiocset(argp, data);
 573                break;
 574
 575        case OPIOCGETOPTNODE:
 576                BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
 577
 578                err = 0;
 579                if (copy_to_user(argp, &options_node->phandle, sizeof(phandle)))
 580                        err = -EFAULT;
 581                break;
 582
 583        case OPIOCGETNEXT:
 584        case OPIOCGETCHILD:
 585                err = opiocgetnext(cmd, argp);
 586                break;
 587
 588        default:
 589                err = -EINVAL;
 590                break;
 591        }
 592        mutex_unlock(&openprom_mutex);
 593
 594        return err;
 595}
 596
 597
 598/*
 599 *      Handoff control to the correct ioctl handler.
 600 */
 601static long openprom_ioctl(struct file * file,
 602                           unsigned int cmd, unsigned long arg)
 603{
 604        DATA *data = file->private_data;
 605
 606        switch (cmd) {
 607        case OPROMGETOPT:
 608        case OPROMNXTOPT:
 609                if ((file->f_mode & FMODE_READ) == 0)
 610                        return -EPERM;
 611                return openprom_sunos_ioctl(file, cmd, arg,
 612                                            options_node);
 613
 614        case OPROMSETOPT:
 615        case OPROMSETOPT2:
 616                if ((file->f_mode & FMODE_WRITE) == 0)
 617                        return -EPERM;
 618                return openprom_sunos_ioctl(file, cmd, arg,
 619                                            options_node);
 620
 621        case OPROMNEXT:
 622        case OPROMCHILD:
 623        case OPROMGETPROP:
 624        case OPROMNXTPROP:
 625                if ((file->f_mode & FMODE_READ) == 0)
 626                        return -EPERM;
 627                return openprom_sunos_ioctl(file, cmd, arg,
 628                                            data->current_node);
 629
 630        case OPROMU2P:
 631        case OPROMGETCONS:
 632        case OPROMGETFBNAME:
 633        case OPROMGETBOOTARGS:
 634        case OPROMSETCUR:
 635        case OPROMPCI2NODE:
 636        case OPROMPATH2NODE:
 637                if ((file->f_mode & FMODE_READ) == 0)
 638                        return -EPERM;
 639                return openprom_sunos_ioctl(file, cmd, arg, NULL);
 640
 641        case OPIOCGET:
 642        case OPIOCNEXTPROP:
 643        case OPIOCGETOPTNODE:
 644        case OPIOCGETNEXT:
 645        case OPIOCGETCHILD:
 646                if ((file->f_mode & FMODE_READ) == 0)
 647                        return -EBADF;
 648                return openprom_bsd_ioctl(file,cmd,arg);
 649
 650        case OPIOCSET:
 651                if ((file->f_mode & FMODE_WRITE) == 0)
 652                        return -EBADF;
 653                return openprom_bsd_ioctl(file,cmd,arg);
 654
 655        default:
 656                return -EINVAL;
 657        };
 658}
 659
 660static long openprom_compat_ioctl(struct file *file, unsigned int cmd,
 661                unsigned long arg)
 662{
 663        long rval = -ENOTTY;
 664
 665        /*
 666         * SunOS/Solaris only, the NetBSD one's have embedded pointers in
 667         * the arg which we'd need to clean up...
 668         */
 669        switch (cmd) {
 670        case OPROMGETOPT:
 671        case OPROMSETOPT:
 672        case OPROMNXTOPT:
 673        case OPROMSETOPT2:
 674        case OPROMNEXT:
 675        case OPROMCHILD:
 676        case OPROMGETPROP:
 677        case OPROMNXTPROP:
 678        case OPROMU2P:
 679        case OPROMGETCONS:
 680        case OPROMGETFBNAME:
 681        case OPROMGETBOOTARGS:
 682        case OPROMSETCUR:
 683        case OPROMPCI2NODE:
 684        case OPROMPATH2NODE:
 685                rval = openprom_ioctl(file, cmd, arg);
 686                break;
 687        }
 688
 689        return rval;
 690}
 691
 692static int openprom_open(struct inode * inode, struct file * file)
 693{
 694        DATA *data;
 695
 696        data = kmalloc(sizeof(DATA), GFP_KERNEL);
 697        if (!data)
 698                return -ENOMEM;
 699
 700        mutex_lock(&openprom_mutex);
 701        data->current_node = of_find_node_by_path("/");
 702        data->lastnode = data->current_node;
 703        file->private_data = (void *) data;
 704        mutex_unlock(&openprom_mutex);
 705
 706        return 0;
 707}
 708
 709static int openprom_release(struct inode * inode, struct file * file)
 710{
 711        kfree(file->private_data);
 712        return 0;
 713}
 714
 715static const struct file_operations openprom_fops = {
 716        .owner =        THIS_MODULE,
 717        .llseek =       no_llseek,
 718        .unlocked_ioctl = openprom_ioctl,
 719        .compat_ioctl = openprom_compat_ioctl,
 720        .open =         openprom_open,
 721        .release =      openprom_release,
 722};
 723
 724static struct miscdevice openprom_dev = {
 725        .minor          = SUN_OPENPROM_MINOR,
 726        .name           = "openprom",
 727        .fops           = &openprom_fops,
 728};
 729
 730static int __init openprom_init(void)
 731{
 732        struct device_node *dp;
 733        int err;
 734
 735        err = misc_register(&openprom_dev);
 736        if (err)
 737                return err;
 738
 739        dp = of_find_node_by_path("/");
 740        dp = dp->child;
 741        while (dp) {
 742                if (!strcmp(dp->name, "options"))
 743                        break;
 744                dp = dp->sibling;
 745        }
 746        options_node = dp;
 747
 748        if (!options_node) {
 749                misc_deregister(&openprom_dev);
 750                return -EIO;
 751        }
 752
 753        return 0;
 754}
 755
 756static void __exit openprom_cleanup(void)
 757{
 758        misc_deregister(&openprom_dev);
 759}
 760
 761module_init(openprom_init);
 762module_exit(openprom_cleanup);
 763