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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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
> 




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux