diff mbox

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

Message ID 1521544776-149935-2-git-send-email-frankja@linux.vnet.ibm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Janosch Frank March 20, 2018, 11:19 a.m. UTC
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@linux.vnet.ibm.com>
---
 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

Comments

Thomas Huth March 22, 2018, 6:11 a.m. UTC | #1
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@linux.vnet.ibm.com>
> ---
>  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@linux.vnet.ibm.com>
> + *
> + * 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)));
> +static char null_buf[32] = {};
[...]
> +
> +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);
> +}
[...]
> +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;
> +
> +	/* Test register/argument checking. */
> +	test_exception_addr();
> +	report_prefix_push("Register check");
> +	test_exception_reg_odd();
> +	test_exception_reg_equal();
> +	report_prefix_pop();

It's rather cosmetical, but in case you respin, I'd prefer if you could
move the report_prefix_push() and report_prefix_pop() statements into
the test_exception_reg_odd() and test_exception_reg_equal() functions
themselves, and also add them to test_exception_addr(), and use a
different prefix string in each of the functions. Then it will be much
easier to distinguish the tests in the output later.

Anyway, the rest of the patch looks fine to me, so with or without that
modification:

Reviewed-by: Thomas Huth <thuth@redhat.com>
David Hildenbrand March 23, 2018, 11:19 a.m. UTC | #2
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@linux.vnet.ibm.com>
> ---
>  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@linux.vnet.ibm.com>
> + *
> + * 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 :)
diff mbox

Patch

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@linux.vnet.ibm.com>
+ *
+ * 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)));
+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;
+}
+
+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;
+}
+
+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;
+
+	/* 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
--- /dev/null
+++ b/s390x/sthyi.h
@@ -0,0 +1,131 @@ 
+/*
+ * STHYI related flags and structure definitions.
+ *
+ * Copyright 2018 IBM Corp.
+ *
+ * Authors:
+ *    Janosch Frank <frankja@linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+#ifndef _STHYI_H_
+#define _STHYI_H_
+
+#include <stdint.h>
+
+enum sthyi_rtn_code {
+	CODE_UNSUPP = 0x04, /* with cc = 3 */
+	CODE_SUCCES = 0x00, /* with cc = 0 */
+};
+
+enum sthyi_hdr_flags {
+	HDR_PERF_UNAV  = 0x80,
+	HDR_STSI_UNAV  = 0x40,
+	HDR_STACK_INCM = 0x20,
+	HDR_NOT_LPAR   = 0x10,
+};
+
+enum sthyi_mach_validity {
+	MACH_CNT_VLD  = 0x80,
+	MACH_ID_VLD   = 0x40,
+	MACH_NAME_VLD = 0x20,
+};
+
+enum sthyi_par_flag {
+	PART_MT_EN = 0x80,
+};
+
+enum sthyi_par_validity {
+	PART_CNT_VLD  = 0x80,
+	PART_WGHT_CAP = 0x40,
+	PART_ABS_CAP  = 0x20,
+	PART_STSI_SUC = 0x10,
+	PART_GRP_VLD  = 0x08,
+};
+
+struct sthyi_hdr_sctn {
+	uint8_t INFHFLG1;
+	uint8_t INFHFLG2; /* reserved */
+	uint8_t INFHVAL1; /* reserved */
+	uint8_t INFHVAL2; /* reserved */
+	uint8_t reserved[3];
+	uint8_t INFHYGCT;
+	uint16_t INFHTOTL;
+	uint16_t INFHDLN;
+	uint16_t INFMOFF;
+	uint16_t INFMLEN;
+	uint16_t INFPOFF;
+	uint16_t INFPLEN;
+	uint16_t INFHOFF1;
+	uint16_t INFHLEN1;
+	uint16_t INFGOFF1;
+	uint16_t INFGLEN1;
+	uint16_t INFHOFF2;
+	uint16_t INFHLEN2;
+	uint16_t INFGOFF2;
+	uint16_t INFGLEN2;
+	uint16_t INFHOFF3;
+	uint16_t INFHLEN3;
+	uint16_t INFGOFF3;
+	uint16_t INFGLEN3;
+	uint8_t reserved2[4];
+} __attribute__((packed));
+
+struct sthyi_mach_sctn {
+	uint8_t INFMFLG1; /* reserved */
+	uint8_t INFMFLG2; /* reserved */
+	uint8_t INFMVAL1;
+	uint8_t INFMVAL2; /* reserved */
+	uint16_t INFMSCPS;
+	uint16_t INFMDCPS;
+	uint16_t INFMSIFL;
+	uint16_t INFMDIFL;
+	char INFMNAME[8];
+	char INFMTYPE[4];
+	char INFMMANU[16];
+	char INFMSEQ[16];
+	char INFMPMAN[4];
+	uint8_t reserved[4];
+} __attribute__((packed));
+
+struct sthyi_par_sctn {
+	uint8_t INFPFLG1;
+	uint8_t INFPFLG2; /* reserved */
+	uint8_t INFPVAL1;
+	uint8_t INFPVAL2; /* reserved */
+	uint16_t INFPPNUM;
+	uint16_t INFPSCPS;
+	uint16_t INFPDCPS;
+	uint16_t INFPSIFL;
+	uint16_t INFPDIFL;
+	uint16_t reserved;
+	char INFPPNAM[8];
+	uint32_t INFPWBCP;
+	uint32_t INFPABCP;
+	uint32_t INFPWBIF;
+	uint32_t INFPABIF;
+} __attribute__((packed));
+
+struct sthyi_par_sctn_ext {
+	uint8_t INFPFLG1;
+	uint8_t INFPFLG2; /* reserved */
+	uint8_t INFPVAL1;
+	uint8_t INFPVAL2; /* reserved */
+	uint16_t INFPPNUM;
+	uint16_t INFPSCPS;
+	uint16_t INFPDCPS;
+	uint16_t INFPSIFL;
+	uint16_t INFPDIFL;
+	uint16_t reserved;
+	char INFPPNAM[8];
+	uint32_t INFPWBCP;
+	uint32_t INFPABCP;
+	uint32_t INFPWBIF;
+	uint32_t INFPABIF;
+	char INFPLGNM[8];
+	uint32_t INFPLGCP;
+	uint32_t INFPLGIF;
+} __attribute__((packed));
+
+#endif
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