/none/tests/s390x/fixbr
/none/tests/s390x/popcnt
/none/tests/s390x/vector
+/none/tests/s390x/lsc2
+/none/tests/s390x/ppno
# /none/tests/scripts/
/none/tests/scripts/*.dSYM
ULong s390_do_cvd(ULong binary);
ULong s390_do_ecag(ULong op2addr);
UInt s390_do_pfpo(UInt gpr0);
-
+void s390x_dirtyhelper_PPNO_query(VexGuestS390XState *guest_state, ULong r1, ULong r2);
+ULong s390x_dirtyhelper_PPNO_sha512(VexGuestS390XState *guest_state, ULong r1, ULong r2);
+void s390x_dirtyhelper_PPNO_sha512_load_param_block( void );
/* The various ways to compute the condition code. */
enum {
S390_CC_OP_BITWISE = 0,
s390_set_facility_bit(addr, S390_FAC_GIE, 1);
s390_set_facility_bit(addr, S390_FAC_EXEXT, 1);
s390_set_facility_bit(addr, S390_FAC_HIGHW, 1);
+ s390_set_facility_bit(addr, S390_FAC_LSC2, 1);
s390_set_facility_bit(addr, S390_FAC_HFPMAS, 0);
s390_set_facility_bit(addr, S390_FAC_HFPUNX, 0);
#endif
+/*-----------------------------------------------------------------*/
+/*--- Dirty helper for Perform Pseudorandom number instruction ---*/
+/*-----------------------------------------------------------------*/
+
+/* Dummy helper that is needed to indicate load of parameter block.
+ We have to use it because dirty helper cannot have two memory side
+ effects.
+ */
+void s390x_dirtyhelper_PPNO_sha512_load_param_block( void )
+{
+}
+
+#if defined(VGA_s390x)
+
+/* IMPORTANT!
+ We return here bit mask where only supported functions are set to one.
+ If you implement new functions don't forget the supported array.
+ */
+void
+s390x_dirtyhelper_PPNO_query(VexGuestS390XState *guest_state, ULong r1, ULong r2)
+{
+ ULong supported[2] = {0x9000000000000000ULL, 0x0000000000000000ULL};
+ ULong *result = (ULong*) guest_state->guest_r1;
+
+ result[0] = supported[0];
+ result[1] = supported[1];
+}
+
+ULong
+s390x_dirtyhelper_PPNO_sha512(VexGuestS390XState *guest_state, ULong r1, ULong r2)
+{
+ ULong* op1 = (ULong*) (((ULong)(&guest_state->guest_r0)) + r1 * sizeof(ULong));
+ ULong* op2 = (ULong*) (((ULong)(&guest_state->guest_r0)) + r2 * sizeof(ULong));
+
+ register ULong reg0 asm("0") = guest_state->guest_r0;
+ register ULong reg1 asm("1") = guest_state->guest_r1;
+ register ULong reg2 asm("2") = op1[0];
+ register ULong reg3 asm("3") = op1[1];
+ register ULong reg4 asm("4") = op2[0];
+ register ULong reg5 asm("5") = op2[1];
+
+ ULong cc = 0;
+ asm volatile(".insn rre, 0xb93c0000, %%r2, %%r4\n"
+ "ipm %[cc]\n"
+ "srl %[cc], 28\n"
+ : "+d"(reg0), "+d"(reg1),
+ "+d"(reg2), "+d"(reg3),
+ "+d"(reg4), "+d"(reg5),
+ [cc] "=d"(cc)
+ :
+ : "cc", "memory");
+
+ return cc;
+}
+
+#else
+
+void
+s390x_dirtyhelper_PPNO_query(VexGuestS390XState *guest_state, ULong r1, ULong r2)
+{
+}
+
+ULong
+s390x_dirtyhelper_PPNO_sha512(VexGuestS390XState *guest_state, ULong r1, ULong r2)
+{
+ return 0;
+}
+
+#endif /* VGA_s390x */
/*---------------------------------------------------------------*/
/*--- end guest_s390_helpers.c ---*/
/*---------------------------------------------------------------*/
(Int)(Char)i2, m3, (Int)(Short)i4);
}
+static void
+s390_format_RIE_RUPIX(const HChar *(*irgen)(UChar r1, UChar m3, UShort i4,
+ UChar i2),
+ UChar r1, UChar m3, UShort i4, UChar i2, Int xmnm_kind)
+{
+ const HChar *mnm = irgen(r1, m3, i4, i2);
+
+ if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
+ s390_disasm(ENC5(XMNM, GPR, INT, CABM, PCREL), xmnm_kind, mnm, m3, r1,
+ (Int)(Char)i2, m3, (Int)(Short)i4);
+}
+
static void
s390_format_RIL(const HChar *(*irgen)(UChar r1, UInt i2),
UChar r1, UInt i2)
s390_disasm(ENC3(MNM, FPR, UDXB), mnm, r1, d2, x2, b2);
}
+static void
+s390_format_RXE_RRRDR(const HChar *(*irgen)(UChar r1, IRTemp op2addr, UChar m3),
+ UChar r1, UChar x2, UChar b2, UShort d2, UChar m3)
+{
+ const HChar *mnm;
+ IRTemp op2addr = newTemp(Ity_I64);
+
+ assign(op2addr, binop(Iop_Add64, binop(Iop_Add64, mkU64(d2),
+ b2 != 0 ? get_gpr_dw0(b2) : mkU64(0)), x2 != 0 ? get_gpr_dw0(x2) :
+ mkU64(0)));
+
+ mnm = irgen(r1, op2addr, m3);
+
+ if (UNLIKELY(vex_traceflags & VEX_TRACE_FE))
+ s390_disasm(ENC3(MNM, GPR, UDXB), mnm, r1, d2, x2, b2);
+}
+
static void
s390_format_RXF_FRRDF(const HChar *(*irgen)(UChar, IRTemp, UChar),
UChar r3, UChar x2, UChar b2, UShort d2, UChar r1)
return "vno";
}
+static const HChar *
+s390_irgen_LZRF(UChar r1, IRTemp op2addr)
+{
+ IRTemp op2 = newTemp(Ity_I32);
+
+ assign(op2, binop(Iop_And32, load(Ity_I32, mkexpr(op2addr)), mkU32(0xffffff00)));
+ put_gpr_w1(r1, mkexpr(op2));
+
+ return "lzrf";
+}
+
+static const HChar *
+s390_irgen_LZRG(UChar r1, IRTemp op2addr)
+{
+ IRTemp op2 = newTemp(Ity_I64);
+
+ assign(op2, binop(Iop_And64, load(Ity_I64, mkexpr(op2addr)), mkU64(0xffffffffffffff00UL)));
+ put_gpr_dw0(r1, mkexpr(op2));
+
+ return "lzrg";
+}
+
+static const HChar *
+s390_irgen_LLZRGF(UChar r1, IRTemp op2addr)
+{
+ IRTemp op2 = newTemp(Ity_I32);
+
+ assign(op2, binop(Iop_And32, load(Ity_I32, mkexpr(op2addr)), mkU32(0xffffff00)));
+ put_gpr_w1(r1, mkexpr(op2));
+ put_gpr_w0(r1, mkU32(0));
+
+ return "llzrgf";
+}
+
+static const HChar *
+s390_irgen_LOCFH(UChar r1, IRTemp op2addr)
+{
+ /* condition is checked in format handler */
+ put_gpr_w0(r1, load(Ity_I32, mkexpr(op2addr)));
+
+ return "locfh";
+}
+
+static const HChar *
+s390_irgen_LOCFHR(UChar m3, UChar r1, UChar r2)
+{
+ next_insn_if(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0)));
+ put_gpr_w0(r1, get_gpr_w0(r2));
+
+ return "locfhr";
+}
+
+static const HChar *
+s390_irgen_LOCHHI(UChar r1, UChar m3, UShort i2, UChar unused)
+{
+ next_insn_if(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0)));
+ put_gpr_w0(r1, mkU32(i2));
+
+ return "lochhi";
+}
+
+static const HChar *
+s390_irgen_LOCHI(UChar r1, UChar m3, UShort i2, UChar unused)
+{
+ next_insn_if(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0)));
+ put_gpr_w1(r1, mkU32(i2));
+
+ return "lochi";
+}
+
+static const HChar *
+s390_irgen_LOCGHI(UChar r1, UChar m3, UShort i2, UChar unused)
+{
+ next_insn_if(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0)));
+ put_gpr_dw0(r1, mkU64(i2));
+
+ return "locghi";
+}
+
+static const HChar *
+s390_irgen_STOCFH(UChar r1, IRTemp op2addr)
+{
+ /* condition is checked in format handler */
+ store(mkexpr(op2addr), get_gpr_w1(r1));
+
+ return "stocfh";
+}
+
+static const HChar *
+s390_irgen_LCBB(UChar r1, IRTemp op2addr, UChar m3)
+{
+ IRTemp op2 = newTemp(Ity_I32);
+ assign(op2, s390_getCountToBlockBoundary(op2addr, m3));
+ put_gpr_w1(r1, mkexpr(op2));
+
+ IRExpr* cc = mkite(binop(Iop_CmpEQ32, mkexpr(op2), mkU32(16)), mkU64(0), mkU64(3));
+ s390_cc_thunk_fill(mkU64(S390_CC_OP_SET), cc, mkU64(0), mkU64(0));
+
+ return "lcbb";
+}
+
+/* Regarding the use of
+ // Dummy helper which is used to signal VEX library that memory was loaded
+ sha512_loadparam
+ = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_sha512_load_param_block",
+ &s390x_dirtyhelper_PPNO_sha512_load_param_block,
+ mkIRExprVec_0());
+
+ in the following function (s390_irgen_PPNO). This is a workaround to get
+ around the fact that IRDirty annotations cannot indicate two memory side
+ effects, which are unfortunately necessary here. It will possibly lead to
+ losing undefinedness (undefinedness in some inputs might not be propagated
+ to the outputs as it shouod, in Memcheck). The correct fix would be to
+ extend IRDirty to represent two memory side effects, but that's quite a bit
+ of work.
+
+ Here's a summary of what this insn does.
+
+ // getReg(RegisterNumber n) returns the value of GPR number 'n'
+
+ // reg1 and reg2 are even
+ void ppno(RegisterNumber reg1, RegisterNumber reg2) {
+
+ switch(getReg(0)) {
+ case 0x0:
+ // Query mode, ignore reg1 and reg2
+ // Write 16 bytes at getReg(1)
+ break;
+
+ case 0x3:
+ // SHA-512 generate mode, ignore reg2
+
+ // Read 240 bytes at getReg(1)
+ // Write getReg(reg1 + 1) bytes at getReg(reg1)
+ // Write some of 240 bytes starting at getReg(1)
+ break;
+
+ case 0x83:
+ // SHA-512 seed mode, ignore reg1
+
+ // Read some of 240 bytes starting at getReg(1)
+ // Read getReg(reg2 + 1) bytes at getReg(reg2)
+ // Write 240 bytes at getReg(1)
+ break;
+
+ default:
+ // Specification exception, abort execution.
+ }
+ }
+*/
+/* Also known as "prno"
+ If you implement new functions please don't forget to update
+ "s390x_dirtyhelper_PPNO_query" function.
+ */
+static const HChar *
+s390_irgen_PPNO(UChar r1, UChar r2)
+{
+ if (!s390_host_has_msa5) {
+ emulation_failure(EmFail_S390X_ppno);
+ return "ppno";
+ }
+
+ /* Theese conditions lead to specification exception */
+ vassert(r1 % 2 == 0);
+ vassert(r2 % 2 == 0);
+ vassert((r1 != 0) && (r2 != 0));
+
+ IRDirty *query, *sha512_gen, *sha512_seed, *sha512_loadparam;
+ IRTemp gpr1num = newTemp(Ity_I64);
+ IRTemp gpr2num = newTemp(Ity_I64);
+
+ IRTemp funcCode = newTemp(Ity_I8);
+ IRTemp is_query = newTemp(Ity_I1);
+ IRTemp is_sha512_gen = newTemp(Ity_I1);
+ IRTemp is_sha512_seed = newTemp(Ity_I1);
+ IRTemp is_sha512 = newTemp(Ity_I1);
+
+ assign(funcCode, unop(Iop_64to8, binop(Iop_And64, get_gpr_dw0(0), mkU64(0xffULL))));
+ assign(gpr1num, mkU64(r1));
+ assign(gpr2num, mkU64(r2));
+
+ assign(is_query, binop(Iop_CmpEQ8, mkexpr(funcCode), mkU8(S390_PPNO_QUERY)));
+ assign(is_sha512_gen, binop(Iop_CmpEQ8, mkexpr(funcCode), mkU8(S390_PPNO_SHA512_GEN)));
+ assign(is_sha512_seed, binop(Iop_CmpEQ8, mkexpr(funcCode), mkU8(S390_PPNO_SHA512_SEED)));
+ assign(is_sha512, binop(Iop_CmpEQ8,
+ mkU8(S390_PPNO_SHA512_GEN),
+ binop(Iop_And8,
+ mkexpr(funcCode),
+ mkU8(S390_PPNO_SHA512_GEN)
+ )
+ ));
+
+ query = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_query",
+ &s390x_dirtyhelper_PPNO_query,
+ mkIRExprVec_3(IRExpr_GSPTR(), mkexpr(gpr1num), mkexpr(gpr2num)));
+ query->guard = mkexpr(is_query);
+ query->nFxState = 1;
+ vex_bzero(&query->fxState, sizeof(query->fxState));
+ query->fxState[0].fx = Ifx_Read;
+ query->fxState[0].offset = S390X_GUEST_OFFSET(guest_r0);
+ query->fxState[0].size = 2 * sizeof(ULong); /* gpr0 and gpr1 are read */
+ query->mAddr = get_gpr_dw0(1);
+ query->mSize = S390_PPNO_PARAM_BLOCK_SIZE_QUERY;
+ query->mFx = Ifx_Write;
+
+ IRTemp gen_cc = newTemp(Ity_I64);
+ sha512_gen = unsafeIRDirty_1_N(gen_cc, 0, "s390x_dirtyhelper_PPNO_sha512",
+ &s390x_dirtyhelper_PPNO_sha512,
+ mkIRExprVec_3(IRExpr_GSPTR(), mkexpr(gpr1num), mkexpr(gpr2num)));
+ sha512_gen->guard = mkexpr(is_sha512_gen);
+ sha512_gen->nFxState = 3;
+ vex_bzero(&sha512_gen->fxState, sizeof(sha512_gen->fxState));
+ sha512_gen->fxState[0].fx = Ifx_Read;
+ sha512_gen->fxState[0].offset = S390X_GUEST_OFFSET(guest_r0);
+ sha512_gen->fxState[0].size = 2 * sizeof(ULong); /* gpr0 and gpr1 are read */
+ sha512_gen->fxState[1].fx = Ifx_Read;
+ sha512_gen->fxState[1].offset = S390X_GUEST_OFFSET(guest_r0) + r1 * sizeof(ULong);
+ sha512_gen->fxState[1].size = sizeof(ULong);
+ sha512_gen->fxState[2].fx = Ifx_Modify;
+ sha512_gen->fxState[2].offset = S390X_GUEST_OFFSET(guest_r0) + (r1 + 1) * sizeof(ULong);
+ sha512_gen->fxState[2].size = sizeof(ULong);
+ sha512_gen->mAddr = get_gpr_dw0(r1);
+ sha512_gen->mSize = S390_PPNO_MAX_SIZE_SHA512_GEN;
+ sha512_gen->mFx = Ifx_Write;
+
+ IRTemp unused = newTemp(Ity_I64);
+ sha512_seed = unsafeIRDirty_1_N(unused, 0, "s390x_dirtyhelper_PPNO_sha512",
+ &s390x_dirtyhelper_PPNO_sha512,
+ mkIRExprVec_3(IRExpr_GSPTR(), mkexpr(gpr1num), mkexpr(gpr2num)));
+ sha512_seed->guard = mkexpr(is_sha512_seed);
+ sha512_seed->nFxState = 2;
+ vex_bzero(&sha512_seed->fxState, sizeof(sha512_seed->fxState));
+ sha512_seed->fxState[0].fx = Ifx_Read;
+ sha512_seed->fxState[0].offset = S390X_GUEST_OFFSET(guest_r0);
+ sha512_seed->fxState[0].size = 2 * sizeof(ULong); /* gpr0 and gpr1 are read */
+ sha512_seed->fxState[1].fx = Ifx_Read;
+ sha512_seed->fxState[1].offset = S390X_GUEST_OFFSET(guest_r0) + r2 * sizeof(ULong);
+ sha512_seed->fxState[1].size = 2 * sizeof(ULong); /* r2 and r2 + 1 are read */
+ sha512_seed->mAddr = get_gpr_dw0(r2);
+ sha512_seed->mSize = S390_PPNO_MAX_SIZE_SHA512_SEED;
+ sha512_seed->mFx = Ifx_Write;
+
+ /* Dummy helper which is used to signal VEX library that memory was loaded */
+ sha512_loadparam = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_sha512_load_param_block",
+ &s390x_dirtyhelper_PPNO_sha512_load_param_block,
+ mkIRExprVec_0());
+ sha512_loadparam->guard = mkexpr(is_sha512);
+ sha512_loadparam->nFxState = 0;
+ vex_bzero(&sha512_loadparam->fxState, sizeof(sha512_loadparam->fxState));
+ sha512_loadparam->mAddr = get_gpr_dw0(1);
+ sha512_loadparam->mSize = S390_PPNO_PARAM_BLOCK_SIZE_SHA512;
+ sha512_loadparam->mFx = Ifx_Read;
+
+ IRDirty*
+ sha512_saveparam = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_sha512_save_param_block",
+ &s390x_dirtyhelper_PPNO_sha512_load_param_block,
+ mkIRExprVec_0());
+ sha512_saveparam->guard = mkexpr(is_sha512);
+ sha512_saveparam->nFxState = 0;
+ vex_bzero(&sha512_saveparam->fxState, sizeof(sha512_saveparam->fxState));
+ sha512_saveparam->mAddr = get_gpr_dw0(1);
+ sha512_saveparam->mSize = S390_PPNO_PARAM_BLOCK_SIZE_SHA512;
+ sha512_saveparam->mFx = Ifx_Write;
+
+ stmt(IRStmt_Dirty(query));
+ stmt(IRStmt_Dirty(sha512_loadparam));
+ stmt(IRStmt_Dirty(sha512_gen));
+ stmt(IRStmt_Dirty(sha512_seed));
+ stmt(IRStmt_Dirty(sha512_saveparam));
+
+ IRTemp cc = newTemp(Ity_I64);
+ assign(cc,
+ mkite(mkexpr(is_sha512_gen),
+ mkexpr(gen_cc),
+ mkU64(0)
+ )
+ );
+
+ s390_cc_thunk_fill(mkU64(S390_CC_OP_SET), mkexpr(cc), mkU64(0), mkU64(0));
+
+ return "ppno";
+}
/* New insns are added here.
If an insn is contingent on a facility being installed also
ovl.fmt.RRE.r2); goto ok;
case 0xb931: s390_format_RRE_RR(s390_irgen_CLGFR, ovl.fmt.RRE.r1,
ovl.fmt.RRE.r2); goto ok;
- case 0xb93c: /* PPNO */ goto unimplemented;
+ case 0xb93c: s390_format_RRE_RR(s390_irgen_PPNO, ovl.fmt.RRE.r1,
+ ovl.fmt.RRE.r2); goto ok;
case 0xb93e: /* KIMD */ goto unimplemented;
case 0xb93f: /* KLMD */ goto unimplemented;
case 0xb941: s390_format_RRF_UURF(s390_irgen_CFDTR, ovl.fmt.RRF2.m3,
ovl.fmt.RRE.r2); goto ok;
case 0xb9df: s390_format_RRE_RR(s390_irgen_CLHLR, ovl.fmt.RRE.r1,
ovl.fmt.RRE.r2); goto ok;
- case 0xb9e0: /* LOCFHR */ goto unimplemented;
+ case 0xb9e0: s390_format_RRF_U0RR(s390_irgen_LOCFHR, ovl.fmt.RRF3.r3,
+ ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2,
+ S390_XMNM_LOCFHR); goto ok;
case 0xb9e1: s390_format_RRE_RR(s390_irgen_POPCNT, ovl.fmt.RRE.r1,
ovl.fmt.RRE.r2); goto ok;
case 0xb9e2: s390_format_RRF_U0RR(s390_irgen_LOCGR, ovl.fmt.RRF3.r3,
ovl.fmt.RXY.x2, ovl.fmt.RXY.b2,
ovl.fmt.RXY.dl2,
ovl.fmt.RXY.dh2); goto ok;
- case 0xe3000000002aULL: /* LZRG */ goto unimplemented;
+ case 0xe3000000002aULL: s390_format_RXY_RRRD(s390_irgen_LZRG, ovl.fmt.RXY.r1,
+ ovl.fmt.RXY.x2, ovl.fmt.RXY.b2,
+ ovl.fmt.RXY.dl2,
+ ovl.fmt.RXY.dh2); goto ok;
case 0xe3000000002eULL: /* CVDG */ goto unimplemented;
case 0xe3000000002fULL: s390_format_RXY_RRRD(s390_irgen_STRVG,
ovl.fmt.RXY.r1, ovl.fmt.RXY.x2,
ovl.fmt.RXY.dh2); goto ok;
case 0xe30000000038ULL: /* AGH */ goto unimplemented;
case 0xe30000000039ULL: /* SGH */ goto unimplemented;
- case 0xe3000000003aULL: /* LLZRGF */ goto unimplemented;
- case 0xe3000000003bULL: /* LZRF */ goto unimplemented;
+ case 0xe3000000003aULL: s390_format_RXY_RRRD(s390_irgen_LLZRGF, ovl.fmt.RXY.r1,
+ ovl.fmt.RXY.x2, ovl.fmt.RXY.b2,
+ ovl.fmt.RXY.dl2,
+ ovl.fmt.RXY.dh2); goto ok;
+ case 0xe3000000003bULL: s390_format_RXY_RRRD(s390_irgen_LZRF, ovl.fmt.RXY.r1,
+ ovl.fmt.RXY.x2, ovl.fmt.RXY.b2,
+ ovl.fmt.RXY.dl2,
+ ovl.fmt.RXY.dh2); goto ok;
case 0xe3000000003cULL: /* MGH */ goto unimplemented;
case 0xe3000000003eULL: s390_format_RXY_RRRD(s390_irgen_STRV, ovl.fmt.RXY.r1,
ovl.fmt.RXY.x2, ovl.fmt.RXY.b2,
case 0xe70000000021ULL: s390_format_VRS_RRDVM(s390_irgen_VLGV, ovl.fmt.VRS.v1,
ovl.fmt.VRS.b2, ovl.fmt.VRS.d2, ovl.fmt.VRS.v3,
ovl.fmt.VRS.m4, ovl.fmt.VRS.rxb); goto ok;
- case 0xe70000000027ULL: /* LCBB */ goto unimplemented;
case 0xe70000000022ULL: s390_format_VRS_VRRDM(s390_irgen_VLVG, ovl.fmt.VRS.v1,
ovl.fmt.VRS.b2, ovl.fmt.VRS.d2, ovl.fmt.VRS.v3,
ovl.fmt.VRS.m4, ovl.fmt.VRS.rxb); goto ok;
+ case 0xe70000000027ULL: s390_format_RXE_RRRDR(s390_irgen_LCBB, ovl.fmt.RXE.r1,
+ ovl.fmt.RXE.x2, ovl.fmt.RXE.b2,
+ ovl.fmt.RXE.d2, ovl.fmt.RXE.m3); goto ok;
case 0xe70000000030ULL: /* VESL */ goto unimplemented;
case 0xe70000000033ULL: /* VERLL */ goto unimplemented;
case 0xe70000000036ULL: s390_format_VRS_VRDV(s390_irgen_VLM, ovl.fmt.VRS.v1,
ovl.fmt.RSY.dh2); goto ok;
case 0xeb000000004cULL: s390_format_RSY_RRRD(s390_irgen_ECAG, ovl.fmt.RSY.r1,
ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
- ovl.fmt.RSY.dl2,
+ ovl.fmt.RSY.dl2,
ovl.fmt.RSY.dh2); goto ok;
case 0xeb0000000051ULL: s390_format_SIY_URD(s390_irgen_TMY, ovl.fmt.SIY.i2,
ovl.fmt.SIY.b1, ovl.fmt.SIY.dl1,
ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
ovl.fmt.RSY.dl2,
ovl.fmt.RSY.dh2); goto ok;
- case 0xeb00000000e0ULL: /* LOCFH */ goto unimplemented;
- case 0xeb00000000e1ULL: /* STOCFH */ goto unimplemented;
+ case 0xeb00000000e0ULL: s390_format_RSY_RDRM(s390_irgen_LOCFH, ovl.fmt.RSY.r1,
+ ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
+ ovl.fmt.RSY.dl2,
+ ovl.fmt.RSY.dh2,
+ S390_XMNM_LOCFH); goto ok;
+ case 0xeb00000000e1ULL: s390_format_RSY_RDRM(s390_irgen_STOCFH, ovl.fmt.RSY.r1,
+ ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
+ ovl.fmt.RSY.dl2,
+ ovl.fmt.RSY.dh2,
+ S390_XMNM_STOCFH); goto ok;
case 0xeb00000000e2ULL: s390_format_RSY_RDRM(s390_irgen_LOCG, ovl.fmt.RSY.r1,
ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
ovl.fmt.RSY.dl2,
ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
ovl.fmt.RSY.dl2,
ovl.fmt.RSY.dh2); goto ok;
- case 0xec0000000042ULL: /* LOCHI */ goto unimplemented;
+ case 0xec0000000042ULL: s390_format_RIE_RUPIX(s390_irgen_LOCHI,
+ ovl.fmt.RIEv3.r1,
+ ovl.fmt.RIEv3.m3,
+ ovl.fmt.RIEv3.i4,
+ ovl.fmt.RIEv3.i2,
+ S390_XMNM_LOCHI); goto ok;
case 0xec0000000044ULL: s390_format_RIE_RRP(s390_irgen_BRXHG, ovl.fmt.RIE.r1,
ovl.fmt.RIE.r3, ovl.fmt.RIE.i2);
goto ok;
case 0xec0000000045ULL: s390_format_RIE_RRP(s390_irgen_BRXLG, ovl.fmt.RIE.r1,
ovl.fmt.RIE.r3, ovl.fmt.RIE.i2);
goto ok;
- case 0xec0000000046ULL: /* LOCGHI */ goto unimplemented;
- case 0xec000000004eULL: /* LOCHHI */ goto unimplemented;
+ case 0xec0000000046ULL: s390_format_RIE_RUPIX(s390_irgen_LOCGHI,
+ ovl.fmt.RIEv3.r1,
+ ovl.fmt.RIEv3.m3,
+ ovl.fmt.RIEv3.i4,
+ ovl.fmt.RIEv3.i2,
+ S390_XMNM_LOCGHI); goto ok;
+ case 0xec000000004eULL: s390_format_RIE_RUPIX(s390_irgen_LOCHHI,
+ ovl.fmt.RIEv3.r1,
+ ovl.fmt.RIEv3.m3,
+ ovl.fmt.RIEv3.i4,
+ ovl.fmt.RIEv3.i2,
+ S390_XMNM_LOCHHI); goto ok;
case 0xec0000000051ULL: s390_format_RIE_RRUUU(s390_irgen_RISBLG,
ovl.fmt.RIE_RRUUU.r1,
ovl.fmt.RIE_RRUUU.r2,
{
s390_cc_t cond;
ULong target;
- UChar *ptmp = buf;
+ Int delta;
s390_helper_call *helper_call = insn->variant.helper_call.details;
cond = helper_call->cond;
target = helper_call->target;
- if (cond != S390_CC_ALWAYS
- && helper_call->rloc.pri != RLPri_None) {
- /* The call might not happen (it isn't unconditional) and it
- returns a result. In this case we will need to generate a
- control flow diamond to put 0x555..555 in the return
- register(s) in the case where the call doesn't happen. If
- this ever becomes necessary, maybe copy code from the ARM
- equivalent. Until that day, just give up. */
- return buf; /* To denote failure. */
- }
+ const Bool not_always = (cond != S390_CC_ALWAYS);
+ const Bool not_void_return = (helper_call->rloc.pri != RLPri_None);
+
+ /* We have this situation:
+ ( *** code in this braces is for not_always && not_void_return*** )
+ ...
+ before:
+ brc{!cond} else
+ call_helper
+ preElse:
+ *** j after ***
+ else:
+ *** load_64imm $0x5555555555555555, %%r2 *** // e.g. for Int RetLoc
+ after:
+ ...
+ */
- if (cond != S390_CC_ALWAYS) {
- /* So we have something like this
- if (cond) call X;
- Y: ...
- We convert this into
- if (! cond) goto Y; // BRC opcode; 4 bytes
- call X;
- Y:
- */
+ // before:
+ UChar *pBefore = buf;
+ if (not_always) {
/* 4 bytes (a BRC insn) to be filled in here */
buf += 4;
}
+ // call_helper
/* Load the target address into a register, that
(a) is not used for passing parameters to the helper and
(b) can be clobbered by the callee
buf = s390_emit_LFPC(buf, S390_REGNO_STACK_POINTER, // restore FPC
S390_OFFSET_SAVED_FPC_C);
- if (cond != S390_CC_ALWAYS) {
- Int delta = buf - ptmp;
+ // preElse:
+ UChar* pPreElse = buf;
+ if (not_always && not_void_return) {
+ /* 4 bytes (a BRC insn) to be filled in here */
+ buf += 4;
+ }
+
+ // else:
+ UChar* pElse = buf;
+ if (not_always && not_void_return) {
+ switch (helper_call->rloc.pri) {
+ case RLPri_Int:
+ buf = s390_emit_load_64imm(buf, S390_REGNO_RETURN_VALUE, 0x5555555555555555ULL);
+ break;
+ default:
+ ppS390Instr(insn, True);
+ vpanic("s390_insn_helper_call_emit: invalid conditional RetLoc.");
+ }
+ }
+
+ // after:
+ UChar* pAfter = buf;
+ // fill "brc{!cond} else"
+ if(not_always)
+ {
+ delta = pElse - pBefore;
delta >>= 1; /* immediate constant is #half-words */
vassert(delta > 0 && delta < (1 << 16));
- s390_emit_BRC(ptmp, s390_cc_invert(cond), delta);
+ s390_emit_BRC(pBefore, s390_cc_invert(cond), delta);
+ }
+
+ // fill "brc{ALWAYS} after"
+ if (not_always && not_void_return)
+ {
+ delta = pAfter - pPreElse;
+ delta >>= 1; /* immediate constant is #half-words */
+ vassert(delta > 0 && delta < (1 << 16));
+ s390_emit_BRC(pPreElse, S390_CC_ALWAYS, delta);
}
return buf;
(s390_host_hwcaps & (VEX_HWCAPS_S390X_PFPO))
#define s390_host_has_vx \
(s390_host_hwcaps & (VEX_HWCAPS_S390X_VX))
-
+#define s390_host_has_msa5 \
+ (s390_host_hwcaps & (VEX_HWCAPS_S390X_MSA5))
#endif /* ndef __VEX_HOST_S390_DEFS_H */
/*---------------------------------------------------------------*/
S390_PFPO_D128_TO_F128 = 0x01070A
} s390_pfpo_function_t;
+/* PPNO function code as it is encoded in bits [57:63] of GR0
+ when PPNO insn is executed. */
+typedef enum
+{
+ S390_PPNO_QUERY = 0x00,
+ S390_PPNO_SHA512_GEN = 0x03,
+ S390_PPNO_SHA512_SEED = 0x83
+} s390_ppno_function_t;
+
+/* Size of parameter block for PPNO functions.
+ All values are in bytes.
+ */
+#define S390_PPNO_PARAM_BLOCK_SIZE_QUERY 16
+#define S390_PPNO_PARAM_BLOCK_SIZE_SHA512 240
+
+/* Maximum length of modified memory for PPNO functions.
+ All values are in bytes.
+*/
+#define S390_PPNO_MAX_SIZE_SHA512_SEED 512
+#define S390_PPNO_MAX_SIZE_SHA512_GEN 64
+
+
/* The length of the longest mnemonic: locgrnhe */
#define S390_MAX_MNEMONIC_LEN 8
case S390_XMNM_LOCG: prefix = "locg"; break;
case S390_XMNM_STOC: prefix = "stoc"; break;
case S390_XMNM_STOCG: prefix = "stocg"; break;
+ case S390_XMNM_STOCFH: prefix = "stocfh"; break;
+ case S390_XMNM_LOCFH: prefix = "locgh"; break;
+ case S390_XMNM_LOCFHR: prefix = "locghr"; break;
+ case S390_XMNM_LOCHI: prefix = "lochi"; break;
+ case S390_XMNM_LOCGHI: prefix = "locghi"; break;
+ case S390_XMNM_LOCHHI: prefix = "lochhi"; break;
default:
vpanic("cls_operand");
}
case S390_XMNM_LOCG:
case S390_XMNM_STOC:
case S390_XMNM_STOCG:
+ case S390_XMNM_STOCFH:
+ case S390_XMNM_LOCFH:
+ case S390_XMNM_LOCFHR:
+ case S390_XMNM_LOCHI:
+ case S390_XMNM_LOCGHI:
+ case S390_XMNM_LOCHHI:
mask = va_arg(args, UInt);
mnm = cls_operand(kind, mask);
p += vex_sprintf(p, "%s", mnemonic(mnm));
S390_XMNM_LOC = 7,
S390_XMNM_LOCG = 8,
S390_XMNM_STOC = 9,
- S390_XMNM_STOCG = 10
+ S390_XMNM_STOCG = 10,
+ S390_XMNM_STOCFH = 11,
+ S390_XMNM_LOCFH = 12,
+ S390_XMNM_LOCFHR = 13,
+ S390_XMNM_LOCHI = 14,
+ S390_XMNM_LOCGHI = 15,
+ S390_XMNM_LOCHHI = 16
};
void s390_disasm(UInt command, ...);
#define VEX_HWCAPS_S390X_LSC (1<<16) /* Conditional load/store facility */
#define VEX_HWCAPS_S390X_PFPO (1<<17) /* Perform floating point ops facility */
#define VEX_HWCAPS_S390X_VX (1<<18) /* Vector facility */
+#define VEX_HWCAPS_S390X_MSA5 (1<<19) /* message security assistance facility */
+
/* Special value representing all available s390x hwcaps */
#define VEX_HWCAPS_S390X_ALL (VEX_HWCAPS_S390X_LDISP | \
VEX_HWCAPS_S390X_ETF3 | \
VEX_HWCAPS_S390X_ETF2 | \
VEX_HWCAPS_S390X_PFPO | \
- VEX_HWCAPS_S390X_VX)
+ VEX_HWCAPS_S390X_VX | \
+ VEX_HWCAPS_S390X_MSA5)
#define VEX_HWCAPS_S390X(x) ((x) & ~VEX_S390X_MODEL_MASK)
#define VEX_S390X_MODEL(x) ((x) & VEX_S390X_MODEL_MASK)
/* some insn needs vector facility which is not available on this host */
EmFail_S390X_vx,
+ /* ppno insn is not supported on this host */
+ EmFail_S390X_ppno,
+
EmNote_NUMBER
}
VexEmNote;
#define S390_FAC_DFPZC 48 // DFP zoned-conversion
#define S390_FAC_MISC 49 // miscellaneous insn
#define S390_FAC_CTREXE 50 // constrained transactional execution
+#define S390_FAC_LSC2 53 // load/store on condition 2 and load and zero rightmost byte
+#define S390_FAC_MSA5 57 // message-security-assist 5
#define S390_FAC_TREXE 73 // transactional execution
#define S390_FAC_MSA4 77 // message-security-assist 4
#define S390_FAC_VX 128 // vector facility
{ False, S390_FAC_FPEXT, VEX_HWCAPS_S390X_FPEXT, "FPEXT" },
{ False, S390_FAC_LSC, VEX_HWCAPS_S390X_LSC, "LSC" },
{ False, S390_FAC_PFPO, VEX_HWCAPS_S390X_PFPO, "PFPO" },
- { False, S390_FAC_VX, VEX_HWCAPS_S390X_VX, "VX" }
+ { False, S390_FAC_VX, VEX_HWCAPS_S390X_VX, "VX" },
+ { False, S390_FAC_MSA5, VEX_HWCAPS_S390X_MSA5, "MSA5" }
};
/* Set hwcaps according to the detected facilities */
spechelper-cr spechelper-clr \
spechelper-ltr spechelper-or \
spechelper-icm-1 spechelper-icm-2 spechelper-tmll \
- spechelper-tm laa vector
+ spechelper-tm laa vector lsc2 ppno
if BUILD_DFP_TESTS
INSN_TESTS += dfp-1 dfp-2 dfp-3 dfp-4 dfptest dfpext dfpconv srnmt pfpo
endif
fpext_CFLAGS = $(AM_CFLAGS) @FLAG_MLONG_DOUBLE_128@
ex_clone_LDADD = -lpthread
vector_CFLAGS = $(AM_CFLAGS) -march=z13
+lsc2_CFLAGS = -march=z13 -DS390_TESTS_NOCOLOR
--- /dev/null
+/*
+ * s390x z13 instructions test
+ *
+ * Copyright (c) 2017 Vadim Barkov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define _GNU_SOURCE
+#include "assert.h"
+#include "math.h"
+#include "stdbool.h"
+#include "stdint.h"
+#include "stdio.h"
+#include "string.h"
+
+/* Colors for output */
+#ifndef S390_TESTS_NOCOLOR
+# define RED "\033[31m"
+# define GREEN "\033[32m"
+# define RESET "\033[0m"
+#else
+# define RED
+# define GREEN
+# define RESET
+#endif
+
+#define printPassed(insn) \
+ printf(RESET "%15s :\t " GREEN "PASSED" RESET "\n", #insn)
+#define printFailed(insn) \
+ printf(RESET "%15s :\t " RED "FAILED" RESET "\n", #insn)
+
+#define test(insn) \
+ { \
+ if (test_##insn()) \
+ printPassed(insn); \
+ else \
+ printFailed(insn); \
+ }
+
+#define SMART_RETURN_R64(insn) \
+ bool result = after == expected; \
+ if (!result) { \
+ printf("[ERROR] %s:\n", #insn); \
+ printf("after: %lx\n", (uint64_t) after); \
+ printf("expected: %lx\n", (uint64_t) expected); \
+ } \
+ return result
+
+/* Constant value for immediate instructions' tests */
+#define IMMEDIATE_VALUE 0x0abc
+#define IMMEDIATE_VALUE_STR "0x0abc"
+
+/* Useful macros for testing many input values
+ and printing message after test execution
+ */
+#define test_each(type, var, instruction) \
+ { \
+ type##_foreach((var), test_##instruction((var))); \
+ }
+
+#define test_range(var, from, to, instruction) \
+ { \
+ bool result = true; \
+ __int_foreach(var, from, to, result &= (test_##instruction((var)))); \
+ if (result) \
+ printPassed(instruction); \
+ else \
+ printFailed(instruction); \
+ }
+
+#define __int_foreach(var, minValue, maxValue, statement) \
+ { \
+ for (var = minValue; var < maxValue; var++) { \
+ statement; \
+ } \
+ \
+ { statement; } \
+ }
+
+#define uint32_foreach(var, statement) \
+ __int_foreach(var, 0, UINT32_MAX, statement)
+#define int32_foreach(var, statement) \
+ __int_foreach(var, INT32_MIN, INT32_MAX, statement)
+#define uint64_foreach(var, statement) \
+ __int_foreach(var, 0, UINT64_MAX, statement)
+
+/* load and zero rightmost byte */
+static bool test_lzrf(const uint32_t testedValue)
+{
+ uint32_t after = testedValue;
+ uint32_t expected = testedValue & 0xffffff00;
+ __asm__ volatile("lzrf %%r6, %0\n"
+ "st %%r6, %0\n"
+ :
+ : "g"(after));
+
+ SMART_RETURN_R64(lzrf);
+}
+
+/* load and zero rightmost byte 64bit*/
+static bool test_lzrg(const uint64_t testedValue)
+{
+ uint64_t after = testedValue;
+ uint64_t expected = testedValue & 0xffffffffffffff00UL;
+
+ __asm__ volatile("lzrg %%r6, %0\n"
+ "stg %%r6, %0\n"
+ :
+ : "g"(after));
+
+ SMART_RETURN_R64(lzrg);
+}
+
+/* load logical and zero rightmost byte */
+static bool test_llzrgf(const uint32_t testedValue)
+{
+ uint64_t after = 0;
+ uint64_t expected = ((uint64_t)testedValue << 32) & 0xffffff0000000000UL;
+
+ __asm__ volatile("llzrgf %%r6, %0\n"
+ "st %%r6, %1\n"
+ :
+ : "g"(testedValue), "g"(after));
+
+ SMART_RETURN_R64(llzrgf);
+}
+
+/* compare instructions */
+#define __compare_r "cr"
+#define __compare_m "c"
+
+/* load high on condition */
+#define declare_load_high_on_condition(TESTED_INSTRUCTION, CONDITION_SYMBOL, \
+ ARGUMENT_ASM_TYPE) \
+ bool test_##TESTED_INSTRUCTION(const int32_t testedValue) \
+ { \
+ uint64_t expected; \
+ const uint64_t valueBeforeTest = 0x6666666699999999UL; \
+ const uint64_t overrideValue = 0xeeeeeeeeeeeeeeeeUL; \
+ const int32_t invertedValue = testedValue ^ 0xffffffff; \
+ uint64_t after = valueBeforeTest; \
+ if (testedValue CONDITION_SYMBOL invertedValue) { \
+ expected = 0xeeeeeeee99999999UL; \
+ } else { \
+ expected = valueBeforeTest; \
+ } \
+ \
+ __asm__ volatile( \
+ "cr %[testedValue], %[invertedValue]\n" #TESTED_INSTRUCTION \
+ " %[after], %[overrideValue]\n" \
+ : [after] "=r"(after) \
+ : [testedValue] "r"(testedValue), \
+ [invertedValue] "r"(invertedValue), \
+ [overrideValue] #ARGUMENT_ASM_TYPE(overrideValue), \
+ "[after]"(after)); \
+ \
+ SMART_RETURN_R64(TESTED_INSTRUCTION); \
+ }
+
+declare_load_high_on_condition(locfhre, ==, r)
+declare_load_high_on_condition(locfhrne, !=, r)
+declare_load_high_on_condition(locfhrh, >, r)
+declare_load_high_on_condition(locfhrl, <, r)
+declare_load_high_on_condition(locfhe, ==, m)
+declare_load_high_on_condition(locfhne, !=, m)
+declare_load_high_on_condition(locfhh, >, m)
+declare_load_high_on_condition(locfhl, <, m)
+
+/* store high on condition */
+#define declare_store_high_on_condition(TESTED_INSTRUCTION, CONDITION_SYMBOL, \
+ ARGUMENT_ASM_TYPE) \
+ bool test_##TESTED_INSTRUCTION(const int32_t testedValue) \
+ { \
+ uint64_t expected; \
+ const uint64_t valueBeforeTest = 0x6666666699999999UL; \
+ const uint64_t overrideValue = 0xeeeeeeeeeeeeeeeeUL; \
+ const int32_t invertedValue = testedValue ^ 0xffffffff; \
+ uint64_t after = valueBeforeTest; \
+ if (testedValue CONDITION_SYMBOL invertedValue) { \
+ expected = 0xeeeeeeee99999999UL; \
+ } else { \
+ expected = valueBeforeTest; \
+ } \
+ \
+ __asm__ volatile( \
+ "cr %[testedValue], %[invertedValue]\n" #TESTED_INSTRUCTION \
+ " %[overrideValue], %[after]\n" \
+ : [after] "=" #ARGUMENT_ASM_TYPE(after) \
+ : [testedValue] "r"(testedValue), \
+ [invertedValue] "r"(invertedValue), \
+ [overrideValue] "r"(overrideValue)); \
+ \
+ SMART_RETURN_R64(TESTED_INSTRUCTION); \
+ }
+
+declare_store_high_on_condition(stocfhe, ==, m)
+declare_store_high_on_condition(stocfhne, !=, m)
+declare_store_high_on_condition(stocfhh, >, m)
+declare_store_high_on_condition(stocfhl, <, m)
+
+#define __format_uint32_t "%x"
+#define __format_uint64_t "%lx"
+#define __halfword_valueBefore_uint32_t 0x66669999
+#define __halfword_valueBefore_uint64_t 0x6666666699999999UL
+/* load halfword immediate on condition */
+#define declare_load_halfword_immediate_on_condition( \
+ TESTED_INSTRUCTION, ARGUMENT_TYPE, CONDITION_SYMBOL, ARGUMENT_ASM_TYPE) \
+ bool test_##TESTED_INSTRUCTION(const int32_t testedValue) \
+ { \
+ ARGUMENT_TYPE expected; \
+ const ARGUMENT_TYPE valueBeforeTest = \
+ __halfword_valueBefore_##ARGUMENT_TYPE; \
+ const int32_t invertedValue = testedValue ^ 0xffffffff; \
+ ARGUMENT_TYPE after = valueBeforeTest; \
+ if (testedValue CONDITION_SYMBOL invertedValue) { \
+ expected = IMMEDIATE_VALUE; \
+ } else { \
+ expected = valueBeforeTest; \
+ } \
+ \
+ __asm__ volatile( \
+ "cr %[testedValue], %[invertedValue]\n" #TESTED_INSTRUCTION \
+ " %[after], " IMMEDIATE_VALUE_STR "\n" \
+ : [after] "=r"(after) \
+ : [testedValue] "r"(testedValue), \
+ [invertedValue] "r"(invertedValue), \
+ "[after]"(after)); \
+ \
+ SMART_RETURN_R64(TESTED_INSTRUCTION); \
+ }
+
+declare_load_halfword_immediate_on_condition(lochie, uint32_t, ==, r)
+declare_load_halfword_immediate_on_condition(lochine, uint32_t, !=, r)
+declare_load_halfword_immediate_on_condition(lochih, uint32_t, >, r)
+declare_load_halfword_immediate_on_condition(lochil, uint32_t, <, r)
+declare_load_halfword_immediate_on_condition(locghie, uint64_t, ==, r)
+declare_load_halfword_immediate_on_condition(locghine, uint64_t, !=, r)
+declare_load_halfword_immediate_on_condition(locghih, uint64_t, >, r)
+declare_load_halfword_immediate_on_condition(locghil, uint64_t, <, r)
+
+/* load halfword high immediate on condition */
+#define declare_load_halfword_high_immediate_on_condition( \
+ TESTED_INSTRUCTION, CONDITION_SYMBOL, ARGUMENT_ASM_TYPE) \
+ bool test_##TESTED_INSTRUCTION(const int32_t testedValue) \
+ { \
+ uint64_t expected; \
+ const uint64_t valueBeforeTest = __halfword_valueBefore_uint64_t; \
+ const int32_t invertedValue = testedValue ^ 0xffffffff; \
+ uint64_t after = valueBeforeTest; \
+ if (testedValue CONDITION_SYMBOL invertedValue) { \
+ expected = IMMEDIATE_VALUE; \
+ expected <<= 32; \
+ expected += \
+ __halfword_valueBefore_uint64_t & 0x00000000ffffffffUL; \
+ } else { \
+ expected = valueBeforeTest; \
+ } \
+ \
+ __asm__ volatile( \
+ "cr %[testedValue], %[invertedValue]\n" #TESTED_INSTRUCTION \
+ " %[after], " IMMEDIATE_VALUE_STR "\n" \
+ : [after] "=r"(after) \
+ : [testedValue] "r"(testedValue), \
+ [invertedValue] "r"(invertedValue), \
+ "[after]"(after)); \
+ \
+ SMART_RETURN_R64(TESTED_INSTRUCTION); \
+ }
+
+declare_load_halfword_high_immediate_on_condition(lochhie, ==, r)
+declare_load_halfword_high_immediate_on_condition(lochhine, !=, r)
+declare_load_halfword_high_immediate_on_condition(lochhih, >, r)
+declare_load_halfword_high_immediate_on_condition(lochhil, <, r)
+
+static void test_all_locfh()
+{
+ int32_t signed32bit = 0;
+
+ test_each(int32, signed32bit, locfhe);
+ test_each(int32, signed32bit, locfhne);
+ test_each(int32, signed32bit, locfhh);
+ test_each(int32, signed32bit, locfhl);
+
+ test_each(int32, signed32bit, locfhre);
+ test_each(int32, signed32bit, locfhrne);
+ test_each(int32, signed32bit, locfhrh);
+ test_each(int32, signed32bit, locfhrl);
+}
+
+/* load count to block boundary */
+#define declare_load_count_to_block_boundary(M_FIELD) \
+ bool test_lcbb##M_FIELD(const uint32_t testedValue) \
+ { \
+ const size_t boundary = 64 * pow(2, M_FIELD); \
+ const uint32_t *testedPointer = \
+ (uint32_t *)(boundary - \
+ ((testedValue < boundary) ? testedValue : 0)); \
+ uint32_t after = 0; \
+ uint32_t expected = boundary - ((size_t)testedPointer % boundary); \
+ if (expected > 16) \
+ expected = 16; \
+ \
+ __asm__ volatile("lcbb %[after], %[testedPointer], " #M_FIELD \
+ "\n" \
+ : [after] "=r"(after) \
+ : [testedPointer] "m"(*testedPointer)); \
+ \
+ SMART_RETURN_R64(lcbb##M_FIELD); \
+ }
+
+declare_load_count_to_block_boundary(0)
+declare_load_count_to_block_boundary(1)
+declare_load_count_to_block_boundary(2)
+declare_load_count_to_block_boundary(3)
+declare_load_count_to_block_boundary(4)
+declare_load_count_to_block_boundary(5)
+declare_load_count_to_block_boundary(6)
+
+static bool test_lcbb0_cc(const uint32_t testedValue)
+{
+ const size_t boundary = 64;
+ const uint32_t *testedPointer =
+ (uint32_t *)(boundary -
+ ((testedValue < boundary) ? testedValue : 0));
+ uint32_t expectedForLCBB = boundary - ((size_t)testedPointer % boundary);
+
+ uint32_t after = 0;
+ uint32_t expected = (expectedForLCBB >= 16) ? 0 : 3;
+ __asm__ volatile("lcbb %[cc], %[testedPointer], 0 \n"
+ "ipm %[cc] \n"
+ "srl %[cc], 28 \n"
+ : [cc] "=d"(after)
+ : [testedPointer] "m"(*testedPointer)
+ : "cc");
+ SMART_RETURN_R64(lcbb0_cc);
+}
+
+
+static void test_all_lzr()
+{
+ uint32_t unsigned32bit = 0;
+
+ test_each(uint32, unsigned32bit, lzrf);
+ test_each(uint32, unsigned32bit, lzrg);
+}
+
+static void test_all_stocfh()
+{
+ int32_t signed32bit = 0;
+
+ test_each(int32, signed32bit, stocfhne);
+ test_each(int32, signed32bit, stocfhe);
+ test_each(int32, signed32bit, stocfhh);
+ test_each(int32, signed32bit, stocfhl);
+}
+
+static void test_all_lochi()
+{
+ int32_t signed32bit = 0;
+
+ test_each(int32, signed32bit, locghine);
+ test_each(int32, signed32bit, locghine);
+ test_each(int32, signed32bit, locghih);
+ test_each(int32, signed32bit, locghil);
+
+ test_each(int32, signed32bit, lochine);
+ test_each(int32, signed32bit, lochie);
+ test_each(int32, signed32bit, lochil);
+ test_each(int32, signed32bit, lochih);
+}
+
+static void test_all_lochhi()
+{
+ int32_t signed32bit = 0;
+
+ test_each(int32, signed32bit, lochhine);
+ test_each(int32, signed32bit, lochhie);
+ test_each(int32, signed32bit, lochhih);
+ test_each(int32, signed32bit, lochhil);
+}
+
+static void test_all_lcbb()
+{
+ size_t tested = 0;
+
+ test_range(tested, 0, 64, lcbb0);
+ test_range(tested, 0, 128, lcbb1);
+ test_range(tested, 0, 256, lcbb2);
+ test_range(tested, 0, 512, lcbb3);
+ test_range(tested, 0, 1024, lcbb4);
+ test_range(tested, 0, 2048, lcbb5);
+ test_range(tested, 0, 4096, lcbb6);
+ test_range(tested, 0, 64, lcbb0_cc);
+}
+
+void test_long_all() {
+ uint64_t unsigned64bit = 0;
+
+ test_all_lcbb();
+ test_all_lochhi();
+ test_all_lochi();
+ test_all_stocfh();
+ test_all_locfh();
+ test_all_lzr();
+ test_each(uint64, unsigned64bit, llzrgf);
+}
+
+#define SHORT_TESTS_UNSIGNED_FROM 0
+#define SHORT_TESTS_SIGNED_FROM -0xffff
+#define SHORT_TESTS_TO 0xffff
+
+void test_short_all()
+{
+ uint32_t unsigned32bit = 0;
+ int32_t signed32bit = 0;
+ uint64_t unsigned64bit = 0;
+
+ test_range(unsigned32bit, SHORT_TESTS_UNSIGNED_FROM, SHORT_TESTS_TO, lzrf);
+ test_range(unsigned64bit, SHORT_TESTS_UNSIGNED_FROM, SHORT_TESTS_TO, lzrg);
+ test_range(unsigned64bit, SHORT_TESTS_UNSIGNED_FROM, SHORT_TESTS_TO, llzrgf);
+
+ /* stocfh */
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, stocfhne);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, stocfhe);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, stocfhh);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, stocfhl);
+
+ /* locfh */
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhe);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhne);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhh);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhl);
+
+ /* locfhr */
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhre);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhrne);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhrh);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locfhrl);
+
+ /* lochhi */
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochhine);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochhie);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochhih);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochhil);
+
+ /* lochi */
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochine);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochie);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochih);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, lochil);
+
+ /* locghi */
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locghine);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locghie);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locghih);
+ test_range(signed32bit, SHORT_TESTS_SIGNED_FROM, SHORT_TESTS_TO, locghil);
+
+ test_all_lcbb(); /* These test is not long, so we can run it on all range */
+}
+
+int main(int argc, char *argv[])
+{
+ bool shouldRunLongTests = false;
+
+ /* --long option forces to test all possible values (it is very long)*/
+ if (argc > 1)
+ if (strcmp(argv[1], "--long") == 0)
+ shouldRunLongTests = true;
+
+ printf("Tests started:\n");
+ if (shouldRunLongTests)
+ test_long_all();
+ else
+ test_short_all();
+
+ printf("Tests ended.\n");
+ return 0;
+}
--- /dev/null
+Tests started:
+ lzrf : PASSED
+ lzrg : PASSED
+ llzrgf : PASSED
+ stocfhne : PASSED
+ stocfhe : PASSED
+ stocfhh : PASSED
+ stocfhl : PASSED
+ locfhe : PASSED
+ locfhne : PASSED
+ locfhh : PASSED
+ locfhl : PASSED
+ locfhre : PASSED
+ locfhrne : PASSED
+ locfhrh : PASSED
+ locfhrl : PASSED
+ lochhine : PASSED
+ lochhie : PASSED
+ lochhih : PASSED
+ lochhil : PASSED
+ lochine : PASSED
+ lochie : PASSED
+ lochih : PASSED
+ lochil : PASSED
+ locghine : PASSED
+ locghie : PASSED
+ locghih : PASSED
+ locghil : PASSED
+ lcbb0 : PASSED
+ lcbb1 : PASSED
+ lcbb2 : PASSED
+ lcbb3 : PASSED
+ lcbb4 : PASSED
+ lcbb5 : PASSED
+ lcbb6 : PASSED
+ lcbb0_cc : PASSED
+Tests ended.
--- /dev/null
+prog: lsc2
--- /dev/null
+#include "stdio.h"
+#include "stdbool.h"
+#include "stdint.h"
+#include "string.h"
+#include "vector.h"
+
+#define S390_PPNO_SHA512_BLOCK_SIZE (240 / 8)
+static uint64_t block[S390_PPNO_SHA512_BLOCK_SIZE];
+#define HASH_COUNT 2 /* exactly two hashes for generate */
+static uint64_t hash[HASH_COUNT];
+
+static void print_sha512_block()
+{
+ printf("Current block state:\n");
+ for(size_t elem = 0; elem < S390_PPNO_SHA512_BLOCK_SIZE; elem++)
+ {
+ print_uint64_t(block[elem]);
+ }
+
+ printf("end of block.\n");
+}
+
+static void print_sha512_hash()
+{
+ printf("Current hash:\n");
+ for(size_t index = 0; index < HASH_COUNT; index++)
+ print_uint64_t(hash[index]);
+
+ printf("end of hash\n");
+}
+
+/* The problem with this test is different results on different architectures.
+ E.g. z13 will return (1 << 0)|(1 << 3) value while more recent versions of CPU
+ can (and will!) add their own operations here. Anyway z13 or any later arch will
+ have "SHA-512-DRNG" (3-rd bit of result) and "Query" (0-th bit) functions
+ so we test only this bits and ignore others.
+*/
+static bool test_ppno_query()
+{
+ uint32_t output[4];
+ register uint64_t functionCode __asm__("0") = 0ULL;
+ register uint64_t paramBlock __asm__("1") = (uint64_t) &output;
+
+ __asm__ volatile (
+ ".insn rre, 0xb93c0000, %%r2, %%r4 \n" // GPR's are ignored here
+ : "+d"(functionCode), "+d"(paramBlock)
+ :
+ : "cc", "memory"
+ );
+
+ /* 0x9 = (1 << 0)|(1 << 3), see explanation above */
+ uint32_t expected = 0x90000000U;
+ uint32_t after = output[0];
+
+ bool result = expected == after;
+ if(!result)
+ printf("ERROR: basic ppno functions are not supported : %d\n", after);
+
+ return result;
+}
+
+static void test_ppno_sha512_seed()
+{
+ /* Important! The program should zero paramBlock for seeding. */
+ memset(block, 0, sizeof(block));
+
+ uint8_t seed[512];
+ for(size_t index = 0; index < 512; index++)
+ seed[index] = random_element();
+ seed[511] = 0;
+ const uint64_t seedLength = strlen((char*)seed);
+
+ register uint64_t functionCode __asm__("0") = 0x83ULL;
+ register uint64_t paramBlock __asm__("1") = (uint64_t) █
+ register uint64_t gpr2 __asm__("2") = (uint64_t) &seed;
+ register uint64_t gpr3 __asm__("3") = seedLength;
+ __asm__ volatile (
+ ".insn rre, 0xb93c0000, %%r4, %%r2 \n"
+ :
+ : "d"(functionCode), "d"(paramBlock), "d"(gpr2), "d"(gpr3)
+ : "cc", "memory"
+ );
+
+ printf("seedLength = %ld\n", seedLength);
+ print_sha512_block();
+}
+
+static void test_ppno_sha512_gen()
+{
+ register uint64_t functionCode __asm__("0") = 0x3ULL;
+ register uint64_t paramBlock __asm__("1") = (uint64_t) █
+ register uint64_t gpr2 __asm__("2") = (uint64_t) &hash;
+ register uint64_t gpr3 __asm__("3") = HASH_COUNT;
+
+ __asm__ volatile (
+ "0: .insn rre, 0xb93c0000, %%r2, %%r4 \n"
+ " brc 1, 0 \n" /* handle partial completion */
+ :
+ : "d"(functionCode), "d"(paramBlock), "d"(gpr2), "d"(gpr3)
+ : "cc", "memory"
+ );
+
+ print_sha512_hash();
+ print_sha512_block();
+}
+
+static void test_ppno_sha512()
+{
+ printf(" === sha512_seed: ===\n");
+ test_ppno_sha512_seed();
+ return;
+ printf(" === sha512_gen: ===\n");
+ test_ppno_sha512_gen();
+
+ memset(hash, 0, sizeof(hash));
+}
+
+int main()
+{
+ size_t iteration;
+ if(!test_ppno_query())
+ return 1;
+
+ test(ppno_sha512);
+
+ return 0;
+}
--- /dev/null
+ === sha512_seed: ===
+seedLength = 0
+Current block state:
+0000000000000001
+0000000000000000
+003e226456bf592f
+441da979b2879409
+fc6f0e6192b8e7a7
+4ab3c33ebe6211bc
+4d4e1319fa123ab6
+5d34f1db2a58545c
+56fdbba651cffc31
+93bad6df02b799e3
+aaaa0c62548a2b53
+2621bf68328e3354
+678049ba8d9f1dc4
+ab9b96e348df64d6
+73f6e4aa1e2a772f
+8cd1a86bec20d436
+00f78e432b75bb7a
+60296ca43d1c6a9b
+ab6531522f5c1327
+2ceeb7b13076587a
+7becdef486f4f207
+ce5059ceb74ced11
+7309c2683f68a9d5
+e7458e632be82b57
+83754f8941faf016
+10f6eec6c45b195d
+018034f6220e3728
+9ac61bb9dc8e100c
+3f5e85d5a6537e0d
+8381c3f2ca21f4a5
+end of block.
+ === sha512_seed: ===
+seedLength = 11
+Current block state:
+0000000000000001
+0000000000000000
+004eb1b72a8a5538
+99417cd142461d13
+a4e7fdd4fe5bec3c
+2d84993f3c7dfecd
+81c7a13cd7a2e1dd
+1a72bbca637c9f35
+a9e73691ed4dab56
+2749ba30f6d1f93b
+4152bce5396879b3
+eddda1b12d8d083d
+a385ab81199f7f36
+1cf637397957dfd4
+bde5cab81dd1e412
+72931ea30835862a
+004469806a5b73d8
+aeaf1f9d0cae7c37
+8adf82179d0fca03
+e47741e426d6e305
+b84ebd07593a8225
+7f1c6928f7653501
+d1524486643b460f
+dc6c2402af8aa1af
+86043ac0ac159bb3
+41aaba7230c42a1f
+8be022ac67e2d5b1
+aa3c7ad10bb2f452
+7e91c25bb6bed695
+488c9efa3bedadcf
+end of block.
+ === sha512_seed: ===
+seedLength = 104
+Current block state:
+0000000000000001
+0000000000000000
+0065b7da3facb603
+a39ba35264e352c1
+528ff5b185090e96
+1e23953329b37e51
+ed239bc2b128f806
+f17aea98269226f8
+060c890a3052852c
+5df580976052e63a
+878588f4a8ee6646
+976b9a9768b6e005
+77bbd6d0c9343a00
+0faaa6e742c9dac8
+46a8a5506c2e9401
+9043d2bf21003936
+00a0ae8f2a4cb315
+782760f19dc3092d
+60060ae811f7ebda
+0b1b47143d57a5c2
+b15629bb9162d3cf
+c771535a7d6414b3
+e7e1adfb50e9534a
+bbb340f32c63cf50
+edf209d0db329cbc
+c4f31b47743eecc4
+04c3f5884af60998
+cfea12866218e29e
+5f2363a04592c804
+46d387494a63fec0
+end of block.
+ === sha512_seed: ===
+seedLength = 158
+Current block state:
+0000000000000001
+0000000000000000
+0045781698282993
+4da84904d0e89d9a
+edeaf6024283326f
+527c60b5a7aa2bab
+38e8d26031674b06
+ba0b249df891a67c
+5b364ea73ffea6c0
+e398d258a5ba5917
+c8b87f8f5e33b622
+a49b4ef408e3649e
+3da66fec9b5a6db0
+55d27c3cd383b31c
+5d5150f16c6d7451
+57b9fabe0e028fba
+003613e0f72a16fe
+dbddb109b3c327ba
+197eb5098dc8b240
+04add947f078aead
+6ce9e45c94c1ff24
+ea57dc533fec8520
+41bac59db19c7ac4
+6ebfb7055be23099
+e968ca0ae2f45635
+3a8096f2b2d33744
+c0c43d08f6797136
+c248bf582e5f5c5f
+e79d264972e8dc36
+f3045008abe8a6ca
+end of block.
+ === sha512_seed: ===
+seedLength = 37
+Current block state:
+0000000000000001
+0000000000000000
+00349033d29ca4c9
+dc2d01908a5791be
+c5cf75e0e3ca17cf
+7aed3a3df4990bcd
+87fc573cd5b13ba2
+6fdab83b8464cf82
+1a21797e3a9e7035
+3a06c2b83281705f
+f1d35bf5f068bc49
+a4ad202f6ce656e3
+4c56eb01058ce66d
+0e998ab91d77350a
+767fc3bc6bfa0b20
+07b6e69af6a59bea
+00ba228488e7ac41
+8c2d862fb775f15a
+d669a21dfa69f5b9
+4d134ec98379df9d
+81a2da79e623eca8
+b65a481b7aef0e89
+562a0f4743360402
+bde519a05363cbaf
+82608e5fe7ff8337
+c6030af815307cec
+fcef279b9735498b
+a90c7aebac65655a
+5871203aa8ec538e
+31f5e08b75f40311
+end of block.
+ === sha512_seed: ===
+seedLength = 301
+Current block state:
+0000000000000001
+0000000000000000
+00a2cd9e1108b253
+60f0f515f9436267
+6112b2da9b34e224
+b882cf25f62058f0
+e939aaef747e10f6
+796a3c2c0de9f9b5
+666e3c719678469c
+794413d6cc3ab283
+4d201dbffcbe9d75
+2e7826d27d1fd410
+e591db24ed522dae
+cda5699ea7ff7a10
+5ca19132941d57ed
+64cb80f5965dac83
+00b17bbb282a09b1
+3df5323781179ef5
+fcb78fac7c3e2aab
+88ba2880422809b2
+8d32558e30af18da
+c0030273bffaf70a
+0347475d9d030ce3
+6ce0c491d4a4f74d
+99daef279fdc1b49
+a0a862ab740eb859
+7dd377eaea992d4b
+ef9d973cb7138817
+303998590f022392
+e9a9a39e7ede4eaa
+end of block.
+ === sha512_seed: ===
+seedLength = 185
+Current block state:
+0000000000000001
+0000000000000000
+000108c265da6c80
+0192507c98a4c2c7
+452608dd4fe634a1
+c880a82e782378c7
+c66821bda7472a82
+ab84aa370d9c5dde
+316ce0a0348bc019
+b050cccaf67f178e
+d318738dab56b454
+ae5a95f3f935eb6b
+e77275efef5b7201
+e24690d9039b02b6
+21b3a48bf7ff98e4
+9fef58ec1f88fff7
+003caab18195c6df
+641cd44486e21c8e
+02c18c895dd6cda2
+e23b8cc41c9d0994
+bd7236c9322f655a
+298ee4aa4b23b737
+2c5feadf5cfcfe57
+6bf3bbd877e448eb
+bd1fcf1624ac28be
+041d143f9a9facda
+c9ae1027c3548419
+130e59d52f1d824e
+23af4bf89e504583
+fc017e2eb2a4d28a
+end of block.
+ === sha512_seed: ===
+seedLength = 125
+Current block state:
+0000000000000001
+0000000000000000
+00c4c2ecae21e662
+85b3e6f0042d2baf
+9e484b413ba43943
+9cd159886515ffad
+8cd8a3a5028ed440
+98f350a7b31a397c
+0eb36001904a1384
+07c57472df6c61f1
+a6f3ca8e6675078b
+d9d2325a92030fca
+2a1d516dcae5a8cb
+972cfde019bfcdc0
+74d1e498cb4f4882
+3308b466d31c3023
+00dd0698e4f695a1
+06257726d3b1f4bc
+3b9794261c1401d4
+fd1e82ac81472d04
+0afba5dedbf8003a
+4146305cf5b96e03
+9bb6672bd2ee8acc
+e00d1c05ee4d7639
+8313735518354ab0
+e3f1487cb8f79c96
+bc99d40d7a49afe8
+e8e1139b3864f068
+89ffd2a4948b1967
+e5cf0258f3082fd6
+end of block.
+ === sha512_seed: ===
+seedLength = 30
+Current block state:
+0000000000000001
+0000000000000000
+00b05877ea149ca9
+ddb7203e41de79bd
+f637815de27b89e1
+bda7afbb46634eec
+3f2f329c2509c348
+63a8e954764f99bc
+f0fb5d2ec9f2f996
+7c8cae8651e7b4e0
+4b3e2d33a90095eb
+42b1c6305af4487c
+3e116fc0b9700f1a
+94ac60577d4d1a47
+1c338f226410f72d
+d8231fa60e8f4e53
+0051e3f1e9e02151
+a1d8fc93f9d45b0c
+69e8fef198cb391e
+5952ee22ec3553e7
+fa6ddd6f23387117
+fa71fa2d88b1083d
+2bbc00ae1103daa2
+6eae1e34be4c53d0
+82cb422f047d0758
+f776d289c581ea5f
+b1f0959bef26d7fd
+da4c80f7309a266b
+755f72d68f47c6af
+6d1269427254b971
+end of block.
+ === sha512_seed: ===
+seedLength = 511
+Current block state:
+0000000000000001
+0000000000000000
+002c4bf217887798
+49700ed88f7abec4
+31f331722e562f90
+d3ada9dc23790d8d
+87dac381c4634a22
+5d32c8e62ee3fd4e
+19560e54b7464c02
+da8173723e36b679
+08dc7f22f28c9b8e
+8cfc13e645a13d68
+af4f40f6460007b6
+e98b97ba232b7049
+a8de0efcb7e74212
+187a977bfbe5ad54
+001f54f5715b6e7f
+dc7cec10e2fb028f
+7ff779259762f33f
+1342e9a91ff7015e
+5b12cfa2ab1afd2b
+af95a97ef7cf30ff
+cd5149f31f92a4e4
+c97e4b3af244ce23
+cc74abb567ed5f5b
+4de0d128dce98570
+f097484c991998fe
+3e17f4e5caa21f08
+1e33b2536984cae5
+48ace06455de2fc8
+end of block.
--- /dev/null
+prog: ppno
+prereq: test -e ppno && ../../../tests/s390x_features s390x-msa5
match = facilities[0] & FAC_BIT(45);
} else if (strcmp(feature, "s390x-vx") == 0 ) {
match = facilities[2] & FAC_BIT(0);
+ } else if (strcmp(feature, "s390x-msa5") == 0 ) {
+ match = facilities[0] & FAC_BIT(57); /* message security assist 5 facility */
} else {
return 2; // Unrecognised feature.
}