|
2 | 2 |
|
3 | 3 | #define pr_fmt(fmt) "papr-sysparm: " fmt
|
4 | 4 |
|
| 5 | +#include <linux/anon_inodes.h> |
5 | 6 | #include <linux/bug.h>
|
| 7 | +#include <linux/file.h> |
| 8 | +#include <linux/fs.h> |
6 | 9 | #include <linux/init.h>
|
7 | 10 | #include <linux/kernel.h>
|
| 11 | +#include <linux/miscdevice.h> |
8 | 12 | #include <linux/printk.h>
|
9 | 13 | #include <linux/slab.h>
|
10 |
| -#include <asm/rtas.h> |
| 14 | +#include <linux/uaccess.h> |
| 15 | +#include <asm/machdep.h> |
11 | 16 | #include <asm/papr-sysparm.h>
|
12 | 17 | #include <asm/rtas-work-area.h>
|
| 18 | +#include <asm/rtas.h> |
13 | 19 |
|
14 | 20 | struct papr_sysparm_buf *papr_sysparm_buf_alloc(void)
|
15 | 21 | {
|
@@ -87,7 +93,6 @@ static bool papr_sysparm_buf_can_submit(const struct papr_sysparm_buf *buf)
|
87 | 93 | *
|
88 | 94 | * Return: 0 on success, -errno otherwise. @buf is unmodified on error.
|
89 | 95 | */
|
90 |
| - |
91 | 96 | int papr_sysparm_get(papr_sysparm_t param, struct papr_sysparm_buf *buf)
|
92 | 97 | {
|
93 | 98 | const s32 token = rtas_function_token(RTAS_FN_IBM_GET_SYSTEM_PARAMETER);
|
@@ -196,3 +201,152 @@ int papr_sysparm_set(papr_sysparm_t param, const struct papr_sysparm_buf *buf)
|
196 | 201 |
|
197 | 202 | return ret;
|
198 | 203 | }
|
| 204 | + |
| 205 | +static struct papr_sysparm_buf * |
| 206 | +papr_sysparm_buf_from_user(const struct papr_sysparm_io_block __user *user_iob) |
| 207 | +{ |
| 208 | + struct papr_sysparm_buf *kern_spbuf; |
| 209 | + long err; |
| 210 | + u16 len; |
| 211 | + |
| 212 | + /* |
| 213 | + * The length of valid data that userspace claims to be in |
| 214 | + * user_iob->data[]. |
| 215 | + */ |
| 216 | + if (get_user(len, &user_iob->length)) |
| 217 | + return ERR_PTR(-EFAULT); |
| 218 | + |
| 219 | + static_assert(sizeof(user_iob->data) >= PAPR_SYSPARM_MAX_INPUT); |
| 220 | + static_assert(sizeof(kern_spbuf->val) >= PAPR_SYSPARM_MAX_INPUT); |
| 221 | + |
| 222 | + if (len > PAPR_SYSPARM_MAX_INPUT) |
| 223 | + return ERR_PTR(-EINVAL); |
| 224 | + |
| 225 | + kern_spbuf = papr_sysparm_buf_alloc(); |
| 226 | + if (!kern_spbuf) |
| 227 | + return ERR_PTR(-ENOMEM); |
| 228 | + |
| 229 | + papr_sysparm_buf_set_length(kern_spbuf, len); |
| 230 | + |
| 231 | + if (len > 0 && copy_from_user(kern_spbuf->val, user_iob->data, len)) { |
| 232 | + err = -EFAULT; |
| 233 | + goto free_sysparm_buf; |
| 234 | + } |
| 235 | + |
| 236 | + return kern_spbuf; |
| 237 | + |
| 238 | +free_sysparm_buf: |
| 239 | + papr_sysparm_buf_free(kern_spbuf); |
| 240 | + return ERR_PTR(err); |
| 241 | +} |
| 242 | + |
| 243 | +static int papr_sysparm_buf_to_user(const struct papr_sysparm_buf *kern_spbuf, |
| 244 | + struct papr_sysparm_io_block __user *user_iob) |
| 245 | +{ |
| 246 | + u16 len_out = papr_sysparm_buf_get_length(kern_spbuf); |
| 247 | + |
| 248 | + if (put_user(len_out, &user_iob->length)) |
| 249 | + return -EFAULT; |
| 250 | + |
| 251 | + static_assert(sizeof(user_iob->data) >= PAPR_SYSPARM_MAX_OUTPUT); |
| 252 | + static_assert(sizeof(kern_spbuf->val) >= PAPR_SYSPARM_MAX_OUTPUT); |
| 253 | + |
| 254 | + if (copy_to_user(user_iob->data, kern_spbuf->val, PAPR_SYSPARM_MAX_OUTPUT)) |
| 255 | + return -EFAULT; |
| 256 | + |
| 257 | + return 0; |
| 258 | +} |
| 259 | + |
| 260 | +static long papr_sysparm_ioctl_get(struct papr_sysparm_io_block __user *user_iob) |
| 261 | +{ |
| 262 | + struct papr_sysparm_buf *kern_spbuf; |
| 263 | + papr_sysparm_t param; |
| 264 | + long ret; |
| 265 | + |
| 266 | + if (get_user(param.token, &user_iob->parameter)) |
| 267 | + return -EFAULT; |
| 268 | + |
| 269 | + kern_spbuf = papr_sysparm_buf_from_user(user_iob); |
| 270 | + if (IS_ERR(kern_spbuf)) |
| 271 | + return PTR_ERR(kern_spbuf); |
| 272 | + |
| 273 | + ret = papr_sysparm_get(param, kern_spbuf); |
| 274 | + if (ret) |
| 275 | + goto free_sysparm_buf; |
| 276 | + |
| 277 | + ret = papr_sysparm_buf_to_user(kern_spbuf, user_iob); |
| 278 | + if (ret) |
| 279 | + goto free_sysparm_buf; |
| 280 | + |
| 281 | + ret = 0; |
| 282 | + |
| 283 | +free_sysparm_buf: |
| 284 | + papr_sysparm_buf_free(kern_spbuf); |
| 285 | + return ret; |
| 286 | +} |
| 287 | + |
| 288 | + |
| 289 | +static long papr_sysparm_ioctl_set(struct papr_sysparm_io_block __user *user_iob) |
| 290 | +{ |
| 291 | + struct papr_sysparm_buf *kern_spbuf; |
| 292 | + papr_sysparm_t param; |
| 293 | + long ret; |
| 294 | + |
| 295 | + if (get_user(param.token, &user_iob->parameter)) |
| 296 | + return -EFAULT; |
| 297 | + |
| 298 | + kern_spbuf = papr_sysparm_buf_from_user(user_iob); |
| 299 | + if (IS_ERR(kern_spbuf)) |
| 300 | + return PTR_ERR(kern_spbuf); |
| 301 | + |
| 302 | + ret = papr_sysparm_set(param, kern_spbuf); |
| 303 | + if (ret) |
| 304 | + goto free_sysparm_buf; |
| 305 | + |
| 306 | + ret = 0; |
| 307 | + |
| 308 | +free_sysparm_buf: |
| 309 | + papr_sysparm_buf_free(kern_spbuf); |
| 310 | + return ret; |
| 311 | +} |
| 312 | + |
| 313 | +static long papr_sysparm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) |
| 314 | +{ |
| 315 | + void __user *argp = (__force void __user *)arg; |
| 316 | + long ret; |
| 317 | + |
| 318 | + switch (ioctl) { |
| 319 | + case PAPR_SYSPARM_IOC_GET: |
| 320 | + ret = papr_sysparm_ioctl_get(argp); |
| 321 | + break; |
| 322 | + case PAPR_SYSPARM_IOC_SET: |
| 323 | + if (filp->f_mode & FMODE_WRITE) |
| 324 | + ret = papr_sysparm_ioctl_set(argp); |
| 325 | + else |
| 326 | + ret = -EBADF; |
| 327 | + break; |
| 328 | + default: |
| 329 | + ret = -ENOIOCTLCMD; |
| 330 | + break; |
| 331 | + } |
| 332 | + return ret; |
| 333 | +} |
| 334 | + |
| 335 | +static const struct file_operations papr_sysparm_ops = { |
| 336 | + .unlocked_ioctl = papr_sysparm_ioctl, |
| 337 | +}; |
| 338 | + |
| 339 | +static struct miscdevice papr_sysparm_dev = { |
| 340 | + .minor = MISC_DYNAMIC_MINOR, |
| 341 | + .name = "papr-sysparm", |
| 342 | + .fops = &papr_sysparm_ops, |
| 343 | +}; |
| 344 | + |
| 345 | +static __init int papr_sysparm_init(void) |
| 346 | +{ |
| 347 | + if (!rtas_function_implemented(RTAS_FN_IBM_GET_SYSTEM_PARAMETER)) |
| 348 | + return -ENODEV; |
| 349 | + |
| 350 | + return misc_register(&papr_sysparm_dev); |
| 351 | +} |
| 352 | +machine_device_initcall(pseries, papr_sysparm_init); |
0 commit comments