Commit 3cdfcbfd authored by Paul Mackerras's avatar Paul Mackerras Committed by Michael Ellerman

powerpc: Change analyse_instr so it doesn't modify *regs

The analyse_instr function currently doesn't just work out what an
instruction does, it also executes those instructions whose effect
is only to update CPU registers that are stored in struct pt_regs.
This is undesirable because optprobes uses analyse_instr to work out
if an instruction could be successfully emulated in future.

This changes analyse_instr so it doesn't modify *regs; instead it
stores information in the instruction_op structure to indicate what
registers (GPRs, CR, XER, LR) would be set and what value they would
be set to.  A companion function called emulate_update_regs() can
then use that information to update a pt_regs struct appropriately.

As a minor cleanup, this replaces inline asm using the cntlzw and
cntlzd instructions with calls to __builtin_clz() and __builtin_clzl().
Signed-off-by: default avatarPaul Mackerras <paulus@ozlabs.org>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 93b2d3cf
......@@ -23,9 +23,6 @@ struct pt_regs;
#define IS_RFID(instr) (((instr) & 0xfc0007fe) == 0x4c000024)
#define IS_RFI(instr) (((instr) & 0xfc0007fe) == 0x4c000064)
/* Emulate instructions that cause a transfer of control. */
extern int emulate_step(struct pt_regs *regs, unsigned int instr);
enum instruction_type {
COMPUTE, /* arith/logical/CR op, etc. */
LOAD,
......@@ -55,11 +52,29 @@ enum instruction_type {
#define INSTR_TYPE_MASK 0x1f
/* Compute flags, ORed in with type */
#define SETREG 0x20
#define SETCC 0x40
#define SETXER 0x80
/* Branch flags, ORed in with type */
#define SETLK 0x20
#define BRTAKEN 0x40
#define DECCTR 0x80
/* Load/store flags, ORed in with type */
#define SIGNEXT 0x20
#define UPDATE 0x40 /* matches bit in opcode 31 instructions */
#define BYTEREV 0x80
/* Barrier type field, ORed in with type */
#define BARRIER_MASK 0xe0
#define BARRIER_SYNC 0x00
#define BARRIER_ISYNC 0x20
#define BARRIER_EIEIO 0x40
#define BARRIER_LWSYNC 0x60
#define BARRIER_PTESYNC 0x80
/* Cacheop values, ORed in with type */
#define CACHEOP_MASK 0x700
#define DCBST 0
......@@ -83,7 +98,36 @@ struct instruction_op {
int update_reg;
/* For MFSPR */
int spr;
u32 ccval;
u32 xerval;
};
extern int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
/*
* Decode an instruction, and return information about it in *op
* without changing *regs.
*
* Return value is 1 if the instruction can be emulated just by
* updating *regs with the information in *op, -1 if we need the
* GPRs but *regs doesn't contain the full register set, or 0
* otherwise.
*/
extern int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
unsigned int instr);
/*
* Emulate an instruction that can be executed just by updating
* fields in *regs.
*/
void emulate_update_regs(struct pt_regs *reg, struct instruction_op *op);
/*
* Emulate instructions that cause a transfer of control,
* arithmetic/logical instructions, loads and stores,
* cache operations and barriers.
*
* Returns 1 if the instruction was emulated successfully,
* 0 if it could not be emulated, or -1 for an instruction that
* should not be emulated (rfid, mtmsrd clearing MSR_RI, etc.).
*/
extern int emulate_step(struct pt_regs *regs, unsigned int instr);
......@@ -62,15 +62,17 @@ static nokprobe_inline unsigned long truncate_if_32bit(unsigned long msr,
/*
* Determine whether a conditional branch instruction would branch.
*/
static nokprobe_inline int branch_taken(unsigned int instr, struct pt_regs *regs)
static nokprobe_inline int branch_taken(unsigned int instr,
const struct pt_regs *regs,
struct instruction_op *op)
{
unsigned int bo = (instr >> 21) & 0x1f;
unsigned int bi;
if ((bo & 4) == 0) {
/* decrement counter */
--regs->ctr;
if (((bo >> 1) & 1) ^ (regs->ctr == 0))
op->type |= DECCTR;
if (((bo >> 1) & 1) ^ (regs->ctr == 1))
return 0;
}
if ((bo & 0x10) == 0) {
......@@ -92,7 +94,8 @@ static nokprobe_inline long address_ok(struct pt_regs *regs, unsigned long ea, i
/*
* Calculate effective address for a D-form instruction
*/
static nokprobe_inline unsigned long dform_ea(unsigned int instr, struct pt_regs *regs)
static nokprobe_inline unsigned long dform_ea(unsigned int instr,
const struct pt_regs *regs)
{
int ra;
unsigned long ea;
......@@ -109,7 +112,8 @@ static nokprobe_inline unsigned long dform_ea(unsigned int instr, struct pt_regs
/*
* Calculate effective address for a DS-form instruction
*/
static nokprobe_inline unsigned long dsform_ea(unsigned int instr, struct pt_regs *regs)
static nokprobe_inline unsigned long dsform_ea(unsigned int instr,
const struct pt_regs *regs)
{
int ra;
unsigned long ea;
......@@ -127,7 +131,7 @@ static nokprobe_inline unsigned long dsform_ea(unsigned int instr, struct pt_reg
* Calculate effective address for an X-form instruction
*/
static nokprobe_inline unsigned long xform_ea(unsigned int instr,
struct pt_regs *regs)
const struct pt_regs *regs)
{
int ra, rb;
unsigned long ea;
......@@ -526,24 +530,27 @@ static nokprobe_inline int do_vsx_store(int rn, int (*func)(int, unsigned long),
: "=r" (err) \
: "r" (addr), "i" (-EFAULT), "0" (err))
static nokprobe_inline void set_cr0(struct pt_regs *regs, int rd)
static nokprobe_inline void set_cr0(const struct pt_regs *regs,
struct instruction_op *op, int rd)
{
long val = regs->gpr[rd];
regs->ccr = (regs->ccr & 0x0fffffff) | ((regs->xer >> 3) & 0x10000000);
op->type |= SETCC;
op->ccval = (regs->ccr & 0x0fffffff) | ((regs->xer >> 3) & 0x10000000);
#ifdef __powerpc64__
if (!(regs->msr & MSR_64BIT))
val = (int) val;
#endif
if (val < 0)
regs->ccr |= 0x80000000;
op->ccval |= 0x80000000;
else if (val > 0)
regs->ccr |= 0x40000000;
op->ccval |= 0x40000000;
else
regs->ccr |= 0x20000000;
op->ccval |= 0x20000000;
}
static nokprobe_inline void add_with_carry(struct pt_regs *regs, int rd,
static nokprobe_inline void add_with_carry(const struct pt_regs *regs,
struct instruction_op *op, int rd,
unsigned long val1, unsigned long val2,
unsigned long carry_in)
{
......@@ -551,24 +558,29 @@ static nokprobe_inline void add_with_carry(struct pt_regs *regs, int rd,
if (carry_in)
++val;
regs->gpr[rd] = val;
op->type = COMPUTE + SETREG + SETXER;
op->reg = rd;
op->val = val;
#ifdef __powerpc64__
if (!(regs->msr & MSR_64BIT)) {
val = (unsigned int) val;
val1 = (unsigned int) val1;
}
#endif
op->xerval = regs->xer;
if (val < val1 || (carry_in && val == val1))
regs->xer |= XER_CA;
op->xerval |= XER_CA;
else
regs->xer &= ~XER_CA;
op->xerval &= ~XER_CA;
}
static nokprobe_inline void do_cmp_signed(struct pt_regs *regs, long v1, long v2,
int crfld)
static nokprobe_inline void do_cmp_signed(const struct pt_regs *regs,
struct instruction_op *op,
long v1, long v2, int crfld)
{
unsigned int crval, shift;
op->type = COMPUTE + SETCC;
crval = (regs->xer >> 31) & 1; /* get SO bit */
if (v1 < v2)
crval |= 8;
......@@ -577,14 +589,17 @@ static nokprobe_inline void do_cmp_signed(struct pt_regs *regs, long v1, long v2
else
crval |= 2;
shift = (7 - crfld) * 4;
regs->ccr = (regs->ccr & ~(0xf << shift)) | (crval << shift);
op->ccval = (regs->ccr & ~(0xf << shift)) | (crval << shift);
}
static nokprobe_inline void do_cmp_unsigned(struct pt_regs *regs, unsigned long v1,
unsigned long v2, int crfld)
static nokprobe_inline void do_cmp_unsigned(const struct pt_regs *regs,
struct instruction_op *op,
unsigned long v1,
unsigned long v2, int crfld)
{
unsigned int crval, shift;
op->type = COMPUTE + SETCC;
crval = (regs->xer >> 31) & 1; /* get SO bit */
if (v1 < v2)
crval |= 8;
......@@ -593,11 +608,12 @@ static nokprobe_inline void do_cmp_unsigned(struct pt_regs *regs, unsigned long
else
crval |= 2;
shift = (7 - crfld) * 4;
regs->ccr = (regs->ccr & ~(0xf << shift)) | (crval << shift);
op->ccval = (regs->ccr & ~(0xf << shift)) | (crval << shift);
}
static nokprobe_inline void do_cmpb(struct pt_regs *regs, unsigned long v1,
unsigned long v2, int rd)
static nokprobe_inline void do_cmpb(const struct pt_regs *regs,
struct instruction_op *op,
unsigned long v1, unsigned long v2)
{
unsigned long long out_val, mask;
int i;
......@@ -608,16 +624,16 @@ static nokprobe_inline void do_cmpb(struct pt_regs *regs, unsigned long v1,
if ((v1 & mask) == (v2 & mask))
out_val |= mask;
}
regs->gpr[rd] = out_val;
op->val = out_val;
}
/*
* The size parameter is used to adjust the equivalent popcnt instruction.
* popcntb = 8, popcntw = 32, popcntd = 64
*/
static nokprobe_inline void do_popcnt(struct pt_regs *regs, unsigned long v1,
int size, int ra)
static nokprobe_inline void do_popcnt(const struct pt_regs *regs,
struct instruction_op *op,
unsigned long v1, int size)
{
unsigned long long out = v1;
......@@ -626,23 +642,24 @@ static nokprobe_inline void do_popcnt(struct pt_regs *regs, unsigned long v1,
out = (out + (out >> 4)) & 0x0f0f0f0f0f0f0f0f;
if (size == 8) { /* popcntb */
regs->gpr[ra] = out;
op->val = out;
return;
}
out += out >> 8;
out += out >> 16;
if (size == 32) { /* popcntw */
regs->gpr[ra] = out & 0x0000003f0000003f;
op->val = out & 0x0000003f0000003f;
return;
}
out = (out + (out >> 32)) & 0x7f;
regs->gpr[ra] = out; /* popcntd */
op->val = out; /* popcntd */
}
#ifdef CONFIG_PPC64
static nokprobe_inline void do_bpermd(struct pt_regs *regs, unsigned long v1,
unsigned long v2, int ra)
static nokprobe_inline void do_bpermd(const struct pt_regs *regs,
struct instruction_op *op,
unsigned long v1, unsigned long v2)
{
unsigned char perm, idx;
unsigned int i;
......@@ -654,26 +671,27 @@ static nokprobe_inline void do_bpermd(struct pt_regs *regs, unsigned long v1,
if (v2 & PPC_BIT(idx))
perm |= 1 << i;
}
regs->gpr[ra] = perm;
op->val = perm;
}
#endif /* CONFIG_PPC64 */
/*
* The size parameter adjusts the equivalent prty instruction.
* prtyw = 32, prtyd = 64
*/
static nokprobe_inline void do_prty(struct pt_regs *regs, unsigned long v,
int size, int ra)
static nokprobe_inline void do_prty(const struct pt_regs *regs,
struct instruction_op *op,
unsigned long v, int size)
{
unsigned long long res = v ^ (v >> 8);
res ^= res >> 16;
if (size == 32) { /* prtyw */
regs->gpr[ra] = res & 0x0000000100000001;
op->val = res & 0x0000000100000001;
return;
}
res ^= res >> 32;
regs->gpr[ra] = res & 1; /*prtyd */
op->val = res & 1; /*prtyd */
}
static nokprobe_inline int trap_compare(long v1, long v2)
......@@ -709,14 +727,18 @@ static nokprobe_inline int trap_compare(long v1, long v2)
#define ROTATE(x, n) ((n) ? (((x) << (n)) | ((x) >> (8 * sizeof(long) - (n)))) : (x))
/*
* Decode an instruction, and execute it if that can be done just by
* modifying *regs (i.e. integer arithmetic and logical instructions,
* branches, and barrier instructions).
* Returns 1 if the instruction has been executed, or 0 if not.
* Sets *op to indicate what the instruction does.
* Decode an instruction, and return information about it in *op
* without changing *regs.
* Integer arithmetic and logical instructions, branches, and barrier
* instructions can be emulated just using the information in *op.
*
* Return value is 1 if the instruction can be emulated just by
* updating *regs with the information in *op, -1 if we need the
* GPRs but *regs doesn't contain the full register set, or 0
* otherwise.
*/
int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
unsigned int instr)
int analyse_instr(struct instruction_op *op, const struct pt_regs *regs,
unsigned int instr)
{
unsigned int opcode, ra, rb, rd, spr, u;
unsigned long int imm;
......@@ -733,12 +755,11 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
imm = (signed short)(instr & 0xfffc);
if ((instr & 2) == 0)
imm += regs->nip;
regs->nip += 4;
regs->nip = truncate_if_32bit(regs->msr, regs->nip);
op->val = truncate_if_32bit(regs->msr, imm);
if (instr & 1)
regs->link = regs->nip;
if (branch_taken(instr, regs))
regs->nip = truncate_if_32bit(regs->msr, imm);
op->type |= SETLK;
if (branch_taken(instr, regs, op))
op->type |= BRTAKEN;
return 1;
#ifdef CONFIG_PPC64
case 17: /* sc */
......@@ -749,38 +770,37 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
return 0;
#endif
case 18: /* b */
op->type = BRANCH;
op->type = BRANCH | BRTAKEN;
imm = instr & 0x03fffffc;
if (imm & 0x02000000)
imm -= 0x04000000;
if ((instr & 2) == 0)
imm += regs->nip;
op->val = truncate_if_32bit(regs->msr, imm);
if (instr & 1)
regs->link = truncate_if_32bit(regs->msr, regs->nip + 4);
imm = truncate_if_32bit(regs->msr, imm);
regs->nip = imm;
op->type |= SETLK;
return 1;
case 19:
switch ((instr >> 1) & 0x3ff) {
case 0: /* mcrf */
op->type = COMPUTE + SETCC;
rd = 7 - ((instr >> 23) & 0x7);
ra = 7 - ((instr >> 18) & 0x7);
rd *= 4;
ra *= 4;
val = (regs->ccr >> ra) & 0xf;
regs->ccr = (regs->ccr & ~(0xfUL << rd)) | (val << rd);
goto instr_done;
op->ccval = (regs->ccr & ~(0xfUL << rd)) | (val << rd);
return 1;
case 16: /* bclr */
case 528: /* bcctr */
op->type = BRANCH;
imm = (instr & 0x400)? regs->ctr: regs->link;
regs->nip = truncate_if_32bit(regs->msr, regs->nip + 4);
imm = truncate_if_32bit(regs->msr, imm);
op->val = truncate_if_32bit(regs->msr, imm);
if (instr & 1)
regs->link = regs->nip;
if (branch_taken(instr, regs))
regs->nip = imm;
op->type |= SETLK;
if (branch_taken(instr, regs, op))
op->type |= BRTAKEN;
return 1;
case 18: /* rfid, scary */
......@@ -790,9 +810,8 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
return 0;
case 150: /* isync */
op->type = BARRIER;
isync();
goto instr_done;
op->type = BARRIER | BARRIER_ISYNC;
return 1;
case 33: /* crnor */
case 129: /* crandc */
......@@ -802,45 +821,47 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
case 289: /* creqv */
case 417: /* crorc */
case 449: /* cror */
op->type = COMPUTE + SETCC;
ra = (instr >> 16) & 0x1f;
rb = (instr >> 11) & 0x1f;
rd = (instr >> 21) & 0x1f;
ra = (regs->ccr >> (31 - ra)) & 1;
rb = (regs->ccr >> (31 - rb)) & 1;
val = (instr >> (6 + ra * 2 + rb)) & 1;
regs->ccr = (regs->ccr & ~(1UL << (31 - rd))) |
op->ccval = (regs->ccr & ~(1UL << (31 - rd))) |
(val << (31 - rd));
goto instr_done;
return 1;
default:
op->type = UNKNOWN;
return 0;
}
break;
case 31:
switch ((instr >> 1) & 0x3ff) {
case 598: /* sync */
op->type = BARRIER;
op->type = BARRIER + BARRIER_SYNC;
#ifdef __powerpc64__
switch ((instr >> 21) & 3) {
case 1: /* lwsync */
asm volatile("lwsync" : : : "memory");
goto instr_done;
op->type = BARRIER + BARRIER_LWSYNC;
break;
case 2: /* ptesync */
asm volatile("ptesync" : : : "memory");
goto instr_done;
op->type = BARRIER + BARRIER_PTESYNC;
break;
}
#endif
mb();
goto instr_done;
return 1;
case 854: /* eieio */
op->type = BARRIER;
eieio();
goto instr_done;
op->type = BARRIER + BARRIER_EIEIO;
return 1;
}
break;
}
/* Following cases refer to regs->gpr[], so we need all regs */
if (!FULL_REGS(regs))
return 0;
return -1;
rd = (instr >> 21) & 0x1f;
ra = (instr >> 16) & 0x1f;
......@@ -851,21 +872,21 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
case 2: /* tdi */
if (rd & trap_compare(regs->gpr[ra], (short) instr))
goto trap;
goto instr_done;
return 1;
#endif
case 3: /* twi */
if (rd & trap_compare((int)regs->gpr[ra], (short) instr))
goto trap;
goto instr_done;
return 1;
case 7: /* mulli */
regs->gpr[rd] = regs->gpr[ra] * (short) instr;
goto instr_done;
op->val = regs->gpr[ra] * (short) instr;
goto compute_done;
case 8: /* subfic */
imm = (short) instr;
add_with_carry(regs, rd, ~regs->gpr[ra], imm, 1);
goto instr_done;
add_with_carry(regs, op, rd, ~regs->gpr[ra], imm, 1);
return 1;
case 10: /* cmpli */
imm = (unsigned short) instr;
......@@ -874,8 +895,8 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
if ((rd & 1) == 0)
val = (unsigned int) val;
#endif
do_cmp_unsigned(regs, val, imm, rd >> 2);
goto instr_done;
do_cmp_unsigned(regs, op, val, imm, rd >> 2);
return 1;
case 11: /* cmpi */
imm = (short) instr;
......@@ -884,47 +905,47 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
if ((rd & 1) == 0)
val = (int) val;
#endif
do_cmp_signed(regs, val, imm, rd >> 2);
goto instr_done;
do_cmp_signed(regs, op, val, imm, rd >> 2);
return 1;
case 12: /* addic */
imm = (short) instr;
add_with_carry(regs, rd, regs->gpr[ra], imm, 0);
goto instr_done;
add_with_carry(regs, op, rd, regs->gpr[ra], imm, 0);
return 1;
case 13: /* addic. */
imm = (short) instr;
add_with_carry(regs, rd, regs->gpr[ra], imm, 0);
set_cr0(regs, rd);
goto instr_done;
add_with_carry(regs, op, rd, regs->gpr[ra], imm, 0);
set_cr0(regs, op, rd);
return 1;
case 14: /* addi */
imm = (short) instr;
if (ra)
imm += regs->gpr[ra];
regs->gpr[rd] = imm;
goto instr_done;
op->val = imm;
goto compute_done;
case 15: /* addis */
imm = ((short) instr) << 16;
if (ra)
imm += regs->gpr[ra];
regs->gpr[rd] = imm;
goto instr_done;
op->val = imm;
goto compute_done;
case 20: /* rlwimi */
mb = (instr >> 6) & 0x1f;
me = (instr >> 1) & 0x1f;
val = DATA32(regs->gpr[rd]);
imm = MASK32(mb, me);
regs->gpr[ra] = (regs->gpr[ra] & ~imm) | (ROTATE(val, rb) & imm);
op->val = (regs->gpr[ra] & ~imm) | (ROTATE(val, rb) & imm);
goto logical_done;
case 21: /* rlwinm */
mb = (instr >> 6) & 0x1f;
me = (instr >> 1) & 0x1f;
val = DATA32(regs->gpr[rd]);
regs->gpr[ra] = ROTATE(val, rb) & MASK32(mb, me);
op->val = ROTATE(val, rb) & MASK32(mb, me);
goto logical_done;
case 23: /* rlwnm */
......@@ -932,40 +953,37 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
me = (instr >> 1) & 0x1f;
rb = regs->gpr[rb] & 0x1f;
val = DATA32(regs->gpr[rd]);
regs->gpr[ra] = ROTATE(val, rb) & MASK32(mb, me);
op->val = ROTATE(val, rb) & MASK32(mb, me);
goto logical_done;
case 24: /* ori */
imm = (unsigned short) instr;
regs->gpr[ra] = regs->gpr[rd] | imm;
goto instr_done;
op->val = regs->gpr[rd] | (unsigned short) instr;
goto logical_done_nocc;
case 25: /* oris */
imm = (unsigned short) instr;
regs->gpr[ra] = regs->gpr[rd] | (imm << 16);
goto instr_done;
op->val = regs->gpr[rd] | (imm << 16);
goto logical_done_nocc;
case 26: /* xori */
imm = (unsigned short) instr;
regs->gpr[ra] = regs->gpr[rd] ^ imm;
goto instr_done;
op->val = regs->gpr[rd] ^ (unsigned short) instr;
goto logical_done_nocc;
case 27: /* xoris */
imm = (unsigned short) instr;
regs->gpr[ra] = regs->gpr[rd] ^ (imm << 16);
goto instr_done;
op->val = regs->gpr[rd] ^ (imm << 16);
goto logical_done_nocc;
case 28: /* andi. */
imm = (unsigned short) instr;
regs->gpr[ra] = regs->gpr[rd] & imm;
set_cr0(regs, ra);
goto instr_done;
op->val = regs->gpr[rd] & (unsigned short) instr;
set_cr0(regs, op, ra);
goto logical_done_nocc;
case 29: /* andis. */
imm = (unsigned short) instr;
regs->gpr[ra] = regs->gpr[rd] & (imm << 16);
set_cr0(regs, ra);
goto instr_done;
op->val = regs->gpr[rd] & (imm << 16);
set_cr0(regs, op, ra);
goto logical_done_nocc;
#ifdef __powerpc64__
case 30: /* rld* */
......@@ -976,34 +994,36 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
val = ROTATE(val, sh);
switch ((instr >> 2) & 3) {
case 0: /* rldicl */
regs->gpr[ra] = val & MASK64_L(mb);
goto logical_done;
val &= MASK64_L(mb);
break;
case 1: /* rldicr */
regs->gpr[ra] = val & MASK64_R(mb);
goto logical_done;
val &= MASK64_R(mb);
break;
case 2: /* rldic */
regs->gpr[ra] = val & MASK64(mb, 63 - sh);
goto logical_done;
val &= MASK64(mb, 63 - sh);
break;
case 3: /* rldimi */
imm = MASK64(mb, 63 - sh);
regs->gpr[ra] = (regs->gpr[ra] & ~imm) |
val = (regs->gpr[ra] & ~imm) |
(val & imm);
goto logical_done;
}
op->val = val;
goto logical_done;
} else {
sh = regs->gpr[rb] & 0x3f;
val = ROTATE(val, sh);
switch ((instr >> 1) & 7) {
case 0: /* rldcl */
regs->gpr[ra] = val & MASK64_L(mb);
op->val = val & MASK64_L(mb);
goto logical_done;
case 1: /* rldcr */
regs->gpr[ra] = val & MASK64_R(mb);
op->val = val & MASK64_R(mb);
goto logical_done;
}
}
#endif
break; /* illegal instruction */
op->type = UNKNOWN; /* illegal instruction */
return 0;
case 31:
switch ((instr >> 1) & 0x3ff) {
......@@ -1012,12 +1032,12 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
(rd & trap_compare((int)regs->gpr[ra],
(int)regs->gpr[rb])))
goto trap;
goto instr_done;
return 1;
#ifdef __powerpc64__
case 68: /* td */
if (rd & trap_compare(regs->gpr[ra], regs->gpr[rb]))
goto trap;
goto instr_done;
return 1;
#endif
case 83: /* mfmsr */
if (regs->msr & MSR_PR)
......@@ -1046,74 +1066,50 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
#endif
case 19: /* mfcr */
imm = 0xffffffffUL;
if ((instr >> 20) & 1) {
imm = 0xf0000000UL;
for (sh = 0; sh < 8; ++sh) {
if (instr & (0x80000 >> sh)) {
regs->gpr[rd] = regs->ccr & imm;
if (instr & (0x80000 >> sh))
break;
}
imm >>= 4;
}
goto instr_done;
}
regs->gpr[rd] = regs->ccr;
regs->gpr[rd] &= 0xffffffffUL;
goto instr_done;
op->val = regs->ccr & imm;
goto compute_done;
case 144: /* mtcrf */
op->type = COMPUTE + SETCC;
imm = 0xf0000000UL;
val = regs->gpr[rd];
op->val = regs->ccr;
for (sh = 0; sh < 8; ++sh) {
if (instr & (0x80000 >> sh))
regs->ccr = (regs->ccr & ~imm) |
op->val = (op->val & ~imm) |
(val & imm);
imm >>= 4;
}
goto instr_done;
return 1;
case 339: /* mfspr */
spr = ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0);
switch (spr) {
case SPRN_XER: /* mfxer */
regs->gpr[rd] = regs->xer;
regs->gpr[rd] &= 0xffffffffUL;
goto instr_done;
case SPRN_LR: /* mflr */
regs->gpr[rd] = regs->link;
goto instr_done;
case SPRN_CTR: /* mfctr */
regs->gpr[rd] = regs->ctr;
goto instr_done;
default:
op->type = MFSPR;
op->reg = rd;
op->spr = spr;
return 0;
}
break;
op->type = MFSPR;
op->reg = rd;
op->spr = spr;
if (spr == SPRN_XER || spr == SPRN_LR ||
spr == SPRN_CTR)
return 1;
return 0;
case 467: /* mtspr */
spr = ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0);
switch (spr) {
case SPRN_XER: /* mtxer */
regs->xer = (regs->gpr[rd] & 0xffffffffUL);
goto instr_done;
case SPRN_LR: /* mtlr */
regs->link = regs->gpr[rd];
goto instr_done;
case SPRN_CTR: /* mtctr */
regs->ctr = regs->gpr[rd];
goto instr_done;
default:
op->type = MTSPR;
op->val = regs->gpr[rd];
op->spr = spr;
return 0;
}
break;
op->type = MTSPR;
op->val = regs->gpr[rd];
op->spr = spr;
if (spr == SPRN_XER || spr == SPRN_LR ||
spr == SPRN_CTR)
return 1;
return 0;
/*
* Compare instructions
......@@ -1128,8 +1124,8 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
val2 = (int) val2;
}
#endif
do_cmp_signed(regs, val, val2, rd >> 2);
goto instr_done;
do_cmp_signed(regs, op, val, val2, rd >> 2);
return 1;
case 32: /* cmpl */
val = regs->gpr[ra];
......@@ -1141,113 +1137,113 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
val2 = (unsigned int) val2;
}
#endif
do_cmp_unsigned(regs, val, val2, rd >> 2);
goto instr_done;
do_cmp_unsigned(regs, op, val, val2, rd >> 2);
return 1;
case 508: /* cmpb */
do_cmpb(regs, regs->gpr[rd], regs->gpr[rb], ra);
goto instr_done;
do_cmpb(regs, op, regs->gpr[rd], regs->gpr[rb]);
goto logical_done_nocc;
/*
* Arithmetic instructions
*/
case 8: /* subfc */
add_with_carry(regs, rd, ~regs->gpr[ra],
add_with_carry(regs, op, rd, ~regs->gpr[ra],
regs->gpr[rb], 1);
goto arith_done;
#ifdef __powerpc64__
case 9: /* mulhdu */
asm("mulhdu %0,%1,%2" : "=r" (regs->gpr[rd]) :
asm("mulhdu %0,%1,%2" : "=r" (op->val) :
"r" (regs->gpr[ra]), "r" (regs->gpr[rb]));
goto arith_done;
#endif
case 10: /* addc */
add_with_carry(regs, rd, regs->gpr[ra],
add_with_carry(regs, op, rd, regs->gpr[ra],
regs->gpr[rb], 0);
goto arith_done;
case 11: /* mulhwu */
asm("mulhwu %0,%1,%2" : "=r" (regs->gpr[rd]) :
asm("mulhwu %0,%1,%2" : "=r" (op->val) :
"r" (regs->gpr[ra]), "r" (regs->gpr[rb]));
goto arith_done;
case 40: /* subf */
regs->gpr[rd] = regs->gpr[rb] - regs->gpr[ra];
op->val = regs->gpr[rb] - regs->gpr[ra];
goto arith_done;
#ifdef __powerpc64__
case 73: /* mulhd */
asm("mulhd %0,%1,%2" : "=r" (regs->gpr[rd]) :
asm("mulhd %0,%1,%2" : "=r" (op->val) :
"r" (regs->gpr[ra]), "r" (regs->gpr[rb]));
goto arith_done;
#endif
case 75: /* mulhw */
asm("mulhw %0,%1,%2" : "=r" (regs->gpr[rd]) :
asm("mulhw %0,%1,%2" : "=r" (op->val) :
"r" (regs->gpr[ra]), "r" (regs->gpr[rb]));
goto arith_done;
case 104: /* neg */
regs->gpr[rd] = -regs->gpr[ra];
op->val = -regs->gpr[ra];
goto arith_done;
case 136: /* subfe */
add_with_carry(regs, rd, ~regs->gpr[ra], regs->gpr[rb],
regs->xer & XER_CA);
add_with_carry(regs, op, rd, ~regs->gpr[ra],
regs->gpr[rb], regs->xer & XER_CA);
goto arith_done;
case 138: /* adde */
add_with_carry(regs, rd, regs->gpr[ra], regs->gpr[rb],
regs->xer & XER_CA);
add_with_carry(regs, op, rd, regs->gpr[ra],
regs->gpr[rb], regs->xer & XER_CA);
goto arith_done;
case 200: /* subfze */
add_with_carry(regs, rd, ~regs->gpr[ra], 0L,
add_with_carry(regs, op, rd, ~regs->gpr[ra], 0L,
regs->xer & XER_CA);
goto arith_done;
case 202: /* addze */
add_with_carry(regs, rd, regs->gpr[ra], 0L,
add_with_carry(regs, op, rd, regs->gpr[ra], 0L,
regs->xer & XER_CA);
goto arith_done;
case 232: /* subfme */
add_with_carry(regs, rd, ~regs->gpr[ra], -1L,
add_with_carry(regs, op, rd, ~regs->gpr[ra], -1L,
regs->xer & XER_CA);
goto arith_done;
#ifdef __powerpc64__
case 233: /* mulld */
regs->gpr[rd] = regs->gpr[ra] * regs->gpr[rb];
op->val = regs->gpr[ra] * regs->gpr[rb];
goto arith_done;
#endif
case 234: /* addme */
add_with_carry(regs, rd, regs->gpr[ra], -1L,
add_with_carry(regs, op, rd, regs->gpr[ra], -1L,
regs->xer & XER_CA);
goto arith_done;
case 235: /* mullw */
regs->gpr[rd] = (unsigned int) regs->gpr[ra] *
op->val = (unsigned int) regs->gpr[ra] *
(unsigned int) regs->gpr[rb];
goto arith_done;
case 266: /* add */
regs->gpr[rd] = regs->gpr[ra] + regs->gpr[rb];
op->val = regs->gpr[ra] + regs->gpr[rb];
goto arith_done;
#ifdef __powerpc64__
case 457: /* divdu */
regs->gpr[rd] = regs->gpr[ra] / regs->gpr[rb];
op->val = regs->gpr[ra] / regs->gpr[rb];
goto arith_done;
#endif
case 459: /* divwu */
regs->gpr[rd] = (unsigned int) regs->gpr[ra] /
op->val = (unsigned int) regs->gpr[ra] /
(unsigned int) regs->gpr[rb];
goto arith_done;
#ifdef __powerpc64__
case 489: /* divd */
regs->gpr[rd] = (long int) regs->gpr[ra] /
op->val = (long int) regs->gpr[ra] /
(long int) regs->gpr[rb];
goto arith_done;
#endif
case 491: /* divw */
regs->gpr[rd] = (int) regs->gpr[ra] /
op->val = (int) regs->gpr[ra] /
(int) regs->gpr[rb];
goto arith_done;
......@@ -1260,85 +1256,83 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
val = (regs->ccr >> (31 - mb)) & 1;
val2 = (ra) ? regs->gpr[ra] : 0;
regs->gpr[rd] = (val) ? val2 : regs->gpr[rb];
goto logical_done;
op->val = (val) ? val2 : regs->gpr[rb];
goto compute_done;
case 26: /* cntlzw */
asm("cntlzw %0,%1" : "=r" (regs->gpr[ra]) :
"r" (regs->gpr[rd]));
op->val = __builtin_clz((unsigned int) regs->gpr[rd]);
goto logical_done;
#ifdef __powerpc64__
case 58: /* cntlzd */
asm("cntlzd %0,%1" : "=r" (regs->gpr[ra]) :
"r" (regs->gpr[rd]));
op->val = __builtin_clzl(regs->gpr[rd]);
goto logical_done;
#endif
case 28: /* and */
regs->gpr[ra] = regs->gpr[rd] & regs->gpr[rb];
op->val = regs->gpr[rd] & regs->gpr[rb];
goto logical_done;
case 60: /* andc */
regs->gpr[ra] = regs->gpr[rd] & ~regs->gpr[rb];
op->val = regs->gpr[rd] & ~regs->gpr[rb];
goto logical_done;
case 122: /* popcntb */
do_popcnt(regs, regs->gpr[rd], 8, ra);
do_popcnt(regs, op, regs->gpr[rd], 8);
goto logical_done;
case 124: /* nor */
regs->gpr[ra] = ~(regs->gpr[rd] | regs->gpr[rb]);
op->val = ~(regs->gpr[rd] | regs->gpr[rb]);
goto logical_done;
case 154: /* prtyw */
do_prty(regs, regs->gpr[rd], 32, ra);
do_prty(regs, op, regs->gpr[rd], 32);
goto logical_done;
case 186: /* prtyd */
do_prty(regs, regs->gpr[rd], 64, ra);
do_prty(regs, op, regs->gpr[rd], 64);
goto logical_done;
#ifdef CONFIG_PPC64
case 252: /* bpermd */
do_bpermd(regs, regs->gpr[rd], regs->gpr[rb], ra);
do_bpermd(regs, op, regs->gpr[rd], regs->gpr[rb]);
goto logical_done;
#endif
case 284: /* xor */
regs->gpr[ra] = ~(regs->gpr[rd] ^ regs->gpr[rb]);
op->val = ~(regs->gpr[rd] ^ regs->gpr[rb]);
goto logical_done;
case 316: /* xor */
regs->gpr[ra] = regs->gpr[rd] ^ regs->gpr[rb];
op->val = regs->gpr[rd] ^ regs->gpr[rb];
goto logical_done;
case 378: /* popcntw */
do_popcnt(regs, regs->gpr[rd], 32, ra);
do_popcnt(regs, op, regs->gpr[rd], 32);
goto logical_done;
case 412: /* orc */
regs->gpr[ra] = regs->gpr[rd] | ~regs->gpr[rb];
op->val = regs->gpr[rd] | ~regs->gpr[rb];
goto logical_done;
case 444: /* or */
regs->gpr[ra] = regs->gpr[rd] | regs->gpr[rb];
op->val = regs->gpr[rd] | regs->gpr[rb];
goto logical_done;
case 476: /* nand */
regs->gpr[ra] = ~(regs->gpr[rd] & regs->gpr[rb]);
op->val = ~(regs->gpr[rd] & regs->gpr[rb]);
goto logical_done;
#ifdef CONFIG_PPC64
case 506: /* popcntd */
do_popcnt(regs, regs->gpr[rd], 64, ra);
do_popcnt(regs, op, regs->gpr[rd], 64);
goto logical_done;
#endif
case 922: /* extsh */
regs->gpr[ra] = (signed short) regs->gpr[rd];
op->val = (signed short) regs->gpr[rd];
goto logical_done;
case 954: /* extsb */
regs->gpr[ra] = (signed char) regs->gpr[rd];
op->val = (signed char) regs->gpr[rd];
goto logical_done;
#ifdef __powerpc64__
case 986: /* extsw */
regs->gpr[ra] = (signed int) regs->gpr[rd];
op->val = (signed int) regs->gpr[rd];
goto logical_done;
#endif
......@@ -1348,75 +1342,83 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
case 24: /* slw */
sh = regs->gpr[rb] & 0x3f;
if (sh < 32)
regs->gpr[ra] = (regs->gpr[rd] << sh) & 0xffffffffUL;
op->val = (regs->gpr[rd] << sh) & 0xffffffffUL;
else
regs->gpr[ra] = 0;
op->val = 0;
goto logical_done;
case 536: /* srw */
sh = regs->gpr[rb] & 0x3f;
if (sh < 32)
regs->gpr[ra] = (regs->gpr[rd] & 0xffffffffUL) >> sh;
op->val = (regs->gpr[rd] & 0xffffffffUL) >> sh;
else
regs->gpr[ra] = 0;
op->val = 0;
goto logical_done;
case 792: /* sraw */
op->type = COMPUTE + SETREG + SETXER;
sh = regs->gpr[rb] & 0x3f;
ival = (signed int) regs->gpr[rd];
regs->gpr[ra] = ival >> (sh < 32 ? sh : 31);
op->val = ival >> (sh < 32 ? sh : 31);
op->xerval = regs->xer;
if (ival < 0 && (sh >= 32 || (ival & ((1ul << sh) - 1)) != 0))
regs->xer |= XER_CA;
op->xerval |= XER_CA;
else
regs->xer &= ~XER_CA;
op->xerval &= ~XER_CA;
goto logical_done;
case 824: /* srawi */
op->type = COMPUTE + SETREG + SETXER;
sh = rb;
ival = (signed int) regs->gpr[rd];
regs->gpr[ra] = ival >> sh;
op->val = ival >> sh;
op->xerval = regs->xer;
if (ival < 0 && (ival & ((1ul << sh) - 1)) != 0)
regs->xer |= XER_CA;
op->xerval |= XER_CA;
else
regs->xer &= ~XER_CA;
op->xerval &= ~XER_CA;
goto logical_done;
#ifdef __powerpc64__
case 27: /* sld */
sh = regs->gpr[rb] & 0x7f;
if (sh < 64)
regs->gpr[ra] = regs->gpr[rd] << sh;
op->val = regs->gpr[rd] << sh;
else
regs->gpr[ra] = 0;
op->val = 0;
goto logical_done;
case 539: /* srd */
sh = regs->gpr[rb] & 0x7f;
if (sh < 64)
regs->gpr[ra] = regs->gpr[rd] >> sh;
op->val = regs->gpr[rd] >> sh;
else
regs->gpr[ra] = 0;
op->val = 0;
goto logical_done;
case 794: /* srad */
op->type = COMPUTE + SETREG + SETXER;
sh = regs->gpr[rb] & 0x7f;
ival = (signed long int) regs->gpr[rd];
regs->gpr[ra] = ival >> (sh < 64 ? sh : 63);
op->val = ival >> (sh < 64 ? sh : 63);
op->xerval = regs->xer;
if (ival < 0 && (sh >= 64 || (ival & ((1ul << sh) - 1)) != 0))
regs->xer |= XER_CA;
op->xerval |= XER_CA;
else
regs->xer &= ~XER_CA;
op->xerval &= ~XER_CA;
goto logical_done;
case 826: /* sradi with sh_5 = 0 */
case 827: /* sradi with sh_5 = 1 */
op->type = COMPUTE + SETREG + SETXER;
sh = rb | ((instr & 2) << 4);
ival = (signed long int) regs->gpr[rd];
regs->gpr[ra] = ival >> sh;
op->val = ival >> sh;
op->xerval = regs->xer;
if (ival < 0 && (ival & ((1ul << sh) - 1)) != 0)
regs->xer |= XER_CA;
op->xerval |= XER_CA;
else
regs->xer &= ~XER_CA;
op->xerval &= ~XER_CA;
goto logical_done;
#endif /* __powerpc64__ */
......@@ -1787,15 +1789,18 @@ int analyse_instr(struct instruction_op *op, struct pt_regs *regs,
logical_done:
if (instr & 1)
set_cr0(regs, ra);
goto instr_done;
set_cr0(regs, op, ra);
logical_done_nocc:
op->reg = ra;
op->type |= SETREG;
return 1;
arith_done:
if (instr & 1)
set_cr0(regs, rd);
instr_done:
regs->nip = truncate_if_32bit(regs->msr, regs->nip + 4);
set_cr0(regs, op, rd);
compute_done:
op->reg = rd;
op->type |= SETREG;
return 1;
priv:
......@@ -1886,6 +1891,92 @@ static nokprobe_inline void do_byterev(unsigned long *valp, int size)
}
}
/*
* Emulate an instruction that can be executed just by updating
* fields in *regs.
*/
void emulate_update_regs(struct pt_regs *regs, struct instruction_op *op)
{
unsigned long next_pc;
next_pc = truncate_if_32bit(regs->msr, regs->nip + 4);
switch (op->type & INSTR_TYPE_MASK) {
case COMPUTE:
if (op->type & SETREG)
regs->gpr[op->reg] = op->val;
if (op->type & SETCC)
regs->ccr = op->ccval;
if (op->type & SETXER)
regs->xer = op->xerval;
break;
case BRANCH:
if (op->type & SETLK)
regs->link = next_pc;
if (op->type & BRTAKEN)
next_pc = op->val;
if (op->type & DECCTR)
--regs->ctr;
break;
case BARRIER:
switch (op->type & BARRIER_MASK) {
case BARRIER_SYNC:
mb();
break;
case BARRIER_ISYNC:
isync();
break;
case BARRIER_EIEIO:
eieio();
break;
case BARRIER_LWSYNC:
asm volatile("lwsync" : : : "memory");
break;
case BARRIER_PTESYNC:
asm volatile("ptesync" : : : "memory");
break;
}
break;
case MFSPR:
switch (op->spr) {
case SPRN_XER:
regs->gpr[op->reg] = regs->xer & 0xffffffffUL;
break;
case SPRN_LR:
regs->gpr[op->reg] = regs->link;
break;
case SPRN_CTR:
regs->gpr[op->reg] = regs->ctr;
break;
default:
WARN_ON_ONCE(1);
}
break;
case MTSPR:
switch (op->spr) {
case SPRN_XER:
regs->xer = op->val & 0xffffffffUL;
break;
case SPRN_LR:
regs->link = op->val;
break;
case SPRN_CTR:
regs->ctr = op->val;
break;
default:
WARN_ON_ONCE(1);
}
break;
default:
WARN_ON_ONCE(1);
}
regs->nip = next_pc;
}
/*
* Emulate instructions that cause a transfer of control,
* loads and stores, and a few other instructions.
......@@ -1902,8 +1993,12 @@ int emulate_step(struct pt_regs *regs, unsigned int instr)
int i, rd, nb;
r = analyse_instr(&op, regs, instr);
if (r != 0)
if (r < 0)
return r;
if (r > 0) {
emulate_update_regs(regs, &op);
return 1;
}
err = 0;
size = GETSIZE(op.type);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment