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: Jordan Niethe <jniethe5@gmail.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Reviewed-by: Alistair Popple <alistair@popple.id.au> Link: https://lore.kernel.org/r/20200506034050.24806-29-jniethe5@gmail.com
This commit is contained in:
committed by
Michael Ellerman
parent
b4657f7650
commit
9409d2f9da
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user