0001
0002
0003
0004
0005
0006
0007
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
0024
0025 static unsigned int sclp_ctl_sccb_wlist[] = {
0026 0x00400002,
0027 0x00410002,
0028 };
0029
0030
0031
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
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
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:
0106 return -ENOTTY;
0107 }
0108 }
0109
0110
0111
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
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);