linux-stable/arch/x86/um/ptrace.c
Benjamin Berg 32f1fde0b6 um: fix sparse warnings from regset refactor
Some variables were not tagged with __user and another was not marked as
static even though it should be.

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202410280655.gOlEFwdG-lkp@intel.com/
Closes: https://lore.kernel.org/oe-kbuild-all/202410281821.WSPsAwq7-lkp@intel.com/
Fixes: 3f17fed214 ("um: switch to regset API and depend on XSTATE")
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Link: https://patch.msgid.link/20241031142017.430420-1-benjamin@sipsolutions.net
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2024-11-07 17:34:50 +01:00

268 lines
6.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/sched.h>
#include <linux/elf.h>
#include <linux/regset.h>
#include <asm/user32.h>
#include <asm/sigcontext.h>
#ifdef CONFIG_X86_32
/*
* FPU tag word conversions.
*/
static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
{
unsigned int tmp; /* to avoid 16 bit prefixes in the code */
/* Transform each pair of bits into 01 (valid) or 00 (empty) */
tmp = ~twd;
tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
/* and move the valid bits to the lower byte. */
tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
return tmp;
}
static inline unsigned long twd_fxsr_to_i387(struct user_fxsr_struct *fxsave)
{
struct _fpxreg *st = NULL;
unsigned long twd = (unsigned long) fxsave->twd;
unsigned long tag;
unsigned long ret = 0xffff0000;
int i;
#define FPREG_ADDR(f, n) ((char *)&(f)->st_space + (n) * 16)
for (i = 0; i < 8; i++) {
if (twd & 0x1) {
st = (struct _fpxreg *) FPREG_ADDR(fxsave, i);
switch (st->exponent & 0x7fff) {
case 0x7fff:
tag = 2; /* Special */
break;
case 0x0000:
if (!st->significand[0] &&
!st->significand[1] &&
!st->significand[2] &&
!st->significand[3]) {
tag = 1; /* Zero */
} else {
tag = 2; /* Special */
}
break;
default:
if (st->significand[3] & 0x8000)
tag = 0; /* Valid */
else
tag = 2; /* Special */
break;
}
} else {
tag = 3; /* Empty */
}
ret |= (tag << (2 * i));
twd = twd >> 1;
}
return ret;
}
/* Get/set the old 32bit i387 registers (pre-FPX) */
static int fpregs_legacy_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
int i;
membuf_store(&to, (unsigned long)fxsave->cwd | 0xffff0000ul);
membuf_store(&to, (unsigned long)fxsave->swd | 0xffff0000ul);
membuf_store(&to, twd_fxsr_to_i387(fxsave));
membuf_store(&to, fxsave->fip);
membuf_store(&to, fxsave->fcs | ((unsigned long)fxsave->fop << 16));
membuf_store(&to, fxsave->foo);
membuf_store(&to, fxsave->fos);
for (i = 0; i < 8; i++)
membuf_write(&to, (void *)fxsave->st_space + i * 16, 10);
return 0;
}
static int fpregs_legacy_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
struct user_fxsr_struct *fxsave = (void *)target->thread.regs.regs.fp;
const struct user_i387_struct *from;
struct user_i387_struct buf;
int i;
if (ubuf) {
if (copy_from_user(&buf, ubuf, sizeof(buf)))
return -EFAULT;
from = &buf;
} else {
from = kbuf;
}
fxsave->cwd = (unsigned short)(from->cwd & 0xffff);
fxsave->swd = (unsigned short)(from->swd & 0xffff);
fxsave->twd = twd_i387_to_fxsr((unsigned short)(from->twd & 0xffff));
fxsave->fip = from->fip;
fxsave->fop = (unsigned short)((from->fcs & 0xffff0000ul) >> 16);
fxsave->fcs = (from->fcs & 0xffff);
fxsave->foo = from->foo;
fxsave->fos = from->fos;
for (i = 0; i < 8; i++) {
memcpy((void *)fxsave->st_space + i * 16,
(void *)from->st_space + i * 10, 10);
}
return 0;
}
#endif
static int genregs_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
int reg;
for (reg = 0; to.left; reg++)
membuf_store(&to, getreg(target, reg * sizeof(unsigned long)));
return 0;
}
static int genregs_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
int ret = 0;
if (kbuf) {
const unsigned long *k = kbuf;
while (count >= sizeof(*k) && !ret) {
ret = putreg(target, pos, *k++);
count -= sizeof(*k);
pos += sizeof(*k);
}
} else {
const unsigned long __user *u = ubuf;
while (count >= sizeof(*u) && !ret) {
unsigned long word;
ret = __get_user(word, u++);
if (ret)
break;
ret = putreg(target, pos, word);
count -= sizeof(*u);
pos += sizeof(*u);
}
}
return ret;
}
static int generic_fpregs_active(struct task_struct *target, const struct user_regset *regset)
{
return regset->n;
}
static int generic_fpregs_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
void *fpregs = task_pt_regs(target)->regs.fp;
membuf_write(&to, fpregs, regset->size * regset->n);
return 0;
}
static int generic_fpregs_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
void *fpregs = task_pt_regs(target)->regs.fp;
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
fpregs, 0, regset->size * regset->n);
}
static struct user_regset uml_regsets[] __ro_after_init = {
[REGSET_GENERAL] = {
.core_note_type = NT_PRSTATUS,
.n = sizeof(struct user_regs_struct) / sizeof(long),
.size = sizeof(long),
.align = sizeof(long),
.regset_get = genregs_get,
.set = genregs_set
},
#ifdef CONFIG_X86_32
/* Old FP registers, they are needed in signal frames */
[REGSET_FP_LEGACY] = {
.core_note_type = NT_PRFPREG,
.n = sizeof(struct user_i387_ia32_struct) / sizeof(long),
.size = sizeof(long),
.align = sizeof(long),
.active = generic_fpregs_active,
.regset_get = fpregs_legacy_get,
.set = fpregs_legacy_set,
},
#endif
[REGSET_FP] = {
#ifdef CONFIG_X86_32
.core_note_type = NT_PRXFPREG,
.n = sizeof(struct user32_fxsr_struct) / sizeof(long),
#else
.core_note_type = NT_PRFPREG,
.n = sizeof(struct user_i387_struct) / sizeof(long),
#endif
.size = sizeof(long),
.align = sizeof(long),
.active = generic_fpregs_active,
.regset_get = generic_fpregs_get,
.set = generic_fpregs_set,
},
[REGSET_XSTATE] = {
.core_note_type = NT_X86_XSTATE,
.size = sizeof(long),
.align = sizeof(long),
.active = generic_fpregs_active,
.regset_get = generic_fpregs_get,
.set = generic_fpregs_set,
},
/* TODO: Add TLS regset for 32bit */
};
static const struct user_regset_view user_uml_view = {
#ifdef CONFIG_X86_32
.name = "i386", .e_machine = EM_386,
#else
.name = "x86_64", .e_machine = EM_X86_64,
#endif
.regsets = uml_regsets, .n = ARRAY_SIZE(uml_regsets)
};
const struct user_regset_view *
task_user_regset_view(struct task_struct *tsk)
{
return &user_uml_view;
}
static int __init init_regset_xstate_info(void)
{
uml_regsets[REGSET_XSTATE].n =
host_fp_size / uml_regsets[REGSET_XSTATE].size;
return 0;
}
arch_initcall(init_regset_xstate_info);