| // SPDX-License-Identifier: GPL-2.0-only | |
| #include <linux/export.h> | |
| #include <linux/slab.h> | |
| #include <linux/regset.h> | |
| static int __regset_get(struct task_struct *target, | |
| const struct user_regset *regset, | |
| unsigned int size, | |
| void **data) | |
| { | |
| void *p = *data, *to_free = NULL; | |
| int res; | |
| if (!regset->regset_get) | |
| return -EOPNOTSUPP; | |
| if (size > regset->n * regset->size) | |
| size = regset->n * regset->size; | |
| if (!p) { | |
| to_free = p = kzalloc(size, GFP_KERNEL); | |
| if (!p) | |
| return -ENOMEM; | |
| } | |
| res = regset->regset_get(target, regset, | |
| (struct membuf){.p = p, .left = size}); | |
| if (res < 0) { | |
| kfree(to_free); | |
| return res; | |
| } | |
| *data = p; | |
| return size - res; | |
| } | |
| int regset_get(struct task_struct *target, | |
| const struct user_regset *regset, | |
| unsigned int size, | |
| void *data) | |
| { | |
| return __regset_get(target, regset, size, &data); | |
| } | |
| EXPORT_SYMBOL(regset_get); | |
| int regset_get_alloc(struct task_struct *target, | |
| const struct user_regset *regset, | |
| unsigned int size, | |
| void **data) | |
| { | |
| *data = NULL; | |
| return __regset_get(target, regset, size, data); | |
| } | |
| EXPORT_SYMBOL(regset_get_alloc); | |
| /** | |
| * copy_regset_to_user - fetch a thread's user_regset data into user memory | |
| * @target: thread to be examined | |
| * @view: &struct user_regset_view describing user thread machine state | |
| * @setno: index in @view->regsets | |
| * @offset: offset into the regset data, in bytes | |
| * @size: amount of data to copy, in bytes | |
| * @data: user-mode pointer to copy into | |
| */ | |
| int copy_regset_to_user(struct task_struct *target, | |
| const struct user_regset_view *view, | |
| unsigned int setno, | |
| unsigned int offset, unsigned int size, | |
| void __user *data) | |
| { | |
| const struct user_regset *regset = &view->regsets[setno]; | |
| void *buf; | |
| int ret; | |
| ret = regset_get_alloc(target, regset, size, &buf); | |
| if (ret > 0) | |
| ret = copy_to_user(data, buf, ret) ? -EFAULT : 0; | |
| kfree(buf); | |
| return ret; | |
| } |