[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[kvm-unit-tests PATCH v5 0/2] Add specification exception tests
From: |
Janis Schoetterl-Glausch |
Subject: |
[kvm-unit-tests PATCH v5 0/2] Add specification exception tests |
Date: |
Wed, 20 Jul 2022 16:25:24 +0200 |
Test that specification exceptions cause the correct interruption code
during both normal and transactional execution.
TCG fails the tests setting an invalid PSW bit.
I had a look at how best to fix it, but where best to check for early
PSW exceptions was not immediately clear to me. Ideas welcome.
v4 -> v5
add lpsw with invalid bit 12 test
TCG fails as with lpswe but must also invert bit 12
update copyright statement
add comments
cleanups and style fixes
v3 -> v4
remove iterations argument in order to simplify the code
for manual performance testing adding a for loop is easy
move report out of fixup_invalid_psw
simplify/improve readability of triggers
use positive error values
v2 -> v3
remove non-ascii symbol
clean up load_psw
fix nits
v1 -> v2
Add license and test description
Split test patch into normal test and transactional execution test
Add comments to
invalid PSW fixup function
with_transaction
Rename some variables/functions
Pass mask as single parameter to asm
Get rid of report_info_if macro
Introduce report_pass/fail and use them
Janis Schoetterl-Glausch (2):
s390x: Add specification exception test
s390x: Test specification exceptions during transaction
s390x/Makefile | 1 +
lib/s390x/asm/arch_def.h | 6 +
s390x/spec_ex.c | 369 +++++++++++++++++++++++++++++++++++++++
s390x/unittests.cfg | 3 +
4 files changed, 379 insertions(+)
create mode 100644 s390x/spec_ex.c
Range-diff against v4:
1: a242e84b ! 1: fd9780d8 s390x: Add specification exception test
@@ s390x/Makefile: tests += $(TEST_DIR)/uv-host.elf
tests += $(TEST_DIR)/spec_ex-sie.elf
+tests += $(TEST_DIR)/spec_ex.elf
tests += $(TEST_DIR)/firq.elf
+ tests += $(TEST_DIR)/epsw.elf
+ tests += $(TEST_DIR)/adtl-status.elf
+
+ ## lib/s390x/asm/arch_def.h ##
+@@ lib/s390x/asm/arch_def.h: struct psw {
+ uint64_t addr;
+ };
- tests_binary = $(patsubst %.elf,%.bin,$(tests))
++struct short_psw {
++ uint32_t mask;
++ uint32_t addr;
++};
++
+ #define AS_PRIM 0
+ #define AS_ACCR 1
+ #define AS_SECN 2
## s390x/spec_ex.c (new) ##
@@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
-+ * Copyright IBM Corp. 2021
++ * Copyright IBM Corp. 2021, 2022
+ *
+ * Specification exception test.
+ * Tests that specification exceptions occur when expected.
@@ s390x/spec_ex.c (new)
+#include <libcflat.h>
+#include <asm/interrupt.h>
+
-+static struct lowcore *lc = (struct lowcore *) 0;
-+
+static bool invalid_psw_expected;
+static struct psw expected_psw;
+static struct psw invalid_psw;
+static struct psw fixup_psw;
+
-+/* The standard program exception handler cannot deal with invalid old
PSWs,
++/*
++ * The standard program exception handler cannot deal with invalid old
PSWs,
+ * especially not invalid instruction addresses, as in that case one
cannot
+ * find the instruction following the faulting one from the old PSW.
+ * The PSW to return to is set by load_psw.
+ */
+static void fixup_invalid_psw(void)
+{
-+ // signal occurrence of invalid psw fixup
++ /* signal occurrence of invalid psw fixup */
+ invalid_psw_expected = false;
-+ invalid_psw = lc->pgm_old_psw;
-+ lc->pgm_old_psw = fixup_psw;
++ invalid_psw = lowcore.pgm_old_psw;
++ lowcore.pgm_old_psw = fixup_psw;
+}
+
-+/* Load possibly invalid psw, but setup fixup_psw before,
-+ * so that *fixup_invalid_psw() can bring us back onto the right track.
++/*
++ * Load possibly invalid psw, but setup fixup_psw before,
++ * so that fixup_invalid_psw() can bring us back onto the right track.
+ * Also acts as compiler barrier, -> none required in
expect/check_invalid_psw
+ */
+static void load_psw(struct psw psw)
@@ s390x/spec_ex.c (new)
+ uint64_t scratch;
+
+ fixup_psw.mask = extract_psw_mask();
-+ asm volatile ( "larl %[scratch],nop%=\n"
++ asm volatile ( "larl %[scratch],0f\n"
+ " stg %[scratch],%[addr]\n"
+ " lpswe %[psw]\n"
-+ "nop%=: nop\n"
-+ : [scratch] "=&r"(scratch),
++ "0: nop\n"
++ : [scratch] "=&d"(scratch),
++ [addr] "=&T"(fixup_psw.addr)
++ : [psw] "Q"(psw)
++ : "cc", "memory"
++ );
++}
++
++static void load_short_psw(struct short_psw psw)
++{
++ uint64_t scratch;
++
++ fixup_psw.mask = extract_psw_mask();
++ asm volatile ( "larl %[scratch],0f\n"
++ " stg %[scratch],%[addr]\n"
++ " lpsw %[psw]\n"
++ "0: nop\n"
++ : [scratch] "=&d"(scratch),
+ [addr] "=&T"(fixup_psw.addr)
+ : [psw] "Q"(psw)
+ : "cc", "memory"
@@ s390x/spec_ex.c (new)
+
+static int check_invalid_psw(void)
+{
-+ // toggled to signal occurrence of invalid psw fixup
++ /* toggled to signal occurrence of invalid psw fixup */
+ if (!invalid_psw_expected) {
+ if (expected_psw.mask == invalid_psw.mask &&
+ expected_psw.addr == invalid_psw.addr)
@@ s390x/spec_ex.c (new)
+ struct psw invalid = { .mask = 0x0008000000000000, .addr =
0x00000000deadbeee};
+
+ expect_invalid_psw(invalid);
-+ load_psw(expected_psw);
++ load_psw(invalid);
+ return check_invalid_psw();
+}
+
++static int short_psw_bit_12_is_0(void)
++{
++ struct short_psw short_invalid = { .mask = 0x00000000, .addr =
0xdeadbeee};
++
++ /*
++ * lpsw may optionally check bit 12 before loading the new psw
++ * -> cannot check the expected invalid psw like with lpswe
++ */
++ load_short_psw(short_invalid);
++ return 0;
++}
++
+static int bad_alignment(void)
+{
+ uint32_t words[5] __attribute__((aligned(16)));
@@ s390x/spec_ex.c (new)
+{
+ uint64_t quad[2] __attribute__((aligned(16))) = {0};
+
-+ asm volatile (".insn rxy,0xe3000000008f,%%r7,%[quad]" //lpq
%%r7,%[quad]
++ asm volatile (".insn rxy,0xe3000000008f,%%r7,%[quad]" /* lpq
%%r7,%[quad] */
+ : : [quad] "T"(quad)
+ : "%r7", "%r8"
+ );
@@ s390x/spec_ex.c (new)
+/* List of all tests to execute */
+static const struct spec_ex_trigger spec_ex_triggers[] = {
+ { "psw_bit_12_is_1", &psw_bit_12_is_1, &fixup_invalid_psw },
++ { "short_psw_bit_12_is_0", &short_psw_bit_12_is_0, &fixup_invalid_psw },
+ { "bad_alignment", &bad_alignment, NULL },
+ { "not_even", ¬_even, NULL },
+ { NULL, NULL, NULL },
@@ s390x/spec_ex.c (new)
+
+static void test_spec_ex(const struct spec_ex_trigger *trigger)
+{
-+ uint16_t expected_pgm = PGM_INT_CODE_SPECIFICATION;
-+ uint16_t pgm;
+ int rc;
+
+ expect_pgm_int();
+ register_pgm_cleanup_func(trigger->fixup);
+ rc = trigger->func();
+ register_pgm_cleanup_func(NULL);
++ /* test failed, nothing to be done, reporting responsibility of trigger
*/
+ if (rc)
+ return;
-+ pgm = clear_pgm_int();
-+ report(pgm == expected_pgm, "Program interrupt: expected(%d) ==
received(%d)",
-+ expected_pgm, pgm);
++ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
+}
+
+int main(int argc, char **argv)
@@ s390x/unittests.cfg: file = mvpg-sie.elf
+[spec_ex]
+file = spec_ex.elf
+
- [firq-linear-cpu-ids]
+ [firq-linear-cpu-ids-kvm]
file = firq.elf
timeout = 20
2: 16ce8bb0 ! 2: c14092a3 s390x: Test specification exceptions during
transaction
@@ Commit message
Signed-off-by: Janis Schoetterl-Glausch <scgl@linux.ibm.com>
## lib/s390x/asm/arch_def.h ##
-@@ lib/s390x/asm/arch_def.h: struct psw {
+@@ lib/s390x/asm/arch_def.h: struct short_psw {
#define PSW_MASK_BA 0x0000000080000000UL
#define PSW_MASK_64 (PSW_MASK_BA | PSW_MASK_EA)
-+#define CTL0_TRANSACT_EX_CTL (63 - 8)
- #define CTL0_LOW_ADDR_PROT (63 - 35)
- #define CTL0_EDAT (63 - 40)
- #define CTL0_IEP (63 - 43)
++#define CTL0_TRANSACT_EX_CTL (63 - 8)
+ #define CTL0_LOW_ADDR_PROT (63 - 35)
+ #define CTL0_EDAT (63 - 40)
+ #define CTL0_FETCH_PROTECTION_OVERRIDE (63 - 38)
## s390x/spec_ex.c ##
@@
@@ s390x/spec_ex.c
#include <asm/interrupt.h>
+#include <asm/facility.h>
- static struct lowcore *lc = (struct lowcore *) 0;
-
+ static bool invalid_psw_expected;
+ static struct psw expected_psw;
@@ s390x/spec_ex.c: static int not_even(void)
/*
* Harness for specification exception testing.
@@ s390x/spec_ex.c: static int not_even(void)
/* List of all tests to execute */
static const struct spec_ex_trigger spec_ex_triggers[] = {
- { "psw_bit_12_is_1", &psw_bit_12_is_1, &fixup_invalid_psw },
+- { "short_psw_bit_12_is_0", &short_psw_bit_12_is_0, &fixup_invalid_psw },
- { "bad_alignment", &bad_alignment, NULL },
- { "not_even", ¬_even, NULL },
- { NULL, NULL, NULL },
+ { "psw_bit_12_is_1", &psw_bit_12_is_1, false, &fixup_invalid_psw },
++ { "short_psw_bit_12_is_0", &short_psw_bit_12_is_0, false,
&fixup_invalid_psw },
+ { "bad_alignment", &bad_alignment, true, NULL },
+ { "not_even", ¬_even, true, NULL },
+ { NULL, NULL, false, NULL },
@@ s390x/spec_ex.c: static int not_even(void)
static void test_spec_ex(const struct spec_ex_trigger *trigger)
@@ s390x/spec_ex.c: static void test_spec_ex(const struct spec_ex_trigger
*trigger)
- expected_pgm, pgm);
+ check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
}
+#define TRANSACTION_COMPLETED 4
+#define TRANSACTION_MAX_RETRIES 5
+
-+/* NULL must be passed to __builtin_tbegin via constant, forbid diagnose
from
++/*
++ * NULL must be passed to __builtin_tbegin via constant, forbid diagnose
from
+ * being NULL to keep things simple
+ */
+static int __attribute__((nonnull))
@@ s390x/spec_ex.c: static void test_spec_ex(const struct spec_ex_trigger
*trigger)
+ int cc;
+
+ cc = __builtin_tbegin(diagnose);
++ /*
++ * Everything between tbegin and tend is part of the transaction,
++ * which either completes in its entirety or does not have any effect.
++ * If the transaction fails, execution is reset to this point with
another
++ * condition code indicating why the transaction failed.
++ */
+ if (cc == _HTM_TBEGIN_STARTED) {
-+ /* return code is meaningless: transaction needs to complete
++ /*
++ * return code is meaningless: transaction needs to complete
+ * in order to return and completion indicates a test failure
+ */
+ trigger();
@@ s390x/spec_ex.c: static void test_spec_ex(const struct spec_ex_trigger
*trigger)
+ trans_result = with_transaction(trigger->func, tdb);
+ if (trans_result == _HTM_TBEGIN_TRANSIENT) {
+ mb();
-+ pgm = lc->pgm_int_code;
-+ if (pgm == 0)
-+ continue;
-+ else if (pgm == expected_pgm)
++ pgm = lowcore.pgm_int_code;
++ if (pgm == expected_pgm)
+ return 0;
++ else if (pgm == 0)
++ /*
++ * Transaction failed for unknown reason but
not because
++ * of an unexpected program exception. Give it
another
++ * go so that hopefully it reaches the
triggering instruction.
++ */
++ continue;
+ }
+ return trans_result;
+ }
@@ s390x/spec_ex.c: static void test_spec_ex(const struct spec_ex_trigger
*trigger)
+ report_fail("Transaction completed without exception");
+ break;
+ case TRANSACTION_MAX_RETRIES:
-+ report_info("Retried transaction %lu times without exception",
++ report_skip("Transaction retried %lu times with transient
failures, giving up",
+ args->max_retries);
+ break;
+ default:
-+ report_fail("Invalid return transaction result");
++ report_fail("Invalid transaction result");
+ break;
+ }
+
+ ctl_clear_bit(0, CTL0_TRANSACT_EX_CTL);
+}
+
++static bool parse_unsigned(const char *arg, unsigned int *out)
++{
++ char *end;
++ long num;
++
++ if (arg[0] == '\0')
++ return false;
++ num = strtol(arg, &end, 10);
++ if (end[0] != '\0' || num < 0)
++ return false;
++ *out = num;
++ return true;
++}
++
+static struct args parse_args(int argc, char **argv)
+{
+ struct args args = {
+ .max_retries = 20,
+ .diagnose = false
+ };
-+ unsigned int i;
-+ long arg;
-+ bool no_arg;
-+ char *end;
++ unsigned int i, arg;
++ bool has_arg;
+ const char *flag;
-+ uint64_t *argp;
+
+ for (i = 1; i < argc; i++) {
-+ no_arg = true;
-+ if (i < argc - 1) {
-+ no_arg = *argv[i + 1] == '\0';
-+ arg = strtol(argv[i + 1], &end, 10);
-+ no_arg |= *end != '\0';
-+ no_arg |= arg < 0;
-+ }
++ if (i + 1 < argc)
++ has_arg = parse_unsigned(argv[i + 1], &arg);
++ else
++ has_arg = false;
+
+ flag = "--max-retries";
-+ argp = &args.max_retries;
+ if (!strcmp(flag, argv[i])) {
-+ if (no_arg)
++ if (!has_arg)
+ report_abort("%s needs a positive parameter",
flag);
-+ *argp = arg;
++ args.max_retries = arg;
+ ++i;
+ continue;
+ }
base-commit: ca85dda2671e88d34acfbca6de48a9ab32b1810d
--
2.36.1
- [kvm-unit-tests PATCH v5 0/2] Add specification exception tests,
Janis Schoetterl-Glausch <=