Linux-libre 4.14.14-gnu
[librecmc/linux-libre.git] / drivers / s390 / char / sclp_ctl.c
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/compat.h>
18 #include <asm/sclp_ctl.h>
19 #include <asm/sclp.h>
20
21 #include "sclp.h"
22
23 /*
24  * Supported command words
25  */
26 static unsigned int sclp_ctl_sccb_wlist[] = {
27         0x00400002,
28         0x00410002,
29 };
30
31 /*
32  * Check if command word is supported
33  */
34 static int sclp_ctl_cmdw_supported(unsigned int cmdw)
35 {
36         int i;
37
38         for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) {
39                 if (cmdw == sclp_ctl_sccb_wlist[i])
40                         return 1;
41         }
42         return 0;
43 }
44
45 static void __user *u64_to_uptr(u64 value)
46 {
47         if (is_compat_task())
48                 return compat_ptr(value);
49         else
50                 return (void __user *)(unsigned long)value;
51 }
52
53 /*
54  * Start SCLP request
55  */
56 static int sclp_ctl_ioctl_sccb(void __user *user_area)
57 {
58         struct sclp_ctl_sccb ctl_sccb;
59         struct sccb_header *sccb;
60         unsigned long copied;
61         int rc;
62
63         if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb)))
64                 return -EFAULT;
65         if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw))
66                 return -EOPNOTSUPP;
67         sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
68         if (!sccb)
69                 return -ENOMEM;
70         copied = PAGE_SIZE -
71                 copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), PAGE_SIZE);
72         if (offsetof(struct sccb_header, length) +
73             sizeof(sccb->length) > copied || sccb->length > copied) {
74                 rc = -EFAULT;
75                 goto out_free;
76         }
77         if (sccb->length < 8) {
78                 rc = -EINVAL;
79                 goto out_free;
80         }
81         rc = sclp_sync_request(ctl_sccb.cmdw, sccb);
82         if (rc)
83                 goto out_free;
84         if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length))
85                 rc = -EFAULT;
86 out_free:
87         free_page((unsigned long) sccb);
88         return rc;
89 }
90
91 /*
92  * SCLP SCCB ioctl function
93  */
94 static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd,
95                            unsigned long arg)
96 {
97         void __user *argp;
98
99         if (is_compat_task())
100                 argp = compat_ptr(arg);
101         else
102                 argp = (void __user *) arg;
103         switch (cmd) {
104         case SCLP_CTL_SCCB:
105                 return sclp_ctl_ioctl_sccb(argp);
106         default: /* unknown ioctl number */
107                 return -ENOTTY;
108         }
109 }
110
111 /*
112  * File operations
113  */
114 static const struct file_operations sclp_ctl_fops = {
115         .owner = THIS_MODULE,
116         .open = nonseekable_open,
117         .unlocked_ioctl = sclp_ctl_ioctl,
118         .compat_ioctl = sclp_ctl_ioctl,
119         .llseek = no_llseek,
120 };
121
122 /*
123  * Misc device definition
124  */
125 static struct miscdevice sclp_ctl_device = {
126         .minor = MISC_DYNAMIC_MINOR,
127         .name = "sclp",
128         .fops = &sclp_ctl_fops,
129 };
130 builtin_misc_device(sclp_ctl_device);