xref: /linux/kernel/regset.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
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 
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 
141e6986c9SAl 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) {
19*6b839b3bSDouglas Anderson 		to_free = p = kvzalloc(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) {
26*6b839b3bSDouglas Anderson 		kvfree(to_free);
277717cb9bSAl Viro 		return res;
287717cb9bSAl Viro 	}
297717cb9bSAl Viro 	*data = p;
307717cb9bSAl Viro 	return size - res;
317717cb9bSAl Viro }
32b4e9c954SAl Viro 
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 
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  */
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;
74*6b839b3bSDouglas Anderson 	kvfree(buf);
75dc12d796SAl Viro 	return ret;
76dc12d796SAl Viro }
77