1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/export.h> 3 #include <linux/slab.h> 4 #include <linux/regset.h> 5 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->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 37 int 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 } 44 EXPORT_SYMBOL(regset_get); 45 46 int 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 } 54 EXPORT_SYMBOL(regset_get_alloc); 55 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 */ 65 int 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 } 81