powerpc: Change analyse_instr so it doesn't modify *regs
authorPaul Mackerras <paulus@ozlabs.org>
Wed, 30 Aug 2017 04:12:25 +0000 (14:12 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 1 Sep 2017 06:39:27 +0000 (16:39 +1000)
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: Paul Mackerras <paulus@ozlabs.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/sstep.h
arch/powerpc/lib/sstep.c

index d3a42cc..442e636 100644 (file)
@@ -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);
+
index a85b82c..e190b8d 100644 (file)
@@ -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:
@@ -1887,6 +1892,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.
  * Returns 1 if the step was emulated, 0 if not,
@@ -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);