Message ID | 20230330114244.35559-3-frankja@linux.ibm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | s390x: Add base AP support | expand |
On Thu, 30 Mar 2023 11:42:41 +0000 Janosch Frank <frankja@linux.ibm.com> wrote: > Add a test that checks the exceptions for the PQAP, NQAP and DQAP > adjunct processor (AP) crypto instructions. > > Since triggering the exceptions doesn't require actual AP hardware, > this test can run without complicated setup. > > Signed-off-by: Janosch Frank <frankja@linux.ibm.com> > --- [...] > + > +static void test_pgms_pqap(void) > +{ > + unsigned long grs[3] = {}; > + struct pqap_r0 *r0 = (struct pqap_r0 *)grs; > + uint8_t *data = alloc_page(); > + uint16_t pgm; > + int fails = 0; > + int i; > + > + report_prefix_push("pqap"); > + > + /* Wrong FC code */ > + report_prefix_push("invalid fc"); > + r0->fc = 42; maybe make a macro out of it, both to avoid magic numbers and to change it easily if code 42 will ever become defined in the future. > + expect_pgm_int(); > + pqap(grs); > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > + memset(grs, 0, sizeof(grs)); > + report_prefix_pop(); > + > + report_prefix_push("invalid gr0 bits"); > + for (i = 42; i < 6; i++) { 42 is not < 6, this whole thing will be skipped? > + expect_pgm_int(); > + grs[0] = BIT(63 - i); > + pqap(grs); > + pgm = clear_pgm_int(); > + > + if (pgm != PGM_INT_CODE_SPECIFICATION) { > + report_fail("fail on bit %d", i); > + fails++; > + } > + } > + report(!fails, "All bits tested"); > + memset(grs, 0, sizeof(grs)); > + fails = 0; > + report_prefix_pop(); > + > + report_prefix_push("alignment"); > + report_prefix_push("fc=4"); > + r0->fc = PQAP_QUERY_AP_CONF_INFO; > + grs[2] = (unsigned long)data; > + for (i = 1; i < 8; i++) { > + expect_pgm_int(); > + grs[2]++; > + pqap(grs); > + pgm = clear_pgm_int(); > + if (pgm != PGM_INT_CODE_SPECIFICATION) { > + report_fail("fail on bit %d", i); > + fails++; > + } > + } > + report(!fails, "All bits tested"); you mean "All alignments tested" ? > + report_prefix_pop(); > + report_prefix_push("fc=6"); > + r0->fc = PQAP_BEST_AP; > + grs[2] = (unsigned long)data; > + for (i = 1; i < 8; i++) { > + expect_pgm_int(); > + grs[2]++; > + pqap(grs); > + pgm = clear_pgm_int(); > + if (pgm != PGM_INT_CODE_SPECIFICATION) { > + report_fail("fail on bit %d", i); > + fails++; > + } > + } > + report(!fails, "All bits tested"); same here? > + report_prefix_pop(); > + report_prefix_pop(); > + > + free_page(data); > + report_prefix_pop(); > +} > + > +static void test_pgms_nqap(void) > +{ > + uint8_t gr0_zeroes_bits[] = { > + 32, 34, 35, 40 > + }; > + uint64_t gr0; > + bool fail; > + int i; > + > + report_prefix_push("nqap"); > + > + /* Registers 0 and 1 are always used, the others are > even/odd pairs */ > + report_prefix_push("spec"); > + report_prefix_push("r1"); > + expect_pgm_int(); > + asm volatile ( > + ".insn rre,0xb2ad0000,3,6\n" > + : : : "cc", "memory", "0", "1", "2", "3"); I would say "0", "1", "2", "3", "4", "6", "7" since there are two ways of doing it wrong when it comes to even-odd register pairs (r and r+1, r&~1 and r&~1+1) > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > + report_prefix_pop(); > + > + report_prefix_push("r2"); > + expect_pgm_int(); > + asm volatile ( > + ".insn rre,0xb2ad0000,2,7\n" > + : : : "cc", "memory", "0", "1", "3", "4"); same here (with the right numbers of course) and I would even add a test with both odd registers > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > + report_prefix_pop(); > + > + report_prefix_push("len==0"); > + expect_pgm_int(); > + asm volatile ( > + "xgr 0,0\n" > + "xgr 5,5\n" > + ".insn rre,0xb2ad0000,2,4\n" > + : : : "cc", "memory", "0", "1", "2", "3", "4", "5"); > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > + report_prefix_pop(); > + > + report_prefix_push("len>12288"); > + expect_pgm_int(); > + asm volatile ( > + "xgr 5,5\n" > + "lghi 5, 12289\n" I would also check setting all the bits above bit 13 (i.e. if an implementation wrongly checks only the lower 16 or 32 bits of the value > + ".insn rre,0xb2ad0000,2,4\n" > + : : : "cc", "memory", "0", "1", "2", "3", "4", "5"); > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > + report_prefix_pop(); > + > + report_prefix_push("gr0_zero_bits"); > + fail = false; > + for (i = 0; i < 4; i++) { i < ARRAY_SIZE(gr0_zeroes_bits) might be more robust and future-proof > + expect_pgm_int(); > + gr0 = BIT_ULL(63 - gr0_zeroes_bits[i]); > + asm volatile ( > + "xgr 5,5\n" > + "lghi 5, 128\n" > + "lg 0, 0(%[val])\n" > + ".insn rre,0xb2ad0000,2,4\n" > + : : [val] "a" (&gr0) > + : "cc", "memory", "0", "1", "2", "3", "4", "5"); > + if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) { > + report_fail("setting gr0 bit %d did not result in a spec exception", > + gr0_zeroes_bits[i]); > + fail = true; > + } > + } > + report(!fail, "set bit specification pgms"); > + report_prefix_pop(); > + > + report_prefix_pop(); > + report_prefix_pop(); > +} > + > +static void test_pgms_dqap(void) > +{ > + uint8_t gr0_zeroes_bits[] = { > + 33, 34, 35, 40, 41 > + }; > + uint64_t gr0; > + bool fail; > + int i; > + > + report_prefix_push("dqap"); > + > + /* Registers 0 and 1 are always used, the others are even/odd pairs */ > + report_prefix_push("spec"); > + report_prefix_push("r1"); > + expect_pgm_int(); > + asm volatile ( > + ".insn rre,0xb2ae0000,3,6\n" > + : : : "cc", "memory", "0", "1", "2", "3"); same concern here with the registers like in the previous test > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > + report_prefix_pop(); > + > + report_prefix_push("r2"); > + expect_pgm_int(); > + asm volatile ( > + ".insn rre,0xb2ae0000,2,7\n" > + : : : "cc", "memory", "0", "1", "3", "4"); as above, plus add a test for both odd > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > + report_prefix_pop(); > + > + report_prefix_push("len==0"); > + expect_pgm_int(); > + asm volatile ( > + "xgr 0,0\n" > + "xgr 5,5\n" > + ".insn rre,0xb2ae0000,2,4\n" > + : : : "cc", "memory", "0", "1", "2", "3", "4", "5"); > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > + report_prefix_pop(); > + > + report_prefix_push("len>12288"); > + expect_pgm_int(); > + asm volatile ( > + "xgr 5,5\n" > + "lghi 5, 12289\n" like the previous test, also test the high bits > + ".insn rre,0xb2ae0000,2,4\n" > + : : : "cc", "memory", "0", "1", "2", "3", "4", "5"); > + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); > + report_prefix_pop(); > + > + report_prefix_push("gr0_zero_bits"); > + fail = false; > + for (i = 0; i < 5; i++) { same concern here with ARRAY_SIZE > + expect_pgm_int(); > + gr0 = BIT_ULL(63 - gr0_zeroes_bits[i]); > + asm volatile ( > + "xgr 5,5\n" > + "lghi 5, 128\n" > + "lg 0, 0(%[val])\n" > + ".insn rre,0xb2ae0000,2,4\n" > + : : [val] "a" (&gr0) > + : "cc", "memory", "0", "1", "2", "3", "4", "5"); > + if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) { > + report_info("setting gr0 bit %d did not result in a spec exception", > + gr0_zeroes_bits[i]); > + fail = true; > + } > + } > + report(!fail, "set bit specification pgms"); > + report_prefix_pop(); > + > + report_prefix_pop(); > + report_prefix_pop(); > +} > + > +static void test_priv(void) > +{ > + struct ap_config_info info = {}; > + > + report_prefix_push("privileged"); > + > + report_prefix_push("pqap"); > + expect_pgm_int(); > + enter_pstate(); > + ap_pqap_qci(&info); > + check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); > + report_prefix_pop(); > + > + /* > + * Enqueue and dequeue take too many registers so a simple > + * inline assembly makes more sense than using the library > + * functions. > + */ > + report_prefix_push("nqap"); > + expect_pgm_int(); > + enter_pstate(); > + asm volatile ( > + ".insn rre,0xb2ad0000,0,2\n" > + : : : "cc", "memory", "0", "1", "2", "3"); > + check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); > + report_prefix_pop(); > + > + report_prefix_push("dqap"); > + expect_pgm_int(); > + enter_pstate(); > + asm volatile ( > + ".insn rre,0xb2ae0000,0,2\n" > + : : : "cc", "memory", "0", "1", "2", "3"); > + check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); > + report_prefix_pop(); > + > + report_prefix_pop(); > +} > + > +int main(void) > +{ > + report_prefix_push("ap"); > + if (!ap_check()) { > + report_skip("AP instructions not available"); > + goto done; > + } > + > + test_priv(); > + test_pgms_pqap(); > + test_pgms_nqap(); > + test_pgms_dqap(); > + > +done: > + report_prefix_pop(); > + return report_summary(); > +} > diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg > index d97eb5e9..9b7c65c8 100644 > --- a/s390x/unittests.cfg > +++ b/s390x/unittests.cfg > @@ -215,3 +215,7 @@ file = migration-skey.elf > smp = 2 > groups = migration > extra_params = -append '--parallel' > + > +[ap] > +file = ap.elf > +
On 3/30/23 18:34, Claudio Imbrenda wrote: > On Thu, 30 Mar 2023 11:42:41 +0000 > Janosch Frank <frankja@linux.ibm.com> wrote: > >> Add a test that checks the exceptions for the PQAP, NQAP and DQAP >> adjunct processor (AP) crypto instructions. >> >> Since triggering the exceptions doesn't require actual AP hardware, >> this test can run without complicated setup. >> >> Signed-off-by: Janosch Frank <frankja@linux.ibm.com> >> --- > > [...] > >> + >> +static void test_pgms_pqap(void) >> +{ >> + unsigned long grs[3] = {}; >> + struct pqap_r0 *r0 = (struct pqap_r0 *)grs; >> + uint8_t *data = alloc_page(); >> + uint16_t pgm; >> + int fails = 0; >> + int i; >> + >> + report_prefix_push("pqap"); >> + >> + /* Wrong FC code */ >> + report_prefix_push("invalid fc"); >> + r0->fc = 42; > > maybe make a macro out of it, both to avoid magic numbers and to change > it easily if code 42 will ever become defined in the future. I don't really see a benefit to that. > >> + expect_pgm_int(); >> + pqap(grs); >> + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); >> + memset(grs, 0, sizeof(grs)); >> + report_prefix_pop(); >> + >> + report_prefix_push("invalid gr0 bits"); >> + for (i = 42; i < 6; i++) { > > 42 is not < 6, this whole thing will be skipped? Right, I've fixed this. [...] >> + >> +static void test_pgms_nqap(void) >> +{ >> + uint8_t gr0_zeroes_bits[] = { >> + 32, 34, 35, 40 >> + }; >> + uint64_t gr0; >> + bool fail; >> + int i; >> + >> + report_prefix_push("nqap"); >> + >> + /* Registers 0 and 1 are always used, the others are >> even/odd pairs */ >> + report_prefix_push("spec"); >> + report_prefix_push("r1"); >> + expect_pgm_int(); >> + asm volatile ( >> + ".insn rre,0xb2ad0000,3,6\n" >> + : : : "cc", "memory", "0", "1", "2", "3"); > > I would say > "0", "1", "2", "3", "4", "6", "7" > > since there are two ways of doing it wrong when it comes to even-odd > register pairs (r and r+1, r&~1 and r&~1+1) R1 & R1 + 1 should never change, same goes for R2. GR0, GR1, R2 + 1 could potentially change. But the more interesting question is: Does it make sense to clobber anything other than cc (if at all) for the PGM checks? If the PGM fails we're in uncharted territory. Seems like I need to look up what the other tests do.
diff --git a/s390x/Makefile b/s390x/Makefile index e8559a4e..f74241d5 100644 --- a/s390x/Makefile +++ b/s390x/Makefile @@ -39,6 +39,7 @@ tests += $(TEST_DIR)/panic-loop-extint.elf tests += $(TEST_DIR)/panic-loop-pgm.elf tests += $(TEST_DIR)/migration-sck.elf tests += $(TEST_DIR)/exittime.elf +tests += $(TEST_DIR)/ap.elf pv-tests += $(TEST_DIR)/pv-diags.elf pv-tests += $(TEST_DIR)/pv-icptcode.elf @@ -102,6 +103,7 @@ cflatobjs += lib/s390x/malloc_io.o cflatobjs += lib/s390x/uv.o cflatobjs += lib/s390x/sie.o cflatobjs += lib/s390x/fault.o +cflatobjs += lib/s390x/ap.o OBJDIRS += lib/s390x diff --git a/s390x/ap.c b/s390x/ap.c new file mode 100644 index 00000000..82ddb6d2 --- /dev/null +++ b/s390x/ap.c @@ -0,0 +1,308 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AP instruction G2 tests + * + * Copyright (c) 2023 IBM Corp + * + * Authors: + * Janosch Frank <frankja@linux.ibm.com> + */ + +#include <libcflat.h> +#include <interrupt.h> +#include <bitops.h> +#include <alloc_page.h> +#include <asm/facility.h> +#include <asm/time.h> +#include <ap.h> + +/* For PQAP PGM checks where we need full control over the input */ +static void pqap(unsigned long grs[3]) +{ + asm volatile( + " lgr 0,%[r0]\n" + " lgr 1,%[r1]\n" + " lgr 2,%[r2]\n" + " .insn rre,0xb2af0000,0,0\n" /* PQAP */ + :: [r0] "d" (grs[0]), [r1] "d" (grs[1]), [r2] "d" (grs[2]) + : "cc", "memory", "0", "1", "2"); +} + +static void test_pgms_pqap(void) +{ + unsigned long grs[3] = {}; + struct pqap_r0 *r0 = (struct pqap_r0 *)grs; + uint8_t *data = alloc_page(); + uint16_t pgm; + int fails = 0; + int i; + + report_prefix_push("pqap"); + + /* Wrong FC code */ + report_prefix_push("invalid fc"); + r0->fc = 42; + expect_pgm_int(); + pqap(grs); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + memset(grs, 0, sizeof(grs)); + report_prefix_pop(); + + report_prefix_push("invalid gr0 bits"); + for (i = 42; i < 6; i++) { + expect_pgm_int(); + grs[0] = BIT(63 - i); + pqap(grs); + pgm = clear_pgm_int(); + + if (pgm != PGM_INT_CODE_SPECIFICATION) { + report_fail("fail on bit %d", i); + fails++; + } + } + report(!fails, "All bits tested"); + memset(grs, 0, sizeof(grs)); + fails = 0; + report_prefix_pop(); + + report_prefix_push("alignment"); + report_prefix_push("fc=4"); + r0->fc = PQAP_QUERY_AP_CONF_INFO; + grs[2] = (unsigned long)data; + for (i = 1; i < 8; i++) { + expect_pgm_int(); + grs[2]++; + pqap(grs); + pgm = clear_pgm_int(); + if (pgm != PGM_INT_CODE_SPECIFICATION) { + report_fail("fail on bit %d", i); + fails++; + } + } + report(!fails, "All bits tested"); + report_prefix_pop(); + report_prefix_push("fc=6"); + r0->fc = PQAP_BEST_AP; + grs[2] = (unsigned long)data; + for (i = 1; i < 8; i++) { + expect_pgm_int(); + grs[2]++; + pqap(grs); + pgm = clear_pgm_int(); + if (pgm != PGM_INT_CODE_SPECIFICATION) { + report_fail("fail on bit %d", i); + fails++; + } + } + report(!fails, "All bits tested"); + report_prefix_pop(); + report_prefix_pop(); + + free_page(data); + report_prefix_pop(); +} + +static void test_pgms_nqap(void) +{ + uint8_t gr0_zeroes_bits[] = { + 32, 34, 35, 40 + }; + uint64_t gr0; + bool fail; + int i; + + report_prefix_push("nqap"); + + /* Registers 0 and 1 are always used, the others are even/odd pairs */ + report_prefix_push("spec"); + report_prefix_push("r1"); + expect_pgm_int(); + asm volatile ( + ".insn rre,0xb2ad0000,3,6\n" + : : : "cc", "memory", "0", "1", "2", "3"); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); + + report_prefix_push("r2"); + expect_pgm_int(); + asm volatile ( + ".insn rre,0xb2ad0000,2,7\n" + : : : "cc", "memory", "0", "1", "3", "4"); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); + + report_prefix_push("len==0"); + expect_pgm_int(); + asm volatile ( + "xgr 0,0\n" + "xgr 5,5\n" + ".insn rre,0xb2ad0000,2,4\n" + : : : "cc", "memory", "0", "1", "2", "3", "4", "5"); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); + + report_prefix_push("len>12288"); + expect_pgm_int(); + asm volatile ( + "xgr 5,5\n" + "lghi 5, 12289\n" + ".insn rre,0xb2ad0000,2,4\n" + : : : "cc", "memory", "0", "1", "2", "3", "4", "5"); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); + + report_prefix_push("gr0_zero_bits"); + fail = false; + for (i = 0; i < 4; i++) { + expect_pgm_int(); + gr0 = BIT_ULL(63 - gr0_zeroes_bits[i]); + asm volatile ( + "xgr 5,5\n" + "lghi 5, 128\n" + "lg 0, 0(%[val])\n" + ".insn rre,0xb2ad0000,2,4\n" + : : [val] "a" (&gr0) + : "cc", "memory", "0", "1", "2", "3", "4", "5"); + if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) { + report_fail("setting gr0 bit %d did not result in a spec exception", + gr0_zeroes_bits[i]); + fail = true; + } + } + report(!fail, "set bit specification pgms"); + report_prefix_pop(); + + report_prefix_pop(); + report_prefix_pop(); +} + +static void test_pgms_dqap(void) +{ + uint8_t gr0_zeroes_bits[] = { + 33, 34, 35, 40, 41 + }; + uint64_t gr0; + bool fail; + int i; + + report_prefix_push("dqap"); + + /* Registers 0 and 1 are always used, the others are even/odd pairs */ + report_prefix_push("spec"); + report_prefix_push("r1"); + expect_pgm_int(); + asm volatile ( + ".insn rre,0xb2ae0000,3,6\n" + : : : "cc", "memory", "0", "1", "2", "3"); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); + + report_prefix_push("r2"); + expect_pgm_int(); + asm volatile ( + ".insn rre,0xb2ae0000,2,7\n" + : : : "cc", "memory", "0", "1", "3", "4"); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); + + report_prefix_push("len==0"); + expect_pgm_int(); + asm volatile ( + "xgr 0,0\n" + "xgr 5,5\n" + ".insn rre,0xb2ae0000,2,4\n" + : : : "cc", "memory", "0", "1", "2", "3", "4", "5"); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); + + report_prefix_push("len>12288"); + expect_pgm_int(); + asm volatile ( + "xgr 5,5\n" + "lghi 5, 12289\n" + ".insn rre,0xb2ae0000,2,4\n" + : : : "cc", "memory", "0", "1", "2", "3", "4", "5"); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); + + report_prefix_push("gr0_zero_bits"); + fail = false; + for (i = 0; i < 5; i++) { + expect_pgm_int(); + gr0 = BIT_ULL(63 - gr0_zeroes_bits[i]); + asm volatile ( + "xgr 5,5\n" + "lghi 5, 128\n" + "lg 0, 0(%[val])\n" + ".insn rre,0xb2ae0000,2,4\n" + : : [val] "a" (&gr0) + : "cc", "memory", "0", "1", "2", "3", "4", "5"); + if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) { + report_info("setting gr0 bit %d did not result in a spec exception", + gr0_zeroes_bits[i]); + fail = true; + } + } + report(!fail, "set bit specification pgms"); + report_prefix_pop(); + + report_prefix_pop(); + report_prefix_pop(); +} + +static void test_priv(void) +{ + struct ap_config_info info = {}; + + report_prefix_push("privileged"); + + report_prefix_push("pqap"); + expect_pgm_int(); + enter_pstate(); + ap_pqap_qci(&info); + check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); + report_prefix_pop(); + + /* + * Enqueue and dequeue take too many registers so a simple + * inline assembly makes more sense than using the library + * functions. + */ + report_prefix_push("nqap"); + expect_pgm_int(); + enter_pstate(); + asm volatile ( + ".insn rre,0xb2ad0000,0,2\n" + : : : "cc", "memory", "0", "1", "2", "3"); + check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); + report_prefix_pop(); + + report_prefix_push("dqap"); + expect_pgm_int(); + enter_pstate(); + asm volatile ( + ".insn rre,0xb2ae0000,0,2\n" + : : : "cc", "memory", "0", "1", "2", "3"); + check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); + report_prefix_pop(); + + report_prefix_pop(); +} + +int main(void) +{ + report_prefix_push("ap"); + if (!ap_check()) { + report_skip("AP instructions not available"); + goto done; + } + + test_priv(); + test_pgms_pqap(); + test_pgms_nqap(); + test_pgms_dqap(); + +done: + report_prefix_pop(); + return report_summary(); +} diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg index d97eb5e9..9b7c65c8 100644 --- a/s390x/unittests.cfg +++ b/s390x/unittests.cfg @@ -215,3 +215,7 @@ file = migration-skey.elf smp = 2 groups = migration extra_params = -append '--parallel' + +[ap] +file = ap.elf +
Add a test that checks the exceptions for the PQAP, NQAP and DQAP adjunct processor (AP) crypto instructions. Since triggering the exceptions doesn't require actual AP hardware, this test can run without complicated setup. Signed-off-by: Janosch Frank <frankja@linux.ibm.com> --- s390x/Makefile | 2 + s390x/ap.c | 308 ++++++++++++++++++++++++++++++++++++++++++++ s390x/unittests.cfg | 4 + 3 files changed, 314 insertions(+) create mode 100644 s390x/ap.c