qemu-s390x
[Top][All Lists]
Advanced

[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", &not_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", &not_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", &not_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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]