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? > + * > + * 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 >