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