linux/drivers/s390/char/sclp_ctl.c
<<
>>
Prefs
   1/*
   2 * IOCTL interface for SCLP
   3 *
   4 * Copyright IBM Corp. 2012
   5 *
   6 * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
   7 */
   8
   9#include <linux/compat.h>
  10#include <linux/uaccess.h>
  11#include <linux/miscdevice.h>
  12#include <linux/gfp.h>
  13#include <linux/module.h>
  14#include <linux/ioctl.h>
  15#include <linux/fs.h>
  16#include <asm/compat.h>
  17#include <asm/sclp_ctl.h>
  18#include <asm/sclp.h>
  19
  20#include "sclp.h"
  21
  22/*
  23 * Supported command words
  24 */
  25static unsigned int sclp_ctl_sccb_wlist[] = {
  26        0x00400002,
  27        0x00410002,
  28};
  29
  30/*
  31 * Check if command word is supported
  32 */
  33static int sclp_ctl_cmdw_supported(unsigned int cmdw)
  34{
  35        int i;
  36
  37        for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) {
  38                if (cmdw == sclp_ctl_sccb_wlist[i])
  39                        return 1;
  40        }
  41        return 0;
  42}
  43
  44static void __user *u64_to_uptr(u64 value)
  45{
  46        if (is_compat_task())
  47                return compat_ptr(value);
  48        else
  49                return (void __user *)(unsigned long)value;
  50}
  51
  52/*
  53 * Start SCLP request
  54 */
  55static int sclp_ctl_ioctl_sccb(void __user *user_area)
  56{
  57        struct sclp_ctl_sccb ctl_sccb;
  58        struct sccb_header *sccb;
  59        int rc;
  60
  61        if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb)))
  62                return -EFAULT;
  63        if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw))
  64                return -EOPNOTSUPP;
  65        sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
  66        if (!sccb)
  67                return -ENOMEM;
  68        if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) {
  69                rc = -EFAULT;
  70                goto out_free;
  71        }
  72        if (sccb->length > PAGE_SIZE || sccb->length < 8)
  73                return -EINVAL;
  74        if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) {
  75                rc = -EFAULT;
  76                goto out_free;
  77        }
  78        rc = sclp_sync_request(ctl_sccb.cmdw, sccb);
  79        if (rc)
  80                goto out_free;
  81        if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length))
  82                rc = -EFAULT;
  83out_free:
  84        free_page((unsigned long) sccb);
  85        return rc;
  86}
  87
  88/*
  89 * SCLP SCCB ioctl function
  90 */
  91static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd,
  92                           unsigned long arg)
  93{
  94        void __user *argp;
  95
  96        if (is_compat_task())
  97                argp = compat_ptr(arg);
  98        else
  99                argp = (void __user *) arg;
 100        switch (cmd) {
 101        case SCLP_CTL_SCCB:
 102                return sclp_ctl_ioctl_sccb(argp);
 103        default: /* unknown ioctl number */
 104                return -ENOTTY;
 105        }
 106}
 107
 108/*
 109 * File operations
 110 */
 111static const struct file_operations sclp_ctl_fops = {
 112        .owner = THIS_MODULE,
 113        .open = nonseekable_open,
 114        .unlocked_ioctl = sclp_ctl_ioctl,
 115        .compat_ioctl = sclp_ctl_ioctl,
 116        .llseek = no_llseek,
 117};
 118
 119/*
 120 * Misc device definition
 121 */
 122static struct miscdevice sclp_ctl_device = {
 123        .minor = MISC_DYNAMIC_MINOR,
 124        .name = "sclp",
 125        .fops = &sclp_ctl_fops,
 126};
 127
 128/*
 129 * Register sclp_ctl misc device
 130 */
 131static int __init sclp_ctl_init(void)
 132{
 133        return misc_register(&sclp_ctl_device);
 134}
 135module_init(sclp_ctl_init);
 136
 137/*
 138 * Deregister sclp_ctl misc device
 139 */
 140static void __exit sclp_ctl_exit(void)
 141{
 142        misc_deregister(&sclp_ctl_device);
 143}
 144module_exit(sclp_ctl_exit);
 145