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