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