aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/kernel/ptrace.c67
-rw-r--r--arch/ia64/kernel/ptrace.c275
-rw-r--r--arch/x86/include/asm/fpu/internal.h1
-rw-r--r--arch/x86/kernel/fpu/regset.c16
-rw-r--r--arch/x86/kernel/fpu/signal.c12
-rw-r--r--fs/binfmt_elf.c54
-rw-r--r--include/linux/regset.h9
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/regset.c54
9 files changed, 232 insertions, 258 deletions
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 68b7f34a08f5..9f769e862f68 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -1237,6 +1237,22 @@ enum compat_regset {
REGSET_COMPAT_VFP,
};
+static inline compat_ulong_t compat_get_user_reg(struct task_struct *task, int idx)
+{
+ struct pt_regs *regs = task_pt_regs(task);
+
+ switch (idx) {
+ case 15:
+ return regs->pc;
+ case 16:
+ return pstate_to_compat_psr(regs->pstate);
+ case 17:
+ return regs->orig_x0;
+ default:
+ return regs->regs[idx];
+ }
+}
+
static int compat_gpr_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
@@ -1255,23 +1271,7 @@ static int compat_gpr_get(struct task_struct *target,
return -EIO;
for (i = 0; i < num_regs; ++i) {
- unsigned int idx = start + i;
- compat_ulong_t reg;
-
- switch (idx) {
- case 15:
- reg = task_pt_regs(target)->pc;
- break;
- case 16:
- reg = task_pt_regs(target)->pstate;
- reg = pstate_to_compat_psr(reg);
- break;
- case 17:
- reg = task_pt_regs(target)->orig_x0;
- break;
- default:
- reg = task_pt_regs(target)->regs[idx];
- }
+ compat_ulong_t reg = compat_get_user_reg(target, start + i);
if (kbuf) {
memcpy(kbuf, &reg, sizeof(reg));
@@ -1541,9 +1541,7 @@ static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off,
else if (off == COMPAT_PT_TEXT_END_ADDR)
tmp = tsk->mm->end_code;
else if (off < sizeof(compat_elf_gregset_t))
- return copy_regset_to_user(tsk, &user_aarch32_view,
- REGSET_COMPAT_GPR, off,
- sizeof(compat_ulong_t), ret);
+ tmp = compat_get_user_reg(tsk, off >> 2);
else if (off >= COMPAT_USER_SZ)
return -EIO;
else
@@ -1555,8 +1553,8 @@ static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off,
static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off,
compat_ulong_t val)
{
- int ret;
- mm_segment_t old_fs = get_fs();
+ struct pt_regs newregs = *task_pt_regs(tsk);
+ unsigned int idx = off / 4;
if (off & 3 || off >= COMPAT_USER_SZ)
return -EIO;
@@ -1564,14 +1562,25 @@ static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off,
if (off >= sizeof(compat_elf_gregset_t))
return 0;
- set_fs(KERNEL_DS);
- ret = copy_regset_from_user(tsk, &user_aarch32_view,
- REGSET_COMPAT_GPR, off,
- sizeof(compat_ulong_t),
- &val);
- set_fs(old_fs);
+ switch (idx) {
+ case 15:
+ newregs.pc = val;
+ break;
+ case 16:
+ newregs.pstate = compat_psr_to_pstate(val);
+ break;
+ case 17:
+ newregs.orig_x0 = val;
+ break;
+ default:
+ newregs.regs[idx] = val;
+ }
+
+ if (!valid_user_regs(&newregs.user_regs, tsk))
+ return -EINVAL;
- return ret;
+ *task_pt_regs(tsk) = newregs;
+ return 0;
}
#ifdef CONFIG_HAVE_HW_BREAKPOINT
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
index 82aaacf64583..eae7b094f608 100644
--- a/arch/ia64/kernel/ptrace.c
+++ b/arch/ia64/kernel/ptrace.c
@@ -1273,52 +1273,43 @@ struct regset_getset {
int ret;
};
+static const ptrdiff_t pt_offsets[32] =
+{
+#define R(n) offsetof(struct pt_regs, r##n)
+ [0] = -1, R(1), R(2), R(3),
+ [4] = -1, [5] = -1, [6] = -1, [7] = -1,
+ R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15),
+ R(16), R(17), R(18), R(19), R(20), R(21), R(22), R(23),
+ R(24), R(25), R(26), R(27), R(28), R(29), R(30), R(31),
+#undef R
+};
+
static int
access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info,
unsigned long addr, unsigned long *data, int write_access)
{
- struct pt_regs *pt;
- unsigned long *ptr = NULL;
- int ret;
- char nat = 0;
+ struct pt_regs *pt = task_pt_regs(target);
+ unsigned reg = addr / sizeof(unsigned long);
+ ptrdiff_t d = pt_offsets[reg];
- pt = task_pt_regs(target);
- switch (addr) {
- case ELF_GR_OFFSET(1):
- ptr = &pt->r1;
- break;
- case ELF_GR_OFFSET(2):
- case ELF_GR_OFFSET(3):
- ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2));
- break;
- case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7):
+ if (d >= 0) {
+ unsigned long *ptr = (void *)pt + d;
+ if (write_access)
+ *ptr = *data;
+ else
+ *data = *ptr;
+ return 0;
+ } else {
+ char nat = 0;
if (write_access) {
/* read NaT bit first: */
unsigned long dummy;
-
- ret = unw_get_gr(info, addr/8, &dummy, &nat);
+ int ret = unw_get_gr(info, reg, &dummy, &nat);
if (ret < 0)
return ret;
}
- return unw_access_gr(info, addr/8, data, &nat, write_access);
- case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11):
- ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8);
- break;
- case ELF_GR_OFFSET(12):
- case ELF_GR_OFFSET(13):
- ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12);
- break;
- case ELF_GR_OFFSET(14):
- ptr = &pt->r14;
- break;
- case ELF_GR_OFFSET(15):
- ptr = &pt->r15;
+ return unw_access_gr(info, reg, data, &nat, write_access);
}
- if (write_access)
- *ptr = *data;
- else
- *data = *ptr;
- return 0;
}
static int
@@ -1490,7 +1481,7 @@ static int
access_elf_reg(struct task_struct *target, struct unw_frame_info *info,
unsigned long addr, unsigned long *data, int write_access)
{
- if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(15))
+ if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(31))
return access_elf_gpreg(target, info, addr, data, write_access);
else if (addr >= ELF_BR_OFFSET(0) && addr <= ELF_BR_OFFSET(7))
return access_elf_breg(target, info, addr, data, write_access);
@@ -1500,10 +1491,7 @@ access_elf_reg(struct task_struct *target, struct unw_frame_info *info,
void do_gpregs_get(struct unw_frame_info *info, void *arg)
{
- struct pt_regs *pt;
struct regset_getset *dst = arg;
- elf_greg_t tmp[16];
- unsigned int i, index, min_copy;
if (unw_unwind_to_user(info) < 0)
return;
@@ -1526,160 +1514,70 @@ void do_gpregs_get(struct unw_frame_info *info, void *arg)
&dst->u.get.kbuf,
&dst->u.get.ubuf,
0, ELF_GR_OFFSET(1));
- if (dst->ret || dst->count == 0)
- return;
- }
-
- /* gr1 - gr15 */
- if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) {
- index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t);
- min_copy = ELF_GR_OFFSET(16) > (dst->pos + dst->count) ?
- (dst->pos + dst->count) : ELF_GR_OFFSET(16);
- for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t),
- index++)
- if (access_elf_reg(dst->target, info, i,
- &tmp[index], 0) < 0) {
- dst->ret = -EIO;
- return;
- }
- dst->ret = user_regset_copyout(&dst->pos, &dst->count,
- &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
- ELF_GR_OFFSET(1), ELF_GR_OFFSET(16));
- if (dst->ret || dst->count == 0)
- return;
- }
-
- /* r16-r31 */
- if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) {
- pt = task_pt_regs(dst->target);
- dst->ret = user_regset_copyout(&dst->pos, &dst->count,
- &dst->u.get.kbuf, &dst->u.get.ubuf, &pt->r16,
- ELF_GR_OFFSET(16), ELF_NAT_OFFSET);
- if (dst->ret || dst->count == 0)
+ if (dst->ret)
return;
}
- /* nat, pr, b0 - b7 */
- if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) {
- index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t);
- min_copy = ELF_CR_IIP_OFFSET > (dst->pos + dst->count) ?
- (dst->pos + dst->count) : ELF_CR_IIP_OFFSET;
- for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t),
- index++)
- if (access_elf_reg(dst->target, info, i,
- &tmp[index], 0) < 0) {
+ while (dst->count && dst->pos < ELF_AR_END_OFFSET) {
+ unsigned int n, from, to;
+ elf_greg_t tmp[16];
+
+ from = dst->pos;
+ to = from + min(dst->count, (unsigned)sizeof(tmp));
+ if (to > ELF_AR_END_OFFSET)
+ to = ELF_AR_END_OFFSET;
+ for (n = 0; from < to; from += sizeof(elf_greg_t), n++) {
+ if (access_elf_reg(dst->target, info, from,
+ &tmp[n], 0) < 0) {
dst->ret = -EIO;
return;
}
+ }
dst->ret = user_regset_copyout(&dst->pos, &dst->count,
&dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
- ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET);
- if (dst->ret || dst->count == 0)
+ dst->pos, to);
+ if (dst->ret)
return;
}
-
- /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat
- * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd
- */
- if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) {
- index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t);
- min_copy = ELF_AR_END_OFFSET > (dst->pos + dst->count) ?
- (dst->pos + dst->count) : ELF_AR_END_OFFSET;
- for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t),
- index++)
- if (access_elf_reg(dst->target, info, i,
- &tmp[index], 0) < 0) {
- dst->ret = -EIO;
- return;
- }
- dst->ret = user_regset_copyout(&dst->pos, &dst->count,
- &dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
- ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET);
- }
}
void do_gpregs_set(struct unw_frame_info *info, void *arg)
{
- struct pt_regs *pt;
struct regset_getset *dst = arg;
- elf_greg_t tmp[16];
- unsigned int i, index;
if (unw_unwind_to_user(info) < 0)
return;
+ if (!dst->count)
+ return;
/* Skip r0 */
- if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) {
+ if (dst->pos < ELF_GR_OFFSET(1)) {
dst->ret = user_regset_copyin_ignore(&dst->pos, &dst->count,
&dst->u.set.kbuf,
&dst->u.set.ubuf,
0, ELF_GR_OFFSET(1));
- if (dst->ret || dst->count == 0)
- return;
- }
-
- /* gr1-gr15 */
- if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) {
- i = dst->pos;
- index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t);
- dst->ret = user_regset_copyin(&dst->pos, &dst->count,
- &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
- ELF_GR_OFFSET(1), ELF_GR_OFFSET(16));
if (dst->ret)
return;
- for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++)
- if (access_elf_reg(dst->target, info, i,
- &tmp[index], 1) < 0) {
- dst->ret = -EIO;
- return;
- }
- if (dst->count == 0)
- return;
}
- /* gr16-gr31 */
- if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) {
- pt = task_pt_regs(dst->target);
- dst->ret = user_regset_copyin(&dst->pos, &dst->count,
- &dst->u.set.kbuf, &dst->u.set.ubuf, &pt->r16,
- ELF_GR_OFFSET(16), ELF_NAT_OFFSET);
- if (dst->ret || dst->count == 0)
- return;
- }
-
- /* nat, pr, b0 - b7 */
- if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) {
- i = dst->pos;
- index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t);
- dst->ret = user_regset_copyin(&dst->pos, &dst->count,
- &dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
- ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET);
- if (dst->ret)
- return;
- for (; i < dst->pos; i += sizeof(elf_greg_t), index++)
- if (access_elf_reg(dst->target, info, i,
- &tmp[index], 1) < 0) {
- dst->ret = -EIO;
- return;
- }
- if (dst->count == 0)
- return;
- }
+ while (dst->count && dst->pos < ELF_AR_END_OFFSET) {
+ unsigned int n, from, to;
+ elf_greg_t tmp[16];
- /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat
- * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd
- */
- if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) {
- i = dst->pos;
- index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t);
+ from = dst->pos;
+ to = from + sizeof(tmp);
+ if (to > ELF_AR_END_OFFSET)
+ to = ELF_AR_END_OFFSET;
+ /* get up to 16 values */
dst->ret = user_regset_copyin(&dst->pos, &dst->count,
&dst->u.set.kbuf, &dst->u.set.ubuf, tmp,
- ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET);
+ from, to);
if (dst->ret)
return;
- for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++)
- if (access_elf_reg(dst->target, info, i,
- &tmp[index], 1) < 0) {
+ /* now copy them into registers */
+ for (n = 0; from < dst->pos; from += sizeof(elf_greg_t), n++)
+ if (access_elf_reg(dst->target, info, from,
+ &tmp[n], 1) < 0) {
dst->ret = -EIO;
return;
}
@@ -1913,7 +1811,6 @@ access_uarea(struct task_struct *child, unsigned long addr,
unsigned long *data, int write_access)
{
unsigned int pos = -1; /* an invalid value */
- int ret;
unsigned long *ptr, regnum;
if ((addr & 0x7) != 0) {
@@ -1945,14 +1842,39 @@ access_uarea(struct task_struct *child, unsigned long addr,
}
if (pos != -1) {
- if (write_access)
- ret = fpregs_set(child, NULL, pos,
- sizeof(unsigned long), data, NULL);
- else
- ret = fpregs_get(child, NULL, pos,
- sizeof(unsigned long), data, NULL);
- if (ret != 0)
- return -1;
+ unsigned reg = pos / sizeof(elf_fpreg_t);
+ int which_half = (pos / sizeof(unsigned long)) & 1;
+
+ if (reg < 32) { /* fr2-fr31 */
+ struct unw_frame_info info;
+ elf_fpreg_t fpreg;
+
+ memset(&info, 0, sizeof(info));
+ unw_init_from_blocked_task(&info, child);
+ if (unw_unwind_to_user(&info) < 0)
+ return 0;
+
+ if (unw_get_fr(&info, reg, &fpreg))
+ return -1;
+ if (write_access) {
+ fpreg.u.bits[which_half] = *data;
+ if (unw_set_fr(&info, reg, fpreg))
+ return -1;
+ } else {
+ *data = fpreg.u.bits[which_half];
+ }
+ } else { /* fph */
+ elf_fpreg_t *p = &child->thread.fph[reg - 32];
+ unsigned long *bits = &p->u.bits[which_half];
+
+ ia64_sync_fph(child);
+ if (write_access)
+ *bits = *data;
+ else if (child->thread.flags & IA64_THREAD_FPH_VALID)
+ *data = *bits;
+ else
+ *data = 0;
+ }
return 0;
}
@@ -2038,15 +1960,14 @@ access_uarea(struct task_struct *child, unsigned long addr,
}
if (pos != -1) {
- if (write_access)
- ret = gpregs_set(child, NULL, pos,
- sizeof(unsigned long), data, NULL);
- else
- ret = gpregs_get(child, NULL, pos,
- sizeof(unsigned long), data, NULL);
- if (ret != 0)
- return -1;
- return 0;
+ struct unw_frame_info info;
+
+ memset(&info, 0, sizeof(info));
+ unw_init_from_blocked_task(&info, child);
+ if (unw_unwind_to_user(&info) < 0)
+ return 0;
+
+ return access_elf_reg(child, &info, pos, data, write_access);
}
/* access debug registers */
diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h
index 42159f45bf9c..0c85a08176c0 100644
--- a/arch/x86/include/asm/fpu/internal.h
+++ b/arch/x86/include/asm/fpu/internal.h
@@ -34,7 +34,6 @@ extern int fpu__copy(struct task_struct *dst, struct task_struct *src);
extern void fpu__clear_user_states(struct fpu *fpu);
extern void fpu__clear_all(struct fpu *fpu);
extern int fpu__exception_code(struct fpu *fpu, int trap_nr);
-extern int dump_fpu(struct pt_regs *ptregs, struct user_i387_struct *fpstate);
/*
* Boot time FPU initialization functions:
diff --git a/arch/x86/kernel/fpu/regset.c b/arch/x86/kernel/fpu/regset.c
index bd1d0649f8ce..91f80aca27fb 100644
--- a/arch/x86/kernel/fpu/regset.c
+++ b/arch/x86/kernel/fpu/regset.c
@@ -356,20 +356,4 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset,
return ret;
}
-/*
- * FPU state for core dumps.
- * This is only used for a.out dumps now.
- * It is declared generically using elf_fpregset_t (which is
- * struct user_i387_struct) but is in fact only used for 32-bit
- * dumps, so on 64-bit it is really struct user_i387_ia32_struct.
- */
-int dump_fpu(struct pt_regs *regs, struct user_i387_struct *ufpu)
-{
- struct task_struct *tsk = current;
-
- return !fpregs_get(tsk, NULL, 0, sizeof(struct user_i387_ia32_struct),
- ufpu, NULL);
-}
-EXPORT_SYMBOL(dump_fpu);
-
#endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c
index 9393a445d73c..e0b832df7404 100644
--- a/arch/x86/kernel/fpu/signal.c
+++ b/arch/x86/kernel/fpu/signal.c
@@ -170,14 +170,14 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size)
ia32_fxstate &= (IS_ENABLED(CONFIG_X86_32) ||
IS_ENABLED(CONFIG_IA32_EMULATION));
+ if (!static_cpu_has(X86_FEATURE_FPU)) {
+ struct user_i387_ia32_struct fp;
+ fpregs_soft_get(current, NULL, 0, sizeof(fp), &fp, NULL);
+ return copy_to_user(buf, &fp, sizeof(fp)) ? -EFAULT : 0;
+ }
+
if (!access_ok(buf, size))
return -EACCES;
-
- if (!static_cpu_has(X86_FEATURE_FPU))
- return fpregs_soft_get(current, NULL, 0,
- sizeof(struct user_i387_ia32_struct), NULL,
- (struct _fpstate_32 __user *) buf) ? -1 : 1;
-
retry:
/*
* Load the FPU registers if they are not valid for the current task.
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 9fe3b51c116a..e922a6abdca8 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1821,7 +1821,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
long signr, size_t *total)
{
unsigned int i;
- unsigned int regset0_size = regset_size(t->task, &view->regsets[0]);
+ int regset0_size;
/*
* NT_PRSTATUS is the one special case, because the regset data
@@ -1830,8 +1830,10 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
* We assume that regset 0 is NT_PRSTATUS.
*/
fill_prstatus(&t->prstatus, t->task, signr);
- (void) view->regsets[0].get(t->task, &view->regsets[0], 0, regset0_size,
- &t->prstatus.pr_reg, NULL);
+ regset0_size = regset_get(t->task, &view->regsets[0],
+ sizeof(t->prstatus.pr_reg), &t->prstatus.pr_reg);
+ if (regset0_size < 0)
+ return 0;
fill_note(&t->notes[0], "CORE", NT_PRSTATUS,
PRSTATUS_SIZE(t->prstatus, regset0_size), &t->prstatus);
@@ -1846,32 +1848,28 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
*/
for (i = 1; i < view->n; ++i) {
const struct user_regset *regset = &view->regsets[i];
+ int note_type = regset->core_note_type;
+ bool is_fpreg = note_type == NT_PRFPREG;
+ void *data;
+ int ret;
+
do_thread_regset_writeback(t->task, regset);
- if (regset->core_note_type && regset->get &&
- (!regset->active || regset->active(t->task, regset) > 0)) {
- int ret;
- size_t size = regset_size(t->task, regset);
- void *data = kzalloc(size, GFP_KERNEL);
- if (unlikely(!data))
- return 0;
- ret = regset->get(t->task, regset,
- 0, size, data, NULL);
- if (unlikely(ret))
- kfree(data);
- else {
- if (regset->core_note_type != NT_PRFPREG)
- fill_note(&t->notes[i], "LINUX",
- regset->core_note_type,
- size, data);
- else {
- SET_PR_FPVALID(&t->prstatus,
- 1, regset0_size);
- fill_note(&t->notes[i], "CORE",
- NT_PRFPREG, size, data);
- }
- *total += notesize(&t->notes[i]);
- }
- }
+ if (!note_type) // not for coredumps
+ continue;
+ if (regset->active && regset->active(t->task, regset) <= 0)
+ continue;
+
+ ret = regset_get_alloc(t->task, regset, ~0U, &data);
+ if (ret < 0)
+ continue;
+
+ if (is_fpreg)
+ SET_PR_FPVALID(&t->prstatus, 1, regset0_size);
+
+ fill_note(&t->notes[i], is_fpreg ? "CORE" : "LINUX",
+ note_type, ret, data);
+
+ *total += notesize(&t->notes[i]);
}
return 1;
diff --git a/include/linux/regset.h b/include/linux/regset.h
index 46d6ae68c455..968a032922d5 100644
--- a/include/linux/regset.h
+++ b/include/linux/regset.h
@@ -353,6 +353,15 @@ static inline int user_regset_copyin_ignore(unsigned int *pos,
return 0;
}
+extern int regset_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int size, void *data);
+
+extern int regset_get_alloc(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int size,
+ void **data);
+
/**
* copy_regset_to_user - fetch a thread's user_regset data into user memory
* @target: thread to be examined
diff --git a/kernel/Makefile b/kernel/Makefile
index f3218bc5ec69..e6e03380a0f1 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,7 +10,7 @@ obj-y = fork.o exec_domain.o panic.o \
extable.o params.o \
kthread.o sys_ni.o nsproxy.o \
notifier.o ksysfs.o cred.o reboot.o \
- async.o range.o smpboot.o ucount.o
+ async.o range.o smpboot.o ucount.o regset.o
obj-$(CONFIG_MODULES) += kmod.o
obj-$(CONFIG_MULTIUSER) += groups.o
diff --git a/kernel/regset.c b/kernel/regset.c
new file mode 100644
index 000000000000..6b39fa0993ec
--- /dev/null
+++ b/kernel/regset.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/regset.h>
+
+static int __regset_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int size,
+ void **data)
+{
+ void *p = *data, *to_free = NULL;
+ int res;
+
+ if (!regset->get)
+ return -EOPNOTSUPP;
+ if (size > regset->n * regset->size)
+ size = regset->n * regset->size;
+ if (!p) {
+ to_free = p = kzalloc(size, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ }
+ res = regset->get(target, regset, 0, size, p, NULL);
+ if (unlikely(res < 0)) {
+ kfree(to_free);
+ return res;
+ }
+ *data = p;
+ if (regset->get_size) { // arm64-only kludge, will go away
+ unsigned max_size = regset->get_size(target, regset);
+ if (size > max_size)
+ size = max_size;
+ }
+ return size;
+}
+
+int regset_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int size,
+ void *data)
+{
+ return __regset_get(target, regset, size, &data);
+}
+EXPORT_SYMBOL(regset_get);
+
+int regset_get_alloc(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int size,
+ void **data)
+{
+ *data = NULL;
+ return __regset_get(target, regset, size, data);
+}
+EXPORT_SYMBOL(regset_get_alloc);