| // 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; |
| } |