[PATCH v2 06/13] powerpc: Support prefixed instructions in alignment handler
Jordan Niethe
jniethe5 at gmail.com
Wed Feb 12 13:55:20 AEDT 2020
On Tue, Feb 11, 2020 at 5:14 PM Christophe Leroy
<christophe.leroy at c-s.fr> wrote:
>
>
>
> Le 11/02/2020 à 06:33, Jordan Niethe a écrit :
> > Alignment interrupts can be caused by prefixed instructions accessing
> > memory. In the alignment handler the instruction that caused the
> > exception is loaded and attempted emulate. If the instruction is a
> > prefixed instruction load the prefix and suffix to emulate. After
> > emulating increment the NIP by 8.
> >
> > Prefixed instructions are not permitted to cross 64-byte boundaries. If
> > they do the alignment interrupt is invoked with SRR1 BOUNDARY bit set.
> > If this occurs send a SIGBUS to the offending process if in user mode.
> > If in kernel mode call bad_page_fault().
> >
> > Signed-off-by: Jordan Niethe <jniethe5 at gmail.com>
> > ---
> > v2: - Move __get_user_instr() and __get_user_instr_inatomic() to this
> > commit (previously in "powerpc sstep: Prepare to support prefixed
> > instructions").
> > - Rename sufx to suffix
> > - Use a macro for calculating instruction length
> > ---
> > arch/powerpc/include/asm/uaccess.h | 30 ++++++++++++++++++++++++++++++
> > arch/powerpc/kernel/align.c | 8 +++++---
> > arch/powerpc/kernel/traps.c | 21 ++++++++++++++++++++-
> > 3 files changed, 55 insertions(+), 4 deletions(-)
> >
> > diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
> > index 2f500debae21..30f63a81c8d8 100644
> > --- a/arch/powerpc/include/asm/uaccess.h
> > +++ b/arch/powerpc/include/asm/uaccess.h
> > @@ -474,4 +474,34 @@ static __must_check inline bool user_access_begin(const void __user *ptr, size_t
> > #define unsafe_copy_to_user(d, s, l, e) \
> > unsafe_op_wrap(raw_copy_to_user_allowed(d, s, l), e)
> >
>
> Could it go close to other __get_user() and friends instead of being at
> the end of the file ?
Will do.
>
> > +/*
> > + * When reading an instruction iff it is a prefix, the suffix needs to be also
> > + * loaded.
> > + */
> > +#define __get_user_instr(x, y, ptr) \
> > +({ \
> > + long __gui_ret = 0; \
> > + y = 0; \
> > + __gui_ret = __get_user(x, ptr); \
> > + if (!__gui_ret) { \
> > + if (IS_PREFIX(x)) \
>
> Does this apply to PPC32 ?
No, for now (and the foreseeable future) it will just affect 64s.
> If not, can we make sure IS_PREFIX is constant 0 on PPC32 so that the
> second read gets dropped at compile time ?
>
> Can we instead do :
>
> if (!__gui_ret && IS_PREFIX(x))
Will do.
>
> > + __gui_ret = __get_user(y, ptr + 1); \
> > + } \
> > + \
> > + __gui_ret; \
> > +})
> > +
> > +#define __get_user_instr_inatomic(x, y, ptr) \
> > +({ \
> > + long __gui_ret = 0; \
> > + y = 0; \
> > + __gui_ret = __get_user_inatomic(x, ptr); \
> > + if (!__gui_ret) { \
> > + if (IS_PREFIX(x)) \
>
> Same commments as above
>
> > + __gui_ret = __get_user_inatomic(y, ptr + 1); \
> > + } \
> > + \
> > + __gui_ret; \
> > +})
> > +
> > #endif /* _ARCH_POWERPC_UACCESS_H */
> > diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
> > index ba3bf5c3ab62..e42cfaa616d3 100644
> > --- a/arch/powerpc/kernel/align.c
> > +++ b/arch/powerpc/kernel/align.c
> > @@ -293,7 +293,7 @@ static int emulate_spe(struct pt_regs *regs, unsigned int reg,
> >
> > int fix_alignment(struct pt_regs *regs)
> > {
> > - unsigned int instr;
> > + unsigned int instr, suffix;
> > struct instruction_op op;
> > int r, type;
> >
> > @@ -303,13 +303,15 @@ int fix_alignment(struct pt_regs *regs)
> > */
> > CHECK_FULL_REGS(regs);
> >
> > - if (unlikely(__get_user(instr, (unsigned int __user *)regs->nip)))
> > + if (unlikely(__get_user_instr(instr, suffix,
> > + (unsigned int __user *)regs->nip)))
> > return -EFAULT;
> > if ((regs->msr & MSR_LE) != (MSR_KERNEL & MSR_LE)) {
> > /* We don't handle PPC little-endian any more... */
> > if (cpu_has_feature(CPU_FTR_PPC_LE))
> > return -EIO;
> > instr = swab32(instr);
> > + suffix = swab32(suffix);
> > }
> >
> > #ifdef CONFIG_SPE
> > @@ -334,7 +336,7 @@ int fix_alignment(struct pt_regs *regs)
> > if ((instr & 0xfc0006fe) == (PPC_INST_COPY & 0xfc0006fe))
> > return -EIO;
> >
> > - r = analyse_instr(&op, regs, instr, PPC_NO_SUFFIX);
> > + r = analyse_instr(&op, regs, instr, suffix);
> > if (r < 0)
> > return -EINVAL;
> >
> > diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
> > index 82a3438300fd..d80b82fc1ae3 100644
> > --- a/arch/powerpc/kernel/traps.c
> > +++ b/arch/powerpc/kernel/traps.c
> > @@ -583,6 +583,10 @@ static inline int check_io_access(struct pt_regs *regs)
> > #define REASON_ILLEGAL (ESR_PIL | ESR_PUO)
> > #define REASON_PRIVILEGED ESR_PPR
> > #define REASON_TRAP ESR_PTR
> > +#define REASON_PREFIXED 0
> > +#define REASON_BOUNDARY 0
> > +
> > +#define inst_length(reason) 4
> >
> > /* single-step stuff */
> > #define single_stepping(regs) (current->thread.debug.dbcr0 & DBCR0_IC)
> > @@ -597,6 +601,10 @@ static inline int check_io_access(struct pt_regs *regs)
> > #define REASON_ILLEGAL SRR1_PROGILL
> > #define REASON_PRIVILEGED SRR1_PROGPRIV
> > #define REASON_TRAP SRR1_PROGTRAP
> > +#define REASON_PREFIXED SRR1_PREFIXED
> > +#define REASON_BOUNDARY SRR1_BOUNDARY
> > +
> > +#define inst_length(reason) (((reason) & REASON_PREFIXED) ? 8 : 4)
> >
> > #define single_stepping(regs) ((regs)->msr & MSR_SE)
> > #define clear_single_step(regs) ((regs)->msr &= ~MSR_SE)
> > @@ -1593,11 +1601,20 @@ void alignment_exception(struct pt_regs *regs)
> > {
> > enum ctx_state prev_state = exception_enter();
> > int sig, code, fixed = 0;
> > + unsigned long reason;
> >
> > /* We restore the interrupt state now */
> > if (!arch_irq_disabled_regs(regs))
> > local_irq_enable();
> >
> > + reason = get_reason(regs);
> > +
> > + if (reason & REASON_BOUNDARY) {
> > + sig = SIGBUS;
> > + code = BUS_ADRALN;
> > + goto bad;
> > + }
> > +
> > if (tm_abort_check(regs, TM_CAUSE_ALIGNMENT | TM_CAUSE_PERSISTENT))
> > goto bail;
> >
> > @@ -1606,7 +1623,8 @@ void alignment_exception(struct pt_regs *regs)
> > fixed = fix_alignment(regs);
> >
> > if (fixed == 1) {
> > - regs->nip += 4; /* skip over emulated instruction */
> > + /* skip over emulated instruction */
> > + regs->nip += inst_length(reason);
> > emulate_single_step(regs);
> > goto bail;
> > }
> > @@ -1619,6 +1637,7 @@ void alignment_exception(struct pt_regs *regs)
> > sig = SIGBUS;
> > code = BUS_ADRALN;
> > }
> > +bad:
> > if (user_mode(regs))
> > _exception(sig, regs, code, regs->dar);
> > else
> >
>
> Christophe
More information about the Linuxppc-dev
mailing list