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 && !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 = kzalloc(size, GFP_KERNEL); 20 if (!p) 21 return -ENOMEM; 22 } 23 if (regset->regset_get) { 24 res = regset->regset_get(target, regset, 25 (struct membuf){.p = p, .left = size}); 26 if (res < 0) { 27 kfree(to_free); 28 return res; 29 } 30 *data = p; 31 return size - res; 32 } 33 res = regset->get(target, regset, 0, size, p, NULL); 34 if (unlikely(res < 0)) { 35 kfree(to_free); 36 return res; 37 } 38 *data = p; 39 if (regset->get_size) { // arm64-only kludge, will go away 40 unsigned max_size = regset->get_size(target, regset); 41 if (size > max_size) 42 size = max_size; 43 } 44 return size; 45 } 46 47 int regset_get(struct task_struct *target, 48 const struct user_regset *regset, 49 unsigned int size, 50 void *data) 51 { 52 return __regset_get(target, regset, size, &data); 53 } 54 EXPORT_SYMBOL(regset_get); 55 56 int regset_get_alloc(struct task_struct *target, 57 const struct user_regset *regset, 58 unsigned int size, 59 void **data) 60 { 61 *data = NULL; 62 return __regset_get(target, regset, size, data); 63 } 64 EXPORT_SYMBOL(regset_get_alloc); 65 66 /** 67 * copy_regset_to_user - fetch a thread's user_regset data into user memory 68 * @target: thread to be examined 69 * @view: &struct user_regset_view describing user thread machine state 70 * @setno: index in @view->regsets 71 * @offset: offset into the regset data, in bytes 72 * @size: amount of data to copy, in bytes 73 * @data: user-mode pointer to copy into 74 */ 75 int copy_regset_to_user(struct task_struct *target, 76 const struct user_regset_view *view, 77 unsigned int setno, 78 unsigned int offset, unsigned int size, 79 void __user *data) 80 { 81 const struct user_regset *regset = &view->regsets[setno]; 82 void *buf; 83 int ret; 84 85 ret = regset_get_alloc(target, regset, size, &buf); 86 if (ret > 0) 87 ret = copy_to_user(data, buf, ret) ? -EFAULT : 0; 88 kfree(buf); 89 return ret; 90 } 91