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

Janosch Frank
2021-02-26T09:27:29Z

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
>