xref: /openbmc/linux/drivers/s390/char/sclp_ctl.c (revision 3eb66e91a25497065c5322b1268cbc3953642227)
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