On Fri, 26 Feb 2021 10:24:13 +0100 Janosch Frank <frankja@xxxxxxxxxxxxx> 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@xxxxxxxxxxxxx> > > Acked-by: Janosch Frank <frankja@xxxxxxxxxxxxx> > > --- > > 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@xxxxxxxxxxxxx> > > + */ > > +#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 > > >