Re: [kvm-unit-tests PATCH v2 2/2] s390x: mvpg: simple test

Claudio Imbrenda
2021-02-26T10:17:28Z

On Fri, 26 Feb 2021 10:24:13 +0100
Janosch Frank <frankja@linux.ibm.com> wrote:

> On 2/23/21 3:24 PM, Claudio Imbrenda wrote:
> > A simple unit test for the MVPG instruction.
> >
> > The timeout is set to 10 seconds because the test should complete
> > in a fraction of a second even on busy machines. If the test is run
> > in VSIE and the host of the host is not handling MVPG properly, the
> > test will probably hang.
> >
> > Testing MVPG behaviour in VSIE is the main motivation for this test.
> >
> > Anything related to storage keys is not tested.
> >
> > Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
> > Acked-by: Janosch Frank <frankja@linux.ibm.com>
> > ---
> > s390x/Makefile | 1 +
> > s390x/mvpg.c | 266
> > ++++++++++++++++++++++++++++++++++++++++++++ s390x/unittests.cfg |
> > 4 + 3 files changed, 271 insertions(+)
> > create mode 100644 s390x/mvpg.c
> >
> > diff --git a/s390x/Makefile b/s390x/Makefile
> > index 08d85c9f..770eaa0b 100644
> > --- a/s390x/Makefile
> > +++ b/s390x/Makefile
> > @@ -20,6 +20,7 @@ tests += $(TEST_DIR)/sclp.elf
> > tests += $(TEST_DIR)/css.elf
> > tests += $(TEST_DIR)/uv-guest.elf
> > tests += $(TEST_DIR)/sie.elf
> > +tests += $(TEST_DIR)/mvpg.elf
> >
> > tests_binary = $(patsubst %.elf,%.bin,$(tests))
> > ifneq ($(HOST_KEY_DOCUMENT),)
> > diff --git a/s390x/mvpg.c b/s390x/mvpg.c
> > new file mode 100644
> > index 00000000..7a89a462
> > --- /dev/null
> > +++ b/s390x/mvpg.c
> > @@ -0,0 +1,266 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Move Page instruction tests
> > + *
> > + * Copyright (c) 2020 IBM Corp
>
> 2021
>
> I'd like to queue these patches soonish, can I fix that up or do you
> want to send a new version for me to queue?

you can fix it :)

> > + *
> > + * Authors:
> > + * Claudio Imbrenda <imbrenda@linux.ibm.com>
> > + */
> > +#include <libcflat.h>
> > +#include <asm/asm-offsets.h>
> > +#include <asm-generic/barrier.h>
> > +#include <asm/interrupt.h>
> > +#include <asm/pgtable.h>
> > +#include <mmu.h>
> > +#include <asm/page.h>
> > +#include <asm/facility.h>
> > +#include <asm/mem.h>
> > +#include <asm/sigp.h>
> > +#include <smp.h>
> > +#include <alloc_page.h>
> > +#include <bitops.h>
> > +
> > +/* Used to build the appropriate test values for register 0 */
> > +#define KFC(x) ((x) << 10)
> > +#define CCO 0x100
> > +
> > +/* How much memory to allocate for the test */
> > +#define MEM_ORDER 12
> > +/* How many iterations to perform in the loops */
> > +#define ITER 8
> > +
> > +/* Used to generate the simple pattern */
> > +#define MAGIC 42
> > +
> > +static uint8_t source[PAGE_SIZE]
> > __attribute__((aligned(PAGE_SIZE))); +static uint8_t
> > buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +
> > +/* Keep track of fresh memory */
> > +static uint8_t *fresh;
> > +
> > +static inline int mvpg(unsigned long r0, void *dest, void *src)
> > +{
> > + register unsigned long reg0 asm ("0") = r0;
> > + int cc;
> > +
> > + asm volatile(" mvpg %1,%2\n"
> > + " ipm %0\n"
> > + " srl %0,28"
> > + : "=&d" (cc) : "a" (dest), "a" (src), "d" (reg0)
> > + : "memory", "cc");
> > + return cc;
> > +}
> > +
> > +/*
> > + * Initialize a page with a simple pattern
> > + */
> > +static void init_page(uint8_t *p)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < PAGE_SIZE; i++)
> > + p[i] = i + MAGIC;
> > +}
> > +
> > +/*
> > + * Check if the given page contains the simple pattern
> > + */
> > +static int page_ok(const uint8_t *p)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < PAGE_SIZE; i++)
> > + if (p[i] != (uint8_t)(i + MAGIC))
> > + return 0;
> > + return 1;
> > +}
> > +
> > +static void test_exceptions(void)
> > +{
> > + int i, expected;
> > +
> > + report_prefix_push("exceptions");
> > +
> > + /*
> > + * Key Function Control values 4 and 5 are allowed only in
> > supervisor
> > + * state, and even then, only if the move-page-and-set-key
> > facility
> > + * is present (STFLE bit 149)
> > + */
> > + report_prefix_push("privileged");
> > + if (test_facility(149)) {
> > + expected = PGM_INT_CODE_PRIVILEGED_OPERATION;
> > + for (i = 4; i < 6; i++) {
> > + expect_pgm_int();
> > + enter_pstate();
> > + mvpg(KFC(i), buffer, source);
> > + report(clear_pgm_int() == expected, "Key
> > Function Control value %d", i);
> > + }
> > + } else {
> > + report_skip("Key Function Control value %d", 4);
> > + report_skip("Key Function Control value %d", 5);
> > + i = 4;
> > + }
> > + report_prefix_pop();
> > +
> > + /*
> > + * Invalid values of the Key Function Control, or setting
> > the
> > + * reserved bits, should result in a specification
> > exception
> > + */
> > + report_prefix_push("specification");
> > + expected = PGM_INT_CODE_SPECIFICATION;
> > + expect_pgm_int();
> > + mvpg(KFC(3), buffer, source);
> > + report(clear_pgm_int() == expected, "Key Function Control
> > value 3");
> > + for (; i < 32; i++) {
> > + expect_pgm_int();
> > + mvpg(KFC(i), buffer, source);
> > + report(clear_pgm_int() == expected, "Key Function
> > Control value %d", i);
> > + }
> > + report_prefix_pop();
> > +
> > + /* Operands outside memory result in addressing
> > exceptions, as usual */
> > + report_prefix_push("addressing");
> > + expected = PGM_INT_CODE_ADDRESSING;
> > + expect_pgm_int();
> > + mvpg(0, buffer, (void *)PAGE_MASK);
> > + report(clear_pgm_int() == expected, "Second operand
> > outside memory"); +
> > + expect_pgm_int();
> > + mvpg(0, (void *)PAGE_MASK, source);
> > + report(clear_pgm_int() == expected, "First operand outside
> > memory");
> > + report_prefix_pop();
> > +
> > + report_prefix_pop();
> > +}
> > +
> > +static void test_success(void)
> > +{
> > + int cc;
> > +
> > + report_prefix_push("success");
> > + /* Test successful scenarios, both in supervisor and
> > problem state */
> > + cc = mvpg(0, buffer, source);
> > + report(page_ok(buffer) && !cc, "Supervisor state MVPG
> > successful"); +
> > + enter_pstate();
> > + cc = mvpg(0, buffer, source);
> > + leave_pstate();
> > + report(page_ok(buffer) && !cc, "Problem state MVPG
> > successful"); +
> > + report_prefix_pop();
> > +}
> > +
> > +static void test_small_loop(const void *string)
> > +{
> > + uint8_t *dest;
> > + int i, cc;
> > +
> > + /* Looping over cold and warm pages helps catch VSIE bugs
> > */
> > + report_prefix_push(string);
> > + dest = fresh;
> > + for (i = 0; i < ITER; i++) {
> > + cc = mvpg(0, fresh, source);
> > + report(page_ok(fresh) && !cc, "cold: %p, %p",
> > source, fresh);
> > + fresh += PAGE_SIZE;
> > + }
> > +
> > + for (i = 0; i < ITER; i++) {
> > + memset(dest, 0, PAGE_SIZE);
> > + cc = mvpg(0, dest, source);
> > + report(page_ok(dest) && !cc, "warm: %p, %p",
> > source, dest);
> > + dest += PAGE_SIZE;
> > + }
> > + report_prefix_pop();
> > +}
> > +
> > +static void test_mmu_prot(void)
> > +{
> > + int cc;
> > +
> > + report_prefix_push("protection");
> > + report_prefix_push("cco=0");
> > +
> > + /* MVPG should still succeed when the source is read-only
> > */
> > + protect_page(source, PAGE_ENTRY_P);
> > + cc = mvpg(0, fresh, source);
> > + report(page_ok(fresh) && !cc, "source read only");
> > + unprotect_page(source, PAGE_ENTRY_P);
> > + fresh += PAGE_SIZE;
> > +
> > + /*
> > + * When the source or destination are invalid, a page
> > translation
> > + * exception should be raised; when the destination is
> > read-only,
> > + * a protection exception should be raised.
> > + */
> > + protect_page(fresh, PAGE_ENTRY_P);
> > + expect_pgm_int();
> > + mvpg(0, fresh, source);
> > + report(clear_pgm_int() == PGM_INT_CODE_PROTECTION,
> > "destination read only");
> > + fresh += PAGE_SIZE;
> > +
> > + protect_page(source, PAGE_ENTRY_I);
> > + expect_pgm_int();
> > + mvpg(0, fresh, source);
> > + report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION,
> > "source invalid");
> > + unprotect_page(source, PAGE_ENTRY_I);
> > + fresh += PAGE_SIZE;
> > +
> > + protect_page(fresh, PAGE_ENTRY_I);
> > + expect_pgm_int();
> > + mvpg(0, fresh, source);
> > + report(clear_pgm_int() == PGM_INT_CODE_PAGE_TRANSLATION,
> > "destination invalid");
> > + fresh += PAGE_SIZE;
> > +
> > + report_prefix_pop();
> > + report_prefix_push("cco=1");
> > + /*
> > + * Setting the CCO bit should suppress page translation
> > exceptions,
> > + * but not protection exceptions.
> > + */
> > + protect_page(fresh, PAGE_ENTRY_P);
> > + expect_pgm_int();
> > + mvpg(CCO, fresh, source);
> > + report(clear_pgm_int() == PGM_INT_CODE_PROTECTION,
> > "destination read only");
> > + fresh += PAGE_SIZE;
> > +
> > + protect_page(fresh, PAGE_ENTRY_I);
> > + cc = mvpg(CCO, fresh, source);
> > + report(cc == 1, "destination invalid");
> > + fresh += PAGE_SIZE;
> > +
> > + protect_page(source, PAGE_ENTRY_I);
> > + cc = mvpg(CCO, fresh, source);
> > + report(cc == 2, "source invalid");
> > + fresh += PAGE_SIZE;
> > +
> > + protect_page(fresh, PAGE_ENTRY_I);
> > + cc = mvpg(CCO, fresh, source);
> > + report(cc == 2, "source and destination invalid");
> > + fresh += PAGE_SIZE;
> > +
> > + unprotect_page(source, PAGE_ENTRY_I);
> > + report_prefix_pop();
> > + report_prefix_pop();
> > +}
> > +
> > +int main(void)
> > +{
> > + report_prefix_push("mvpg");
> > +
> > + init_page(source);
> > + fresh = alloc_pages_flags(MEM_ORDER, FLAG_DONTZERO |
> > FLAG_FRESH);
> > + assert(fresh);
> > +
> > + test_exceptions();
> > + test_success();
> > + test_small_loop("nommu");
> > +
> > + setup_vm();
> > +
> > + test_small_loop("mmu");
> > + test_mmu_prot();
> > +
> > + report_prefix_pop();
> > + return report_summary();
> > +}
> > diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
> > index 2298be6c..9f81a608 100644
> > --- a/s390x/unittests.cfg
> > +++ b/s390x/unittests.cfg
> > @@ -99,3 +99,7 @@ file = uv-guest.elf
> >
> > [sie]
> > file = sie.elf
> > +
> > +[mvpg]
> > +file = mvpg.elf
> > +timeout = 10
> >
>