Merge branch 'work.regset' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull ptrace regset updates from Al Viro:
 "Internal regset API changes:

   - regularize copy_regset_{to,from}_user() callers

   - switch to saner calling conventions for ->get()

   - kill user_regset_copyout()

  The ->put() side of things will have to wait for the next cycle,
  unfortunately.

  The balance is about -1KLoC and replacements for ->get() instances are
  a lot saner"

* 'work.regset' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (41 commits)
  regset: kill user_regset_copyout{,_zero}()
  regset(): kill ->get_size()
  regset: kill ->get()
  csky: switch to ->regset_get()
  xtensa: switch to ->regset_get()
  parisc: switch to ->regset_get()
  nds32: switch to ->regset_get()
  nios2: switch to ->regset_get()
  hexagon: switch to ->regset_get()
  h8300: switch to ->regset_get()
  openrisc: switch to ->regset_get()
  riscv: switch to ->regset_get()
  c6x: switch to ->regset_get()
  ia64: switch to ->regset_get()
  arc: switch to ->regset_get()
  arm: switch to ->regset_get()
  sh: convert to ->regset_get()
  arm64: switch to ->regset_get()
  mips: switch to ->regset_get()
  sparc: switch to ->regset_get()
  ...
This commit is contained in:
Linus Torvalds
2020-08-07 09:29:25 -07:00
41 changed files with 1369 additions and 2348 deletions

View File

@@ -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);
@@ -1498,12 +1489,17 @@ access_elf_reg(struct task_struct *target, struct unw_frame_info *info,
return access_elf_areg(target, info, addr, data, write_access);
}
struct regset_membuf {
struct membuf to;
int ret;
};
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;
struct regset_membuf *dst = arg;
struct membuf to = dst->to;
unsigned int n;
elf_greg_t reg;
if (unw_unwind_to_user(info) < 0)
return;
@@ -1521,165 +1517,53 @@ void do_gpregs_get(struct unw_frame_info *info, void *arg)
/* Skip r0 */
if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) {
dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count,
&dst->u.get.kbuf,
&dst->u.get.ubuf,
0, ELF_GR_OFFSET(1));
if (dst->ret || dst->count == 0)
membuf_zero(&to, 8);
for (n = 8; to.left && n < ELF_AR_END_OFFSET; n += 8) {
if (access_elf_reg(info->task, info, n, &reg, 0) < 0) {
dst->ret = -EIO;
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)
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) {
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)
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);
}
membuf_store(&to, reg);
}
}
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;
}
while (dst->count && dst->pos < ELF_AR_END_OFFSET) {
unsigned int n, from, to;
elf_greg_t tmp[16];
/* 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);
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_NAT_OFFSET, ELF_CR_IIP_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) {
dst->ret = -EIO;
return;
}
if (dst->count == 0)
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)) {
i = dst->pos;
index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t);
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);
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;
}
@@ -1690,60 +1574,36 @@ void do_gpregs_set(struct unw_frame_info *info, void *arg)
void do_fpregs_get(struct unw_frame_info *info, void *arg)
{
struct regset_getset *dst = arg;
struct task_struct *task = dst->target;
elf_fpreg_t tmp[30];
int index, min_copy, i;
struct task_struct *task = info->task;
struct regset_membuf *dst = arg;
struct membuf to = dst->to;
elf_fpreg_t reg;
unsigned int n;
if (unw_unwind_to_user(info) < 0)
return;
/* Skip pos 0 and 1 */
if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(2)) {
dst->ret = user_regset_copyout_zero(&dst->pos, &dst->count,
&dst->u.get.kbuf,
&dst->u.get.ubuf,
0, ELF_FP_OFFSET(2));
if (dst->count == 0 || dst->ret)
return;
}
membuf_zero(&to, 2 * sizeof(elf_fpreg_t));
/* fr2-fr31 */
if (dst->count > 0 && dst->pos < ELF_FP_OFFSET(32)) {
index = (dst->pos - ELF_FP_OFFSET(2)) / sizeof(elf_fpreg_t);
min_copy = min(((unsigned int)ELF_FP_OFFSET(32)),
dst->pos + dst->count);
for (i = dst->pos; i < min_copy; i += sizeof(elf_fpreg_t),
index++)
if (unw_get_fr(info, i / sizeof(elf_fpreg_t),
&tmp[index])) {
dst->ret = -EIO;
return;
}
dst->ret = user_regset_copyout(&dst->pos, &dst->count,
&dst->u.get.kbuf, &dst->u.get.ubuf, tmp,
ELF_FP_OFFSET(2), ELF_FP_OFFSET(32));
if (dst->count == 0 || dst->ret)
for (n = 2; to.left && n < 32; n++) {
if (unw_get_fr(info, n, &reg)) {
dst->ret = -EIO;
return;
}
membuf_write(&to, &reg, sizeof(reg));
}
/* fph */
if (dst->count > 0) {
ia64_flush_fph(dst->target);
if (task->thread.flags & IA64_THREAD_FPH_VALID)
dst->ret = user_regset_copyout(
&dst->pos, &dst->count,
&dst->u.get.kbuf, &dst->u.get.ubuf,
&dst->target->thread.fph,
ELF_FP_OFFSET(32), -1);
else
/* Zero fill instead. */
dst->ret = user_regset_copyout_zero(
&dst->pos, &dst->count,
&dst->u.get.kbuf, &dst->u.get.ubuf,
ELF_FP_OFFSET(32), -1);
}
if (!to.left)
return;
ia64_flush_fph(task);
if (task->thread.flags & IA64_THREAD_FPH_VALID)
membuf_write(&to, &task->thread.fph, 96 * sizeof(reg));
else
membuf_zero(&to, 96 * sizeof(reg));
}
void do_fpregs_set(struct unw_frame_info *info, void *arg)
@@ -1819,6 +1679,20 @@ void do_fpregs_set(struct unw_frame_info *info, void *arg)
}
}
static void
unwind_and_call(void (*call)(struct unw_frame_info *, void *),
struct task_struct *target, void *data)
{
if (target == current)
unw_init_running(call, data);
else {
struct unw_frame_info info;
memset(&info, 0, sizeof(info));
unw_init_from_blocked_task(&info, target);
(*call)(&info, data);
}
}
static int
do_regset_call(void (*call)(struct unw_frame_info *, void *),
struct task_struct *target,
@@ -1830,27 +1704,18 @@ do_regset_call(void (*call)(struct unw_frame_info *, void *),
.pos = pos, .count = count,
.u.set = { .kbuf = kbuf, .ubuf = ubuf },
.ret = 0 };
if (target == current)
unw_init_running(call, &info);
else {
struct unw_frame_info ufi;
memset(&ufi, 0, sizeof(ufi));
unw_init_from_blocked_task(&ufi, target);
(*call)(&ufi, &info);
}
unwind_and_call(call, target, &info);
return info.ret;
}
static int
gpregs_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
struct membuf to)
{
return do_regset_call(do_gpregs_get, target, regset, pos, count,
kbuf, ubuf);
struct regset_membuf info = {.to = to};
unwind_and_call(do_gpregs_get, target, &info);
return info.ret;
}
static int gpregs_set(struct task_struct *target,
@@ -1892,11 +1757,11 @@ fpregs_active(struct task_struct *target, const struct user_regset *regset)
static int fpregs_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
struct membuf to)
{
return do_regset_call(do_fpregs_get, target, regset, pos, count,
kbuf, ubuf);
struct regset_membuf info = {.to = to};
unwind_and_call(do_fpregs_get, target, &info);
return info.ret;
}
static int fpregs_set(struct task_struct *target,
@@ -1913,7 +1778,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 +1809,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 +1927,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 */
@@ -2112,14 +2000,14 @@ static const struct user_regset native_regsets[] = {
.core_note_type = NT_PRSTATUS,
.n = ELF_NGREG,
.size = sizeof(elf_greg_t), .align = sizeof(elf_greg_t),
.get = gpregs_get, .set = gpregs_set,
.regset_get = gpregs_get, .set = gpregs_set,
.writeback = gpregs_writeback
},
{
.core_note_type = NT_PRFPREG,
.n = ELF_NFPREG,
.size = sizeof(elf_fpreg_t), .align = sizeof(elf_fpreg_t),
.get = fpregs_get, .set = fpregs_set, .active = fpregs_active
.regset_get = fpregs_get, .set = fpregs_set, .active = fpregs_active
},
};