Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * IOCTL interface for SCLP
0004  *
0005  * Copyright IBM Corp. 2012
0006  *
0007  * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com>
0008  */
0009 
0010 #include <linux/compat.h>
0011 #include <linux/uaccess.h>
0012 #include <linux/miscdevice.h>
0013 #include <linux/gfp.h>
0014 #include <linux/init.h>
0015 #include <linux/ioctl.h>
0016 #include <linux/fs.h>
0017 #include <asm/sclp_ctl.h>
0018 #include <asm/sclp.h>
0019 
0020 #include "sclp.h"
0021 
0022 /*
0023  * Supported command words
0024  */
0025 static unsigned int sclp_ctl_sccb_wlist[] = {
0026     0x00400002,
0027     0x00410002,
0028 };
0029 
0030 /*
0031  * Check if command word is supported
0032  */
0033 static int sclp_ctl_cmdw_supported(unsigned int cmdw)
0034 {
0035     int i;
0036 
0037     for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) {
0038         if (cmdw == sclp_ctl_sccb_wlist[i])
0039             return 1;
0040     }
0041     return 0;
0042 }
0043 
0044 static void __user *u64_to_uptr(u64 value)
0045 {
0046     if (is_compat_task())
0047         return compat_ptr(value);
0048     else
0049         return (void __user *)(unsigned long)value;
0050 }
0051 
0052 /*
0053  * Start SCLP request
0054  */
0055 static int sclp_ctl_ioctl_sccb(void __user *user_area)
0056 {
0057     struct sclp_ctl_sccb ctl_sccb;
0058     struct sccb_header *sccb;
0059     unsigned long copied;
0060     int rc;
0061 
0062     if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb)))
0063         return -EFAULT;
0064     if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw))
0065         return -EOPNOTSUPP;
0066     sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
0067     if (!sccb)
0068         return -ENOMEM;
0069     copied = PAGE_SIZE -
0070         copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), PAGE_SIZE);
0071     if (offsetof(struct sccb_header, length) +
0072         sizeof(sccb->length) > copied || sccb->length > copied) {
0073         rc = -EFAULT;
0074         goto out_free;
0075     }
0076     if (sccb->length < 8) {
0077         rc = -EINVAL;
0078         goto out_free;
0079     }
0080     rc = sclp_sync_request(ctl_sccb.cmdw, sccb);
0081     if (rc)
0082         goto out_free;
0083     if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length))
0084         rc = -EFAULT;
0085 out_free:
0086     free_page((unsigned long) sccb);
0087     return rc;
0088 }
0089 
0090 /*
0091  * SCLP SCCB ioctl function
0092  */
0093 static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd,
0094                unsigned long arg)
0095 {
0096     void __user *argp;
0097 
0098     if (is_compat_task())
0099         argp = compat_ptr(arg);
0100     else
0101         argp = (void __user *) arg;
0102     switch (cmd) {
0103     case SCLP_CTL_SCCB:
0104         return sclp_ctl_ioctl_sccb(argp);
0105     default: /* unknown ioctl number */
0106         return -ENOTTY;
0107     }
0108 }
0109 
0110 /*
0111  * File operations
0112  */
0113 static const struct file_operations sclp_ctl_fops = {
0114     .owner = THIS_MODULE,
0115     .open = nonseekable_open,
0116     .unlocked_ioctl = sclp_ctl_ioctl,
0117     .compat_ioctl = sclp_ctl_ioctl,
0118     .llseek = no_llseek,
0119 };
0120 
0121 /*
0122  * Misc device definition
0123  */
0124 static struct miscdevice sclp_ctl_device = {
0125     .minor = MISC_DYNAMIC_MINOR,
0126     .name = "sclp",
0127     .fops = &sclp_ctl_fops,
0128 };
0129 builtin_misc_device(sclp_ctl_device);