From: Julian Seward Date: Sat, 3 May 2014 21:20:56 +0000 (+0000) Subject: ARM64: add support for cache management instructions (VEX side): X-Git-Tag: svn/VALGRIND_3_10_1^2~119 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7e0b163a2ab2f653c16e3fa8c2bc0382917f0486;p=thirdparty%2Fvalgrind.git ARM64: add support for cache management instructions (VEX side): dc cvau, regX ic ivau, regX mrs regX, ctr_el0 Fixes #333228 and #333230. git-svn-id: svn://svn.valgrind.org/vex/trunk@2851 --- diff --git a/VEX/priv/guest_arm64_toIR.c b/VEX/priv/guest_arm64_toIR.c index bb762e07b4..e9ad82af8e 100644 --- a/VEX/priv/guest_arm64_toIR.c +++ b/VEX/priv/guest_arm64_toIR.c @@ -4386,7 +4386,8 @@ Bool dis_ARM64_load_store(/*MB_OUT*/DisResult* dres, UInt insn) /*------------------------------------------------------------*/ static -Bool dis_ARM64_branch_etc(/*MB_OUT*/DisResult* dres, UInt insn) +Bool dis_ARM64_branch_etc(/*MB_OUT*/DisResult* dres, UInt insn, + VexArchInfo* archinfo) { # define INSN(_bMax,_bMin) SLICE_UInt(insn, (_bMax), (_bMin)) @@ -4628,8 +4629,93 @@ Bool dis_ARM64_branch_etc(/*MB_OUT*/DisResult* dres, UInt insn) DIP("mrs %s, dczid_el0 (FAKED)\n", nameIReg64orZR(tt)); return True; } + /* Cases for CTR_EL0 + We just handle reads, and make up a value from the D and I line + sizes in the VexArchInfo we are given, and patch in the following + fields that the Foundation model gives ("natively"): + CWG = 0b0100, ERG = 0b0100, L1Ip = 0b11 + D5 3B 00 001 Rt MRS rT, dczid_el0 + */ + if ((INSN(31,0) & 0xFFFFFFE0) == 0xD53B0020) { + UInt tt = INSN(4,0); + /* Need to generate a value from dMinLine_lg2_szB and + dMinLine_lg2_szB. The value in the register is in 32-bit + units, so need to subtract 2 from the values in the + VexArchInfo. We can assume that the values here are valid -- + disInstr_ARM64 checks them -- so there's no need to deal with + out-of-range cases. */ + vassert(archinfo->arm64_dMinLine_lg2_szB >= 2 + && archinfo->arm64_dMinLine_lg2_szB <= 17 + && archinfo->arm64_iMinLine_lg2_szB >= 2 + && archinfo->arm64_iMinLine_lg2_szB <= 17); + UInt val + = 0x8440c000 | ((0xF & (archinfo->arm64_dMinLine_lg2_szB - 2)) << 16) + | ((0xF & (archinfo->arm64_iMinLine_lg2_szB - 2)) << 0); + putIReg64orZR(tt, mkU64(val)); + DIP("mrs %s, ctr_el0\n", nameIReg64orZR(tt)); + return True; + } + + /* ------------------ IC_IVAU ------------------ */ + /* D5 0B 75 001 Rt ic ivau, rT + */ + if ((INSN(31,0) & 0xFFFFFFE0) == 0xD50B7520) { + /* We will always be provided with a valid iMinLine value. */ + vassert(archinfo->arm64_iMinLine_lg2_szB >= 2 + && archinfo->arm64_iMinLine_lg2_szB <= 17); + /* Round the requested address, in rT, down to the start of the + containing block. */ + UInt tt = INSN(4,0); + ULong lineszB = 1ULL << archinfo->arm64_iMinLine_lg2_szB; + IRTemp addr = newTemp(Ity_I64); + assign( addr, binop( Iop_And64, + getIReg64orZR(tt), + mkU64(~(lineszB - 1))) ); + /* Set the invalidation range, request exit-and-invalidate, with + continuation at the next instruction. */ + stmt(IRStmt_Put(OFFB_TISTART, mkexpr(addr))); + stmt(IRStmt_Put(OFFB_TILEN, mkU64(lineszB))); + /* be paranoid ... */ + stmt( IRStmt_MBE(Imbe_Fence) ); + putPC(mkU64( guest_PC_curr_instr + 4 )); + dres->whatNext = Dis_StopHere; + dres->jk_StopHere = Ijk_TInval; + DIP("ic ivau, %s\n", nameIReg64orZR(tt)); + return True; + } - /* ------------------ ISB, DSB ------------------ */ + /* ------------------ DC_CVAU ------------------ */ + /* D5 0B 7B 001 Rt dc cvau, rT + */ + if ((INSN(31,0) & 0xFFFFFFE0) == 0xD50B7B20) { + /* Exactly the same scheme as for IC IVAU, except we observe the + dMinLine size, and request an Ijk_InvalData instead of + Ijk_TInval. */ + /* We will always be provided with a valid dMinLine value. */ + vassert(archinfo->arm64_dMinLine_lg2_szB >= 2 + && archinfo->arm64_dMinLine_lg2_szB <= 17); + /* Round the requested address, in rT, down to the start of the + containing block. */ + UInt tt = INSN(4,0); + ULong lineszB = 1ULL << archinfo->arm64_dMinLine_lg2_szB; + IRTemp addr = newTemp(Ity_I64); + assign( addr, binop( Iop_And64, + getIReg64orZR(tt), + mkU64(~(lineszB - 1))) ); + /* Set the flush range, request exit-and-flush, with + continuation at the next instruction. */ + stmt(IRStmt_Put(OFFB_TISTART, mkexpr(addr))); + stmt(IRStmt_Put(OFFB_TILEN, mkU64(lineszB))); + /* be paranoid ... */ + stmt( IRStmt_MBE(Imbe_Fence) ); + putPC(mkU64( guest_PC_curr_instr + 4 )); + dres->whatNext = Dis_StopHere; + dres->jk_StopHere = Ijk_FlushDCache; + DIP("dc cvau, %s\n", nameIReg64orZR(tt)); + return True; + } + + /* ------------------ ISB, DMB, DSB ------------------ */ if (INSN(31,0) == 0xD5033FDF) { stmt(IRStmt_MBE(Imbe_Fence)); DIP("isb\n"); @@ -4640,6 +4726,11 @@ Bool dis_ARM64_branch_etc(/*MB_OUT*/DisResult* dres, UInt insn) DIP("dmb ish\n"); return True; } + if (INSN(31,0) == 0xD5033B9F) { + stmt(IRStmt_MBE(Imbe_Fence)); + DIP("dsb ish\n"); + return True; + } /* -------------------- NOP -------------------- */ if (INSN(31,0) == 0xD503201F) { @@ -7161,7 +7252,7 @@ Bool disInstr_ARM64_WRK ( break; case BITS4(1,0,1,0): case BITS4(1,0,1,1): // Branch, exception generation and system instructions - ok = dis_ARM64_branch_etc(dres, insn); + ok = dis_ARM64_branch_etc(dres, insn, archinfo); break; case BITS4(0,1,0,0): case BITS4(0,1,1,0): case BITS4(1,1,0,0): case BITS4(1,1,1,0): @@ -7229,6 +7320,11 @@ DisResult disInstr_ARM64 ( IRSB* irsb_IN, host_is_bigendian = host_bigendian_IN; guest_PC_curr_instr = (Addr64)guest_IP; + /* Sanity checks */ + /* (x::UInt - 2) <= 15 === x >= 2 && x <= 17 (I hope) */ + vassert((archinfo->arm64_dMinLine_lg2_szB - 2) <= 15); + vassert((archinfo->arm64_iMinLine_lg2_szB - 2) <= 15); + /* Try to decode */ Bool ok = disInstr_ARM64_WRK( &dres, resteerOkFn, resteerCisOk, callback_opaque, diff --git a/VEX/priv/host_arm64_defs.c b/VEX/priv/host_arm64_defs.c index bab569cb4c..51f35b3ca4 100644 --- a/VEX/priv/host_arm64_defs.c +++ b/VEX/priv/host_arm64_defs.c @@ -4461,7 +4461,8 @@ Int emit_ARM64Instr ( /*MB_MOD*/Bool* is_profInc, //case Ijk_EmWarn: trcval = VEX_TRC_JMP_EMWARN; break; //case Ijk_MapFail: trcval = VEX_TRC_JMP_MAPFAIL; break; case Ijk_NoDecode: trcval = VEX_TRC_JMP_NODECODE; break; - //case Ijk_TInval: trcval = VEX_TRC_JMP_TINVAL; break; + case Ijk_TInval: trcval = VEX_TRC_JMP_TINVAL; break; + case Ijk_FlushDCache: trcval = VEX_TRC_JMP_FLUSHDCACHE; break; case Ijk_NoRedir: trcval = VEX_TRC_JMP_NOREDIR; break; //case Ijk_SigTRAP: trcval = VEX_TRC_JMP_SIGTRAP; break; //case Ijk_SigSEGV: trcval = VEX_TRC_JMP_SIGSEGV; break; diff --git a/VEX/priv/host_arm64_isel.c b/VEX/priv/host_arm64_isel.c index e9e5c1c226..8e9e4f556f 100644 --- a/VEX/priv/host_arm64_isel.c +++ b/VEX/priv/host_arm64_isel.c @@ -6915,7 +6915,8 @@ static void iselNext ( ISelEnv* env, case Ijk_NoDecode: case Ijk_NoRedir: case Ijk_Sys_syscall: -//ZZ case Ijk_TInval: + case Ijk_TInval: + case Ijk_FlushDCache: //ZZ case Ijk_Yield: { HReg r = iselIntExpr_R(env, next); diff --git a/VEX/priv/ir_defs.c b/VEX/priv/ir_defs.c index f37a161d11..8489d220ab 100644 --- a/VEX/priv/ir_defs.c +++ b/VEX/priv/ir_defs.c @@ -1413,6 +1413,7 @@ void ppIRJumpKind ( IRJumpKind kind ) case Ijk_NoDecode: vex_printf("NoDecode"); break; case Ijk_MapFail: vex_printf("MapFail"); break; case Ijk_TInval: vex_printf("Invalidate"); break; + case Ijk_FlushDCache: vex_printf("FlushDCache"); break; case Ijk_NoRedir: vex_printf("NoRedir"); break; case Ijk_SigILL: vex_printf("SigILL"); break; case Ijk_SigTRAP: vex_printf("SigTRAP"); break; diff --git a/VEX/priv/main_main.c b/VEX/priv/main_main.c index 93ca478cce..ebb8d41af9 100644 --- a/VEX/priv/main_main.c +++ b/VEX/priv/main_main.c @@ -92,6 +92,7 @@ static Int sdiv32 ( Int x, Int y ) { return x/y; } void LibVEX_default_VexControl ( /*OUT*/ VexControl* vcon ) { + vex_bzero(vcon, sizeof(*vcon)); vcon->iropt_verbosity = 0; vcon->iropt_level = 2; vcon->iropt_register_updates = VexRegUpdUnwindregsAtMemAccess; @@ -1225,20 +1226,23 @@ const HChar* LibVEX_ppVexHwCaps ( VexArch arch, UInt hwcaps ) /* Write default settings info *vai. */ void LibVEX_default_VexArchInfo ( /*OUT*/VexArchInfo* vai ) { + vex_bzero(vai, sizeof(*vai)); vai->hwcaps = 0; vai->ppc_icache_line_szB = 0; vai->ppc_dcbz_szB = 0; vai->ppc_dcbzl_szB = 0; - + vai->arm64_dMinLine_lg2_szB = 0; + vai->arm64_iMinLine_lg2_szB = 0; vai->hwcache_info.num_levels = 0; vai->hwcache_info.num_caches = 0; - vai->hwcache_info.caches = NULL; + vai->hwcache_info.caches = NULL; vai->hwcache_info.icaches_maintain_coherence = True; // whatever } /* Write default settings info *vbi. */ void LibVEX_default_VexAbiInfo ( /*OUT*/VexAbiInfo* vbi ) { + vex_bzero(vbi, sizeof(*vbi)); vbi->guest_stack_redzone_size = 0; vbi->guest_amd64_assume_fs_is_zero = False; vbi->guest_amd64_assume_gs_is_0x60 = False; diff --git a/VEX/pub/libvex.h b/VEX/pub/libvex.h index 2d408332ee..6da52ac864 100644 --- a/VEX/pub/libvex.h +++ b/VEX/pub/libvex.h @@ -276,9 +276,14 @@ typedef /* PPC32/PPC64 only: size of instruction cache line */ Int ppc_icache_line_szB; /* PPC32/PPC64 only: sizes zeroed by the dcbz/dcbzl instructions - * (bug#135264) */ + (bug#135264) */ UInt ppc_dcbz_szB; UInt ppc_dcbzl_szB; /* 0 means unsupported (SIGILL) */ + /* ARM64: I- and D- minimum line sizes in log2(bytes), as + obtained from ctr_el0.DminLine and .IminLine. For example, a + line size of 64 bytes would be encoded here as 6. */ + UInt arm64_dMinLine_lg2_szB; + UInt arm64_iMinLine_lg2_szB; } VexArchInfo; diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index 7acd298128..d590802441 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -2082,6 +2082,10 @@ extern Bool eqIRAtom ( IRExpr*, IRExpr* ); ensure that these are filled in with suitable values before issuing a jump of kind Ijk_TInval. + Ijk_TInval requests invalidation of translations taken from the + requested range. Ijk_FlushDCache requests flushing of the D cache + for the specified range. + Re Ijk_EmWarn and Ijk_EmFail: the guest state must have a pseudo-register guest_EMNOTE, which is 32-bits regardless of the host or guest word size. That register should be made to hold a @@ -2109,7 +2113,8 @@ typedef Ijk_EmFail, /* emulation critical (FATAL) error; give up */ Ijk_NoDecode, /* current instruction cannot be decoded */ Ijk_MapFail, /* Vex-provided address translation failed */ - Ijk_TInval, /* Invalidate translations before continuing. */ + Ijk_TInval, /* Inval icache to PoU in [TISTART, +TILEN) */ + Ijk_FlushDCache, /* Clean dcache to PoU in [TISTART, +TILEN) */ Ijk_NoRedir, /* Jump to un-redirected guest addr */ Ijk_SigILL, /* current instruction synths SIGILL */ Ijk_SigTRAP, /* current instruction synths SIGTRAP */ diff --git a/VEX/pub/libvex_trc_values.h b/VEX/pub/libvex_trc_values.h index 58a8e3006e..5738ec1b4b 100644 --- a/VEX/pub/libvex_trc_values.h +++ b/VEX/pub/libvex_trc_values.h @@ -46,15 +46,12 @@ These values should be 61 or above so as not to conflict with Valgrind's VG_TRC_ values, which are 60 or below. - - These values *must* be odd (have bit 0 set) because the dispatchers - (coregrind/m_dispatch/dispatch-*-*.S) use this fact to distinguish - a TRC value from the unchanged baseblock pointer -- which has 0 as - its lowest bit. */ #define VEX_TRC_JMP_TINVAL 61 /* invalidate translations before continuing */ +#define VEX_TRC_JMP_FLUSHDCACHE 103 /* flush dcache before continuing */ + #define VEX_TRC_JMP_NOREDIR 81 /* jump to undirected guest addr */ #define VEX_TRC_JMP_SIGTRAP 85 /* deliver trap (SIGTRAP) before continuing */