From patchwork Fri Sep 6 15:13:00 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Musta X-Patchwork-Id: 273432 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from ozlabs.org (localhost [IPv6:::1]) by ozlabs.org (Postfix) with ESMTP id A28F92C0231 for ; Sun, 8 Sep 2013 21:42:57 +1000 (EST) Received: from mail-pa0-x241.google.com (mail-pa0-x241.google.com [IPv6:2607:f8b0:400e:c03::241]) (using TLSv1 with cipher ECDHE-RSA-RC4-SHA (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority" (not verified)) by ozlabs.org (Postfix) with ESMTPS id F24E52C00EB for ; Sat, 7 Sep 2013 01:13:02 +1000 (EST) Received: by mail-pa0-f65.google.com with SMTP id fb10so1407908pad.0 for ; Fri, 06 Sep 2013 08:13:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:date:message-id:subject:from:to:content-type; bh=7PJOUasSEJHLdypQJTU6U7xWKskUVZDErjVuavTuLqI=; b=pVlonNxcR8G9nYSGir6eAfhmTSQOxweq3XaS4LVD0Cl0/E/nVDbA1d3X7Q1H3OlFYw n7quwY7BABROHGTYXWvUB6CZZyyjWHJP3oEWr+crALOb0Egz0YUybAZ6gZBo5Cn20ewH mnATljKNDN7DB8AEdYzvG0GTB2QBPu426kOSKwRSq6OKIQeROkv4mPnvYaCYPqAr75ks 1LgSu7c4rkpC9HLGP9o6/Nf67jzTcgydeSuoRanaug4JlapaeMS36D+bbVPiZAAjxNTM OLitWg5vTEyViXriHqalAr08TEDGlgj66D9jdn77MW8A/gvSxl8goXAxKDmfDQajLBC9 1gxw== MIME-Version: 1.0 X-Received: by 10.66.158.72 with SMTP id ws8mr4611630pab.39.1378480380741; Fri, 06 Sep 2013 08:13:00 -0700 (PDT) Received: by 10.70.80.34 with HTTP; Fri, 6 Sep 2013 08:13:00 -0700 (PDT) Date: Fri, 6 Sep 2013 10:13:00 -0500 Message-ID: Subject: [PATCH] powerpc: OE=1 Form Instructions Not Decoded Correctly From: Tom Musta To: linuxppc-dev@lists.ozlabs.org X-Mailman-Approved-At: Sun, 08 Sep 2013 21:42:28 +1000 X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.16rc2 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" To: linuxppc-dev@lists.ozlabs.org Subject: [PATCH] powerpc: OE=1 Form Instructions Not Decoded Correctly From: Tom Musta 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 --- arch/powerpc/lib/sstep.c | 208 +++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 185 insertions(+), 23 deletions(-) * 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; 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 /*