Message ID | 20210223142429.256420-3-imbrenda@linux.ibm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | s390x: mvpg test | expand |
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? > + * > + * 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 >
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 > > >
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 + * + * 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