diff options
author | Paul Mackerras <paulus@samba.org> | 2014-09-02 14:35:08 +1000 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2014-09-25 23:14:51 +1000 |
commit | cf87c3f6b64791ce5d4c7e591c915065d31a162d (patch) | |
tree | 3ca06fa36c998b21a839690cf1ac73ece11d9f03 /arch/powerpc/lib | |
parent | be96f63375a14ee8e690856ac77e579c75bd0bae (diff) | |
download | linux-cf87c3f6b64791ce5d4c7e591c915065d31a162d.tar.bz2 |
powerpc: Emulate icbi, mcrf and conditional-trap instructions
This extends the instruction emulation done by analyse_instr() and
emulate_step() to handle a few more instructions that are found in
the kernel.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch/powerpc/lib')
-rw-r--r-- | arch/powerpc/lib/sstep.c | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c index 3726a03179ab..209a506f8517 100644 --- a/arch/powerpc/lib/sstep.c +++ b/arch/powerpc/lib/sstep.c @@ -600,6 +600,23 @@ static void __kprobes do_cmp_unsigned(struct pt_regs *regs, unsigned long v1, regs->ccr = (regs->ccr & ~(0xf << shift)) | (crval << shift); } +static int __kprobes trap_compare(long v1, long v2) +{ + int ret = 0; + + if (v1 < v2) + ret |= 0x10; + else if (v1 > v2) + ret |= 0x08; + else + ret |= 0x04; + if ((unsigned long)v1 < (unsigned long)v2) + ret |= 0x02; + else if ((unsigned long)v1 > (unsigned long)v2) + ret |= 0x01; + return ret; +} + /* * Elements of 32-bit rotate and mask instructions. */ @@ -669,6 +686,13 @@ int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs, return 1; case 19: switch ((instr >> 1) & 0x3ff) { + case 0: /* mcrf */ + rd = (instr >> 21) & 0x1c; + ra = (instr >> 16) & 0x1c; + val = (regs->ccr >> ra) & 0xf; + regs->ccr = (regs->ccr & ~(0xfUL << rd)) | (val << rd); + goto instr_done; + case 16: /* bclr */ case 528: /* bcctr */ op->type = BRANCH; @@ -745,6 +769,17 @@ int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs, rb = (instr >> 11) & 0x1f; switch (opcode) { +#ifdef __powerpc64__ + case 2: /* tdi */ + if (rd & trap_compare(regs->gpr[ra], (short) instr)) + goto trap; + goto instr_done; +#endif + case 3: /* twi */ + if (rd & trap_compare((int)regs->gpr[ra], (short) instr)) + goto trap; + goto instr_done; + case 7: /* mulli */ regs->gpr[rd] = regs->gpr[ra] * (short) instr; goto instr_done; @@ -893,6 +928,18 @@ int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs, case 31: switch ((instr >> 1) & 0x3ff) { + case 4: /* tw */ + if (rd == 0x1f || + (rd & trap_compare((int)regs->gpr[ra], + (int)regs->gpr[rb]))) + goto trap; + goto instr_done; +#ifdef __powerpc64__ + case 68: /* td */ + if (rd & trap_compare(regs->gpr[ra], regs->gpr[rb])) + goto trap; + goto instr_done; +#endif case 83: /* mfmsr */ if (regs->msr & MSR_PR) goto priv; @@ -1269,6 +1316,11 @@ int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs, op->ea = xform_ea(instr, regs); op->reg = rd; return 0; + + case 982: /* icbi */ + op->type = MKOP(CACHEOP, ICBI, 0); + op->ea = xform_ea(instr, regs); + return 0; } break; } @@ -1597,6 +1649,11 @@ int __kprobes analyse_instr(struct instruction_op *op, struct pt_regs *regs, op->val = SRR1_PROGPRIV; return 0; + trap: + op->type = INTERRUPT | 0x700; + op->val = SRR1_PROGTRAP; + return 0; + #ifdef CONFIG_PPC_FPU fpunavail: op->type = INTERRUPT | 0x800; @@ -1714,6 +1771,9 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr) if (op.reg == 0) prefetch((void *) op.ea); break; + case ICBI: + __cacheop_user_asmx(op.ea, err, "icbi"); + break; } if (err) return 0; |