From fa4d21af44861e504a398412c9a120b28d6ceb4e Mon Sep 17 00:00:00 2001 From: Carl Love Date: Thu, 27 May 2021 11:54:22 -0500 Subject: [PATCH] PPC64: Add support for copy, cpabort, paste instructions --- NEWS | 2 + VEX/priv/guest_ppc_defs.h | 6 + VEX/priv/guest_ppc_helpers.c | 29 ++++ VEX/priv/guest_ppc_toIR.c | 160 ++++++++++++++++++++ none/tests/ppc64/Makefile.am | 10 +- none/tests/ppc64/test_copy_paste.c | 110 ++++++++++++++ none/tests/ppc64/test_copy_paste.stderr.exp | 2 + none/tests/ppc64/test_copy_paste.stdout.exp | 1 + none/tests/ppc64/test_copy_paste.vgtest | 2 + 9 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 none/tests/ppc64/test_copy_paste.c create mode 100644 none/tests/ppc64/test_copy_paste.stderr.exp create mode 100644 none/tests/ppc64/test_copy_paste.stdout.exp create mode 100644 none/tests/ppc64/test_copy_paste.vgtest diff --git a/NEWS b/NEWS index 4cf932d303..c109b765dd 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,7 @@ support for X86/macOS 10.13, AMD64/macOS 10.13 and nanoMIPS/Linux. - ISA 3.1 support is now complete - ISA 3.0 support for the darn instruction added. - ISA 3.0 support for the vector system call instruction scv added. + - ISA 3.0 support for the copy, paste and cpabort instructions added. * ==================== TOOL CHANGES ==================== @@ -40,6 +41,7 @@ are not entered into bugzilla tend to get forgotten about or ignored. 434840 PPC64 darn instruction not supported 434296 s390x: False-positive memcheck diagnostics from vector string instructions +435665 PPC ISA 3.0 copy, paste, cpabort instructions are not supported 435908 valgrind tries to fetch from deubginfod for files which already have debug information diff --git a/VEX/priv/guest_ppc_defs.h b/VEX/priv/guest_ppc_defs.h index 7c50f85819..93ada1ba98 100644 --- a/VEX/priv/guest_ppc_defs.h +++ b/VEX/priv/guest_ppc_defs.h @@ -219,6 +219,11 @@ extern void vector_gen_pvc_dword_mask_dirty_helper( VexGuestPPC64State* gst, #define XVF64GERNP 0b01111010 #define XVF64GERNN 0b11111010 +#define INVALD_INST 1 +#define COPY_INST 2 +#define PASTE_INST 3 +#define CPABORT_INST 4 + /* --- DIRTY HELPERS --- */ extern ULong ppcg_dirtyhelper_MFTB ( void ); @@ -273,6 +278,7 @@ extern void vsx_matrix_64bit_float_ger_dirty_helper( VexGuestPPC64State* gst, ULong srcY_lo, UInt masks_inst ); extern ULong darn_dirty_helper ( UInt L ); +extern UInt copy_paste_abort_dirty_helper(UInt addr, UInt op); #endif /* ndef __VEX_GUEST_PPC_DEFS_H */ diff --git a/VEX/priv/guest_ppc_helpers.c b/VEX/priv/guest_ppc_helpers.c index c755739989..6c8191a474 100644 --- a/VEX/priv/guest_ppc_helpers.c +++ b/VEX/priv/guest_ppc_helpers.c @@ -3163,6 +3163,35 @@ VexGuestLayout } }; +UInt copy_paste_abort_dirty_helper(UInt addr, UInt op) { +# if defined(__powerpc__) + ULong ret; + UInt cr; + + if (op == COPY_INST) + __asm__ __volatile__ ("copy 0,%0" :: "r" (addr)); + + else if (op == PASTE_INST) + __asm__ __volatile__ ("paste. 0,%0" :: "r" (addr)); + + else if (op == CPABORT_INST) + __asm__ __volatile__ ("cpabort"); + + else + /* Unknown operation */ + vassert(0); + + /* Return the CR0 value. Contains status for the paste instruction. */ + __asm__ __volatile__ ("mfocrf %0,128" : "=r" (cr)); + __asm__ __volatile__ ("srawi %0,%1,28" : "=r" (ret) : "r" (cr)); + /* Make sure the upper bits of the return value are zero per the hack + described in function dis_copy_paste(). */ + return 0xFF & ret; +# else + return 0; +# endif +} + /*---------------------------------------------------------------*/ /*--- end guest_ppc_helpers.c ---*/ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/guest_ppc_toIR.c b/VEX/priv/guest_ppc_toIR.c index 1ed6f8d6f7..f223fe9456 100644 --- a/VEX/priv/guest_ppc_toIR.c +++ b/VEX/priv/guest_ppc_toIR.c @@ -35603,6 +35603,151 @@ static Bool dis_vector_generate_pvc_from_mask ( UInt prefix, return True; } +static Int dis_copy_paste ( UInt prefix, UInt theInstr, + const VexAbiInfo* vbi ) +{ + IRType ty = mode64 ? Ity_I64 : Ity_I32; + Bool L = IFIELD( theInstr, 21, 1 ); + UInt bit0 = IFIELD( theInstr, 0, 1 ); + UInt opc2 = ifieldOPClo10( theInstr ); + UChar rA_addr = ifieldRegA(theInstr); + UChar rB_addr = ifieldRegB(theInstr); + IRTemp cr0 = newTemp( Ity_I8 ); + UInt operation = INVALD_INST; + IRTemp EA_base = newTemp(ty); + IRExpr** args; + IRDirty* d; + UInt mFx = Ifx_None; + IRTemp helper_rtn = newTemp(Ity_I32); + + /* There is no prefixed version of these instructions. */ + PREFIX_CHECK + + assign( EA_base, ea_rAor0_idxd(rA_addr, rB_addr) ); + + if (ty != Ity_I64) { + vpanic( "ERROR PPC: copy, paste, cpabort only supported on 64-bit systems"); + return False; + } + + /* The dirty helper is passed the EA_bse for the 128-byte buffer and + and operation, i.e. which instruction to issue on the host. It returns + uint32_t result. The result is condition code CR0. Only for the paste + instruction is the return value relevant and must be used to update the + guest state. */ + + if (( opc2 == 0x306 ) && ( L == 1 )) { // copy + DIP("copy %u,%u\n", rA_addr, rB_addr); + operation = COPY_INST; + mFx = Ifx_Read; + + } else if ( opc2 == 0x346 ) { // cpabort + DIP("cpabort\n"); + operation = CPABORT_INST; + /* Abort data transfer if one is in progress. */ + /* cpabort does nothing to the guest state, just resets operation + on the host. */ + + } else if (( opc2 == 0x386 ) && ( bit0 == 1 )) { // paste. + + /* The Ifx_write will cause Memcheck will instrument the buffer, if + there is any undefinedness in the inputs, then all of the outputs + will be undefined. Hence: + + if EA_base or operation contain any undefined bits + + then the return value is undefined and the specified 128-byte + memory area are undefined after the call + + else the return value is undefined and the specified 128-byte + memory area are defined after the call */ + DIP("paste %u,%u\n", rA_addr, rB_addr); + operation = PASTE_INST; + mFx = Ifx_Write; + + } else { + /* Unknown instruction, should never get here. */ + return False; + } + + /* Call dirty helper to issue the copy, paste or cpabort instruction on the + host. */ + args = mkIRExprVec_2( mkexpr(EA_base), mkU32(operation) ); + + /* The dirty helper needs to return the 8-bit condition code result from + the copy/paste instructions run on the host. The follwoing hack is used + to get Memcheck to return an error if any of the bits in the 128-byte + copy-paste buffer are uninitialized. The bottom 8-bits of helper_rtn + contain the condition code CR0. The upper bits must all be zero. */ + + d = unsafeIRDirty_1_N ( + helper_rtn, + 0/*regparms*/, + "copy_paste_abort_dirty_helper", + fnptr_to_fnentry( vbi, ©_paste_abort_dirty_helper ), + args ); + + /* As part of the hack, we must set mFx/mAddr/mSize so as to declare the + memory area used by the copy/paste instructions. */ + d->mAddr = NULL; + + if (mFx != Ifx_None) { + d->mFx = mFx; + d->mAddr = mkexpr(EA_base); + d->mSize = 128; /* 128 byte memory region */ + } + + stmt( IRStmt_Dirty(d) ); + + /* The following Exit state is inserted with a test that the IR + optimization cannot remove. */ + stmt( IRStmt_Exit( + binop(Iop_CmpNE32, binop( Iop_And32, mkexpr(helper_rtn), + mkU32(0xFF00)), + mkU32(0)), + Ijk_SigTRAP, + mode64 ? IRConst_U64(guest_CIA_curr_instr) : + IRConst_U32((UInt) guest_CIA_curr_instr), + OFFB_CIA + )); + /* The effects of this hack are as follows: + + (1) the above stmt() asks the IR to exit, asking Valgrind to hand + the program a SIGTRAP at this point, if the fake return value is + nonzero, however .. + + (2) .. that never happens, because the actual return value is maked + out and the upper bits of the return are always zero. + + (3) Memcheck will believe that any undefinedness in the copy/paste + area read by the helper will be propagated through to the helper_rtn + value, and will generate instrumentation to cause that to happen. + + (4) Memcheck will instrument the IRStmt_Exit to check the definedness + computed by (3) and emit an error if helper_rtn value contains any + undefined bits. Hence Memcheck will generate a warning for undefined + bits in the copy/paste buffer. + + (5) Note that the IR optimisation passes do not know what value the + helper call will return. Hence we are guaranteed that they can't + optimise away the IRStmt_Exit and its associated check. */ + + /* Need to extract the actual return value and put it into the guest + state. */ + assign( cr0, unop(Iop_16to8, + unop(Iop_32to16, mkexpr(helper_rtn)))); + + if (( opc2 == 0x386 ) && (bit0 == 1 )) { + /* Only the paste instruction sets CR0. + Update CR0 bits [3:1] with the copy/paste result with the host CR0 + result value. CR0 bit 0 must match the guest XER_OV value. */ + putCR0 ( 0, binop(Iop_And8, mkU8( 1 ), getXER_OV() ) ); + putCR321( 0, binop(Iop_And8, mkU8( 0xE ), mkexpr(cr0) ) ); + } + + return True; +} + static Int dis_nop_prefix ( UInt prefix, UInt theInstr ) { Bool is_prefix = prefix_instruction( prefix ); @@ -37533,6 +37678,21 @@ DisResult disInstr_PPC_WRK ( if (dis_int_logic( prefix, theInstr )) goto decode_success; goto decode_failure; + case 0x306: // copy + if ( !mode64 || !allow_isa_3_0 ) goto decode_failure; + if (dis_copy_paste( prefix, theInstr, abiinfo )) goto decode_success; + goto decode_failure; + + case 0x346: // cpabort + if ( !mode64 || !allow_isa_3_0 ) goto decode_failure; + if (dis_copy_paste( prefix, theInstr, abiinfo )) goto decode_success; + goto decode_failure; + + case 0x386: // paste. + if ( !mode64 || !allow_isa_3_0 ) goto decode_failure; + if (dis_copy_paste( prefix, theInstr, abiinfo )) goto decode_success; + goto decode_failure; + default: /* Deal with some other cases that we would otherwise have punted on. */ diff --git a/none/tests/ppc64/Makefile.am b/none/tests/ppc64/Makefile.am index 1636613345..5c2ee87d58 100644 --- a/none/tests/ppc64/Makefile.am +++ b/none/tests/ppc64/Makefile.am @@ -59,7 +59,10 @@ EXTRA_DIST = \ subnormal_test.stderr.exp subnormal_test.stdout.exp \ subnormal_test.vgtest test_darn_inst.stderr.exp \ test_darn_inst.stdout.exp test_darn_inst.vgtest \ - scv_test.stderr.exp scv_test.stdout.exp scv_test.vgtest + scv_test.stderr.exp scv_test.stdout.exp scv_test.vgtest \ + test_copy_paste.stderr.exp test_copy_paste.stdout.exp \ + test_copy_paste.vgtest + check_PROGRAMS = \ allexec \ @@ -70,7 +73,7 @@ check_PROGRAMS = \ test_isa_3_0 test_mod_instructions \ test_isa_3_1_RT test_isa_3_1_XT test_isa_3_1_VRT \ test_isa_3_1_Misc test_isa_3_1_AT \ - subnormal_test test_darn_inst \ + subnormal_test test_darn_inst test_copy_paste \ test_tm test_touch_tm data-cache-instructions \ power6_mf_gpr std_reg_imm \ twi_tdi tw_td power6_bcmp scv_test @@ -204,6 +207,9 @@ test_isa_3_1_AT_CFLAGS = $(test_isa_3_1_CFLAGS) subnormal_test_CFLAGS = $(AM_CFLAGS) -Winline -Wall -O -g -mregnames $(VSX_FLAG) $(ISA_2_06_FLAG) \ @FLAG_M64@ $(ALTIVEC_FLAG) $(BUILD_FLAG_VSX) $(BUILD_FLAGS_ISA_2_06) +test_copy_paste_CFLAGS = $(AM_CFLAGS) -Winline -Wall -O -g -mregnames $(VSX_FLAG) $(ISA_3_1_FLAG) \ + @FLAG_M64@ $(ALTIVEC_FLAG) $(BUILD_FLAG_VSX) $(BUILD_FLAGS_ISA_3_1) + test_isa_2_06_part3_LDADD = -lm test_dfp1_LDADD = -lm test_dfp2_LDADD = -lm diff --git a/none/tests/ppc64/test_copy_paste.c b/none/tests/ppc64/test_copy_paste.c new file mode 100644 index 0000000000..0f4659ed88 --- /dev/null +++ b/none/tests/ppc64/test_copy_paste.c @@ -0,0 +1,110 @@ +/* The copy, paste, cpabort are ISA 3.0 instructions. However, the memory + to memory copy is only supported on ISA 3.1 era machines. */ + +#include +#include +#include +#include +#include +#include + +/* return CR0 in least significant bits */ +#define GET_CR0(_lval) \ + __asm__ __volatile__ ("mfcr %0" : "=b"(_lval) ); \ + __asm__ __volatile__ ("srawi %0,%1,28" : "=r" (_lval) : "r" (_lval)); + +void test_copy (uint8_t *reg) +{ + __asm__ __volatile__ ("copy 0,%0" : : "r" (reg)); +} + +void test_paste (uint8_t *reg) +{ + __asm__ __volatile__ ("paste. 0,%0" : : "r" (reg)); +} + +void test_cpabort (void) +{ + __asm__ __volatile__ ("cpabort"); +} + +#define NUM_ELEMENTS 128 +#define SUCCESS 1 +#define FAILURE 2 +#define DEBUG 0 +#define PASTE_ERROR 0 + +int main() +{ + int i; + unsigned int cc_value; + int result = SUCCESS; + + /* buffers must be 128 bytes long and aligned to 128 bytes */ + uint8_t src_buffer[NUM_ELEMENTS] __attribute__ ((aligned (128))); + uint8_t dst_buffer[NUM_ELEMENTS] __attribute__ ((aligned (128))); + + for (i=0; i