<div dir="ltr">To: <a href="mailto:linuxppc-dev@lists.ozlabs.org">linuxppc-dev@lists.ozlabs.org</a><br>Subject: [PATCH] powerpc: OE=1 Form Instructions Not Decoded Correctly <br>From: Tom Musta <<a href="mailto:tommusta@gmail.com">tommusta@gmail.com</a>><br>
<br>PowerISA uses instruction bit 21 to indicate that the overflow (OV) bit<br>of the XER is to be set, as well as its corresponding sticky bit (SO).<br>This patch addresses two defects in the implementation of the PowerISA <br>
single step code for this category of instructions:  (a) the OE=1 case<br>is not correctly accounted for in the case statement for the extended<br>opcode handling.  (b) the implementation is not setting XER[OV] and<br>XER[SO].<br>
<br>Signed-off-by: Tom Musta <<a href="mailto:tommusta@gmail.com">tommusta@gmail.com</a>><br><br>---<br> arch/powerpc/lib/sstep.c |  208 +++++++++++++++++++++++++++++++++++++++++-----<br> 1 files changed, 185 insertions(+), 23 deletions(-)<br>
<br><br>diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c<br>index d220b88..a4f343d 100644<br>--- a/arch/powerpc/lib/sstep.c<br>+++ b/arch/powerpc/lib/sstep.c<br>@@ -31,6 +31,13 @@ extern char system_call_common[];<br>
 #define XER_OV 0x40000000U<br> #define XER_CA 0x20000000U<br> <br>+#define U32_MSK         0xFFFFFFFF00000000ul<br>+#define L32_MSK         0x00000000FFFFFFFFul<br>+#define SGN_32_MSK      0x0000000080000000ul<br>+#define SGN_64_MSK      0x8000000000000000ul<br>
+#define SGN_EXT_32(x)   (((x) & SGN_32_MSK) ? ((x) | U32_MSK) : ((x) & L32_MSK))<br>+#define L32(x)          ((x) & L32_MSK)<br>+<br> #ifdef CONFIG_PPC_FPU<br> /*<br>  * Functions in ldstfp.S<br>@@ -467,6 +474,13 @@ static int __kprobes do_vsx_store(int rn, int (*func)(int, unsigned long),<br>
  ".previous" \<br>  : "=r" (err) \<br>  : "r" (addr), "i" (-EFAULT), "0" (err))<br>+static void __kprobes set_xer_ov(struct pt_regs *regs, int ov)<br>+{<br>+ if (ov)<br>+ regs->xer |= (XER_SO | XER_OV);<br>
+ else<br>+ regs->xer &= ~XER_OV;<br>+}<br> <br> static void __kprobes set_cr0(struct pt_regs *regs, int rd)<br> {<br>@@ -487,7 +501,7 @@ static void __kprobes set_cr0(struct pt_regs *regs, int rd)<br> <br> static void __kprobes add_with_carry(struct pt_regs *regs, int rd,<br>
      unsigned long val1, unsigned long val2,<br>-     unsigned long carry_in)<br>+     unsigned long carry_in, int oe)<br> {<br>  unsigned long val = val1 + val2;<br> <br>@@ -504,6 +518,13 @@ static void __kprobes add_with_carry(struct pt_regs *regs, int rd,<br>
  regs->xer |= XER_CA;<br>  else<br>  regs->xer &= ~XER_CA;<br>+ if (oe) {<br>+ unsigned long m = ((regs->msr & MSR_64BIT) == 0) ?<br>+ SGN_32_MSK : SGN_64_MSK;<br>+ int ov = ((val1 & m) == (val2 & m)) &&<br>
+ ((val1 & m) != (val & m));<br>+ set_xer_ov(regs, ov);<br>+ }<br> }<br> <br> static void __kprobes do_cmp_signed(struct pt_regs *regs, long v1, long v2,<br>@@ -552,7 +573,7 @@ static void __kprobes do_cmp_unsigned(struct pt_regs *regs, unsigned long v1,<br>
 #define DATA32(x) (x)<br> #endif<br> #define ROTATE(x, n) ((n) ? (((x) << (n)) | ((x) >> (8 * sizeof(long) - (n)))) : (x))<br>-<br>+#define OE(instr)       (((instr) >> (31-21)) & 1)<br> /*<br>  * Emulate instructions that cause a transfer of control,<br>
  * loads and stores, and a few other instructions.<br>@@ -693,7 +714,7 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)<br> <br>  case 8: /* subfic */<br>  imm = (short) instr;<br>- add_with_carry(regs, rd, ~regs->gpr[ra], imm, 1);<br>
+ add_with_carry(regs, rd, ~regs->gpr[ra], imm, 1, 0);<br>  goto instr_done;<br> <br>  case 10: /* cmpli */<br>@@ -718,12 +739,12 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)<br> <br>  case 12: /* addic */<br>
  imm = (short) instr;<br>- add_with_carry(regs, rd, regs->gpr[ra], imm, 0);<br>+ add_with_carry(regs, rd, regs->gpr[ra], imm, 0, 0);<br>  goto instr_done;<br> <br>  case 13: /* addic. */<br>  imm = (short) instr;<br>
- add_with_carry(regs, rd, regs->gpr[ra], imm, 0);<br>+ add_with_carry(regs, rd, regs->gpr[ra], imm, 0, 0);<br>  set_cr0(regs, rd);<br>  goto instr_done;<br> <br>@@ -944,8 +965,9 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)<br>
  * Arithmetic instructions<br>  */<br>  case 8: /* subfc */<br>+ case 520: /* subfco */<br>  add_with_carry(regs, rd, ~regs->gpr[ra],<br>-       regs->gpr[rb], 1);<br>+       regs->gpr[rb], 1, OE(instr));<br>  goto arith_done;<br>
 #ifdef __powerpc64__<br>  case 9: /* mulhdu */<br>@@ -954,8 +976,9 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)<br>  goto arith_done;<br> #endif<br>  case 10: /* addc */<br>+ case 522:       /* addco */<br>
  add_with_carry(regs, rd, regs->gpr[ra],<br>-       regs->gpr[rb], 0);<br>+       regs->gpr[rb], 0, OE(instr));<br>  goto arith_done;<br> <br>  case 11: /* mulhwu */<br>@@ -964,7 +987,19 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)<br>
  goto arith_done;<br> <br>  case 40: /* subf */<br>- regs->gpr[rd] = regs->gpr[rb] - regs->gpr[ra];<br>+ case 552: /* subfo */<br>+ val = regs->gpr[rb] - regs->gpr[ra];<br>+ if (OE(instr)) { /* subfo */<br>
+ unsigned long m =<br>+ ((regs->msr & MSR_64BIT) == 0) ?<br>+ SGN_32_MSK : SGN_64_MSK;<br>+ int ov =<br>+ ((~regs->gpr[ra] & m) ==<br>+ (regs->gpr[rb] & m)) &&<br>+ ((regs->gpr[rb] & m) != (val & m));<br>
+ set_xer_ov(regs, ov);<br>+ }<br>+ regs->gpr[rd] = val;<br>  goto arith_done;<br> #ifdef __powerpc64__<br>  case 73: /* mulhd */<br>@@ -978,69 +1013,196 @@ int __kprobes emulate_step(struct pt_regs *regs, unsigned int instr)<br>
  goto arith_done;<br> <br>  case 104: /* neg */<br>+ case 616: /* nego */<br>  regs->gpr[rd] = -regs->gpr[ra];<br>+ if (OE(instr)) { /* nego */<br>+ int ov = (regs->msr & MSR_64BIT) ?<br>+ (regs->gpr[rd] == 0x8000000000000000l) :<br>
+ (regs->gpr[rd] == 0x80000000l);<br>+ set_xer_ov(regs, ov);<br>+ }<br>  goto arith_done;<br> <br>  case 136: /* subfe */<br>+ case 648: /* subfeo */<br>  add_with_carry(regs, rd, ~regs->gpr[ra], regs->gpr[rb],<br>
-       regs->xer & XER_CA);<br>+       regs->xer & XER_CA, OE(instr));<br>  goto arith_done;<br> <br>  case 138: /* adde */<br>+ case 650: /* addeo */<br>  add_with_carry(regs, rd, regs->gpr[ra], regs->gpr[rb],<br>
-       regs->xer & XER_CA);<br>+       regs->xer & XER_CA, OE(instr));<br>  goto arith_done;<br> <br>  case 200: /* subfze */<br>+ case 712: /* subfzeo */<br>  add_with_carry(regs, rd, ~regs->gpr[ra], 0L,<br>
-       regs->xer & XER_CA);<br>+       regs->xer & XER_CA, OE(instr));<br>  goto arith_done;<br> <br>  case 202: /* addze */<br>+ case 714: /* addzeo */<br>  add_with_carry(regs, rd, regs->gpr[ra], 0L,<br>
-       regs->xer & XER_CA);<br>+       regs->xer & XER_CA, OE(instr));<br>  goto arith_done;<br> <br>  case 232: /* subfme */<br>+ case 744: /* subfmeo */<br>  add_with_carry(regs, rd, ~regs->gpr[ra], -1L,<br>
-       regs->xer & XER_CA);<br>+       regs->xer & XER_CA, OE(instr));<br>  goto arith_done;<br> #ifdef __powerpc64__<br>  case 233: /* mulld */<br>- regs->gpr[rd] = regs->gpr[ra] * regs->gpr[rb];<br>
+ case 745:       /* mulldo */<br>+ {<br>+ unsigned long xer_copy;<br>+ asm("mulldo %0,%2,%3;"<br>+ "mfxer %1" :<br>+ "=r" (regs->gpr[rd]), "=r" (xer_copy) :<br>+ "r" (regs->gpr[ra]), "r" (regs->gpr[rb]));<br>
+ if (OE(instr))<br>+ set_xer_ov(regs, (xer_copy & XER_OV) != 0);<br>  goto arith_done;<br>+ }<br> #endif<br>  case 234: /* addme */<br>+ case 746: /* addme0 */<br>  add_with_carry(regs, rd, regs->gpr[ra], -1L,<br>
-       regs->xer & XER_CA);<br>+       regs->xer & XER_CA, OE(instr));<br>  goto arith_done;<br> <br>  case 235: /* mullw */<br>- regs->gpr[rd] = (unsigned int) regs->gpr[ra] *<br>- (unsigned int) regs->gpr[rb];<br>
+ case 747: /* mullwo */<br>+ regs->gpr[rd] = SGN_EXT_32(regs->gpr[ra]) *<br>+ SGN_EXT_32(regs->gpr[rb]);<br>+ if (OE(instr)) { /* mullwo */<br>+ int ov = 0;<br>+ if ((regs->gpr[rd] & U32_MSK) == 0)<br>+ ov = (regs->gpr[rd] & SGN_32_MSK) != 0;<br>
+ else if ((regs->gpr[rd] & U32_MSK) == U32_MSK)<br>+ ov = (regs->gpr[rd] & SGN_32_MSK) == 0;<br>+ else<br>+ ov = 1;<br>+ set_xer_ov(regs, ov);<br>+ }<br>  goto arith_done;<br> <br>  case 266: /* add */<br>- regs->gpr[rd] = regs->gpr[ra] + regs->gpr[rb];<br>
+ case 778: /* addo */<br>+ val = regs->gpr[ra] + regs->gpr[rb];<br>+ if (OE(instr)) { /* addo */<br>+ unsigned long m = ((regs->msr & MSR_64BIT) == 0)<br>+ ? SGN_32_MSK : SGN_64_MSK;<br>+ int ov = ((regs->gpr[ra] & m) ==<br>
+ (regs->gpr[rb] & m)) &&<br>+ ((regs->gpr[ra] & m) != (val & m));<br>+ set_xer_ov(regs, ov);<br>+ }<br>+ regs->gpr[rd] = val;<br>+ goto arith_done;<br>+#ifdef __powerpc64__<br>+ case 393: /* divdeu */<br>
+ case 905:       /* divdeuo */<br>+ {<br>+ unsigned long xer_copy;<br>+ asm("divdeuo %0,%2,%3;"<br>+ "mfxer %1" :<br>+ "=r" (regs->gpr[rd]), "=r" (xer_copy) :<br>+ "r" (regs->gpr[ra]), "r" (regs->gpr[rb]));<br>
+ if (OE(instr))<br>+ set_xer_ov(regs, (xer_copy & XER_OV) != 0);<br>+ goto arith_done;<br>+ }<br>+#endif<br>+ case 395: /* divweu */<br>+ case 907: /* divweuo */<br>+ val = (L32(regs->gpr[ra]) << 32) / L32(regs->gpr[rb]);<br>
+ if (OE(instr)) {<br>+ int ov = (L32(regs->gpr[rb]) == 0) ||<br>+ ((val & U32_MSK) != 0);<br>+ set_xer_ov(regs, ov);<br>+ }<br>+ regs->gpr[rd] = val;<br>+ goto arith_done;<br>+#ifdef __powerpc64__<br>+ case 425: /* divde */<br>
+ case 937:       /* divdeo */<br>+ {<br>+ unsigned long xer_copy;<br>+ asm("divdeo %0,%2,%3;"<br>+ " mfxer %1" :<br>+ "=r" (regs->gpr[rd]), "=r" (xer_copy) :<br>+ "r" (regs->gpr[ra]), "r" (regs->gpr[rb]));<br>
+ if (OE(instr))<br>+ set_xer_ov(regs, (xer_copy & XER_OV) != 0);<br>+ goto arith_done;<br>+ }<br>+#endif<br>+ case 427: /* divwe */<br>+ case 939: /* divweo */<br>+ val = ((long int)SGN_EXT_32(regs->gpr[ra]) << 32) /<br>
+ (long int)SGN_EXT_32(regs->gpr[rb]);<br>+ if (OE(instr)) {<br>+ int ov = (L32(regs->gpr[rb]) == 0) ||<br>+ ((L32(regs->gpr[ra]) == SGN_32_MSK) &&<br>+ (L32(regs->gpr[rb]) == L32_MSK));<br>+ if ((val & U32_MSK) == 0)<br>
+ ov |= (val & SGN_32_MSK) != 0;<br>+ else if ((val & U32_MSK) == U32_MSK)<br>+ ov |= (val & SGN_32_MSK) == 0;<br>+ else<br>+ ov = 1;<br>+ set_xer_ov(regs, ov);<br>+ }<br>+ regs->gpr[rd] = val;<br>  goto arith_done;<br>
 #ifdef __powerpc64__<br>  case 457: /* divdu */<br>- regs->gpr[rd] = regs->gpr[ra] / regs->gpr[rb];<br>+ case 969: /* divduo */<br>+ val = regs->gpr[ra] / regs->gpr[rb];<br>+ if (OE(instr)) { /* divduo */<br>
+ int ov = (regs->gpr[rb] == 0);<br>+ set_xer_ov(regs, ov);<br>+ }<br>+ regs->gpr[rd] = val;<br>  goto arith_done;<br> #endif<br>  case 459: /* divwu */<br>- regs->gpr[rd] = (unsigned int) regs->gpr[ra] /<br>+ case 971: /* divwuo */<br>
+ val = (unsigned int) regs->gpr[ra] /<br>  (unsigned int) regs->gpr[rb];<br>+ if (OE(instr)) { /* divwuo */<br>+ int ov = (L32(regs->gpr[rb]) == 0);<br>+ set_xer_ov(regs, ov);<br>+ }<br>+ regs->gpr[rd] = val;<br>
  goto arith_done;<br> #ifdef __powerpc64__<br>  case 489: /* divd */<br>- regs->gpr[rd] = (long int) regs->gpr[ra] /<br>+ case 1001: /* divdo */<br>+ val = (long int) regs->gpr[ra] /<br>  (long int) regs->gpr[rb];<br>
+ if (OE(instr)) { /* divdo */<br>+ int ov = (regs->gpr[rb] == 0) ||<br>+ ((regs->gpr[ra] == SGN_64_MSK) &&<br>+ (regs->gpr[rb] == -1ul));<br>+ set_xer_ov(regs, ov);<br>+ }<br>+ regs->gpr[rd] = val;<br>
  goto arith_done;<br> #endif<br>  case 491: /* divw */<br>- regs->gpr[rd] = (int) regs->gpr[ra] /<br>- (int) regs->gpr[rb];<br>+ case 1003: /* divwo */<br>+ val = (long int)SGN_EXT_32(regs->gpr[ra]) /<br>+ (long int)SGN_EXT_32(regs->gpr[rb]);<br>
+ if (OE(instr)) {<br>+ int ov = (L32(regs->gpr[rb]) == 0) ||<br>+ ((L32(regs->gpr[ra]) == SGN_32_MSK) &&<br>+ (L32(regs->gpr[rb]) == L32_MSK));<br>+ set_xer_ov(regs, ov);<br>+ }<br>+ regs->gpr[rd] = val;<br>
  goto arith_done;<br> <br> <br></div>