Commit 9409d2f9 authored by Jordan Niethe's avatar Jordan Niethe Committed by Michael Ellerman
Browse files

powerpc: Support prefixed instructions in alignment handler



If a prefixed instruction results in an alignment exception, the
SRR1_PREFIXED bit is set. The handler attempts to emulate the
responsible instruction and then increment the NIP past it. Use
SRR1_PREFIXED to determine by how much the NIP should be incremented.

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: default avatarJordan Niethe <jniethe5@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Reviewed-by: default avatarAlistair Popple <alistair@popple.id.au>
Link: https://lore.kernel.org/r/20200506034050.24806-29-jniethe5@gmail.com
parent b4657f76
Loading
Loading
Loading
Loading
+18 −1
Original line number Diff line number Diff line
@@ -588,6 +588,8 @@ 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

/* single-step stuff */
#define single_stepping(regs)	(current->thread.debug.dbcr0 & DBCR0_IC)
@@ -602,12 +604,16 @@ 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 single_stepping(regs)	((regs)->msr & MSR_SE)
#define clear_single_step(regs)	((regs)->msr &= ~MSR_SE)
#define clear_br_trace(regs)	((regs)->msr &= ~MSR_BE)
#endif

#define inst_length(reason)	(((reason) & REASON_PREFIXED) ? 8 : 4)

#if defined(CONFIG_E500)
int machine_check_e500mc(struct pt_regs *regs)
{
@@ -1610,11 +1616,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;

@@ -1623,7 +1638,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;
	}
@@ -1636,6 +1652,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