linux/drivers/s390/char/sclp_ctl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * IOCTL interface for SCLP
   4 *
   5 * Copyright IBM Corp. 2012
   6 *
   7 * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
   8 */
   9
  10#include <linux/compat.h>
  11#include <linux/uaccess.h>
  12#include <linux/miscdevice.h>
  13#include <linux/gfp.h>
  14#include <linux/init.h>
  15#include <linux/ioctl.h>
  16#include <linux/fs.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        unsigned long copied;
  60        int rc;
  61
  62        if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb)))
  63                return -EFAULT;
  64        if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw))
  65                return -EOPNOTSUPP;
  66        sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
  67        if (!sccb)
  68                return -ENOMEM;
  69        copied = PAGE_SIZE -
  70                copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), PAGE_SIZE);
  71        if (offsetof(struct sccb_header, length) +
  72            sizeof(sccb->length) > copied || sccb->length > copied) {
  73                rc = -EFAULT;
  74                goto out_free;
  75        }
  76        if (sccb->length < 8) {
  77                rc = -EINVAL;
  78                goto out_free;
  79        }
  80        rc = sclp_sync_request(ctl_sccb.cmdw, sccb);
  81        if (rc)
  82                goto out_free;
  83        if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length))
  84                rc = -EFAULT;
  85out_free:
  86        free_page((unsigned long) sccb);
  87        return rc;
  88}
  89
  90/*
  91 * SCLP SCCB ioctl function
  92 */
  93static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd,
  94                           unsigned long arg)
  95{
  96        void __user *argp;
  97
  98        if (is_compat_task())
  99                argp = compat_ptr(arg);
 100        else
 101                argp = (void __user *) arg;
 102        switch (cmd) {
 103        case SCLP_CTL_SCCB:
 104                return sclp_ctl_ioctl_sccb(argp);
 105        default: /* unknown ioctl number */
 106                return -ENOTTY;
 107        }
 108}
 109
 110/*
 111 * File operations
 112 */
 113static const struct file_operations sclp_ctl_fops = {
 114        .owner = THIS_MODULE,
 115        .open = nonseekable_open,
 116        .unlocked_ioctl = sclp_ctl_ioctl,
 117        .compat_ioctl = sclp_ctl_ioctl,
 118        .llseek = no_llseek,
 119};
 120
 121/*
 122 * Misc device definition
 123 */
 124static struct miscdevice sclp_ctl_device = {
 125        .minor = MISC_DYNAMIC_MINOR,
 126        .name = "sclp",
 127        .fops = &sclp_ctl_fops,
 128};
 129builtin_misc_device(sclp_ctl_device);
 130