[PATCH] powerpc: OE=1 Form Instructions Not Decoded Correctly

Tom Musta tommusta at gmail.com
Sat Sep 7 01:13:00 EST 2013


To: linuxppc-dev at lists.ozlabs.org
Subject: [PATCH] powerpc: OE=1 Form Instructions Not Decoded Correctly
From: Tom Musta <tommusta at gmail.com>

PowerISA uses instruction bit 21 to indicate that the overflow (OV) bit
of the XER is to be set, as well as its corresponding sticky bit (SO).
This patch addresses two defects in the implementation of the PowerISA
single step code for this category of instructions:  (a) the OE=1 case
is not correctly accounted for in the case statement for the extended
opcode handling.  (b) the implementation is not setting XER[OV] and
XER[SO].

Signed-off-by: Tom Musta <tommusta at gmail.com>

---
 arch/powerpc/lib/sstep.c |  208
+++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 185 insertions(+), 23 deletions(-)


diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
index d220b88..a4f343d 100644
--- a/arch/powerpc/lib/sstep.c
+++ b/arch/powerpc/lib/sstep.c
@@ -31,6 +31,13 @@ extern char system_call_common[];
 #define XER_OV 0x40000000U
 #define XER_CA 0x20000000U

+#define U32_MSK         0xFFFFFFFF00000000ul
+#define L32_MSK         0x00000000FFFFFFFFul
+#define SGN_32_MSK      0x0000000080000000ul
+#define SGN_64_MSK      0x8000000000000000ul
+#define SGN_EXT_32(x)   (((x) & SGN_32_MSK) ? ((x) | U32_MSK) : ((x) &
L32_MSK))
+#define L32(x)          ((x) & L32_MSK)
+
 #ifdef CONFIG_PPC_FPU
 /*
  * Functions in ldstfp.S
@@ -467,6 +474,13 @@ static int __kprobes do_vsx_store(int rn, int
(*func)(int, unsigned long),
  ".previous" \
  : "=r" (err) \
  : "r" (addr), "i" (-EFAULT), "0" (err))
+static void __kprobes set_xer_ov(struct pt_regs *regs, int ov)
+{
+ if (ov)
+ regs->xer |= (XER_SO | XER_OV);
+ else
+ regs->xer &= ~XER_OV;
+}

 static void __kprobes set_cr0(struct pt_regs *regs, int rd)
 {
@@ -487,7 +501,7 @@ static void __kprobes set_cr0(struct pt_regs *regs, int
rd)

 static void __kprobes add_with_carry(struct pt_regs *regs, int rd,
      unsigned long val1, unsigned long val2,
-     unsigned long carry_in)
+     unsigned long carry_in, int oe)
 {
  unsigned long val = val1 + val2;

@@ -504,6 +518,13 @@ static void __kprobes add_with_carry(struct pt_regs
*regs, int rd,
  regs->xer |= XER_CA;
  else
  regs->xer &= ~XER_CA;
+ if (oe) {
+ unsigned long m = ((regs->msr & MSR_64BIT) == 0) ?
+ SGN_32_MSK : SGN_64_MSK;
+ int ov = ((val1 & m) == (val2 & m)) &&
+ ((val1 & m) != (val & m));
+ set_xer_ov(regs, ov);
+ }
 }

 static void __kprobes do_cmp_signed(struct pt_regs *regs, long v1, long v2,
@@ -552,7 +573,7 @@ static void __kprobes do_cmp_unsigned(struct pt_regs
*regs, unsigned long v1,
 #define DATA32(x) (x)
 #endif
 #define ROTATE(x, n) ((n) ? (((x) << (n)) | ((x) >> (8 * sizeof(long) -
(n)))) : (x))
-
+#define OE(instr)       (((instr) >> (31-21)) & 1)
 /*
  * Emulate instructions that cause a transfer of control,
  * loads and stores, and a few other instructions.
@@ -693,7 +714,7 @@ int __kprobes emulate_step(struct pt_regs *regs,
unsigned int instr)

  case 8: /* subfic */
  imm = (short) instr;
- add_with_carry(regs, rd, ~regs->gpr[ra], imm, 1);
+ add_with_carry(regs, rd, ~regs->gpr[ra], imm, 1, 0);
  goto instr_done;

  case 10: /* cmpli */
@@ -718,12 +739,12 @@ int __kprobes emulate_step(struct pt_regs *regs,
unsigned int instr)

  case 12: /* addic */
  imm = (short) instr;
- add_with_carry(regs, rd, regs->gpr[ra], imm, 0);
+ add_with_carry(regs, rd, regs->gpr[ra], imm, 0, 0);
  goto instr_done;

  case 13: /* addic. */
  imm = (short) instr;
- add_with_carry(regs, rd, regs->gpr[ra], imm, 0);
+ add_with_carry(regs, rd, regs->gpr[ra], imm, 0, 0);
  set_cr0(regs, rd);
  goto instr_done;

@@ -944,8 +965,9 @@ int __kprobes emulate_step(struct pt_regs *regs,
unsigned int instr)
  * Arithmetic instructions
  */
  case 8: /* subfc */
+ case 520: /* subfco */
  add_with_carry(regs, rd, ~regs->gpr[ra],
-       regs->gpr[rb], 1);
+       regs->gpr[rb], 1, OE(instr));
  goto arith_done;
 #ifdef __powerpc64__
  case 9: /* mulhdu */
@@ -954,8 +976,9 @@ int __kprobes emulate_step(struct pt_regs *regs,
unsigned int instr)
  goto arith_done;
 #endif
  case 10: /* addc */
+ case 522:       /* addco */
  add_with_carry(regs, rd, regs->gpr[ra],
-       regs->gpr[rb], 0);
+       regs->gpr[rb], 0, OE(instr));
  goto arith_done;

  case 11: /* mulhwu */
@@ -964,7 +987,19 @@ int __kprobes emulate_step(struct pt_regs *regs,
unsigned int instr)
  goto arith_done;

  case 40: /* subf */
- regs->gpr[rd] = regs->gpr[rb] - regs->gpr[ra];
+ case 552: /* subfo */
+ val = regs->gpr[rb] - regs->gpr[ra];
+ if (OE(instr)) { /* subfo */
+ unsigned long m =
+ ((regs->msr & MSR_64BIT) == 0) ?
+ SGN_32_MSK : SGN_64_MSK;
+ int ov =
+ ((~regs->gpr[ra] & m) ==
+ (regs->gpr[rb] & m)) &&
+ ((regs->gpr[rb] & m) != (val & m));
+ set_xer_ov(regs, ov);
+ }
+ regs->gpr[rd] = val;
  goto arith_done;
 #ifdef __powerpc64__
  case 73: /* mulhd */
@@ -978,69 +1013,196 @@ int __kprobes emulate_step(struct pt_regs *regs,
unsigned int instr)
  goto arith_done;

  case 104: /* neg */
+ case 616: /* nego */
  regs->gpr[rd] = -regs->gpr[ra];
+ if (OE(instr)) { /* nego */
+ int ov = (regs->msr & MSR_64BIT) ?
+ (regs->gpr[rd] == 0x8000000000000000l) :
+ (regs->gpr[rd] == 0x80000000l);
+ set_xer_ov(regs, ov);
+ }
  goto arith_done;

  case 136: /* subfe */
+ case 648: /* subfeo */
  add_with_carry(regs, rd, ~regs->gpr[ra], regs->gpr[rb],
-       regs->xer & XER_CA);
+       regs->xer & XER_CA, OE(instr));
  goto arith_done;

  case 138: /* adde */
+ case 650: /* addeo */
  add_with_carry(regs, rd, regs->gpr[ra], regs->gpr[rb],
-       regs->xer & XER_CA);
+       regs->xer & XER_CA, OE(instr));
  goto arith_done;

  case 200: /* subfze */
+ case 712: /* subfzeo */
  add_with_carry(regs, rd, ~regs->gpr[ra], 0L,
-       regs->xer & XER_CA);
+       regs->xer & XER_CA, OE(instr));
  goto arith_done;

  case 202: /* addze */
+ case 714: /* addzeo */
  add_with_carry(regs, rd, regs->gpr[ra], 0L,
-       regs->xer & XER_CA);
+       regs->xer & XER_CA, OE(instr));
  goto arith_done;

  case 232: /* subfme */
+ case 744: /* subfmeo */
  add_with_carry(regs, rd, ~regs->gpr[ra], -1L,
-       regs->xer & XER_CA);
+       regs->xer & XER_CA, OE(instr));
  goto arith_done;
 #ifdef __powerpc64__
  case 233: /* mulld */
- regs->gpr[rd] = regs->gpr[ra] * regs->gpr[rb];
+ case 745:       /* mulldo */
+ {
+ unsigned long xer_copy;
+ asm("mulldo %0,%2,%3;"
+ "mfxer %1" :
+ "=r" (regs->gpr[rd]), "=r" (xer_copy) :
+ "r" (regs->gpr[ra]), "r" (regs->gpr[rb]));
+ if (OE(instr))
+ set_xer_ov(regs, (xer_copy & XER_OV) != 0);
  goto arith_done;
+ }
 #endif
  case 234: /* addme */
+ case 746: /* addme0 */
  add_with_carry(regs, rd, regs->gpr[ra], -1L,
-       regs->xer & XER_CA);
+       regs->xer & XER_CA, OE(instr));
  goto arith_done;

  case 235: /* mullw */
- regs->gpr[rd] = (unsigned int) regs->gpr[ra] *
- (unsigned int) regs->gpr[rb];
+ case 747: /* mullwo */
+ regs->gpr[rd] = SGN_EXT_32(regs->gpr[ra]) *
+ SGN_EXT_32(regs->gpr[rb]);
+ if (OE(instr)) { /* mullwo */
+ int ov = 0;
+ if ((regs->gpr[rd] & U32_MSK) == 0)
+ ov = (regs->gpr[rd] & SGN_32_MSK) != 0;
+ else if ((regs->gpr[rd] & U32_MSK) == U32_MSK)
+ ov = (regs->gpr[rd] & SGN_32_MSK) == 0;
+ else
+ ov = 1;
+ set_xer_ov(regs, ov);
+ }
  goto arith_done;

  case 266: /* add */
- regs->gpr[rd] = regs->gpr[ra] + regs->gpr[rb];
+ case 778: /* addo */
+ val = regs->gpr[ra] + regs->gpr[rb];
+ if (OE(instr)) { /* addo */
+ unsigned long m = ((regs->msr & MSR_64BIT) == 0)
+ ? SGN_32_MSK : SGN_64_MSK;
+ int ov = ((regs->gpr[ra] & m) ==
+ (regs->gpr[rb] & m)) &&
+ ((regs->gpr[ra] & m) != (val & m));
+ set_xer_ov(regs, ov);
+ }
+ regs->gpr[rd] = val;
+ goto arith_done;
+#ifdef __powerpc64__
+ case 393: /* divdeu */
+ case 905:       /* divdeuo */
+ {
+ unsigned long xer_copy;
+ asm("divdeuo %0,%2,%3;"
+ "mfxer %1" :
+ "=r" (regs->gpr[rd]), "=r" (xer_copy) :
+ "r" (regs->gpr[ra]), "r" (regs->gpr[rb]));
+ if (OE(instr))
+ set_xer_ov(regs, (xer_copy & XER_OV) != 0);
+ goto arith_done;
+ }
+#endif
+ case 395: /* divweu */
+ case 907: /* divweuo */
+ val = (L32(regs->gpr[ra]) << 32) / L32(regs->gpr[rb]);
+ if (OE(instr)) {
+ int ov = (L32(regs->gpr[rb]) == 0) ||
+ ((val & U32_MSK) != 0);
+ set_xer_ov(regs, ov);
+ }
+ regs->gpr[rd] = val;
+ goto arith_done;
+#ifdef __powerpc64__
+ case 425: /* divde */
+ case 937:       /* divdeo */
+ {
+ unsigned long xer_copy;
+ asm("divdeo %0,%2,%3;"
+ " mfxer %1" :
+ "=r" (regs->gpr[rd]), "=r" (xer_copy) :
+ "r" (regs->gpr[ra]), "r" (regs->gpr[rb]));
+ if (OE(instr))
+ set_xer_ov(regs, (xer_copy & XER_OV) != 0);
+ goto arith_done;
+ }
+#endif
+ case 427: /* divwe */
+ case 939: /* divweo */
+ val = ((long int)SGN_EXT_32(regs->gpr[ra]) << 32) /
+ (long int)SGN_EXT_32(regs->gpr[rb]);
+ if (OE(instr)) {
+ int ov = (L32(regs->gpr[rb]) == 0) ||
+ ((L32(regs->gpr[ra]) == SGN_32_MSK) &&
+ (L32(regs->gpr[rb]) == L32_MSK));
+ if ((val & U32_MSK) == 0)
+ ov |= (val & SGN_32_MSK) != 0;
+ else if ((val & U32_MSK) == U32_MSK)
+ ov |= (val & SGN_32_MSK) == 0;
+ else
+ ov = 1;
+ set_xer_ov(regs, ov);
+ }
+ regs->gpr[rd] = val;
  goto arith_done;
 #ifdef __powerpc64__
  case 457: /* divdu */
- regs->gpr[rd] = regs->gpr[ra] / regs->gpr[rb];
+ case 969: /* divduo */
+ val = regs->gpr[ra] / regs->gpr[rb];
+ if (OE(instr)) { /* divduo */
+ int ov = (regs->gpr[rb] == 0);
+ set_xer_ov(regs, ov);
+ }
+ regs->gpr[rd] = val;
  goto arith_done;
 #endif
  case 459: /* divwu */
- regs->gpr[rd] = (unsigned int) regs->gpr[ra] /
+ case 971: /* divwuo */
+ val = (unsigned int) regs->gpr[ra] /
  (unsigned int) regs->gpr[rb];
+ if (OE(instr)) { /* divwuo */
+ int ov = (L32(regs->gpr[rb]) == 0);
+ set_xer_ov(regs, ov);
+ }
+ regs->gpr[rd] = val;
  goto arith_done;
 #ifdef __powerpc64__
  case 489: /* divd */
- regs->gpr[rd] = (long int) regs->gpr[ra] /
+ case 1001: /* divdo */
+ val = (long int) regs->gpr[ra] /
  (long int) regs->gpr[rb];
+ if (OE(instr)) { /* divdo */
+ int ov = (regs->gpr[rb] == 0) ||
+ ((regs->gpr[ra] == SGN_64_MSK) &&
+ (regs->gpr[rb] == -1ul));
+ set_xer_ov(regs, ov);
+ }
+ regs->gpr[rd] = val;
  goto arith_done;
 #endif
  case 491: /* divw */
- regs->gpr[rd] = (int) regs->gpr[ra] /
- (int) regs->gpr[rb];
+ case 1003: /* divwo */
+ val = (long int)SGN_EXT_32(regs->gpr[ra]) /
+ (long int)SGN_EXT_32(regs->gpr[rb]);
+ if (OE(instr)) {
+ int ov = (L32(regs->gpr[rb]) == 0) ||
+ ((L32(regs->gpr[ra]) == SGN_32_MSK) &&
+ (L32(regs->gpr[rb]) == L32_MSK));
+ set_xer_ov(regs, ov);
+ }
+ regs->gpr[rd] = val;
  goto arith_done;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ozlabs.org/pipermail/linuxppc-dev/attachments/20130906/82583601/attachment.html>


More information about the Linuxppc-dev mailing list