1b4e9c954SAl Viro // SPDX-License-Identifier: GPL-2.0-only
2b4e9c954SAl Viro #include <linux/export.h>
3b4e9c954SAl Viro #include <linux/slab.h>
4b4e9c954SAl Viro #include <linux/regset.h>
5b4e9c954SAl Viro
__regset_get(struct task_struct * target,const struct user_regset * regset,unsigned int size,void ** data)6b4e9c954SAl Viro static int __regset_get(struct task_struct *target,
7b4e9c954SAl Viro const struct user_regset *regset,
8b4e9c954SAl Viro unsigned int size,
9b4e9c954SAl Viro void **data)
10b4e9c954SAl Viro {
11b4e9c954SAl Viro void *p = *data, *to_free = NULL;
12b4e9c954SAl Viro int res;
13b4e9c954SAl Viro
14*1e6986c9SAl Viro if (!regset->regset_get)
15b4e9c954SAl Viro return -EOPNOTSUPP;
16b4e9c954SAl Viro if (size > regset->n * regset->size)
17b4e9c954SAl Viro size = regset->n * regset->size;
18b4e9c954SAl Viro if (!p) {
19b4e9c954SAl Viro to_free = p = kzalloc(size, GFP_KERNEL);
20b4e9c954SAl Viro if (!p)
21b4e9c954SAl Viro return -ENOMEM;
22b4e9c954SAl Viro }
237717cb9bSAl Viro res = regset->regset_get(target, regset,
247717cb9bSAl Viro (struct membuf){.p = p, .left = size});
257717cb9bSAl Viro if (res < 0) {
267717cb9bSAl Viro kfree(to_free);
277717cb9bSAl Viro return res;
287717cb9bSAl Viro }
297717cb9bSAl Viro *data = p;
307717cb9bSAl Viro return size - res;
317717cb9bSAl Viro }
32b4e9c954SAl Viro
regset_get(struct task_struct * target,const struct user_regset * regset,unsigned int size,void * data)33b4e9c954SAl Viro int regset_get(struct task_struct *target,
34b4e9c954SAl Viro const struct user_regset *regset,
35b4e9c954SAl Viro unsigned int size,
36b4e9c954SAl Viro void *data)
37b4e9c954SAl Viro {
38b4e9c954SAl Viro return __regset_get(target, regset, size, &data);
39b4e9c954SAl Viro }
40b4e9c954SAl Viro EXPORT_SYMBOL(regset_get);
41b4e9c954SAl Viro
regset_get_alloc(struct task_struct * target,const struct user_regset * regset,unsigned int size,void ** data)42b4e9c954SAl Viro int regset_get_alloc(struct task_struct *target,
43b4e9c954SAl Viro const struct user_regset *regset,
44b4e9c954SAl Viro unsigned int size,
45b4e9c954SAl Viro void **data)
46b4e9c954SAl Viro {
47b4e9c954SAl Viro *data = NULL;
48b4e9c954SAl Viro return __regset_get(target, regset, size, data);
49b4e9c954SAl Viro }
50b4e9c954SAl Viro EXPORT_SYMBOL(regset_get_alloc);
51dc12d796SAl Viro
52dc12d796SAl Viro /**
53dc12d796SAl Viro * copy_regset_to_user - fetch a thread's user_regset data into user memory
54dc12d796SAl Viro * @target: thread to be examined
55dc12d796SAl Viro * @view: &struct user_regset_view describing user thread machine state
56dc12d796SAl Viro * @setno: index in @view->regsets
57dc12d796SAl Viro * @offset: offset into the regset data, in bytes
58dc12d796SAl Viro * @size: amount of data to copy, in bytes
59dc12d796SAl Viro * @data: user-mode pointer to copy into
60dc12d796SAl Viro */
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)61dc12d796SAl Viro int copy_regset_to_user(struct task_struct *target,
62dc12d796SAl Viro const struct user_regset_view *view,
63dc12d796SAl Viro unsigned int setno,
64dc12d796SAl Viro unsigned int offset, unsigned int size,
65dc12d796SAl Viro void __user *data)
66dc12d796SAl Viro {
67dc12d796SAl Viro const struct user_regset *regset = &view->regsets[setno];
68dc12d796SAl Viro void *buf;
69dc12d796SAl Viro int ret;
70dc12d796SAl Viro
71dc12d796SAl Viro ret = regset_get_alloc(target, regset, size, &buf);
72dc12d796SAl Viro if (ret > 0)
73dc12d796SAl Viro ret = copy_to_user(data, buf, ret) ? -EFAULT : 0;
74dc12d796SAl Viro kfree(buf);
75dc12d796SAl Viro return ret;
76dc12d796SAl Viro }
77