xref: /openbmc/linux/drivers/s390/char/sclp_ctl.c (revision 82e6fdd6)
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);
131