[PATCH v2 06/13] powerpc: Support prefixed instructions in alignment handler

Christophe Leroy christophe.leroy at c-s.fr
Tue Feb 11 17:14:27 AEDT 2020



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 ?

> +/*
> + * 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 ?
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))

> +			__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