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