From 5d404b97d44001565403c7ca4a6a2eebfde4413d Mon Sep 17 00:00:00 2001 From: Andreas Arnez Date: Tue, 10 Sep 2024 18:38:49 +0200 Subject: [PATCH] s390x: Add MSA support Handle instructions that were added to z/Architecture with the message-security assist (MSA) facility or with one of its extensions up to MSA extension 9: km -- ``cipher message'' kmc -- ``cipher message with chaining'' kimd -- ``compute intermediate message digest'' klmd -- ``compute last message digest'' kmac -- ``compute message authentication code'' kmf -- ``cipher message with cipher feedback'' kmctr -- ``cipher message with counter'' kmo -- ``cipher message with output feedback'' pcc -- ``perform cryptographic computation'' kma -- ``cipher message with authentication'' kdsa -- ``compute digital signature authentication'' Each of these instructions has multiple functions. Support all functions described by MSA levels up to extension 9. Handle the instructions as "extensions" and essentially forward them to the instructions themselves, as long as they are available on the host. Note that the MSA-3 instruction pkcmo -- ``perform cryptographic key management operation'' will not be handled by this change, since it is privileged and should not occur in user-space programs. The MSA facilities are typically used by cryptographic libraries like OpenSSL or openCryptoki. So far Valgrind suppresses the facility bits indicating any MSA support, which causes such libraries to revert to a software implementation. This change enables running cryptographic applications under Valgrind without reverting to an alternate code path. --- NEWS | 3 + VEX/priv/guest_s390_toIR.c | 125 ++- VEX/pub/libvex_s390x_common.h | 11 + coregrind/m_extension/extension-s390x.c | 988 +++++++++++++++++++++++- docs/internals/s390-opcodes.csv | 22 +- none/tests/s390x/stfle.c | 8 +- none/tests/s390x/stfle.stdout.exp | 2 +- none/tests/s390x/stfle.stdout.exp-z16 | 2 +- 8 files changed, 1094 insertions(+), 67 deletions(-) diff --git a/NEWS b/NEWS index 1c3b8c5f8d..3906beedf9 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,9 @@ AMD64/macOS 10.13 and nanoMIPS/Linux. * S390X added support for the DFLTCC instruction provided by the deflate-conversion facility (z15/arch13). +* S390X added support for the instructions provided by the MSA facility + and MSA extensions 1-9. + * ==================== TOOL CHANGES =================== * ==================== FIXED BUGS ==================== diff --git a/VEX/priv/guest_s390_toIR.c b/VEX/priv/guest_s390_toIR.c index 1d8fb72cc5..c3816a10ed 100644 --- a/VEX/priv/guest_s390_toIR.c +++ b/VEX/priv/guest_s390_toIR.c @@ -19707,6 +19707,99 @@ s390_irgen_NNPA(void) return "nnpa"; } +static const HChar * +s390_irgen_KM(UChar r1, UChar r2) +{ + s390_insn_assert("km", r1 != 0 && r1 % 2 == 0 && r2 != 0 && r2 % 2 == 0); + extension(S390_EXT_KM, r1 | (r2 << 4)); + return "km"; +} + +static const HChar * +s390_irgen_KMC(UChar r1, UChar r2) +{ + s390_insn_assert("kmc", r1 != 0 && r1 % 2 == 0 && r2 != 0 && r2 % 2 == 0); + extension(S390_EXT_KMC, r1 | (r2 << 4)); + return "kmc"; +} + +static const HChar * +s390_irgen_KIMD(UChar r1, UChar r2) +{ + /* r1 is reserved */ + s390_insn_assert("kimd", r2 != 0 && r2 % 2 == 0); + extension(S390_EXT_KIMD, r1 | (r2 << 4)); + return "kimd"; +} + +static const HChar * +s390_irgen_KLMD(UChar r1, UChar r2) +{ + /* r1 is only used by some functions */ + s390_insn_assert("klmd", r2 != 0 && r2 % 2 == 0); + extension(S390_EXT_KLMD, r1 | (r2 << 4)); + return "klmd"; +} + +static const HChar * +s390_irgen_KMAC(UChar r1, UChar r2) +{ + /* r1 is ignored */ + s390_insn_assert("kmac", r2 != 0 && r2 % 2 == 0); + extension(S390_EXT_KMAC, r1 | (r2 << 4)); + return "kmac"; +} + +static const HChar * +s390_irgen_PCC(void) +{ + extension(S390_EXT_PCC, 0); + return "pcc"; +} + +static const HChar * +s390_irgen_KMCTR(UChar r3, UChar r1, UChar r2) +{ + s390_insn_assert("kmctr", r1 % 2 == 0 && r1 != 0 && r2 % 2 == 0 && r2 != 0 && + r3 % 2 == 0 && r3 != 0); + extension(S390_EXT_KMCTR, r1 | (r2 << 4) | (r3 << 8)); + return "kmctr"; +} + +static const HChar * +s390_irgen_KMO(UChar r1, UChar r2) +{ + s390_insn_assert("kmo", r1 != 0 && r1 % 2 == 0 && r2 != 0 && r2 % 2 == 0); + extension(S390_EXT_KMO, r1 | (r2 << 4)); + return "kmo"; +} + +static const HChar * +s390_irgen_KMF(UChar r1, UChar r2) +{ + s390_insn_assert("kmf", r1 != 0 && r1 % 2 == 0 && r2 != 0 && r2 % 2 == 0); + extension(S390_EXT_KMF, r1 | (r2 << 4)); + return "kmf"; +} + +static const HChar * +s390_irgen_KMA(UChar r3, UChar r1, UChar r2) +{ + s390_insn_assert("kma", r1 % 2 == 0 && r1 != 0 && r2 % 2 == 0 && r2 != 0 && + r3 % 2 == 0 && r3 != 0); + extension(S390_EXT_KMA, r1 | (r2 << 4) | (r3 << 8)); + return "kma"; +} + +static const HChar * +s390_irgen_KDSA(UChar r1, UChar r2) +{ + /* r1 is reserved */ + s390_insn_assert("kdsa", r2 != 0 && r2 % 2 == 0); + extension(S390_EXT_KDSA, r1 | (r2 << 4)); + return "kdsa"; +} + /* New insns are added here. If an insn is contingent on a facility being installed also check whether the list of supported facilities in function @@ -20457,7 +20550,8 @@ s390_decode_4byte_and_irgen(const UChar *bytes) RRE_r2(ovl)); goto ok; case 0xb91d: s390_format_RRE_RR(s390_irgen_DSGFR, RRE_r1(ovl), RRE_r2(ovl)); goto ok; - case 0xb91e: /* KMAC */ goto unimplemented; + case 0xb91e: s390_format_RRE_RR(s390_irgen_KMAC, RRE_r1(ovl), + RRE_r2(ovl)); goto ok; case 0xb91f: s390_format_RRE_RR(s390_irgen_LRVR, RRE_r1(ovl), RRE_r2(ovl)); goto ok; case 0xb920: s390_format_RRE_RR(s390_irgen_CGR, RRE_r1(ovl), @@ -20470,13 +20564,19 @@ s390_decode_4byte_and_irgen(const UChar *bytes) case 0xb927: s390_format_RRE_RR(s390_irgen_LHR, RRE_r1(ovl), RRE_r2(ovl)); goto ok; case 0xb928: /* PCKMO */ goto unimplemented; - case 0xb929: /* KMA */ goto unimplemented; - case 0xb92a: /* KMF */ goto unimplemented; - case 0xb92b: /* KMO */ goto unimplemented; - case 0xb92c: /* PCC */ goto unimplemented; - case 0xb92d: /* KMCTR */ goto unimplemented; - case 0xb92e: /* KM */ goto unimplemented; - case 0xb92f: /* KMC */ goto unimplemented; + case 0xb929: s390_format_RRF_R0RR2(s390_irgen_KMA, RRF4_r3(ovl), + RRF4_r1(ovl), RRF4_r2(ovl)); goto ok; + case 0xb92a: s390_format_RRE_RR(s390_irgen_KMF, RRE_r1(ovl), + RRE_r2(ovl)); goto ok; + case 0xb92b: s390_format_RRE_RR(s390_irgen_KMO, RRE_r1(ovl), + RRE_r2(ovl)); goto ok; + case 0xb92c: s390_format_E(s390_irgen_PCC); goto ok; + case 0xb92d: s390_format_RRF_R0RR2(s390_irgen_KMCTR, RRF4_r3(ovl), + RRF4_r1(ovl), RRF4_r2(ovl)); goto ok; + case 0xb92e: s390_format_RRE_RR(s390_irgen_KM, RRE_r1(ovl), + RRE_r2(ovl)); goto ok; + case 0xb92f: s390_format_RRE_RR(s390_irgen_KMC, RRE_r1(ovl), + RRE_r2(ovl)); goto ok; case 0xb930: s390_format_RRE_RR(s390_irgen_CGFR, RRE_r1(ovl), RRE_r2(ovl)); goto ok; case 0xb931: s390_format_RRE_RR(s390_irgen_CLGFR, RRE_r1(ovl), @@ -20484,12 +20584,15 @@ s390_decode_4byte_and_irgen(const UChar *bytes) case 0xb938: /* SORTL */ goto unimplemented; case 0xb939: s390_format_RRF_R0RR2(s390_irgen_DFLTCC, RRF4_r3(ovl), RRF4_r1(ovl), RRF4_r2(ovl)); goto ok; - case 0xb93a: /* KDSA */ goto unimplemented; + case 0xb93a: s390_format_RRE_RR(s390_irgen_KDSA, RRE_r1(ovl), + RRE_r2(ovl)); goto ok; case 0xb93b: s390_format_E(s390_irgen_NNPA); goto ok; case 0xb93c: s390_format_RRE_RR(s390_irgen_PPNO, RRE_r1(ovl), RRE_r2(ovl)); goto ok; - case 0xb93e: /* KIMD */ goto unimplemented; - case 0xb93f: /* KLMD */ goto unimplemented; + case 0xb93e: s390_format_RRE_RR(s390_irgen_KIMD, RRE_r1(ovl), + RRE_r2(ovl)); goto ok; + case 0xb93f: s390_format_RRE_RR(s390_irgen_KLMD, RRE_r1(ovl), + RRE_r2(ovl)); goto ok; case 0xb941: s390_format_RRF_UURF(s390_irgen_CFDTR, RRF2_m3(ovl), RRF2_m4(ovl), RRF2_r1(ovl), RRF2_r2(ovl)); goto ok; diff --git a/VEX/pub/libvex_s390x_common.h b/VEX/pub/libvex_s390x_common.h index 5b03fb7c4e..944347242d 100644 --- a/VEX/pub/libvex_s390x_common.h +++ b/VEX/pub/libvex_s390x_common.h @@ -120,6 +120,17 @@ #define S390_EXT_NNPA 2 #define S390_EXT_DFLT 3 #define S390_EXT_STFLE 4 +#define S390_EXT_KM 5 +#define S390_EXT_KMC 6 +#define S390_EXT_KIMD 7 +#define S390_EXT_KLMD 8 +#define S390_EXT_KMAC 9 +#define S390_EXT_PCC 10 +#define S390_EXT_KMCTR 11 +#define S390_EXT_KMO 12 +#define S390_EXT_KMF 13 +#define S390_EXT_KMA 14 +#define S390_EXT_KDSA 15 /*--------------------------------------------------------------*/ /*--- Miscellaneous ---*/ diff --git a/coregrind/m_extension/extension-s390x.c b/coregrind/m_extension/extension-s390x.c index 82bd231cf1..34e2e0409d 100644 --- a/coregrind/m_extension/extension-s390x.c +++ b/coregrind/m_extension/extension-s390x.c @@ -79,6 +79,89 @@ union reg_pair { unsigned __int128 pair; }; +/* To avoid code duplication, provide macros for generating inline assembly + functions where applicable. */ + +#define S390_DEFINE_DO_RRR_INSN(fname, opc) \ + static Int fname(ULong func, ULong parms, ULong* addr1, ULong* addr2, \ + ULong* len2, ULong* addr3, ULong* len3) \ + { \ + union reg_pair op1 = {{*addr1, 0}}; \ + union reg_pair op2 = {{*addr2, *len2}}; \ + union reg_pair op3 = {{*addr3, *len3}}; \ + register ULong reg0 asm("0") = func; \ + register void* reg1 asm("1") = (void*)parms; \ + UInt cc; \ + \ + asm volatile(".insn rrf, " #opc "0000, %[op1], %[op2], %[op3], 0\n" \ + "ipm %[cc]\n" \ + : [cc] "=d"(cc), [op1] "+a"(op1.pair), \ + [op2] "+a"(op2.pair), [op3] "+a"(op3.pair) \ + : "d"(reg0), "d"(reg1) \ + : "cc", "memory"); \ + *addr1 = op1.a; \ + *addr2 = op2.a; \ + *len2 = op2.b; \ + *addr3 = op3.a; \ + *len3 = op3.b; \ + return cc >> 28; \ + } + +#define S390_DEFINE_DO_RR_INSN(fname, opc) \ + static Int fname(ULong func, ULong parms, ULong* addr1, ULong* len1, \ + ULong* addr2, ULong* len2) \ + { \ + union reg_pair op1 = {{*addr1, *len1}}; \ + union reg_pair op2 = {{*addr2, *len2}}; \ + register ULong reg0 asm("0") = func; \ + register void* reg1 asm("1") = (void*)parms; \ + UInt cc; \ + \ + asm volatile(".insn rre, " #opc "0000, %[op1], %[op2]\n" \ + "ipm %[cc]\n" \ + : [cc] "=d"(cc), [op1] "+a"(op1.pair), [op2] "+a"(op2.pair) \ + : "d"(reg0), "d"(reg1) \ + : "cc", "memory"); \ + *addr1 = op1.a; \ + *len1 = op1.b; \ + *addr2 = op2.a; \ + *len2 = op2.b; \ + return cc >> 28; \ + } + +#define S390_DEFINE_DO_0R_INSN(fname, opc) \ + static Int fname(ULong func, ULong parms, ULong* addr2, ULong* len2) \ + { \ + union reg_pair op2 = {{*addr2, *len2}}; \ + register ULong reg0 asm("0") = func; \ + register void* reg1 asm("1") = (void*)parms; \ + UInt cc; \ + \ + asm volatile(".insn rre, " #opc "0000, 0, %[op2]\n" \ + "ipm %[cc]\n" \ + : [cc] "=d"(cc), [op2] "+a"(op2.pair) \ + : "d"(reg0), "d"(reg1) \ + : "cc", "memory"); \ + *addr2 = op2.a; \ + *len2 = op2.b; \ + return cc >> 28; \ + } + +#define S390_DEFINE_DO_00_INSN(fname, opc) \ + static Int fname(ULong func, ULong parms) \ + { \ + register ULong reg0 asm("0") = func; \ + register void* reg1 asm("1") = (void*)parms; \ + UInt cc; \ + \ + asm volatile(".insn rre, " #opc "0000, 0, 0\n" \ + "ipm %[cc]\n" \ + : [cc] "=d"(cc) \ + : "d"(reg0), "d"(reg1) \ + : "cc", "memory"); \ + return cc >> 28; \ + } + #define S390_SETBIT(x) (1UL << (63 - (x % 64))) #define S390_SETBITS(lo, hi) (((1UL << (hi + 1 - lo)) - 1) << (63 - (hi % 64))) @@ -104,31 +187,7 @@ static void s390_filter_functions(ULong* fc, /*--- PRNO (perform random number operation) ---*/ /*---------------------------------------------------------------*/ -static Int do_PRNO_insn(UChar func, - ULong parms, - ULong* addr1, - ULong* len1, - ULong* addr2, - ULong* len2) -{ - register UChar reg0 asm("0") = func; - register void* reg1 asm("1") = (void*)parms; - union reg_pair op1 = {{*addr1, *len1}}; - union reg_pair op2 = {{*addr2, *len2}}; - Int cc; - - asm volatile(".insn rre, 0xb93c0000, %[op1], %[op2]\n" - "ipm %[cc]\n" - "srl %[cc], 28\n" - : [cc] "=d"(cc), [op1] "+a"(op1.pair), [op2] "+a"(op2.pair) - : "d"(reg0), "d"(reg1) - : "cc", "memory"); - *addr1 = op1.a; - *len1 = op1.b; - *addr2 = op2.a; - *len2 = op2.b; - return cc; -} +S390_DEFINE_DO_RR_INSN(do_PRNO_insn, 0xb93c) /* PRNO functions that we support if the hardware does. */ static const ULong PRNO_functions[] = { @@ -769,9 +828,7 @@ static enum ExtensionError do_extension_STFLE(ThreadState* tst, ULong variant) PRE_MEM_WRITE(tst, "STFLE(bits)", addr, (last_dw + 1) * sizeof(ULong)); static const ULong accepted_facility[] = { /* === 0 .. 63 === */ - (S390_SETBITS(0, 16) - /* 17: message-security-assist, not supported */ - | S390_SETBITS(18, 19) + (S390_SETBITS(0, 19) /* 20: HFP-multiply-and-add/subtract, not supported */ | S390_SETBITS(21, 22) /* 23: HFP-unnormalized-extension, not supported */ @@ -790,16 +847,12 @@ static enum ExtensionError do_extension_STFLE(ThreadState* tst, ULong variant) /* 50: constrained transactional-execution, not supported */ | S390_SETBITS(51, 55) /* 56: unassigned */ - /* 57: MSA5, not supported */ - | S390_SETBITS(58, 63)), + | S390_SETBITS(57, 63)), /* === 64 .. 127 === */ (S390_SETBITS(64, 72) /* 73: transactional-execution, not supported */ - | S390_SETBITS(74, 75) - /* 76: MSA3, not supported */ - /* 77: MSA4, not supported */ - | S390_SETBITS(78, 78) + | S390_SETBITS(74, 78) /* 80: DFP packed-conversion, not supported */ /* 81: PPA-in-order, not supported */ | S390_SETBITS(82, 82) @@ -815,16 +868,13 @@ static enum ExtensionError do_extension_STFLE(ThreadState* tst, ULong variant) /* 137: unassigned */ | S390_SETBITS(138, 142) /* 143: unassigned */ - | S390_SETBITS(144, 145) - /* 146: MSA8, not supported */ - | S390_SETBITS(147, 149) + | S390_SETBITS(144, 149) /* 150: unassigned */ | S390_SETBITS(151, 151) /* 152: vector packed decimal enhancement, not supported */ /* 153: unassigned */ /* 154: unassigned */ - /* 155: MSA9, not supported */ - | S390_SETBITS(156, 156) + | S390_SETBITS(155, 156) /* 157-164: unassigned */ | S390_SETBITS(165, 165) /* 166-167: unassigned */ @@ -858,6 +908,844 @@ static enum ExtensionError do_extension_STFLE(ThreadState* tst, ULong variant) return ExtErr_OK; } +/*---------------------------------------------------------------*/ +/*--- KM (cypher message) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_RR_INSN(do_KM_insn, 0xb92e) + +/* List all the functions supported. This list provides the parameter block + sizes and will also be used for filtering the supported functions. The + function names are included for documentation purposes only. */ + +#define S390_DO_FUNCTIONS \ + S390_DO_FUNC(0, S390_KM_Query, 16) \ + S390_DO_FUNC(1, S390_KM_DEA, 8) \ + S390_DO_FUNC(2, S390_KM_TDEA_128, 16) \ + S390_DO_FUNC(3, S390_KM_TDEA_192, 24) \ + S390_DO_FUNC(9, S390_KM_Encrypted_DEA, 32) \ + S390_DO_FUNC(10, S390_KM_Encrypted_TDEA_128, 40) \ + S390_DO_FUNC(11, S390_KM_Encrypted_TDEA_192, 48) \ + S390_DO_FUNC(18, S390_KM_AES_128, 16) \ + S390_DO_FUNC(19, S390_KM_AES_192, 24) \ + S390_DO_FUNC(20, S390_KM_AES_256, 32) \ + S390_DO_FUNC(26, S390_KM_Encrypted_AES_128, 48) \ + S390_DO_FUNC(27, S390_KM_Encrypted_AES_192, 56) \ + S390_DO_FUNC(28, S390_KM_Encrypted_AES_256, 64) \ + S390_DO_FUNC(50, S390_KM_XTS_AES_128, 32) \ + S390_DO_FUNC(52, S390_KM_XTS_AES_256, 48) \ + S390_DO_FUNC(58, S390_KM_XTS_Encrypted_AES_128, 64) \ + S390_DO_FUNC(60, S390_KM_XTS_Encrypted_AES_256, 80) + +#define S390_DO_FUNC(fc, name, plen) [fc] = plen, +static const UChar S390_KM_parms_len[] = {S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#define S390_DO_FUNC(fc, name, plen) | S390_SETBIT(fc) +static const ULong S390_KM_supported_fc[] = {0 S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#undef S390_DO_FUNCTIONS + +static enum ExtensionError do_extension_KM(ThreadState* tst, ULong variant) +{ + UChar r1 = variant & 0xf; + UChar r2 = (variant >> 4) & 0xf; + UChar func = READ_FUNCTION_CODE(tst, "KM"); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KM(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong orig_addr1, orig_len2 = 0; + ULong addr1 = 0, len1 = 0, addr2 = 0, len2 = 0; + + if (fc < sizeof(S390_KM_parms_len) / sizeof(S390_KM_parms_len[0])) + parms_len = S390_KM_parms_len[fc]; + if (parms_len == 0) + return INSN_ERR("KM: unknown function code\n"); + + if (fc == 0) { // Query + PRE_MEM_WRITE(tst, "KM(parms)", parms, parms_len); + cc = do_KM_insn(func, parms, &addr1, &len1, &addr2, &len2); + s390_filter_functions((ULong*)parms, parms_len, S390_KM_supported_fc, + sizeof(S390_KM_supported_fc)); + POST_MEM_WRITE(tst, parms, parms_len); + } else { + addr1 = orig_addr1 = READ_GPR(tst, "KM(op1_addr)", r1); + addr2 = READ_GPR(tst, "KM(op2_addr)", r2); + len2 = orig_len2 = READ_GPR(tst, "KM(op2_len)", r2 + 1); + PRE_MEM_READ(tst, "KM(parms)", parms, parms_len); + PRE_MEM_WRITE(tst, "KM(op1)", addr1, len2); + PRE_MEM_READ(tst, "KM(op2)", addr2, len2); + cc = do_KM_insn(func, parms, &addr1, &len1, &addr2, &len2); + WRITE_GPR(tst, r1, addr1); + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + POST_MEM_WRITE(tst, orig_addr1, orig_len2 - len2); + } + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- KMC (cypher message with chaining) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_RR_INSN(do_KMC_insn, 0xb92f) + +#define S390_DO_FUNCTIONS \ + S390_DO_FUNC(0, S390_KMC_Query, 16) \ + S390_DO_FUNC(1, S390_KMC_DEA, 16) \ + S390_DO_FUNC(2, S390_KMC_TDEA_128, 24) \ + S390_DO_FUNC(3, S390_KMC_TDEA_192, 32) \ + S390_DO_FUNC(9, S390_KMC_Encrypted_DEA, 40) \ + S390_DO_FUNC(10, S390_KMC_Encrypted_TDEA_128, 48) \ + S390_DO_FUNC(11, S390_KMC_Encrypted_TDEA_192, 56) \ + S390_DO_FUNC(18, S390_KMC_AES_128, 32) \ + S390_DO_FUNC(19, S390_KMC_AES_192, 40) \ + S390_DO_FUNC(20, S390_KMC_AES_256, 48) \ + S390_DO_FUNC(26, S390_KMC_Encrypted_AES_128, 64) \ + S390_DO_FUNC(27, S390_KMC_Encrypted_AES_192, 72) \ + S390_DO_FUNC(28, S390_KMC_Encrypted_AES_256, 80) + +#define S390_DO_FUNCTIONS1 S390_DO_FUNC(67, S390_KMC_PRNG, 32) + +#define S390_DO_FUNC(fc, name, plen) [fc] = plen, +static const UChar S390_KMC_parms_len[] = { + S390_DO_FUNCTIONS S390_DO_FUNCTIONS1}; +#undef S390_DO_FUNC + +#define S390_DO_FUNC(fc, name, plen) | S390_SETBIT(fc) +static const ULong S390_KMC_supported_fc[] = {0 S390_DO_FUNCTIONS, + 0 S390_DO_FUNCTIONS1}; +#undef S390_DO_FUNC + +#undef S390_DO_FUNCTIONS +#undef S390_DO_FUNCTIONS1 + +static enum ExtensionError do_extension_KMC(ThreadState* tst, ULong variant) +{ + UChar r1 = variant & 0xf; + UChar r2 = (variant >> 4) & 0xf; + UChar func = READ_FUNCTION_CODE(tst, "KMC"); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KMC(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong orig_addr1, orig_len2 = 0; + ULong addr1 = 0, len1 = 0, addr2 = 0, len2 = 0; + + if (fc < sizeof(S390_KMC_parms_len) / sizeof(S390_KMC_parms_len[0])) + parms_len = S390_KMC_parms_len[fc]; + if (parms_len == 0) + return INSN_ERR("KMC: unknown function code\n"); + + PRE_MEM_WRITE(tst, "KMC(parms)", parms, parms_len); + if (fc == 0) { // Query + cc = do_KMC_insn(func, parms, &addr1, &len1, &addr2, &len2); + s390_filter_functions((ULong*)parms, parms_len, S390_KMC_supported_fc, + sizeof(S390_KMC_supported_fc)); + } else { + addr1 = orig_addr1 = READ_GPR(tst, "KMC(op1_addr)", r1); + addr2 = READ_GPR(tst, "KMC(op2_addr)", r2); + len2 = orig_len2 = READ_GPR(tst, "KMC(op2_len)", r2 + 1); + PRE_MEM_READ(tst, "KMC(parms)", parms, parms_len); + PRE_MEM_WRITE(tst, "KMC(op1)", addr1, len2); + PRE_MEM_READ(tst, "KMC(op2)", addr2, len2); + cc = do_KMC_insn(func, parms, &addr1, &len1, &addr2, &len2); + WRITE_GPR(tst, r1, addr1); + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + POST_MEM_WRITE(tst, orig_addr1, orig_len2 - len2); + } + POST_MEM_WRITE(tst, parms, parms_len); + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- KIMD (compute intermediate message digest) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_0R_INSN(do_KIMD_insn, 0xb93e) + +#define S390_DO_FUNCTIONS \ + S390_DO_FUNC(0, S390_KIMD_Query, 16) \ + S390_DO_FUNC(1, S390_KIMD_SHA_1, 20) \ + S390_DO_FUNC(2, S390_KIMD_SHA_256, 32) \ + S390_DO_FUNC(3, S390_KIMD_SHA_512, 64) \ + S390_DO_FUNC(32, S390_KIMD_SHA3_224, 200) \ + S390_DO_FUNC(33, S390_KIMD_SHA3_256, 200) \ + S390_DO_FUNC(34, S390_KIMD_SHA3_384, 200) \ + S390_DO_FUNC(35, S390_KIMD_SHA3_512, 200) \ + S390_DO_FUNC(36, S390_KIMD_SHAKE_128, 200) \ + S390_DO_FUNC(37, S390_KIMD_SHAKE_256, 200) \ + S390_DO_FUNC(65, S390_KIMD_GHASH, 32) + +#define S390_DO_FUNC(fc, name, plen) [fc] = plen, +static const UChar S390_KIMD_parms_len[] = {S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#define S390_DO_FUNC(fc, name, plen) | S390_SETBIT(fc) +static const ULong S390_KIMD_supported_fc[] = {0 S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#undef S390_DO_FUNCTIONS + +static enum ExtensionError do_extension_KIMD(ThreadState* tst, ULong variant) +{ + UChar r2 = (variant >> 4) & 0xf; + UChar func = READ_FUNCTION_CODE(tst, "KIMD"); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KIMD(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong addr2 = 0, len2 = 0; + + if (fc < sizeof(S390_KIMD_parms_len) / sizeof(S390_KIMD_parms_len[0])) + parms_len = S390_KIMD_parms_len[fc]; + if (parms_len == 0) + return INSN_ERR("KIMD: unknown function code\n"); + + if (fc == 0) { // Query + PRE_MEM_WRITE(tst, "KIMD(parms)", parms, parms_len); + cc = do_KIMD_insn(func, parms, &addr2, &len2); + s390_filter_functions((ULong*)parms, parms_len, S390_KIMD_supported_fc, + sizeof(S390_KIMD_supported_fc)); + POST_MEM_WRITE(tst, parms, parms_len); + } else { + addr2 = READ_GPR(tst, "KIMD(op2_addr)", r2); + len2 = READ_GPR(tst, "KIMD(op2_len)", r2 + 1); + PRE_MEM_READ(tst, "KIMD(parms)", parms, parms_len); + PRE_MEM_WRITE(tst, "KIMD(parms)", parms, parms_len); + PRE_MEM_READ(tst, "KIMD(op2)", addr2, len2); + cc = do_KIMD_insn(func, parms, &addr2, &len2); + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + POST_MEM_WRITE(tst, parms, parms_len); + } + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- KLMD (compute last message digest) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_RR_INSN(do_KLMD_insn, 0xb93f) + +#define S390_DO_FUNCTIONS \ + S390_DO_FUNC(0, S390_KLMD_Query, 16) \ + S390_DO_FUNC(1, S390_KLMD_SHA_1, 28) \ + S390_DO_FUNC(2, S390_KLMD_SHA_256, 40) \ + S390_DO_FUNC(3, S390_KLMD_SHA_512, 80) \ + S390_DO_FUNC(32, S390_KLMD_SHA3_224, 200) \ + S390_DO_FUNC(33, S390_KLMD_SHA3_256, 200) \ + S390_DO_FUNC(34, S390_KLMD_SHA3_384, 200) \ + S390_DO_FUNC(35, S390_KLMD_SHA3_512, 200) \ + S390_DO_FUNC(36, S390_KLMD_SHAKE_128, 200) \ + S390_DO_FUNC(37, S390_KLMD_SHAKE_256, 200) + +#define S390_DO_FUNC(fc, name, plen) [fc] = plen, +static const UChar S390_KLMD_parms_len[] = {S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#define S390_DO_FUNC(fc, name, plen) | S390_SETBIT(fc) +static const ULong S390_KLMD_supported_fc[] = {0 S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#undef S390_DO_FUNCTIONS + +static enum ExtensionError do_extension_KLMD(ThreadState* tst, ULong variant) +{ + UChar r1 = variant & 0xf; + UChar r2 = (variant >> 4) & 0xf; + ULong func = READ_GPR(tst, "KLMD(r0)", 0); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KLMD(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong orig_addr1 = 0, orig_len1 = 0; + ULong addr1 = 0, len1 = 0, addr2 = 0, len2 = 0; + + if (fc < sizeof(S390_KLMD_parms_len) / sizeof(S390_KLMD_parms_len[0])) + parms_len = S390_KLMD_parms_len[fc]; + if (parms_len == 0) + return INSN_ERR("KLMD: unknown function code\n"); + PRE_MEM_WRITE(tst, "KLMD(parms)", parms, parms_len); + + if (fc == 0) { // Query + cc = do_KLMD_insn(func, parms, &addr1, &len1, &addr2, &len2); + s390_filter_functions((ULong*)parms, parms_len, S390_KLMD_supported_fc, + sizeof(S390_KLMD_supported_fc)); + } else { + /* The "shake" functions use the first operand */ + Bool have_op1 = fc >= 36 && fc <= 37; + + PRE_MEM_READ(tst, "KLMD(parms)", parms, parms_len); + if (have_op1) { + if (r1 == 0 || r1 % 2 != 0) + return INSN_ERR("KLMD: bad r1 field"); + addr1 = orig_addr1 = READ_GPR(tst, "KLMD(op1_addr)", r1); + len1 = orig_len1 = READ_GPR(tst, "KLMD(op1_len)", r1 + 1); + PRE_MEM_WRITE(tst, "KLMD(op1)", addr1, len1); + } + addr2 = READ_GPR(tst, "KLMD(op2_addr)", r2); + len2 = READ_GPR(tst, "KLMD(op2_len)", r2 + 1); + PRE_MEM_READ(tst, "KLMD(op2)", addr2, len2); + cc = do_KLMD_insn(func, parms, &addr1, &len1, &addr2, &len2); + if (have_op1) { + WRITE_GPR(tst, r1, addr1); + WRITE_GPR(tst, r1 + 1, len1); + POST_MEM_WRITE(tst, orig_addr1, orig_len1 - len1); + } + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + } + POST_MEM_WRITE(tst, parms, parms_len); + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- KMAC (compute message authentication code) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_0R_INSN(do_KMAC_insn, 0xb91e) + +#define S390_DO_FUNCTIONS \ + S390_DO_FUNC(0, S390_KMAC_Query, 16) \ + S390_DO_FUNC(1, S390_KMAC_DEA, 16) \ + S390_DO_FUNC(2, S390_KMAC_TDEA_128, 24) \ + S390_DO_FUNC(3, S390_KMAC_TDEA_192, 32) \ + S390_DO_FUNC(9, S390_KMAC_Encrypted_DEA, 40) \ + S390_DO_FUNC(10, S390_KMAC_Encrypted_TDEA_128, 48) \ + S390_DO_FUNC(11, S390_KMAC_Encrypted_TDEA_192, 56) \ + S390_DO_FUNC(18, S390_KMAC_AES_128, 32) \ + S390_DO_FUNC(19, S390_KMAC_AES_192, 40) \ + S390_DO_FUNC(20, S390_KMAC_AES_256, 48) \ + S390_DO_FUNC(26, S390_KMAC_Encrypted_AES_128, 64) \ + S390_DO_FUNC(27, S390_KMAC_Encrypted_AES_192, 72) \ + S390_DO_FUNC(28, S390_KMAC_Encrypted_AES_256, 80) + +#define S390_DO_FUNC(fc, name, plen) [fc] = plen, +static const UChar S390_KMAC_parms_len[] = {S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#define S390_DO_FUNC(fc, name, plen) | S390_SETBIT(fc) +static const ULong S390_KMAC_supported_fc[] = {0 S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#undef S390_DO_FUNCTIONS + +static enum ExtensionError do_extension_KMAC(ThreadState* tst, ULong variant) +{ + UChar r2 = (variant >> 4) & 0xf; + UChar func = READ_FUNCTION_CODE(tst, "KMAC"); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KMAC(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong addr2 = 0, len2 = 0; + + if (fc < sizeof(S390_KMAC_parms_len) / sizeof(S390_KMAC_parms_len[0])) + parms_len = S390_KMAC_parms_len[fc]; + if (parms_len == 0) + return INSN_ERR("KMAC: unknown function code\n"); + + if (fc == 0) { // Query + PRE_MEM_WRITE(tst, "KMAC(parms)", parms, parms_len); + cc = do_KMAC_insn(func, parms, &addr2, &len2); + s390_filter_functions((ULong*)parms, parms_len, S390_KMAC_supported_fc, + sizeof(S390_KMAC_supported_fc)); + POST_MEM_WRITE(tst, parms, parms_len); + } else { + addr2 = READ_GPR(tst, "KMAC(op2_addr)", r2); + len2 = READ_GPR(tst, "KMAC(op2_len)", r2 + 1); + PRE_MEM_READ(tst, "KMAC(parms)", parms, parms_len); + PRE_MEM_WRITE(tst, "KMAC(parms)", parms, parms_len); + PRE_MEM_READ(tst, "KMAC(op2)", addr2, len2); + cc = do_KMAC_insn(func, parms, &addr2, &len2); + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + if (cc != 1) + POST_MEM_WRITE(tst, parms, parms_len); + } + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- PCC (perform cryptographic computation) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_00_INSN(do_PCC_insn, 0xb92c) + +enum PCC_function_class { + PCC_Unassigned = 0, + PCC_Query, + PCC_Compute_Last_Block_CMAC, + PCC_Compute_XTS_Parameter, + PCC_Scalar_Multiply, +}; + +#define S390_DO_FUNCTIONS \ + S390_DO_FUNC(0, PCC_Query, Query, 16, 16) \ + S390_DO_FUNC(1, PCC_Compute_Last_Block_CMAC, Using_DEA, 8, 32) \ + S390_DO_FUNC(2, PCC_Compute_Last_Block_CMAC, Using_TDEA_128, 8, 40) \ + S390_DO_FUNC(3, PCC_Compute_Last_Block_CMAC, Using_TDEA_192, 8, 48) \ + S390_DO_FUNC(9, PCC_Compute_Last_Block_CMAC, Using_Encrypted_DEA, 8, 56) \ + S390_DO_FUNC(10, PCC_Compute_Last_Block_CMAC, Using_Encrypted_TDEA_128, 8, \ + 64) \ + S390_DO_FUNC(11, PCC_Compute_Last_Block_CMAC, Using_Encrypted_TDEA_192, 8, \ + 72) \ + S390_DO_FUNC(18, PCC_Compute_Last_Block_CMAC, Using_AES_128, 16, 56) \ + S390_DO_FUNC(19, PCC_Compute_Last_Block_CMAC, Using_AES_192, 16, 64) \ + S390_DO_FUNC(20, PCC_Compute_Last_Block_CMAC, Using_AES_256, 16, 72) \ + S390_DO_FUNC(26, PCC_Compute_Last_Block_CMAC, Using_Encrypted_AES_128, 16, \ + 88) \ + S390_DO_FUNC(27, PCC_Compute_Last_Block_CMAC, Using_Encrypted_AES_192, 16, \ + 96) \ + S390_DO_FUNC(28, PCC_Compute_Last_Block_CMAC, Using_Encrypted_AES_256, 16, \ + 104) \ + S390_DO_FUNC(50, PCC_Compute_XTS_Parameter, Using_AES_128, 16, 80) \ + S390_DO_FUNC(52, PCC_Compute_XTS_Parameter, Using_AES_256, 16, 96) \ + S390_DO_FUNC(58, PCC_Compute_XTS_Parameter, Using_Encrypted_AES_128, 16, \ + 112) \ + S390_DO_FUNC(60, PCC_Compute_XTS_Parameter, Using_Encrypted_AES_256, 16, 128) + +#define S390_DO_FUNCTIONS1 \ + S390_DO_FUNC(64, PCC_Scalar_Multiply, P256, 64, 168) \ + S390_DO_FUNC(65, PCC_Scalar_Multiply, P384, 96, 248) \ + S390_DO_FUNC(66, PCC_Scalar_Multiply, P521, 160, 408) \ + S390_DO_FUNC(72, PCC_Scalar_Multiply, Ed25519, 64, 168) \ + S390_DO_FUNC(73, PCC_Scalar_Multiply, Ed448, 128, 328) \ + S390_DO_FUNC(80, PCC_Scalar_Multiply, X25519, 32, 104) \ + S390_DO_FUNC(81, PCC_Scalar_Multiply, X448, 64, 200) + +#define S390_DO_FUNC(fc, class, name, rlen, plen) \ + [fc] = {class, rlen / 8, plen / 8}, +static const struct { + UChar fc_class : 3; + UChar result_len : 5; + UChar parms_len; +} S390_PCC_fc_info[] = {S390_DO_FUNCTIONS S390_DO_FUNCTIONS1}; +#undef S390_DO_FUNC + +#define S390_DO_FUNC(fc, class, name, rlen, plen) | S390_SETBIT(fc) +static const ULong S390_PCC_supported_fc[] = {0 S390_DO_FUNCTIONS, + 0 S390_DO_FUNCTIONS1}; +#undef S390_DO_FUNC + +#undef S390_DO_FUNCTIONS +#undef S390_DO_FUNCTIONS1 + +static enum ExtensionError do_extension_PCC(ThreadState* tst, ULong variant) +{ + UChar func = READ_FUNCTION_CODE(tst, "PCC"); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "PCC(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + UChar fc_class = PCC_Unassigned; + ULong result_offs, result_len, msg_len; + + if (fc < sizeof(S390_PCC_fc_info) / sizeof(S390_PCC_fc_info[0])) { + fc_class = S390_PCC_fc_info[fc].fc_class; + result_len = S390_PCC_fc_info[fc].result_len * 8; + parms_len = S390_PCC_fc_info[fc].parms_len * 8; + } + + switch (fc_class) { + case PCC_Query: + PRE_MEM_WRITE(tst, "PCC(parms)", parms, parms_len); + cc = do_PCC_insn(func, parms); + s390_filter_functions((ULong*)parms, parms_len, S390_PCC_supported_fc, + sizeof(S390_PCC_supported_fc)); + result_offs = 0; + break; + case PCC_Compute_Last_Block_CMAC: + /* result_len == sizeof(ICV) == sizeof(message) */ + PRE_MEM_READ(tst, "PCC(parms)", parms, 8); + msg_len = (*(UChar*)parms + 7) / 8; + if (msg_len > result_len) + msg_len = result_len; + if (msg_len != 0) { + PRE_MEM_READ(tst, "PCC(parms)", parms + 8, msg_len); + } + result_offs = 8 + result_len; + PRE_MEM_READ(tst, "PCC(parms)", parms + result_offs, + parms_len - result_offs); + PRE_MEM_WRITE(tst, "PCC(parms.CMAC)", parms + result_offs, result_len); + cc = do_PCC_insn(func, parms); + break; + case PCC_Compute_XTS_Parameter: + /* result_len == sizeof(XTS parameter) */ + result_offs = parms_len - result_len; + PRE_MEM_READ(tst, "PCC(parms)", parms, result_offs - 16); + if (*(ULong*)(parms + result_offs - 32) != 0) { + /* block sequential number non-zero -> read intermediate bit index t */ + result_offs -= 16; + result_len += 16; + PRE_MEM_READ(tst, "PCC(parms.t)", parms + result_offs, 16); + if (*(ULong*)(parms + result_offs) != 0) { + /* t != 0: read partial XTS parameter */ + PRE_MEM_READ(tst, "PCC(parms.XTS)", parms + result_offs + 16, + result_len); + } + } + PRE_MEM_WRITE(tst, "PCC(parms)", parms + result_offs, result_len); + cc = do_PCC_insn(func, parms); + break; + case PCC_Scalar_Multiply: + /* result_len == sizeof(result) == sizeof(source) */ + result_offs = 0; + PRE_MEM_READ(tst, "PCC(parms)", parms + result_len, + parms_len - result_len); + PRE_MEM_WRITE(tst, "PCC(parms)", parms, 4096); + if (*(ULong*)(parms + parms_len - 8) != 0) { + /* continuation -> read the continuation state buffer as well */ + PRE_MEM_READ(tst, "PCC(parms.CSB)", parms + parms_len, + 4096 - parms_len); + } + cc = do_PCC_insn(func, parms); + break; + default: + return INSN_ERR("PCC: unknown function code\n"); + } + + if (cc == 0 || cc == 3) // normal or partial completion + POST_MEM_WRITE(tst, parms + result_offs, result_len); + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- KMCTR (cypher message with counter) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_RRR_INSN(do_KMCTR_insn, 0xb92d) + +#define S390_DO_FUNCTIONS \ + S390_DO_FUNC(0, S390_KMCTR_Query, 16) \ + S390_DO_FUNC(1, S390_KMCTR_DEA, 8) \ + S390_DO_FUNC(2, S390_KMCTR_TDEA_128, 16) \ + S390_DO_FUNC(3, S390_KMCTR_TDEA_192, 24) \ + S390_DO_FUNC(9, S390_KMCTR_Encrypted_DEA, 32) \ + S390_DO_FUNC(10, S390_KMCTR_Encrypted_TDEA_128, 40) \ + S390_DO_FUNC(11, S390_KMCTR_Encrypted_TDEA_192, 48) \ + S390_DO_FUNC(18, S390_KMCTR_AES_128, 16) \ + S390_DO_FUNC(19, S390_KMCTR_AES_192, 24) \ + S390_DO_FUNC(20, S390_KMCTR_AES_256, 32) \ + S390_DO_FUNC(26, S390_KMCTR_Encrypted_AES_128, 48) \ + S390_DO_FUNC(27, S390_KMCTR_Encrypted_AES_192, 56) \ + S390_DO_FUNC(28, S390_KMCTR_Encrypted_AES_256, 64) + +#define S390_DO_FUNC(fc, name, plen) [fc] = plen, +static const UChar S390_KMCTR_parms_len[] = {S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#define S390_DO_FUNC(fc, name, plen) | S390_SETBIT(fc) +static const ULong S390_KMCTR_supported_fc[] = {0 S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#undef S390_DO_FUNCTIONS + +enum { + S390_KMCTR_parms_len_n = + sizeof(S390_KMCTR_parms_len) / sizeof(S390_KMCTR_parms_len[0]) +}; + +static enum ExtensionError do_extension_KMCTR(ThreadState* tst, ULong variant) +{ + UChar r1 = variant & 0xf; + UChar r2 = (variant >> 4) & 0xf; + UChar r3 = (variant >> 8) & 0xf; + UChar func = READ_FUNCTION_CODE(tst, "KMCTR"); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KMCTR(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong orig_addr1, orig_len2 = 0; + ULong addr1 = 0, addr2 = 0, addr3 = 0, len2 = 0, len3 = 0; + + if (fc < S390_KMCTR_parms_len_n) + parms_len = S390_KMCTR_parms_len[fc]; + if (parms_len == 0) + return INSN_ERR("KMCTR: unknown function code\n"); + + if (fc == 0) { // Query + PRE_MEM_WRITE(tst, "KMCTR(parms)", parms, parms_len); + cc = do_KMCTR_insn(func, parms, &addr1, &addr2, &len2, &addr3, &len3); + s390_filter_functions((ULong*)parms, parms_len, S390_KMCTR_supported_fc, + sizeof(S390_KMCTR_supported_fc)); + POST_MEM_WRITE(tst, parms, parms_len); + } else { + addr1 = orig_addr1 = READ_GPR(tst, "KMCTR(op1_addr)", r1); + addr2 = READ_GPR(tst, "KMCTR(op2_addr)", r2); + addr3 = READ_GPR(tst, "KMCTR(op3_addr)", r3); + len2 = orig_len2 = READ_GPR(tst, "KMCTR(op2_len)", r2 + 1); + PRE_MEM_READ(tst, "KMCTR(parms)", parms, parms_len); + PRE_MEM_WRITE(tst, "KMCTR(op1)", addr1, len2); + PRE_MEM_READ(tst, "KMCTR(op2)", addr2, len2); + PRE_MEM_READ(tst, "KMCTR(op3)", addr3, len2); + cc = do_KMCTR_insn(func, parms, &addr1, &addr2, &len2, &addr3, &len3); + WRITE_GPR(tst, r1, addr1); + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + WRITE_GPR(tst, r3, addr3); + POST_MEM_WRITE(tst, orig_addr1, orig_len2 - len2); + } + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- KMO (cypher message with output feedback) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_RR_INSN(do_KMO_insn, 0xb92b) + +/* Same functions and parameter block sizes as for KMCTR */ +static const UChar* const S390_KMO_parms_len = S390_KMCTR_parms_len; +static const ULong* const S390_KMO_supported_fc = S390_KMCTR_supported_fc; +enum { S390_KMO_parms_len_n = S390_KMCTR_parms_len_n }; + +static enum ExtensionError do_extension_KMO(ThreadState* tst, ULong variant) +{ + UChar r1 = variant & 0xf; + UChar r2 = (variant >> 4) & 0xf; + UChar func = READ_FUNCTION_CODE(tst, "KMO"); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KMO(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong orig_addr1, orig_len2 = 0; + ULong addr1 = 0, len1 = 0, addr2 = 0, len2 = 0; + + if (fc < S390_KMO_parms_len_n) + parms_len = S390_KMO_parms_len[fc]; + if (parms_len == 0) + return INSN_ERR("KMO: unknown function code\n"); + + PRE_MEM_WRITE(tst, "KMO(parms)", parms, parms_len); + if (fc == 0) { // Query + cc = do_KMO_insn(func, parms, &addr1, &len1, &addr2, &len2); + s390_filter_functions((ULong*)parms, parms_len, S390_KMO_supported_fc, + sizeof(S390_KMO_supported_fc)); + } else { + addr1 = orig_addr1 = READ_GPR(tst, "KMO(op1_addr)", r1); + addr2 = READ_GPR(tst, "KMO(op2_addr)", r2); + len2 = orig_len2 = READ_GPR(tst, "KMO(op2_len)", r2 + 1); + PRE_MEM_READ(tst, "KMO(parms)", parms, parms_len); + PRE_MEM_WRITE(tst, "KMO(op1)", addr1, len2); + PRE_MEM_READ(tst, "KMO(op2)", addr2, len2); + cc = do_KMO_insn(func, parms, &addr1, &len1, &addr2, &len2); + WRITE_GPR(tst, r1, addr1); + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + POST_MEM_WRITE(tst, orig_addr1, orig_len2 - len2); + } + POST_MEM_WRITE(tst, parms, parms_len); + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- KMF (cypher message with output feedback) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_RR_INSN(do_KMF_insn, 0xb92a) + +/* Same functions and parameter block sizes as for KMCTR */ +static const UChar* const S390_KMF_parms_len = S390_KMCTR_parms_len; +static const ULong* const S390_KMF_supported_fc = S390_KMCTR_supported_fc; +enum { S390_KMF_parms_len_n = S390_KMCTR_parms_len_n }; + +static enum ExtensionError do_extension_KMF(ThreadState* tst, ULong variant) +{ + UChar r1 = variant & 0xf; + UChar r2 = (variant >> 4) & 0xf; + ULong func = READ_GPR(tst, "KLMD(r0)", 0); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KMF(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong orig_addr1, orig_len2 = 0; + ULong addr1 = 0, len1 = 0, addr2 = 0, len2 = 0; + + if (fc < S390_KMF_parms_len_n) + parms_len = S390_KMF_parms_len[fc]; + if (parms_len == 0) + return INSN_ERR("KMF: unknown function code\n"); + + PRE_MEM_WRITE(tst, "KMF(parms)", parms, parms_len); + if (fc == 0) { // Query + cc = do_KMF_insn(func, parms, &addr1, &len1, &addr2, &len2); + s390_filter_functions((ULong*)parms, parms_len, S390_KMF_supported_fc, + sizeof(S390_KMF_supported_fc)); + } else { + addr1 = orig_addr1 = READ_GPR(tst, "KMF(op1_addr)", r1); + addr2 = READ_GPR(tst, "KMF(op2_addr)", r2); + len2 = orig_len2 = READ_GPR(tst, "KMF(op2_len)", r2 + 1); + PRE_MEM_READ(tst, "KMF(parms)", parms, parms_len); + PRE_MEM_WRITE(tst, "KMF(op1)", addr1, len2); + PRE_MEM_READ(tst, "KMF(op2)", addr2, len2); + cc = do_KMF_insn(func, parms, &addr1, &len1, &addr2, &len2); + WRITE_GPR(tst, r1, addr1); + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + POST_MEM_WRITE(tst, orig_addr1, orig_len2 - len2); + } + POST_MEM_WRITE(tst, parms, parms_len); + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- KMA (cypher message with authentication) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_RRR_INSN(do_KMA_insn, 0xb929) + +#define S390_DO_FUNCTIONS \ + S390_DO_FUNC(0, S390_KMA_Query, 16) \ + S390_DO_FUNC(18, S390_KMA_GCM_AES_128, 96) \ + S390_DO_FUNC(19, S390_KMA_GCM_AES_192, 104) \ + S390_DO_FUNC(20, S390_KMA_GCM_AES_256, 112) \ + S390_DO_FUNC(26, S390_KMA_GCM_Encrypted_AES_128, 128) \ + S390_DO_FUNC(27, S390_KMA_GCM_Encrypted_AES_192, 136) \ + S390_DO_FUNC(28, S390_KMA_GCM_Encrypted_AES_256, 144) + +#define S390_DO_FUNC(fc, name, plen) [fc] = plen, +static const UChar S390_KMA_parms_len[] = {S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#define S390_DO_FUNC(fc, name, plen) | S390_SETBIT(fc) +static const ULong S390_KMA_supported_fc[] = {0 S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#undef S390_DO_FUNCTIONS + +static enum ExtensionError do_extension_KMA(ThreadState* tst, ULong variant) +{ + UChar r1 = variant & 0xf; + UChar r2 = (variant >> 4) & 0xf; + UChar r3 = (variant >> 8) & 0xf; + ULong func = READ_GPR(tst, "KMA(gpr0)", 0); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KMA(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong orig_addr1, orig_len2; + ULong addr1 = 0, addr2 = 0, addr3 = 0, len2 = 0, len3 = 0; + + if (fc < sizeof(S390_KMA_parms_len) / sizeof(S390_KMA_parms_len[0])) + parms_len = S390_KMA_parms_len[fc]; + if (parms_len == 0) + return INSN_ERR("KMA: unknown function code\n"); + + if (fc == 0) { // Query + PRE_MEM_WRITE(tst, "KMA(parms)", parms, parms_len); + cc = do_KMA_insn(func, parms, &addr1, &addr2, &len2, &addr3, &len3); + s390_filter_functions((ULong*)parms, parms_len, S390_KMA_supported_fc, + sizeof(S390_KMA_supported_fc)); + POST_MEM_WRITE(tst, parms, parms_len); + } else { + addr1 = orig_addr1 = READ_GPR(tst, "KMA(op1_addr)", r1); + addr2 = READ_GPR(tst, "KMA(op2_addr)", r2); + addr3 = READ_GPR(tst, "KMA(op3_addr)", r3); + len2 = orig_len2 = READ_GPR(tst, "KMA(op2_len)", r2 + 1); + len3 = READ_GPR(tst, "KMA(op3_len)", r3 + 1); + PRE_MEM_READ(tst, "KMA(parms)", parms, parms_len); + PRE_MEM_WRITE(tst, "KMA(op1)", addr1, len2); + PRE_MEM_READ(tst, "KMA(op2)", addr2, len2); + PRE_MEM_READ(tst, "KMA(op3)", addr3, len3); + cc = do_KMA_insn(func, parms, &addr1, &addr2, &len2, &addr3, &len3); + WRITE_GPR(tst, r1, addr1); + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + WRITE_GPR(tst, r3, addr3); + WRITE_GPR(tst, r3 + 1, len3); + POST_MEM_WRITE(tst, orig_addr1, orig_len2 - len2); + } + WRITE_CC(tst, cc); + return ExtErr_OK; +} + +/*---------------------------------------------------------------*/ +/*--- KDSA (compute intermediate message digest) ---*/ +/*---------------------------------------------------------------*/ + +S390_DEFINE_DO_0R_INSN(do_KDSA_insn, 0xb93a) + +/* We specify the parameter block size without the CSB here. Also note that + this approach only supports sizes that are a multiple of 8. */ +#define S390_DO_FUNCTIONS \ + S390_DO_FUNC(0, S390_KDSA_Query, 16) \ + S390_DO_FUNC(1, S390_KDSA_ECDSA_Verify_P256, 168) \ + S390_DO_FUNC(2, S390_KDSA_ECDSA_Verify_P384, 248) \ + S390_DO_FUNC(3, S390_KDSA_ECDSA_Verify_P521, 408) \ + S390_DO_FUNC(9, S390_KDSA_ECDSA_Sign_P256, 168) \ + S390_DO_FUNC(10, S390_KDSA_ECDSA_Sign_P384, 248) \ + S390_DO_FUNC(11, S390_KDSA_ECDSA_Sign_P521, 408) \ + S390_DO_FUNC(17, S390_KDSA_Encrypted_ECDSA_Sign_P256, 200) \ + S390_DO_FUNC(18, S390_KDSA_Encrypted_ECDSA_Sign_P384, 280) \ + S390_DO_FUNC(19, S390_KDSA_Encrypted_ECDSA_Sign_P521, 440) \ + S390_DO_FUNC(32, S390_KDSA_EdDSA_Verify_Ed25519, 104) \ + S390_DO_FUNC(36, S390_KDSA_EdDSA_Verify_Ed448, 200) \ + S390_DO_FUNC(40, S390_KDSA_EdDSA_Sign_Ed25519, 120) \ + S390_DO_FUNC(44, S390_KDSA_EdDSA_Sign_Ed448, 216) \ + S390_DO_FUNC(48, S390_KDSA_Encrypted_EdDSA_Sign_Ed25519, 152) \ + S390_DO_FUNC(52, S390_KDSA_Encrypted_EdDSA_Sign_Ed448, 248) + +#define S390_DO_FUNC(fc, name, plen) [fc] = plen / 8, +static const UChar S390_KDSA_parms_len[] = {S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#define S390_DO_FUNC(fc, name, plen) | S390_SETBIT(fc) +static const ULong S390_KDSA_supported_fc[] = {0 S390_DO_FUNCTIONS}; +#undef S390_DO_FUNC + +#undef S390_DO_FUNCTIONS + +static enum ExtensionError do_extension_KDSA(ThreadState* tst, ULong variant) +{ + UChar r2 = (variant >> 4) & 0xf; + UChar func = READ_FUNCTION_CODE(tst, "KDSA"); + UChar fc = func & 0x7f; + ULong parms = READ_GPR(tst, "KDSA(r1)", 1); + ULong parms_len = 0; + Int cc = 0; + ULong addr2 = 0, len2 = 0; + + if (fc < sizeof(S390_KDSA_parms_len) / sizeof(S390_KDSA_parms_len[0])) + parms_len = S390_KDSA_parms_len[fc] * 8; + if (parms_len == 0) + return INSN_ERR("KDSA: unknown function code\n"); + + if (fc == 0) { // Query + PRE_MEM_WRITE(tst, "KDSA(parms)", parms, parms_len); + cc = do_KDSA_insn(func, parms, &addr2, &len2); + s390_filter_functions((ULong*)parms, parms_len, S390_KDSA_supported_fc, + sizeof(S390_KDSA_supported_fc)); + POST_MEM_WRITE(tst, parms, parms_len); + } else { + addr2 = READ_GPR(tst, "KDSA(op2_addr)", r2); + len2 = READ_GPR(tst, "KDSA(op2_len)", r2 + 1); + PRE_MEM_READ(tst, "KDSA(parms)", parms, parms_len); + /* the CSB must also be writable */ + PRE_MEM_WRITE(tst, "KDSA(parms)", parms, 4096); + PRE_MEM_READ(tst, "KDSA(op2)", addr2, len2); + cc = do_KDSA_insn(func, parms, &addr2, &len2); + WRITE_GPR(tst, r2, addr2); + WRITE_GPR(tst, r2 + 1, len2); + POST_MEM_WRITE(tst, parms, parms_len); + } + WRITE_CC(tst, cc); + return ExtErr_OK; +} + /*---------------------------------------------------------------*/ /*--- Main function: select and call appropriate extension ---*/ /*---------------------------------------------------------------*/ @@ -877,6 +1765,28 @@ enum ExtensionError ML_(do_client_extension)(ThreadState* tst) return do_extension_DFLTCC(tst, variant); case S390_EXT_STFLE: return do_extension_STFLE(tst, variant); + case S390_EXT_KM: + return do_extension_KM(tst, variant); + case S390_EXT_KMC: + return do_extension_KMC(tst, variant); + case S390_EXT_KIMD: + return do_extension_KIMD(tst, variant); + case S390_EXT_KLMD: + return do_extension_KLMD(tst, variant); + case S390_EXT_KMAC: + return do_extension_KMAC(tst, variant); + case S390_EXT_PCC: + return do_extension_PCC(tst, variant); + case S390_EXT_KMCTR: + return do_extension_KMCTR(tst, variant); + case S390_EXT_KMO: + return do_extension_KMO(tst, variant); + case S390_EXT_KMF: + return do_extension_KMF(tst, variant); + case S390_EXT_KMA: + return do_extension_KMA(tst, variant); + case S390_EXT_KDSA: + return do_extension_KDSA(tst, variant); default: VG_(core_panic)("unknown extension ID"); } diff --git a/docs/internals/s390-opcodes.csv b/docs/internals/s390-opcodes.csv index 97eb66beea..2b6fae36db 100644 --- a/docs/internals/s390-opcodes.csv +++ b/docs/internals/s390-opcodes.csv @@ -619,15 +619,15 @@ msdr,"multiply and subtract long hfp","won't do","hfp instruction" msd,"multiply and subtract long hfp","won't do","hfp instruction" mser,"mutliply and subtract short hfp","won't do","hfp instruction" mse,"multiply and subttract short hfp","won't do","hfp instruction" -km,"cipher message","not implemented", -kmc,"cipher message with chaining","not implemented", -kmf,"cipher message with CFB","not implemented", -kmo,"cipher message with OFB","not implemented", -kmctr,"cipher message with counter","not implemented", -pcc,"perform cryptographic computation","not implemented", -kimd,"compute intermediate message digest","not implemented", -klmd,"compute last message digest","not implemented", -kmac,"compute message authentication code","not implemented", +km,"cipher message",implemented, +kmc,"cipher message with chaining",implemented, +kmf,"cipher message with CFB",implemented, +kmo,"cipher message with OFB",implemented, +kmctr,"cipher message with counter",implemented, +pcc,"perform cryptographic computation",implemented, +kimd,"compute intermediate message digest",implemented, +klmd,"compute last message digest",implemented, +kmac,"compute message authentication code",implemented, afi,"add immediate 32",implemented, agfi,"add immediate 64<32",implemented, alfi,"add logical immediate 32",implemented, @@ -1670,7 +1670,7 @@ lgg,"load guarded 64 bit","not implemented","arch12" llgfsg,"load logical and shift guarded 64 bit","not implemented","arch12" lgsc,"load guarded storage controls","not implemented","arch12" stgsc,"store guarded storage controls","not implemented","arch12" -kma,"cipher message with galois counter mode","not implemented","arch12" +kma,"cipher message with galois counter mode",implemented,"arch12" ncrk,"and with complement 32 bit",implemented,arch13 ncgrk,"and with complement 64 bit",implemented,arch13 mvcrl,"move right to left",implemented,arch13 @@ -1745,7 +1745,7 @@ vclfeb,"vector fp convert to logical 32 bit",implemented,arch13 wclfeb,"vector fp convert to logical 32 bit",implemented,arch13 dfltcc,"deflate conversion call",implemented,arch13 sortl,"sort lists","not implemented",arch13 -kdsa,"compute digital signature authentication","not implemented",arch13 +kdsa,"compute digital signature authentication",implemented,arch13 vschp,"decimal scale and convert to hfp","not implemented",arch14 vscshp,"decimal scale and convert and split to hfp","not implemented",arch14 vcsph,"vector convert hfp to scaled decimal","not implemented",arch14 diff --git a/none/tests/s390x/stfle.c b/none/tests/s390x/stfle.c index 22ddf86635..5926964c6a 100644 --- a/none/tests/s390x/stfle.c +++ b/none/tests/s390x/stfle.c @@ -53,11 +53,11 @@ int main() else printf("The z/Architecture architectural mode is not installed\n"); - /* Test #4: Message security assist */ - if (stfle(dw, 17)) { - printf("MSA facility is present\n"); + /* Test #4: Constrained transactional-execution */ + if (stfle(dw, 50)) { + printf("Constrained transactional-execution is supported\n"); } else { - printf("No MSA facility available\n"); + printf("No constrained transactional-execution facility available\n"); } return 0; } diff --git a/none/tests/s390x/stfle.stdout.exp b/none/tests/s390x/stfle.stdout.exp index 2cf903c789..895551e071 100644 --- a/none/tests/s390x/stfle.stdout.exp +++ b/none/tests/s390x/stfle.stdout.exp @@ -7,4 +7,4 @@ the value of cc is 3 and #double words is 3 the value of cc is 3 and #double words is 3 The z/Architecture architectural mode is installed and active the value of cc is 3 and #double words is 3 -No MSA facility available +No constrained transactional-execution facility available diff --git a/none/tests/s390x/stfle.stdout.exp-z16 b/none/tests/s390x/stfle.stdout.exp-z16 index b857fae3d7..8b01a2becd 100644 --- a/none/tests/s390x/stfle.stdout.exp-z16 +++ b/none/tests/s390x/stfle.stdout.exp-z16 @@ -7,4 +7,4 @@ the value of cc is 3 and #double words is 4 the value of cc is 3 and #double words is 4 The z/Architecture architectural mode is installed and active the value of cc is 3 and #double words is 4 -No MSA facility available +No constrained transactional-execution facility available -- 2.47.2