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