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