2020-02-28 08:14:45 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
|
|
#include <linux/regset.h>
|
|
|
|
#include <linux/elf.h>
|
|
|
|
#include <linux/nospec.h>
|
|
|
|
#include <linux/pkeys.h>
|
|
|
|
|
|
|
|
#include "ptrace-decl.h"
|
|
|
|
|
|
|
|
struct pt_regs_offset {
|
|
|
|
const char *name;
|
|
|
|
int offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define STR(s) #s /* convert to string */
|
|
|
|
#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
|
|
|
|
#define GPR_OFFSET_NAME(num) \
|
|
|
|
{.name = STR(r##num), .offset = offsetof(struct pt_regs, gpr[num])}, \
|
|
|
|
{.name = STR(gpr##num), .offset = offsetof(struct pt_regs, gpr[num])}
|
|
|
|
#define REG_OFFSET_END {.name = NULL, .offset = 0}
|
|
|
|
|
|
|
|
static const struct pt_regs_offset regoffset_table[] = {
|
|
|
|
GPR_OFFSET_NAME(0),
|
|
|
|
GPR_OFFSET_NAME(1),
|
|
|
|
GPR_OFFSET_NAME(2),
|
|
|
|
GPR_OFFSET_NAME(3),
|
|
|
|
GPR_OFFSET_NAME(4),
|
|
|
|
GPR_OFFSET_NAME(5),
|
|
|
|
GPR_OFFSET_NAME(6),
|
|
|
|
GPR_OFFSET_NAME(7),
|
|
|
|
GPR_OFFSET_NAME(8),
|
|
|
|
GPR_OFFSET_NAME(9),
|
|
|
|
GPR_OFFSET_NAME(10),
|
|
|
|
GPR_OFFSET_NAME(11),
|
|
|
|
GPR_OFFSET_NAME(12),
|
|
|
|
GPR_OFFSET_NAME(13),
|
|
|
|
GPR_OFFSET_NAME(14),
|
|
|
|
GPR_OFFSET_NAME(15),
|
|
|
|
GPR_OFFSET_NAME(16),
|
|
|
|
GPR_OFFSET_NAME(17),
|
|
|
|
GPR_OFFSET_NAME(18),
|
|
|
|
GPR_OFFSET_NAME(19),
|
|
|
|
GPR_OFFSET_NAME(20),
|
|
|
|
GPR_OFFSET_NAME(21),
|
|
|
|
GPR_OFFSET_NAME(22),
|
|
|
|
GPR_OFFSET_NAME(23),
|
|
|
|
GPR_OFFSET_NAME(24),
|
|
|
|
GPR_OFFSET_NAME(25),
|
|
|
|
GPR_OFFSET_NAME(26),
|
|
|
|
GPR_OFFSET_NAME(27),
|
|
|
|
GPR_OFFSET_NAME(28),
|
|
|
|
GPR_OFFSET_NAME(29),
|
|
|
|
GPR_OFFSET_NAME(30),
|
|
|
|
GPR_OFFSET_NAME(31),
|
|
|
|
REG_OFFSET_NAME(nip),
|
|
|
|
REG_OFFSET_NAME(msr),
|
|
|
|
REG_OFFSET_NAME(ctr),
|
|
|
|
REG_OFFSET_NAME(link),
|
|
|
|
REG_OFFSET_NAME(xer),
|
|
|
|
REG_OFFSET_NAME(ccr),
|
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
REG_OFFSET_NAME(softe),
|
|
|
|
#else
|
|
|
|
REG_OFFSET_NAME(mq),
|
|
|
|
#endif
|
|
|
|
REG_OFFSET_NAME(trap),
|
|
|
|
REG_OFFSET_NAME(dar),
|
|
|
|
REG_OFFSET_NAME(dsisr),
|
|
|
|
REG_OFFSET_END,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* regs_query_register_offset() - query register offset from its name
|
|
|
|
* @name: the name of a register
|
|
|
|
*
|
|
|
|
* regs_query_register_offset() returns the offset of a register in struct
|
|
|
|
* pt_regs from its name. If the name is invalid, this returns -EINVAL;
|
|
|
|
*/
|
|
|
|
int regs_query_register_offset(const char *name)
|
|
|
|
{
|
|
|
|
const struct pt_regs_offset *roff;
|
|
|
|
for (roff = regoffset_table; roff->name != NULL; roff++)
|
|
|
|
if (!strcmp(roff->name, name))
|
|
|
|
return roff->offset;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* regs_query_register_name() - query register name from its offset
|
|
|
|
* @offset: the offset of a register in struct pt_regs.
|
|
|
|
*
|
|
|
|
* regs_query_register_name() returns the name of a register from its
|
|
|
|
* offset in struct pt_regs. If the @offset is invalid, this returns NULL;
|
|
|
|
*/
|
|
|
|
const char *regs_query_register_name(unsigned int offset)
|
|
|
|
{
|
|
|
|
const struct pt_regs_offset *roff;
|
|
|
|
for (roff = regoffset_table; roff->name != NULL; roff++)
|
|
|
|
if (roff->offset == offset)
|
|
|
|
return roff->name;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* does not yet catch signals sent when the child dies.
|
|
|
|
* in exit.c or in signal.c.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static unsigned long get_user_msr(struct task_struct *task)
|
|
|
|
{
|
|
|
|
return task->thread.regs->msr | task->thread.fpexc_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_user_msr(struct task_struct *task, unsigned long msr)
|
|
|
|
{
|
|
|
|
task->thread.regs->msr &= ~MSR_DEBUGCHANGE;
|
|
|
|
task->thread.regs->msr |= msr & MSR_DEBUGCHANGE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
static int get_user_dscr(struct task_struct *task, unsigned long *data)
|
|
|
|
{
|
|
|
|
*data = task->thread.dscr;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_user_dscr(struct task_struct *task, unsigned long dscr)
|
|
|
|
{
|
|
|
|
task->thread.dscr = dscr;
|
|
|
|
task->thread.dscr_inherit = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int get_user_dscr(struct task_struct *task, unsigned long *data)
|
|
|
|
{
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_user_dscr(struct task_struct *task, unsigned long dscr)
|
|
|
|
{
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We prevent mucking around with the reserved area of trap
|
|
|
|
* which are used internally by the kernel.
|
|
|
|
*/
|
|
|
|
static int set_user_trap(struct task_struct *task, unsigned long trap)
|
|
|
|
{
|
2020-05-07 20:13:30 +08:00
|
|
|
set_trap(task->thread.regs, trap);
|
2020-02-28 08:14:45 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get contents of register REGNO in task TASK.
|
|
|
|
*/
|
|
|
|
int ptrace_get_reg(struct task_struct *task, int regno, unsigned long *data)
|
|
|
|
{
|
|
|
|
unsigned int regs_max;
|
|
|
|
|
|
|
|
if (task->thread.regs == NULL || !data)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (regno == PT_MSR) {
|
|
|
|
*data = get_user_msr(task);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (regno == PT_DSCR)
|
|
|
|
return get_user_dscr(task, data);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* softe copies paca->irq_soft_mask variable state. Since irq_soft_mask is
|
|
|
|
* no more used as a flag, lets force usr to alway see the softe value as 1
|
|
|
|
* which means interrupts are not soft disabled.
|
|
|
|
*/
|
|
|
|
if (IS_ENABLED(CONFIG_PPC64) && regno == PT_SOFTE) {
|
|
|
|
*data = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
regs_max = sizeof(struct user_pt_regs) / sizeof(unsigned long);
|
|
|
|
if (regno < regs_max) {
|
|
|
|
regno = array_index_nospec(regno, regs_max);
|
|
|
|
*data = ((unsigned long *)task->thread.regs)[regno];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write contents of register REGNO in task TASK.
|
|
|
|
*/
|
|
|
|
int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data)
|
|
|
|
{
|
|
|
|
if (task->thread.regs == NULL)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (regno == PT_MSR)
|
|
|
|
return set_user_msr(task, data);
|
|
|
|
if (regno == PT_TRAP)
|
|
|
|
return set_user_trap(task, data);
|
|
|
|
if (regno == PT_DSCR)
|
|
|
|
return set_user_dscr(task, data);
|
|
|
|
|
|
|
|
if (regno <= PT_MAX_PUT_REG) {
|
|
|
|
regno = array_index_nospec(regno, PT_MAX_PUT_REG + 1);
|
|
|
|
((unsigned long *)task->thread.regs)[regno] = data;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gpr_get(struct task_struct *target, const struct user_regset *regset,
|
2020-02-23 00:01:44 +08:00
|
|
|
struct membuf to)
|
2020-02-28 08:14:45 +08:00
|
|
|
{
|
2020-02-23 00:01:44 +08:00
|
|
|
int i;
|
2020-02-28 08:14:45 +08:00
|
|
|
|
|
|
|
if (target->thread.regs == NULL)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (!FULL_REGS(target->thread.regs)) {
|
|
|
|
/* We have a partial register set. Fill 14-31 with bogus values */
|
|
|
|
for (i = 14; i < 32; i++)
|
|
|
|
target->thread.regs->gpr[i] = NV_REG_POISON;
|
|
|
|
}
|
|
|
|
|
2020-02-23 00:01:44 +08:00
|
|
|
membuf_write(&to, target->thread.regs, offsetof(struct pt_regs, msr));
|
|
|
|
membuf_store(&to, get_user_msr(target));
|
2020-02-28 08:14:45 +08:00
|
|
|
|
|
|
|
BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) !=
|
|
|
|
offsetof(struct pt_regs, msr) + sizeof(long));
|
|
|
|
|
2020-02-23 00:01:44 +08:00
|
|
|
membuf_write(&to, &target->thread.regs->orig_gpr3,
|
|
|
|
sizeof(struct user_pt_regs) -
|
|
|
|
offsetof(struct pt_regs, orig_gpr3));
|
|
|
|
return membuf_zero(&to, ELF_NGREG * sizeof(unsigned long) -
|
|
|
|
sizeof(struct user_pt_regs));
|
2020-02-28 08:14:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gpr_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count, const void *kbuf,
|
|
|
|
const void __user *ubuf)
|
|
|
|
{
|
|
|
|
unsigned long reg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (target->thread.regs == NULL)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
CHECK_FULL_REGS(target->thread.regs);
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
target->thread.regs,
|
|
|
|
0, PT_MSR * sizeof(reg));
|
|
|
|
|
|
|
|
if (!ret && count > 0) {
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®,
|
|
|
|
PT_MSR * sizeof(reg),
|
|
|
|
(PT_MSR + 1) * sizeof(reg));
|
|
|
|
if (!ret)
|
|
|
|
ret = set_user_msr(target, reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) !=
|
|
|
|
offsetof(struct pt_regs, msr) + sizeof(long));
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.regs->orig_gpr3,
|
|
|
|
PT_ORIG_R3 * sizeof(reg),
|
|
|
|
(PT_MAX_PUT_REG + 1) * sizeof(reg));
|
|
|
|
|
|
|
|
if (PT_MAX_PUT_REG + 1 < PT_TRAP && !ret)
|
|
|
|
ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
|
|
|
|
(PT_MAX_PUT_REG + 1) * sizeof(reg),
|
|
|
|
PT_TRAP * sizeof(reg));
|
|
|
|
|
|
|
|
if (!ret && count > 0) {
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®,
|
|
|
|
PT_TRAP * sizeof(reg),
|
|
|
|
(PT_TRAP + 1) * sizeof(reg));
|
|
|
|
if (!ret)
|
|
|
|
ret = set_user_trap(target, reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
|
|
|
|
(PT_TRAP + 1) * sizeof(reg), -1);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
static int ppr_get(struct task_struct *target, const struct user_regset *regset,
|
2020-02-23 00:01:44 +08:00
|
|
|
struct membuf to)
|
2020-02-28 08:14:45 +08:00
|
|
|
{
|
2020-02-23 00:01:44 +08:00
|
|
|
return membuf_write(&to, &target->thread.regs->ppr, sizeof(u64));
|
2020-02-28 08:14:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ppr_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count, const void *kbuf,
|
|
|
|
const void __user *ubuf)
|
|
|
|
{
|
|
|
|
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.regs->ppr, 0, sizeof(u64));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dscr_get(struct task_struct *target, const struct user_regset *regset,
|
2020-02-23 00:01:44 +08:00
|
|
|
struct membuf to)
|
2020-02-28 08:14:45 +08:00
|
|
|
{
|
2020-02-23 00:01:44 +08:00
|
|
|
return membuf_write(&to, &target->thread.dscr, sizeof(u64));
|
2020-02-28 08:14:45 +08:00
|
|
|
}
|
|
|
|
static int dscr_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count, const void *kbuf,
|
|
|
|
const void __user *ubuf)
|
|
|
|
{
|
|
|
|
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.dscr, 0, sizeof(u64));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S_64
|
|
|
|
static int tar_get(struct task_struct *target, const struct user_regset *regset,
|
2020-02-23 00:01:44 +08:00
|
|
|
struct membuf to)
|
2020-02-28 08:14:45 +08:00
|
|
|
{
|
2020-02-23 00:01:44 +08:00
|
|
|
return membuf_write(&to, &target->thread.tar, sizeof(u64));
|
2020-02-28 08:14:45 +08:00
|
|
|
}
|
|
|
|
static int tar_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count, const void *kbuf,
|
|
|
|
const void __user *ubuf)
|
|
|
|
{
|
|
|
|
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.tar, 0, sizeof(u64));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ebb_active(struct task_struct *target, const struct user_regset *regset)
|
|
|
|
{
|
|
|
|
if (!cpu_has_feature(CPU_FTR_ARCH_207S))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (target->thread.used_ebb)
|
|
|
|
return regset->n;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ebb_get(struct task_struct *target, const struct user_regset *regset,
|
2020-02-23 00:01:44 +08:00
|
|
|
struct membuf to)
|
2020-02-28 08:14:45 +08:00
|
|
|
{
|
|
|
|
/* Build tests */
|
|
|
|
BUILD_BUG_ON(TSO(ebbrr) + sizeof(unsigned long) != TSO(ebbhr));
|
|
|
|
BUILD_BUG_ON(TSO(ebbhr) + sizeof(unsigned long) != TSO(bescr));
|
|
|
|
|
|
|
|
if (!cpu_has_feature(CPU_FTR_ARCH_207S))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (!target->thread.used_ebb)
|
|
|
|
return -ENODATA;
|
|
|
|
|
2020-02-23 00:01:44 +08:00
|
|
|
return membuf_write(&to, &target->thread.ebbrr, 3 * sizeof(unsigned long));
|
2020-02-28 08:14:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ebb_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;
|
|
|
|
|
|
|
|
/* Build tests */
|
|
|
|
BUILD_BUG_ON(TSO(ebbrr) + sizeof(unsigned long) != TSO(ebbhr));
|
|
|
|
BUILD_BUG_ON(TSO(ebbhr) + sizeof(unsigned long) != TSO(bescr));
|
|
|
|
|
|
|
|
if (!cpu_has_feature(CPU_FTR_ARCH_207S))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (target->thread.used_ebb)
|
|
|
|
return -ENODATA;
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.ebbrr,
|
|
|
|
0, sizeof(unsigned long));
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.ebbhr, sizeof(unsigned long),
|
|
|
|
2 * sizeof(unsigned long));
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.bescr, 2 * sizeof(unsigned long),
|
|
|
|
3 * sizeof(unsigned long));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int pmu_active(struct task_struct *target, const struct user_regset *regset)
|
|
|
|
{
|
|
|
|
if (!cpu_has_feature(CPU_FTR_ARCH_207S))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return regset->n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pmu_get(struct task_struct *target, const struct user_regset *regset,
|
2020-02-23 00:01:44 +08:00
|
|
|
struct membuf to)
|
2020-02-28 08:14:45 +08:00
|
|
|
{
|
|
|
|
/* Build tests */
|
|
|
|
BUILD_BUG_ON(TSO(siar) + sizeof(unsigned long) != TSO(sdar));
|
|
|
|
BUILD_BUG_ON(TSO(sdar) + sizeof(unsigned long) != TSO(sier));
|
|
|
|
BUILD_BUG_ON(TSO(sier) + sizeof(unsigned long) != TSO(mmcr2));
|
|
|
|
BUILD_BUG_ON(TSO(mmcr2) + sizeof(unsigned long) != TSO(mmcr0));
|
|
|
|
|
|
|
|
if (!cpu_has_feature(CPU_FTR_ARCH_207S))
|
|
|
|
return -ENODEV;
|
|
|
|
|
2020-02-23 00:01:44 +08:00
|
|
|
return membuf_write(&to, &target->thread.siar, 5 * sizeof(unsigned long));
|
2020-02-28 08:14:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int pmu_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;
|
|
|
|
|
|
|
|
/* Build tests */
|
|
|
|
BUILD_BUG_ON(TSO(siar) + sizeof(unsigned long) != TSO(sdar));
|
|
|
|
BUILD_BUG_ON(TSO(sdar) + sizeof(unsigned long) != TSO(sier));
|
|
|
|
BUILD_BUG_ON(TSO(sier) + sizeof(unsigned long) != TSO(mmcr2));
|
|
|
|
BUILD_BUG_ON(TSO(mmcr2) + sizeof(unsigned long) != TSO(mmcr0));
|
|
|
|
|
|
|
|
if (!cpu_has_feature(CPU_FTR_ARCH_207S))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.siar,
|
|
|
|
0, sizeof(unsigned long));
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.sdar, sizeof(unsigned long),
|
|
|
|
2 * sizeof(unsigned long));
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.sier, 2 * sizeof(unsigned long),
|
|
|
|
3 * sizeof(unsigned long));
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.mmcr2, 3 * sizeof(unsigned long),
|
|
|
|
4 * sizeof(unsigned long));
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&target->thread.mmcr0, 4 * sizeof(unsigned long),
|
|
|
|
5 * sizeof(unsigned long));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_PPC_MEM_KEYS
|
|
|
|
static int pkey_active(struct task_struct *target, const struct user_regset *regset)
|
|
|
|
{
|
|
|
|
if (!arch_pkeys_enabled())
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return regset->n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pkey_get(struct task_struct *target, const struct user_regset *regset,
|
2020-02-23 00:01:44 +08:00
|
|
|
struct membuf to)
|
2020-02-28 08:14:45 +08:00
|
|
|
{
|
|
|
|
BUILD_BUG_ON(TSO(amr) + sizeof(unsigned long) != TSO(iamr));
|
|
|
|
|
|
|
|
if (!arch_pkeys_enabled())
|
|
|
|
return -ENODEV;
|
|
|
|
|
powerpc updates for 5.9
- Add support for (optionally) using queued spinlocks & rwlocks.
- Support for a new faster system call ABI using the scv instruction on Power9
or later.
- Drop support for the PROT_SAO mmap/mprotect flag as it will be unsupported on
Power10 and future processors, leaving us with no way to implement the
functionality it requests. This risks breaking userspace, though we believe
it is unused in practice.
- A bug fix for, and then the removal of, our custom stack expansion checking.
We now allow stack expansion up to the rlimit, like other architectures.
- Remove the remnants of our (previously disabled) topology update code, which
tried to react to NUMA layout changes on virtualised systems, but was prone
to crashes and other problems.
- Add PMU support for Power10 CPUs.
- A change to our signal trampoline so that we don't unbalance the link stack
(branch return predictor) in the signal delivery path.
- Lots of other cleanups, refactorings, smaller features and so on as usual.
Thanks to:
Abhishek Goel, Alastair D'Silva, Alexander A. Klimov, Alexey Kardashevskiy,
Alistair Popple, Andrew Donnellan, Aneesh Kumar K.V, Anju T Sudhakar, Anton
Blanchard, Arnd Bergmann, Athira Rajeev, Balamuruhan S, Bharata B Rao, Bill
Wendling, Bin Meng, Cédric Le Goater, Chris Packham, Christophe Leroy,
Christoph Hellwig, Daniel Axtens, Dan Williams, David Lamparter, Desnes A.
Nunes do Rosario, Erhard F., Finn Thain, Frederic Barrat, Ganesh Goudar,
Gautham R. Shenoy, Geoff Levand, Greg Kurz, Gustavo A. R. Silva, Hari Bathini,
Harish, Imre Kaloz, Joel Stanley, Joe Perches, John Crispin, Jordan Niethe,
Kajol Jain, Kamalesh Babulal, Kees Cook, Laurent Dufour, Leonardo Bras, Li
RongQing, Madhavan Srinivasan, Mahesh Salgaonkar, Mark Cave-Ayland, Michal
Suchanek, Milton Miller, Mimi Zohar, Murilo Opsfelder Araujo, Nathan
Chancellor, Nathan Lynch, Naveen N. Rao, Nayna Jain, Nicholas Piggin, Oliver
O'Halloran, Palmer Dabbelt, Pedro Miraglia Franco de Carvalho, Philippe
Bergheaud, Pingfan Liu, Pratik Rajesh Sampat, Qian Cai, Qinglang Miao, Randy
Dunlap, Ravi Bangoria, Sachin Sant, Sam Bobroff, Sandipan Das, Santosh
Sivaraj, Satheesh Rajendran, Shirisha Ganta, Sourabh Jain, Srikar Dronamraju,
Stan Johnson, Stephen Rothwell, Thadeu Lima de Souza Cascardo, Thiago Jung
Bauermann, Tom Lane, Vaibhav Jain, Vladis Dronov, Wei Yongjun, Wen Xiong,
YueHaibing.
-----BEGIN PGP SIGNATURE-----
iQJHBAABCAAxFiEEJFGtCPCthwEv2Y/bUevqPMjhpYAFAl8tOxATHG1wZUBlbGxl
cm1hbi5pZC5hdQAKCRBR6+o8yOGlgDQfEAClXHWf6hnxB84bEu39D51NkVotL1IG
BRWFvyix+xHuUkHIouBPAAMl6ngY5X6wkYd+Z+CY9zHNtdSDoVlJE30YXdMQA/dE
L/rYxR1884yGR/uU/3wusboO68ReXwcKQPmKOymUfh0zH7ujyJsSWLpXFK1YDC5d
2TVVTi0Q+P5ucMHDh0L+AHirIxZvtZSp43+J7xLtywsj+XAxJWCTGo5WCJbdgbCA
Qbv3aOkVyUa3EgsbdM/STPpv82ebqT+PHxeSIO4Jw6ZODtKRH0R5YsWCApuY9eZ+
ebY9RLmgv9ZAhJqB2fv9A5NDcMoGpZNmjM7HrWpXwULKQpkBGHCzJ9FcSdHVMOx8
nbVMFjt4uzLwV1w8lFYslQ2tNH/uH2o9BlryV1RLpiiKokDAJO/NOsWN9y0u/I4J
EmAM5DSX2LgVvvas96IlGK8KX4xkOkf8FLX/H5UDvvAfloH8J4CZXk/CWCab/nqY
KEHPnMmYvQZ1w9SzyZg9sO/1p6Bl1Gmm75Jv2F1lBiRW/42VcGBI/qLsJ4lC59Fc
KbwufYNYYG38wbxDLW1HAPJhRonxIcaZj3EEqk7aTiLZ55nNbu8e2k32CpNXTGqt
npOhzJHimcq7L6+878ZW+xpbZwogIEUdRSsmwb6aT8za3ShnYwSA2Q3LYxh9xyGH
j3GifvPq6Efp3Q==
=QMY1
-----END PGP SIGNATURE-----
Merge tag 'powerpc-5.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull powerpc updates from Michael Ellerman:
- Add support for (optionally) using queued spinlocks & rwlocks.
- Support for a new faster system call ABI using the scv instruction on
Power9 or later.
- Drop support for the PROT_SAO mmap/mprotect flag as it will be
unsupported on Power10 and future processors, leaving us with no way
to implement the functionality it requests. This risks breaking
userspace, though we believe it is unused in practice.
- A bug fix for, and then the removal of, our custom stack expansion
checking. We now allow stack expansion up to the rlimit, like other
architectures.
- Remove the remnants of our (previously disabled) topology update
code, which tried to react to NUMA layout changes on virtualised
systems, but was prone to crashes and other problems.
- Add PMU support for Power10 CPUs.
- A change to our signal trampoline so that we don't unbalance the link
stack (branch return predictor) in the signal delivery path.
- Lots of other cleanups, refactorings, smaller features and so on as
usual.
Thanks to: Abhishek Goel, Alastair D'Silva, Alexander A. Klimov, Alexey
Kardashevskiy, Alistair Popple, Andrew Donnellan, Aneesh Kumar K.V, Anju
T Sudhakar, Anton Blanchard, Arnd Bergmann, Athira Rajeev, Balamuruhan
S, Bharata B Rao, Bill Wendling, Bin Meng, Cédric Le Goater, Chris
Packham, Christophe Leroy, Christoph Hellwig, Daniel Axtens, Dan
Williams, David Lamparter, Desnes A. Nunes do Rosario, Erhard F., Finn
Thain, Frederic Barrat, Ganesh Goudar, Gautham R. Shenoy, Geoff Levand,
Greg Kurz, Gustavo A. R. Silva, Hari Bathini, Harish, Imre Kaloz, Joel
Stanley, Joe Perches, John Crispin, Jordan Niethe, Kajol Jain, Kamalesh
Babulal, Kees Cook, Laurent Dufour, Leonardo Bras, Li RongQing, Madhavan
Srinivasan, Mahesh Salgaonkar, Mark Cave-Ayland, Michal Suchanek, Milton
Miller, Mimi Zohar, Murilo Opsfelder Araujo, Nathan Chancellor, Nathan
Lynch, Naveen N. Rao, Nayna Jain, Nicholas Piggin, Oliver O'Halloran,
Palmer Dabbelt, Pedro Miraglia Franco de Carvalho, Philippe Bergheaud,
Pingfan Liu, Pratik Rajesh Sampat, Qian Cai, Qinglang Miao, Randy
Dunlap, Ravi Bangoria, Sachin Sant, Sam Bobroff, Sandipan Das, Santosh
Sivaraj, Satheesh Rajendran, Shirisha Ganta, Sourabh Jain, Srikar
Dronamraju, Stan Johnson, Stephen Rothwell, Thadeu Lima de Souza
Cascardo, Thiago Jung Bauermann, Tom Lane, Vaibhav Jain, Vladis Dronov,
Wei Yongjun, Wen Xiong, YueHaibing.
* tag 'powerpc-5.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (337 commits)
selftests/powerpc: Fix pkey syscall redefinitions
powerpc: Fix circular dependency between percpu.h and mmu.h
powerpc/powernv/sriov: Fix use of uninitialised variable
selftests/powerpc: Skip vmx/vsx/tar/etc tests on older CPUs
powerpc/40x: Fix assembler warning about r0
powerpc/papr_scm: Add support for fetching nvdimm 'fuel-gauge' metric
powerpc/papr_scm: Fetch nvdimm performance stats from PHYP
cpuidle: pseries: Fixup exit latency for CEDE(0)
cpuidle: pseries: Add function to parse extended CEDE records
cpuidle: pseries: Set the latency-hint before entering CEDE
selftests/powerpc: Fix online CPU selection
powerpc/perf: Consolidate perf_callchain_user_[64|32]()
powerpc/pseries/hotplug-cpu: Remove double free in error path
powerpc/pseries/mobility: Add pr_debug() for device tree changes
powerpc/pseries/mobility: Set pr_fmt()
powerpc/cacheinfo: Warn if cache object chain becomes unordered
powerpc/cacheinfo: Improve diagnostics about malformed cache lists
powerpc/cacheinfo: Use name@unit instead of full DT path in debug messages
powerpc/cacheinfo: Set pr_fmt()
powerpc: fix function annotations to avoid section mismatch warnings with gcc-10
...
2020-08-08 01:33:50 +08:00
|
|
|
membuf_write(&to, &target->thread.amr, 2 * sizeof(unsigned long));
|
|
|
|
return membuf_store(&to, default_uamor);
|
2020-02-28 08:14:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int pkey_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count, const void *kbuf,
|
|
|
|
const void __user *ubuf)
|
|
|
|
{
|
|
|
|
u64 new_amr;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!arch_pkeys_enabled())
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
/* Only the AMR can be set from userspace */
|
|
|
|
if (pos != 0 || count != sizeof(new_amr))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&new_amr, 0, sizeof(new_amr));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-07-09 11:29:42 +08:00
|
|
|
/*
|
|
|
|
* UAMOR determines which bits of the AMR can be set from userspace.
|
|
|
|
* UAMOR value 0b11 indicates that the AMR value can be modified
|
|
|
|
* from userspace. If the kernel is using a specific key, we avoid
|
|
|
|
* userspace modifying the AMR value for that key by masking them
|
|
|
|
* via UAMOR 0b00.
|
|
|
|
*
|
|
|
|
* Pick the AMR values for the keys that kernel is using. This
|
|
|
|
* will be indicated by the ~default_uamor bits.
|
|
|
|
*/
|
|
|
|
target->thread.amr = (new_amr & default_uamor) | (target->thread.amr & ~default_uamor);
|
2020-02-28 08:14:45 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PPC_MEM_KEYS */
|
|
|
|
|
|
|
|
static const struct user_regset native_regsets[] = {
|
|
|
|
[REGSET_GPR] = {
|
|
|
|
.core_note_type = NT_PRSTATUS, .n = ELF_NGREG,
|
|
|
|
.size = sizeof(long), .align = sizeof(long),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = gpr_get, .set = gpr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_FPR] = {
|
|
|
|
.core_note_type = NT_PRFPREG, .n = ELF_NFPREG,
|
|
|
|
.size = sizeof(double), .align = sizeof(double),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = fpr_get, .set = fpr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#ifdef CONFIG_ALTIVEC
|
|
|
|
[REGSET_VMX] = {
|
|
|
|
.core_note_type = NT_PPC_VMX, .n = 34,
|
|
|
|
.size = sizeof(vector128), .align = sizeof(vector128),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = vr_active, .regset_get = vr_get, .set = vr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_VSX
|
|
|
|
[REGSET_VSX] = {
|
|
|
|
.core_note_type = NT_PPC_VSX, .n = 32,
|
|
|
|
.size = sizeof(double), .align = sizeof(double),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = vsr_active, .regset_get = vsr_get, .set = vsr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SPE
|
|
|
|
[REGSET_SPE] = {
|
|
|
|
.core_note_type = NT_PPC_SPE, .n = 35,
|
|
|
|
.size = sizeof(u32), .align = sizeof(u32),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = evr_active, .regset_get = evr_get, .set = evr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
|
|
|
|
[REGSET_TM_CGPR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG,
|
|
|
|
.size = sizeof(long), .align = sizeof(long),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_cgpr_active, .regset_get = tm_cgpr_get, .set = tm_cgpr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CFPR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
|
|
|
|
.size = sizeof(double), .align = sizeof(double),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_cfpr_active, .regset_get = tm_cfpr_get, .set = tm_cfpr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CVMX] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX,
|
|
|
|
.size = sizeof(vector128), .align = sizeof(vector128),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_cvmx_active, .regset_get = tm_cvmx_get, .set = tm_cvmx_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CVSX] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CVSX, .n = ELF_NVSX,
|
|
|
|
.size = sizeof(double), .align = sizeof(double),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_cvsx_active, .regset_get = tm_cvsx_get, .set = tm_cvsx_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_SPR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_spr_active, .regset_get = tm_spr_get, .set = tm_spr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CTAR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CTAR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_tar_active, .regset_get = tm_tar_get, .set = tm_tar_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CPPR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CPPR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_ppr_active, .regset_get = tm_ppr_get, .set = tm_ppr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CDSCR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CDSCR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_dscr_active, .regset_get = tm_dscr_get, .set = tm_dscr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
[REGSET_PPR] = {
|
|
|
|
.core_note_type = NT_PPC_PPR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = ppr_get, .set = ppr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_DSCR] = {
|
|
|
|
.core_note_type = NT_PPC_DSCR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = dscr_get, .set = dscr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S_64
|
|
|
|
[REGSET_TAR] = {
|
|
|
|
.core_note_type = NT_PPC_TAR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = tar_get, .set = tar_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_EBB] = {
|
|
|
|
.core_note_type = NT_PPC_EBB, .n = ELF_NEBB,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = ebb_active, .regset_get = ebb_get, .set = ebb_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_PMR] = {
|
|
|
|
.core_note_type = NT_PPC_PMU, .n = ELF_NPMU,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = pmu_active, .regset_get = pmu_get, .set = pmu_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_PPC_MEM_KEYS
|
|
|
|
[REGSET_PKEY] = {
|
|
|
|
.core_note_type = NT_PPC_PKEY, .n = ELF_NPKEY,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = pkey_active, .regset_get = pkey_get, .set = pkey_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct user_regset_view user_ppc_native_view = {
|
|
|
|
.name = UTS_MACHINE, .e_machine = ELF_ARCH, .ei_osabi = ELF_OSABI,
|
|
|
|
.regsets = native_regsets, .n = ARRAY_SIZE(native_regsets)
|
|
|
|
};
|
|
|
|
|
|
|
|
#include <linux/compat.h>
|
|
|
|
|
|
|
|
int gpr32_get_common(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-02-23 00:01:44 +08:00
|
|
|
struct membuf to, unsigned long *regs)
|
2020-02-28 08:14:45 +08:00
|
|
|
{
|
2020-02-23 00:01:44 +08:00
|
|
|
int i;
|
2020-02-28 08:14:45 +08:00
|
|
|
|
2020-02-23 00:01:44 +08:00
|
|
|
for (i = 0; i < PT_MSR; i++)
|
|
|
|
membuf_store(&to, (u32)regs[i]);
|
|
|
|
membuf_store(&to, (u32)get_user_msr(target));
|
|
|
|
for (i++ ; i < PT_REGS_COUNT; i++)
|
|
|
|
membuf_store(&to, (u32)regs[i]);
|
|
|
|
return membuf_zero(&to, (ELF_NGREG - PT_REGS_COUNT) * sizeof(u32));
|
2020-02-28 08:14:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int gpr32_set_common(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf,
|
|
|
|
unsigned long *regs)
|
|
|
|
{
|
|
|
|
const compat_ulong_t *k = kbuf;
|
|
|
|
const compat_ulong_t __user *u = ubuf;
|
|
|
|
compat_ulong_t reg;
|
|
|
|
|
|
|
|
pos /= sizeof(reg);
|
|
|
|
count /= sizeof(reg);
|
|
|
|
|
|
|
|
if (kbuf)
|
|
|
|
for (; count > 0 && pos < PT_MSR; --count)
|
|
|
|
regs[pos++] = *k++;
|
|
|
|
else
|
|
|
|
for (; count > 0 && pos < PT_MSR; --count) {
|
|
|
|
if (__get_user(reg, u++))
|
|
|
|
return -EFAULT;
|
|
|
|
regs[pos++] = reg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (count > 0 && pos == PT_MSR) {
|
|
|
|
if (kbuf)
|
|
|
|
reg = *k++;
|
|
|
|
else if (__get_user(reg, u++))
|
|
|
|
return -EFAULT;
|
|
|
|
set_user_msr(target, reg);
|
|
|
|
++pos;
|
|
|
|
--count;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (kbuf) {
|
|
|
|
for (; count > 0 && pos <= PT_MAX_PUT_REG; --count)
|
|
|
|
regs[pos++] = *k++;
|
|
|
|
for (; count > 0 && pos < PT_TRAP; --count, ++pos)
|
|
|
|
++k;
|
|
|
|
} else {
|
|
|
|
for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) {
|
|
|
|
if (__get_user(reg, u++))
|
|
|
|
return -EFAULT;
|
|
|
|
regs[pos++] = reg;
|
|
|
|
}
|
|
|
|
for (; count > 0 && pos < PT_TRAP; --count, ++pos)
|
|
|
|
if (__get_user(reg, u++))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count > 0 && pos == PT_TRAP) {
|
|
|
|
if (kbuf)
|
|
|
|
reg = *k++;
|
|
|
|
else if (__get_user(reg, u++))
|
|
|
|
return -EFAULT;
|
|
|
|
set_user_trap(target, reg);
|
|
|
|
++pos;
|
|
|
|
--count;
|
|
|
|
}
|
|
|
|
|
|
|
|
kbuf = k;
|
|
|
|
ubuf = u;
|
|
|
|
pos *= sizeof(reg);
|
|
|
|
count *= sizeof(reg);
|
|
|
|
return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
|
|
|
|
(PT_TRAP + 1) * sizeof(reg), -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gpr32_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-02-23 00:01:44 +08:00
|
|
|
struct membuf to)
|
2020-02-28 08:14:45 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (target->thread.regs == NULL)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (!FULL_REGS(target->thread.regs)) {
|
|
|
|
/*
|
|
|
|
* We have a partial register set.
|
|
|
|
* Fill 14-31 with bogus values.
|
|
|
|
*/
|
|
|
|
for (i = 14; i < 32; i++)
|
|
|
|
target->thread.regs->gpr[i] = NV_REG_POISON;
|
|
|
|
}
|
2020-02-23 00:01:44 +08:00
|
|
|
return gpr32_get_common(target, regset, to,
|
2020-02-28 08:14:45 +08:00
|
|
|
&target->thread.regs->gpr[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gpr32_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
if (target->thread.regs == NULL)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
CHECK_FULL_REGS(target->thread.regs);
|
|
|
|
return gpr32_set_common(target, regset, pos, count, kbuf, ubuf,
|
|
|
|
&target->thread.regs->gpr[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These are the regset flavors matching the CONFIG_PPC32 native set.
|
|
|
|
*/
|
|
|
|
static const struct user_regset compat_regsets[] = {
|
|
|
|
[REGSET_GPR] = {
|
|
|
|
.core_note_type = NT_PRSTATUS, .n = ELF_NGREG,
|
|
|
|
.size = sizeof(compat_long_t), .align = sizeof(compat_long_t),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = gpr32_get, .set = gpr32_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_FPR] = {
|
|
|
|
.core_note_type = NT_PRFPREG, .n = ELF_NFPREG,
|
|
|
|
.size = sizeof(double), .align = sizeof(double),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = fpr_get, .set = fpr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#ifdef CONFIG_ALTIVEC
|
|
|
|
[REGSET_VMX] = {
|
|
|
|
.core_note_type = NT_PPC_VMX, .n = 34,
|
|
|
|
.size = sizeof(vector128), .align = sizeof(vector128),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = vr_active, .regset_get = vr_get, .set = vr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SPE
|
|
|
|
[REGSET_SPE] = {
|
|
|
|
.core_note_type = NT_PPC_SPE, .n = 35,
|
|
|
|
.size = sizeof(u32), .align = sizeof(u32),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = evr_active, .regset_get = evr_get, .set = evr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
|
|
|
|
[REGSET_TM_CGPR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CGPR, .n = ELF_NGREG,
|
|
|
|
.size = sizeof(long), .align = sizeof(long),
|
|
|
|
.active = tm_cgpr_active,
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = tm_cgpr32_get, .set = tm_cgpr32_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CFPR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CFPR, .n = ELF_NFPREG,
|
|
|
|
.size = sizeof(double), .align = sizeof(double),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_cfpr_active, .regset_get = tm_cfpr_get, .set = tm_cfpr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CVMX] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CVMX, .n = ELF_NVMX,
|
|
|
|
.size = sizeof(vector128), .align = sizeof(vector128),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_cvmx_active, .regset_get = tm_cvmx_get, .set = tm_cvmx_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CVSX] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CVSX, .n = ELF_NVSX,
|
|
|
|
.size = sizeof(double), .align = sizeof(double),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_cvsx_active, .regset_get = tm_cvsx_get, .set = tm_cvsx_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_SPR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_SPR, .n = ELF_NTMSPRREG,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_spr_active, .regset_get = tm_spr_get, .set = tm_spr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CTAR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CTAR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_tar_active, .regset_get = tm_tar_get, .set = tm_tar_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CPPR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CPPR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_ppr_active, .regset_get = tm_ppr_get, .set = tm_ppr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_TM_CDSCR] = {
|
|
|
|
.core_note_type = NT_PPC_TM_CDSCR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = tm_dscr_active, .regset_get = tm_dscr_get, .set = tm_dscr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
[REGSET_PPR] = {
|
|
|
|
.core_note_type = NT_PPC_PPR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = ppr_get, .set = ppr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_DSCR] = {
|
|
|
|
.core_note_type = NT_PPC_DSCR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = dscr_get, .set = dscr_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_PPC_BOOK3S_64
|
|
|
|
[REGSET_TAR] = {
|
|
|
|
.core_note_type = NT_PPC_TAR, .n = 1,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.regset_get = tar_get, .set = tar_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
[REGSET_EBB] = {
|
|
|
|
.core_note_type = NT_PPC_EBB, .n = ELF_NEBB,
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64),
|
2020-02-23 00:01:44 +08:00
|
|
|
.active = ebb_active, .regset_get = ebb_get, .set = ebb_set
|
2020-02-28 08:14:45 +08:00
|
|
|
},
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct user_regset_view user_ppc_compat_view = {
|
|
|
|
.name = "ppc", .e_machine = EM_PPC, .ei_osabi = ELF_OSABI,
|
|
|
|
.regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets)
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
|
|
|
|
{
|
|
|
|
if (IS_ENABLED(CONFIG_PPC64) && test_tsk_thread_flag(task, TIF_32BIT))
|
|
|
|
return &user_ppc_compat_view;
|
|
|
|
return &user_ppc_native_view;
|
|
|
|
}
|