Re: [kvm-unit-tests PATCH v2 1/8] s390x: Add sthyi tests

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

 



On 20.03.2018 12:19, Janosch Frank wrote:
> The Store Hypervisor Information (STHYI) instruction provides system
> information like available CPU resources on each system level. The
> instruction is fully emulated by the hypervisor.
> 
> Signed-off-by: Janosch Frank <frankja@xxxxxxxxxxxxxxxxxx>
> ---
>  s390x/Makefile      |   1 +
>  s390x/sthyi.c       | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  s390x/sthyi.h       | 131 +++++++++++++++++++++++++++++
>  s390x/unittests.cfg |   4 +
>  4 files changed, 368 insertions(+)
>  create mode 100644 s390x/sthyi.c
>  create mode 100644 s390x/sthyi.h
> 
> diff --git a/s390x/Makefile b/s390x/Makefile
> index 2d3336c..e062727 100644
> --- a/s390x/Makefile
> +++ b/s390x/Makefile
> @@ -2,6 +2,7 @@ tests = $(TEST_DIR)/selftest.elf
>  tests += $(TEST_DIR)/intercept.elf
>  tests += $(TEST_DIR)/emulator.elf
>  tests += $(TEST_DIR)/sieve.elf
> +tests += $(TEST_DIR)/sthyi.elf
>  
>  all: directories test_cases
>  
> diff --git a/s390x/sthyi.c b/s390x/sthyi.c
> new file mode 100644
> index 0000000..a557b49
> --- /dev/null
> +++ b/s390x/sthyi.c
> @@ -0,0 +1,232 @@
> +/*
> + * Tests exceptions and data validity for the emulated sthyi
> + * instruction.
> + *
> + * Copyright 2018 IBM Corp.
> + *
> + * Authors:
> + *    Janosch Frank <frankja@xxxxxxxxxxxxxxxxxx>
> + *
> + * This code is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Library General Public License version 2.
> + */
> +#include <libcflat.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/interrupt.h>
> +#include <asm/page.h>
> +#include <asm/facility.h>
> +
> +#include "sthyi.h"
> +
> +static uint8_t pagebuf[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));


You can also use alloc_page() instead now.


> +static char null_buf[32] = {};
> +
> +static int sthyi(uint64_t vaddr, uint64_t fcode)
> +{
> +	register uint64_t code asm("0") = fcode;
> +	register uint64_t dummy1 asm("1") = 0;
> +	register uint64_t addr asm("2") = vaddr;
> +	register uint64_t rc asm("3") = 0;
> +	int cc = 0;
> +
> +	asm volatile(
> +		".insn	 rre,0xB2560000,%[code],%[addr]\n"
> +		"ipm	 %[cc]\n"
> +		"srl	 %[cc],28\n"
> +		: "=d" (rc), [cc] "=d" (cc) /* outputs */
> +		: [code] "d" (code), "d" (dummy1), [addr] "a" (addr) /*inputs*/
> +		: "memory", "cc" /* clobbers */);
> +	return cc;
> +}
> +
> +static int sthyi_ill_reg_odd1(void)
> +{
> +	register uint64_t code asm("1") = 0;
> +	register uint64_t dummy1 asm("2") = 0;
> +	register uint64_t addr asm("4") = 0x424242424242000;
> +	register uint64_t rc asm("5") = 0;
> +	int cc = 0;
> +
> +	asm volatile(
> +		".insn	 rre,0xB2560000,%[code],%[addr]\n"
> +		"ipm	 %[cc]\n"
> +		"srl	 %[cc],28\n"
> +		: "=d" (rc), [cc] "=d" (cc) /* outputs */
> +		: [code] "d" (code), "d" (dummy1), [addr] "a" (addr) /*inputs*/
> +		: "memory", "cc" /* clobbers */);
> +	return cc;
> +}
> +
> +static int sthyi_ill_reg_odd2(void)
> +{
> +	register uint64_t code asm("0") = 0;
> +	register uint64_t dummy1 asm("1") = 0;
> +	register uint64_t addr asm("3") = 0x424242424242000;
> +	register uint64_t rc asm("4") = 0;
> +	int cc = 0;
> +
> +	asm volatile(
> +		".insn	 rre,0xB2560000,%[code],%[addr]\n"
> +		"ipm	 %[cc]\n"
> +		"srl	 %[cc],28\n"
> +		: "=d" (rc), [cc] "=d" (cc) /* outputs */
> +		: [code] "d" (code), "d" (dummy1), [addr] "a" (addr) /*inputs*/
> +		: "memory", "cc" /* clobbers */);
> +	return cc;
> +}

What about merging these 3 functions like we do for __test_cpacf() ? ...

(see s390x/emulator.c)

> +
> +static int sthyi_ill_reg_eq(void)
> +{
> +	int cc = 0;
> +
> +	asm volatile(
> +		".insn	 rre,0xB2560000,0,0\n"
> +		"ipm	 %[cc]\n"
> +		"srl	 %[cc],28\n"
> +		: [cc] "=d" (cc) /* outputs */
> +		: /*inputs*/
> +		: "memory", "cc" /* clobbers */);
> +	return cc;
> +}
> +
> +static int sthyi_ill_fcode(uint64_t *urc)
> +{
> +	register uint64_t code asm("0") = 0x0000000000004242;
> +	register uint64_t dummy1 asm("1") = 0;
> +	register uint64_t addr asm("2") = 0x424242424242000;
> +	register uint64_t rc asm("3") = 0;
> +	uint64_t cc = 0;
> +
> +	asm volatile(
> +		".insn	 rre,0xB2560000,%[code],%[addr]\n"
> +		"ipm	 %[cc]\n"
> +		"srl	 %[cc],28\n"
> +		: "=d" (rc), [cc] "=d" (cc) /* outputs */
> +		: [code] "d" (code), "d" (dummy1), [addr] "a" (addr) /*inputs*/
> +		: "memory", "cc" /* clobbers */);
> +	*urc = rc;
> +	return cc;
> +}

... and this one, too? Guess you can just parameterize what you need. So
we can live with one ASM statement only.

> +
> +static void test_exception_addr(void)
> +{
> +	expect_pgm_int();
> +	sthyi(42042, 0);
> +	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
> +}
> +
> +static void test_exception_reg_odd(void)
> +{
> +	expect_pgm_int();
> +	sthyi_ill_reg_odd1();
> +	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
> +	expect_pgm_int();
> +	sthyi_ill_reg_odd2();
> +	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
> +}
> +
> +static void test_exception_reg_equal(void)
> +{
> +	expect_pgm_int();
> +	sthyi_ill_reg_eq();
> +	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
> +}
> +
> +static void test_function_code(uint64_t addr)
> +{
> +	uint64_t urc = 0;
> +	int cc = sthyi_ill_fcode(&urc);
> +
> +	report("Ill. fcode", cc == 3 && urc == CODE_UNSUPP);
> +}
> +
> +static void test_fcode0_hdr(struct sthyi_hdr_sctn *hdr)
> +{
> +	report("HDR length", (hdr->INFHDLN >= sizeof(*hdr)
> +			      && !(hdr->INFHDLN % 8)));
> +	report("MACH sctn length", (hdr->INFMLEN >= sizeof(struct sthyi_mach_sctn)
> +				    && !(hdr->INFMLEN % 8)));
> +	report("PAR sctn length", (hdr->INFPLEN >= sizeof(struct sthyi_par_sctn)
> +				   && !(hdr->INFPLEN % 8)));
> +
> +	report("MACH offset", hdr->INFMOFF >= hdr->INFHDLN);
> +	report("PAR offset", hdr->INFPOFF >= hdr->INFHDLN);
> +}
> +
> +static void test_fcode0_mach(struct sthyi_mach_sctn *mach)
> +{
> +	int sum = mach->INFMSCPS + mach->INFMDCPS + mach->INFMSIFL + mach->INFMDIFL;
> +
> +	if (mach->INFMVAL1 & MACH_ID_VLD) {
> +		report("MACH type", memcmp(mach->INFMTYPE, null_buf, sizeof(mach->INFMTYPE)));
> +		report("MACH manu", memcmp(mach->INFMMANU, null_buf, sizeof(mach->INFMMANU)));
> +		report("MACH seq", memcmp(mach->INFMSEQ, null_buf, sizeof(mach->INFMSEQ)));
> +		report("MACH plant", memcmp(mach->INFMPMAN, null_buf, sizeof(mach->INFMPMAN)));
> +	}
> +
> +	if (mach->INFMVAL1 & MACH_NAME_VLD)
> +		report("MACH name", memcmp(mach->INFMNAME, null_buf,
> +					   sizeof(mach->INFMNAME)));
> +
> +	if (mach->INFMVAL1 & MACH_CNT_VLD)
> +		report("MACH core counts", sum);
> +}
> +
> +static void test_fcode0_par(struct sthyi_par_sctn *par)
> +{
> +	int sum = par->INFPSCPS + par->INFPDCPS + par->INFPSIFL + par->INFPDIFL;
> +
> +	if (par->INFPVAL1 & PART_CNT_VLD)
> +		report("PAR core counts", sum);
> +
> +	if (par->INFPVAL1 & PART_STSI_SUC) {
> +		report("PAR number", par->INFPPNUM);
> +		report("PAR name", memcmp(par->INFPPNAM, null_buf, sizeof(par->INFPPNAM)));
> +	}
> +}
> +
> +static void test_fcode0(void)
> +{
> +	struct sthyi_hdr_sctn *hdr;
> +	struct sthyi_mach_sctn *mach;
> +	struct sthyi_par_sctn *par;
> +
> +	/* Zero destination memory. */
> +	memset(pagebuf, 0, PAGE_SIZE);
> +
> +	sthyi((uint64_t)pagebuf, 0);
> +	hdr = (void *)pagebuf;
> +	mach = (void *)pagebuf + hdr->INFMOFF;
> +	par = (void *)pagebuf + hdr->INFPOFF;
> +
> +	test_fcode0_hdr(hdr);
> +	test_fcode0_mach(mach);
> +	test_fcode0_par(par);
> +}
> +
> +int main(void)
> +{
> +	bool has_sthyi = test_facility(74);
> +
> +	report_prefix_push("sthyi");
> +
> +	/* Test for availability */
> +	report_xfail("STHYI available", !has_sthyi, has_sthyi);
> +	if (!has_sthyi)
> +		goto done;

Not sure if "if (has_sthyi) ..." is better. Avoids the goto.

> +
> +	/* Test register/argument checking. */
> +	test_exception_addr();
> +	report_prefix_push("Register check");
> +	test_exception_reg_odd();
> +	test_exception_reg_equal();
> +	report_prefix_pop();
> +	test_function_code((uint64_t) pagebuf);
> +
> +	/* Test function code 0 - CP and IFL Capacity Information */
> +	test_fcode0();
> +
> +done:
> +	report_prefix_pop();
> +	return report_summary();
> +}
> diff --git a/s390x/sthyi.h b/s390x/sthyi.h
> new file mode 100644
> index 0000000..06f757f





> diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
> index 7506d06..506891a 100644
> --- a/s390x/unittests.cfg
> +++ b/s390x/unittests.cfg
> @@ -34,3 +34,7 @@ file = sieve.elf
>  groups = selftest
>  # can take fairly long when KVM is nested inside z/VM
>  timeout = 600
> +
> +[sthyi]
> +file = sthyi.elf
> +accel = kvm

Is that really necessary? The availability test should be enough (e.g.
TCG does not indicate it, so the test is marked as skipped).


Thanks for submitting kvm-unit-tests for s390x :)

-- 

Thanks,

David / dhildenb



[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