Test the parameter checks and if the query function correctly works.
Tests will only be executed if the corresponding stfl(e) bit for an
instruction is indicated.
Signed-off-by: David Hildenbrand <david@xxxxxxxxxx>
---
s390x/emulator.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 227 insertions(+)
diff --git a/s390x/emulator.c b/s390x/emulator.c
index 7854498..a8b4212 100644
--- a/s390x/emulator.c
+++ b/s390x/emulator.c
@@ -11,6 +11,8 @@
* under the terms of the GNU Library General Public License version 2.
*/
#include <libcflat.h>
+#include <asm/cpacf.h>
+#include <asm/interrupt.h>
static inline void __test_spm_ipm(uint8_t cc, uint8_t key)
{
@@ -41,11 +43,236 @@ static void test_spm_ipm(void)
__test_spm_ipm(0, 0);
}
+static inline void __test_cpacf(unsigned int opcode, unsigned long func,
+ unsigned int r1, unsigned int r2,
+ unsigned int r3)
+{
+ register unsigned long gr0 asm("0") = func;
+ cpacf_mask_t mask;
+ register unsigned long gr1 asm("1") = (unsigned long)&mask;
+
+ asm volatile(".insn rrf,%[opc] << 16,%[r1],%[r2],%[r3],0\n"
+ : : "d" (gr0), "d" (gr1), [opc] "i" (opcode),
+ [r1] "i" (r1), [r2] "i" (r2), [r3] "i" (r3));
+}
+
+static inline void __test_cpacf_r1_odd(unsigned int opcode)
+{
+ report_prefix_push("r1 odd");
+ expect_pgm_int();
+ __test_cpacf(opcode, 0, 1, 4, 6);
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+ report_prefix_pop();
+}
+
+static inline void __test_cpacf_r1_null(unsigned int opcode)
+{
+ report_prefix_push("r1 null");
+ expect_pgm_int();
+ __test_cpacf(opcode, 0, 0, 4, 6);
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+ report_prefix_pop();
+}
+
+static inline void __test_cpacf_r2_odd(unsigned int opcode)
+{
+ report_prefix_push("r2 odd");
+ expect_pgm_int();
+ __test_cpacf(opcode, 0, 2, 3, 6);
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+ report_prefix_pop();
+}
+
+static inline void __test_cpacf_r2_null(unsigned int opcode)
+{
+ report_prefix_push("r2 null");
+ expect_pgm_int();
+ __test_cpacf(opcode, 0, 2, 0, 6);
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+ report_prefix_pop();
+}
+
+static inline void __test_cpacf_r3_odd(unsigned int opcode)
+{
+ report_prefix_push("r3 odd");
+ expect_pgm_int();
+ __test_cpacf(opcode, 0, 2, 4, 5);
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+ report_prefix_pop();
+}
+
+static inline void __test_cpacf_r3_null(unsigned int opcode)
+{
+ report_prefix_push("r3 null");
+ expect_pgm_int();
+ __test_cpacf(opcode, 0, 2, 4, 0);
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+ report_prefix_pop();
+}
+
+static inline void __test_cpacf_mod_bit(unsigned int opcode)
+{
+ report_prefix_push("mod bit");
+ expect_pgm_int();
+ __test_cpacf(opcode, CPACF_DECRYPT, 2, 4, 6);
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+ report_prefix_pop();
+}
+
+static inline void __test_cpacf_invalid_func(unsigned int opcode)
+{
+ report_prefix_push("invalid subfunction");
+ expect_pgm_int();
+ /* 127 is unassigned for now. We don't simply use any, as HW
+ * might simply mask valid codes in query but they might still work */
+ if (cpacf_query_func(opcode, 127)) {
+ report_skip("127 not invalid");
+ } else {
+ __test_cpacf(opcode, 127, 2, 4, 6);
+ }
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+ report_prefix_pop();
+}
+
+static inline void __test_cpacf_invalid_parm(unsigned int opcode)
+{
+ report_prefix_push("invalid parm address");
+ expect_pgm_int();
+ __cpacf_query(opcode, (void *) -1);
+ check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
+ report_prefix_pop();
+}
+
+static inline void __test_cpacf_protected_parm(unsigned int opcode)
+{
+ report_prefix_push("protected parm address");
+ expect_pgm_int();
+ low_prot_enable();
+ __cpacf_query(opcode, (void *) 8);
+ low_prot_disable();
+ check_pgm_int_code(PGM_INT_CODE_PROTECTION);
+ report_prefix_pop();
+}
+
+static inline void __test_basic_cpacf_opcode(unsigned int opcode)
+{
+ bool mod_bit_allowed = false;
+
+ if (!__cpacf_check_opcode(opcode)) {
+ report_skip("not available");
+ return;
+ }
+ report("query indicated in query", cpacf_query_func(opcode, 0));
+
+ switch (opcode) {
+ case CPACF_KMCTR:
+ __test_cpacf_r3_odd(opcode);
+ __test_cpacf_r3_null(opcode);
+ /* FALL THROUGH */
+ case CPACF_PRNO:
+ case CPACF_KMF:
+ case CPACF_KMC:
+ case CPACF_KMO:
+ case CPACF_KM:
+ __test_cpacf_r1_odd(opcode);
+ __test_cpacf_r1_null(opcode);
+ mod_bit_allowed = true;
+ /* FALL THROUGH */
+ case CPACF_KMAC:
+ case CPACF_KIMD:
+ case CPACF_KLMD:
+ __test_cpacf_r2_odd(opcode);
+ __test_cpacf_r2_null(opcode);
+ break;
+ }
+ if (!mod_bit_allowed)
+ __test_cpacf_mod_bit(opcode);
+ __test_cpacf_invalid_func(opcode);
+ __test_cpacf_invalid_parm(opcode);
+ __test_cpacf_protected_parm(opcode);
+}
+
+/* COMPUTE MESSAGE AUTHENTICATION CODE */
+static void test_kmac(void)
+{
+ __test_basic_cpacf_opcode(CPACF_KMAC);
+}
+
+/* CIPHER MESSAGE */
+static void test_km(void)
+{
+ __test_basic_cpacf_opcode(CPACF_KM);
+}
+/* CIPHER MESSAGE WITH CHAINING */
+static void test_kmc(void)
+{
+ __test_basic_cpacf_opcode(CPACF_KMC);
+}
+
+/* COMPUTE INTERMEDIATE MESSAGE DIGEST */
+static void test_kimd(void)
+{
+ __test_basic_cpacf_opcode(CPACF_KIMD);
+}
+
+/* COMPUTE LAST MESSAGE DIGEST */
+static void test_klmd(void)
+{
+ __test_basic_cpacf_opcode(CPACF_KLMD);
+}
+
+/* PERFORM CRYPTOGRAPHIC KEY MANAGEMENT OPERATION */
+static void test_pckmo(void)
+{
+ __test_basic_cpacf_opcode(CPACF_PCKMO);
+}
+
+/* CIPHER MESSAGE WITH CIPHER FEEDBACK */
+static void test_kmf(void)
+{
+ __test_basic_cpacf_opcode(CPACF_KMF);
+}
+
+/* PERFORM CRYPTOGRAPHIC KEY MANAGEMENT OPERATION */
+static void test_kmo(void)
+{
+ __test_basic_cpacf_opcode(CPACF_KMO);
+}
+
+/* PERFORM CRYPTOGRAPHIC COMPUTATION */
+static void test_pcc(void)
+{
+ __test_basic_cpacf_opcode(CPACF_PCC);
+}
+
+/* CIPHER MESSAGE WITH COUNTER */
+static void test_kmctr(void)
+{
+ __test_basic_cpacf_opcode(CPACF_KMCTR);
+}
+
+/* PERFORM RANDOM NUMBER OPERATION (formerly PPNO) */
+static void test_prno(void)
+{
+ __test_basic_cpacf_opcode(CPACF_PRNO);
+}
+
static struct {
const char *name;
void (*func)(void);
} tests[] = {
{ "spm/ipm", test_spm_ipm },
+ { "kmac", test_kmac },
+ { "km", test_km },
+ { "kmc", test_kmc },
+ { "kimd", test_kimd },
+ { "klmd", test_klmd },
+ { "pckmo", test_pckmo },
+ { "kmf", test_kmf },
+ { "kmo", test_kmo },
+ { "pcc", test_pcc },
+ { "kmctr", test_kmctr },
+ { "prno", test_prno },
{ NULL, NULL }
};
--
2.13.5