Merge tag 'asoc-fix-v5.14-rc2' of https://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / arch / s390 / hypfs / hypfs_sprp.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *    Hypervisor filesystem for Linux on s390.
4  *    Set Partition-Resource Parameter interface.
5  *
6  *    Copyright IBM Corp. 2013
7  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
8  */
9
10 #include <linux/compat.h>
11 #include <linux/errno.h>
12 #include <linux/gfp.h>
13 #include <linux/string.h>
14 #include <linux/types.h>
15 #include <linux/uaccess.h>
16 #include <asm/diag.h>
17 #include <asm/sclp.h>
18 #include "hypfs.h"
19
20 #define DIAG304_SET_WEIGHTS     0
21 #define DIAG304_QUERY_PRP       1
22 #define DIAG304_SET_CAPPING     2
23
24 #define DIAG304_CMD_MAX         2
25
26 static inline unsigned long __hypfs_sprp_diag304(void *data, unsigned long cmd)
27 {
28         union register_pair r1 = { .even = (unsigned long)data, };
29
30         asm volatile("diag %[r1],%[r3],0x304\n"
31                      : [r1] "+&d" (r1.pair)
32                      : [r3] "d" (cmd)
33                      : "memory");
34         return r1.odd;
35 }
36
37 static unsigned long hypfs_sprp_diag304(void *data, unsigned long cmd)
38 {
39         diag_stat_inc(DIAG_STAT_X304);
40         return __hypfs_sprp_diag304(data, cmd);
41 }
42
43 static void hypfs_sprp_free(const void *data)
44 {
45         free_page((unsigned long) data);
46 }
47
48 static int hypfs_sprp_create(void **data_ptr, void **free_ptr, size_t *size)
49 {
50         unsigned long rc;
51         void *data;
52
53         data = (void *) get_zeroed_page(GFP_KERNEL);
54         if (!data)
55                 return -ENOMEM;
56         rc = hypfs_sprp_diag304(data, DIAG304_QUERY_PRP);
57         if (rc != 1) {
58                 *data_ptr = *free_ptr = NULL;
59                 *size = 0;
60                 free_page((unsigned long) data);
61                 return -EIO;
62         }
63         *data_ptr = *free_ptr = data;
64         *size = PAGE_SIZE;
65         return 0;
66 }
67
68 static int __hypfs_sprp_ioctl(void __user *user_area)
69 {
70         struct hypfs_diag304 *diag304;
71         unsigned long cmd;
72         void __user *udata;
73         void *data;
74         int rc;
75
76         rc = -ENOMEM;
77         data = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
78         diag304 = kzalloc(sizeof(*diag304), GFP_KERNEL);
79         if (!data || !diag304)
80                 goto out;
81
82         rc = -EFAULT;
83         if (copy_from_user(diag304, user_area, sizeof(*diag304)))
84                 goto out;
85         rc = -EINVAL;
86         if ((diag304->args[0] >> 8) != 0 || diag304->args[1] > DIAG304_CMD_MAX)
87                 goto out;
88
89         rc = -EFAULT;
90         udata = (void __user *)(unsigned long) diag304->data;
91         if (diag304->args[1] == DIAG304_SET_WEIGHTS ||
92             diag304->args[1] == DIAG304_SET_CAPPING)
93                 if (copy_from_user(data, udata, PAGE_SIZE))
94                         goto out;
95
96         cmd = *(unsigned long *) &diag304->args[0];
97         diag304->rc = hypfs_sprp_diag304(data, cmd);
98
99         if (diag304->args[1] == DIAG304_QUERY_PRP)
100                 if (copy_to_user(udata, data, PAGE_SIZE)) {
101                         rc = -EFAULT;
102                         goto out;
103                 }
104
105         rc = copy_to_user(user_area, diag304, sizeof(*diag304)) ? -EFAULT : 0;
106 out:
107         kfree(diag304);
108         free_page((unsigned long) data);
109         return rc;
110 }
111
112 static long hypfs_sprp_ioctl(struct file *file, unsigned int cmd,
113                                unsigned long arg)
114 {
115         void __user *argp;
116
117         if (!capable(CAP_SYS_ADMIN))
118                 return -EACCES;
119         if (is_compat_task())
120                 argp = compat_ptr(arg);
121         else
122                 argp = (void __user *) arg;
123         switch (cmd) {
124         case HYPFS_DIAG304:
125                 return __hypfs_sprp_ioctl(argp);
126         default: /* unknown ioctl number */
127                 return -ENOTTY;
128         }
129         return 0;
130 }
131
132 static struct hypfs_dbfs_file hypfs_sprp_file = {
133         .name           = "diag_304",
134         .data_create    = hypfs_sprp_create,
135         .data_free      = hypfs_sprp_free,
136         .unlocked_ioctl = hypfs_sprp_ioctl,
137 };
138
139 void hypfs_sprp_init(void)
140 {
141         if (!sclp.has_sprp)
142                 return;
143         hypfs_dbfs_create_file(&hypfs_sprp_file);
144 }
145
146 void hypfs_sprp_exit(void)
147 {
148         if (!sclp.has_sprp)
149                 return;
150         hypfs_dbfs_remove_file(&hypfs_sprp_file);
151 }