kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [kvm-unit-tests PATCH v2 0/2] s390x: mvpg test
@ 2021-02-23 14:24 Claudio Imbrenda
  2021-02-23 14:24 ` [kvm-unit-tests PATCH v2 1/2] s390x: introduce leave_pstate to leave userspace Claudio Imbrenda
  2021-02-23 14:24 ` [kvm-unit-tests PATCH v2 2/2] s390x: mvpg: simple test Claudio Imbrenda
  0 siblings, 2 replies; 6+ messages in thread
From: Claudio Imbrenda @ 2021-02-23 14:24 UTC (permalink / raw)
  To: kvm; +Cc: david, thuth, frankja, cohuck, pmorel, borntraeger

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.

v1->v2
* droppped patch 2 which introduced is_pgm();
* patch 1: replace a hardcoded value with the new macro SVC_LEAVE_PSTATE
* patch 2: clear_pgm_int() returns the old value, use that instad of is_pgm()

Claudio Imbrenda (2):
  s390x: introduce leave_pstate to leave userspace
  s390x: mvpg: simple test

 s390x/Makefile           |   1 +
 lib/s390x/asm/arch_def.h |   7 ++
 lib/s390x/interrupt.c    |  12 +-
 s390x/mvpg.c             | 266 +++++++++++++++++++++++++++++++++++++++
 s390x/unittests.cfg      |   4 +
 5 files changed, 288 insertions(+), 2 deletions(-)
 create mode 100644 s390x/mvpg.c

-- 
2.26.2


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [kvm-unit-tests PATCH v2 1/2] s390x: introduce leave_pstate to leave userspace
  2021-02-23 14:24 [kvm-unit-tests PATCH v2 0/2] s390x: mvpg test Claudio Imbrenda
@ 2021-02-23 14:24 ` Claudio Imbrenda
  2021-02-23 16:19   ` Janosch Frank
  2021-02-23 14:24 ` [kvm-unit-tests PATCH v2 2/2] s390x: mvpg: simple test Claudio Imbrenda
  1 sibling, 1 reply; 6+ messages in thread
From: Claudio Imbrenda @ 2021-02-23 14:24 UTC (permalink / raw)
  To: kvm; +Cc: david, thuth, frankja, cohuck, pmorel, borntraeger

In most testcases, we enter problem state (userspace) just to test if a
privileged instruction causes a fault. In some cases, though, we need
to test if an instruction works properly in userspace. This means that
we do not expect a fault, and we need an orderly way to leave problem
state afterwards.

This patch introduces a simple system based on the SVC instruction.

Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Acked-by: David Hildenbrand <david@redhat.com>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
---
 lib/s390x/asm/arch_def.h |  7 +++++++
 lib/s390x/interrupt.c    | 12 ++++++++++--
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/lib/s390x/asm/arch_def.h b/lib/s390x/asm/arch_def.h
index 9c4e330a..4cf8eb11 100644
--- a/lib/s390x/asm/arch_def.h
+++ b/lib/s390x/asm/arch_def.h
@@ -173,6 +173,8 @@ struct cpuid {
 	uint64_t reserved : 15;
 };
 
+#define SVC_LEAVE_PSTATE 1
+
 static inline unsigned short stap(void)
 {
 	unsigned short cpu_address;
@@ -276,6 +278,11 @@ static inline void enter_pstate(void)
 	load_psw_mask(mask);
 }
 
+static inline void leave_pstate(void)
+{
+	asm volatile("	svc %0\n" : : "i" (SVC_LEAVE_PSTATE));
+}
+
 static inline int stsi(void *addr, int fc, int sel1, int sel2)
 {
 	register int r0 asm("0") = (fc << 28) | sel1;
diff --git a/lib/s390x/interrupt.c b/lib/s390x/interrupt.c
index 1ce36073..d0567845 100644
--- a/lib/s390x/interrupt.c
+++ b/lib/s390x/interrupt.c
@@ -188,6 +188,14 @@ int unregister_io_int_func(void (*f)(void))
 
 void handle_svc_int(void)
 {
-	report_abort("Unexpected supervisor call interrupt: on cpu %d at %#lx",
-		     stap(), lc->svc_old_psw.addr);
+	uint16_t code = lc->svc_int_code;
+
+	switch (code) {
+	case SVC_LEAVE_PSTATE:
+		lc->svc_old_psw.mask &= ~PSW_MASK_PSTATE;
+		break;
+	default:
+		report_abort("Unexpected supervisor call interrupt: code %#x on cpu %d at %#lx",
+			      code, stap(), lc->svc_old_psw.addr);
+	}
 }
-- 
2.26.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [kvm-unit-tests PATCH v2 2/2] s390x: mvpg: simple test
  2021-02-23 14:24 [kvm-unit-tests PATCH v2 0/2] s390x: mvpg test Claudio Imbrenda
  2021-02-23 14:24 ` [kvm-unit-tests PATCH v2 1/2] s390x: introduce leave_pstate to leave userspace Claudio Imbrenda
@ 2021-02-23 14:24 ` Claudio Imbrenda
  2021-02-26  9:24   ` Janosch Frank
  1 sibling, 1 reply; 6+ messages in thread
From: Claudio Imbrenda @ 2021-02-23 14:24 UTC (permalink / raw)
  To: kvm; +Cc: david, thuth, frankja, cohuck, pmorel, borntraeger

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


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [kvm-unit-tests PATCH v2 1/2] s390x: introduce leave_pstate to leave userspace
  2021-02-23 14:24 ` [kvm-unit-tests PATCH v2 1/2] s390x: introduce leave_pstate to leave userspace Claudio Imbrenda
@ 2021-02-23 16:19   ` Janosch Frank
  0 siblings, 0 replies; 6+ messages in thread
From: Janosch Frank @ 2021-02-23 16:19 UTC (permalink / raw)
  To: Claudio Imbrenda, kvm; +Cc: david, thuth, cohuck, pmorel, borntraeger

On 2/23/21 3:24 PM, Claudio Imbrenda wrote:
> In most testcases, we enter problem state (userspace) just to test if a
> privileged instruction causes a fault. In some cases, though, we need
> to test if an instruction works properly in userspace. This means that
> we do not expect a fault, and we need an orderly way to leave problem
> state afterwards.
> 
> This patch introduces a simple system based on the SVC instruction.
> 
> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
> Acked-by: David Hildenbrand <david@redhat.com>
> Reviewed-by: Cornelia Huck <cohuck@redhat.com>

Reviewed-by: Janosch Frank <frankja@linux.ibm.com>

> ---
>  lib/s390x/asm/arch_def.h |  7 +++++++
>  lib/s390x/interrupt.c    | 12 ++++++++++--
>  2 files changed, 17 insertions(+), 2 deletions(-)
> 
> diff --git a/lib/s390x/asm/arch_def.h b/lib/s390x/asm/arch_def.h
> index 9c4e330a..4cf8eb11 100644
> --- a/lib/s390x/asm/arch_def.h
> +++ b/lib/s390x/asm/arch_def.h
> @@ -173,6 +173,8 @@ struct cpuid {
>  	uint64_t reserved : 15;
>  };
>  
> +#define SVC_LEAVE_PSTATE 1
> +
>  static inline unsigned short stap(void)
>  {
>  	unsigned short cpu_address;
> @@ -276,6 +278,11 @@ static inline void enter_pstate(void)
>  	load_psw_mask(mask);
>  }
>  
> +static inline void leave_pstate(void)
> +{
> +	asm volatile("	svc %0\n" : : "i" (SVC_LEAVE_PSTATE));
> +}

When we add other svc calls then we should create a svc(int code)
function and call it here.

> +
>  static inline int stsi(void *addr, int fc, int sel1, int sel2)
>  {
>  	register int r0 asm("0") = (fc << 28) | sel1;
> diff --git a/lib/s390x/interrupt.c b/lib/s390x/interrupt.c
> index 1ce36073..d0567845 100644
> --- a/lib/s390x/interrupt.c
> +++ b/lib/s390x/interrupt.c
> @@ -188,6 +188,14 @@ int unregister_io_int_func(void (*f)(void))
>  
>  void handle_svc_int(void)
>  {
> -	report_abort("Unexpected supervisor call interrupt: on cpu %d at %#lx",
> -		     stap(), lc->svc_old_psw.addr);
> +	uint16_t code = lc->svc_int_code;
> +
> +	switch (code) {
> +	case SVC_LEAVE_PSTATE:
> +		lc->svc_old_psw.mask &= ~PSW_MASK_PSTATE;
> +		break;
> +	default:
> +		report_abort("Unexpected supervisor call interrupt: code %#x on cpu %d at %#lx",
> +			      code, stap(), lc->svc_old_psw.addr);
> +	}
>  }
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [kvm-unit-tests PATCH v2 2/2] s390x: mvpg: simple test
  2021-02-23 14:24 ` [kvm-unit-tests PATCH v2 2/2] s390x: mvpg: simple test Claudio Imbrenda
@ 2021-02-26  9:24   ` Janosch Frank
  2021-02-26 10:14     ` Claudio Imbrenda
  0 siblings, 1 reply; 6+ messages in thread
From: Janosch Frank @ 2021-02-26  9:24 UTC (permalink / raw)
  To: Claudio Imbrenda, kvm; +Cc: david, thuth, cohuck, pmorel, borntraeger

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
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [kvm-unit-tests PATCH v2 2/2] s390x: mvpg: simple test
  2021-02-26  9:24   ` Janosch Frank
@ 2021-02-26 10:14     ` Claudio Imbrenda
  0 siblings, 0 replies; 6+ messages in thread
From: Claudio Imbrenda @ 2021-02-26 10:14 UTC (permalink / raw)
  To: Janosch Frank; +Cc: kvm, david, thuth, cohuck, pmorel, borntraeger

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
> >   
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-02-26 10:17 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-23 14:24 [kvm-unit-tests PATCH v2 0/2] s390x: mvpg test Claudio Imbrenda
2021-02-23 14:24 ` [kvm-unit-tests PATCH v2 1/2] s390x: introduce leave_pstate to leave userspace Claudio Imbrenda
2021-02-23 16:19   ` Janosch Frank
2021-02-23 14:24 ` [kvm-unit-tests PATCH v2 2/2] s390x: mvpg: simple test Claudio Imbrenda
2021-02-26  9:24   ` Janosch Frank
2021-02-26 10:14     ` Claudio Imbrenda

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).