diff mbox

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

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

Commit Message

Janosch Frank March 13, 2018, 12:01 p.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       | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 s390x/sthyi.h       | 131 ++++++++++++++++++++++++++++++++
 s390x/unittests.cfg |   4 +
 4 files changed, 346 insertions(+)
 create mode 100644 s390x/sthyi.c
 create mode 100644 s390x/sthyi.h

Comments

Thomas Huth March 13, 2018, 4:07 p.m. UTC | #1
On 13.03.2018 13:01, 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       | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  s390x/sthyi.h       | 131 ++++++++++++++++++++++++++++++++
>  s390x/unittests.cfg |   4 +
>  4 files changed, 346 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..e53cdb2
> --- /dev/null
> +++ b/s390x/sthyi.c
> @@ -0,0 +1,210 @@
> +/*
> + * Tests exceptions and data validity for the emulated sthyi
> + * instruction.
> + *
> + * Copyright 2017 IBM Corp.

Maybe update that to 2018?

> + * 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 * 2] __attribute__((aligned(PAGE_SIZE * 2)));

According to my documentation of sthyi, one 4k page should be enough?
Why two pages?

> +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_odd(void)
> +{
> +	register uint64_t code asm("1") = 0;
> +	register uint64_t dummy1 asm("2") = 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)
> +{
> +	register uint64_t code asm("0") = 0;
> +	register uint64_t dummy1 asm("0") = 0;
> +	register uint64_t addr asm("0") = 0x424242424242000;
> +	register uint64_t rc asm("0") = 0;

I wonder why the compiler does not complain here ... defining multiple
variables which all point to the same register ...
Since you don't really use the variables in the inline assembly below at
all, I think it would be better to simple do this without the register
variables here.

> +	int cc = 0;
> +
> +	asm volatile(
> +		".insn	 rre,0xB2560000,0,0\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_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_odd();

Maybe this should check both registers separately, and not at the same
time, to make sure that the hypervisor checks both registers properly?
Otherwise one of the checks might cover the missing check for the other
register...

> +	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);
> +}

For the above three tests that only check with check_pgm_int_code(),
could you please add a report_prefix_push(...) at the beginning of the
function and a report_prefix_pop() at the end? I think the output will
look better with that.

> +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)));

It might be better to check for hdr->INFHDLN >= sizeof(*hdr) in case the
hypervisor supports a longer header in the future? Or can we be sure
that the header will never be extended again?

> +	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 * 2);
> +
> +	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)
> +{
> +	report_prefix_push("sthyi");
> +
> +	/* Test for availability */
> +	if (!test_facility(74))
> +		goto exit;

Could you maybe change this to something like:

	has_sthyi = test_facility(74);
	report_xfail("STHYI available", !has_sthyi, has_sthyi);
	if (!has_sthyi)
		goto exit;

... so that the user sees at least something if the facility is not
available.

> +	/* Test register/argument checking. */
> +	test_exception_addr();
> +	test_exception_reg_odd();
> +	test_exception_reg_equal();
> +	test_function_code((uint64_t) pagebuf);
> +
> +	/* Test function code 0 - CP and IFL Capacity Information */
> +	test_fcode0();
> +
> +exit:

Maybe use a different label name? ... I'm normally trying to avoid to
use names that are part of the standard libc, e.g. the exit() function
in this case.

> +	report_prefix_pop();
> +	return report_summary();
> +}

Apart from the nits, this looks pretty good to me already!

 Thanks,
  Thomas
Janosch Frank March 14, 2018, 8:18 a.m. UTC | #2
On 13.03.2018 17:07, Thomas Huth wrote:
> On 13.03.2018 13:01, 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       | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  s390x/sthyi.h       | 131 ++++++++++++++++++++++++++++++++
>>  s390x/unittests.cfg |   4 +
>>  4 files changed, 346 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..e53cdb2
>> --- /dev/null
>> +++ b/s390x/sthyi.c
>> @@ -0,0 +1,210 @@
>> +/*
>> + * Tests exceptions and data validity for the emulated sthyi
>> + * instruction.
>> + *
>> + * Copyright 2017 IBM Corp.
> 
> Maybe update that to 2018?

Done

> 
>> + * 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 * 2] __attribute__((aligned(PAGE_SIZE * 2)));
> 
> According to my documentation of sthyi, one 4k page should be enough?
> Why two pages?

Copy-paste...

> 
>> +static char null_buf[32] = {};
>> +
[...]
>> +static int sthyi_ill_reg_eq(void)
>> +{
>> +	register uint64_t code asm("0") = 0;
>> +	register uint64_t dummy1 asm("0") = 0;
>> +	register uint64_t addr asm("0") = 0x424242424242000;
>> +	register uint64_t rc asm("0") = 0;
> 
> I wonder why the compiler does not complain here ... defining multiple
> variables which all point to the same register ...
> Since you don't really use the variables in the inline assembly below at
> all, I think it would be better to simple do this without the register
> variables here.

I did this series to improve my inline assembly knowledge, so I'm happy
about suggestions and you already did find the assembly problems in the
other patches.
Done

> 
>> +	int cc = 0;
>> +
>> +	asm volatile(
>> +		".insn	 rre,0xB2560000,0,0\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_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_odd();
> 
> Maybe this should check both registers separately, and not at the same
> time, to make sure that the hypervisor checks both registers properly?
> Otherwise one of the checks might cover the missing check for the other
> register...

Sounds good, will add

> 
>> +	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);
>> +}
> 
> For the above three tests that only check with check_pgm_int_code(),
> could you please add a report_prefix_push(...) at the beginning of the
> function and a report_prefix_pop() at the end? I think the output will
> look better with that.

Sure

> 
>> +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)));
> 
> It might be better to check for hdr->INFHDLN >= sizeof(*hdr) in case the
> hypervisor supports a longer header in the future? Or can we be sure
> that the header will never be extended again?

This test was originally a test I used to check my sthyi implementation,
so it was pretty tailored to the KVM code. So, I should make it >= to
keep it more general.

> 
>> +	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);
>> +}
>> +
[...]
>> +
>> +int main(void)
>> +{
>> +	report_prefix_push("sthyi");
>> +
>> +	/* Test for availability */
>> +	if (!test_facility(74))
>> +		goto exit;
> 
> Could you maybe change this to something like:
> 
> 	has_sthyi = test_facility(74);
> 	report_xfail("STHYI available", !has_sthyi, has_sthyi);
> 	if (!has_sthyi)
> 		goto exit;
> 
> ... so that the user sees at least something if the facility is not
> available.

Sure

> 
>> +	/* Test register/argument checking. */
>> +	test_exception_addr();
>> +	test_exception_reg_odd();
>> +	test_exception_reg_equal();
>> +	test_function_code((uint64_t) pagebuf);
>> +
>> +	/* Test function code 0 - CP and IFL Capacity Information */
>> +	test_fcode0();
>> +
>> +exit:
> 
> Maybe use a different label name? ... I'm normally trying to avoid to
> use names that are part of the standard libc, e.g. the exit() function
> in this case.

Ok, will change for all patches in the series, how about:
test_report:

> 
>> +	report_prefix_pop();
>> +	return report_summary();
>> +}
> 
> Apart from the nits, this looks pretty good to me already!>
>  Thanks,
>   Thomas
>
Thomas Huth March 14, 2018, 9:26 a.m. UTC | #3
On 14.03.2018 09:18, Janosch Frank wrote:
> On 13.03.2018 17:07, Thomas Huth wrote:
>> On 13.03.2018 13:01, 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.
[...]
>>> +
>>> +int main(void)
>>> +{
>>> +	report_prefix_push("sthyi");
>>> +
>>> +	/* Test for availability */
>>> +	if (!test_facility(74))
>>> +		goto exit;
>>
>> Could you maybe change this to something like:
>>
>> 	has_sthyi = test_facility(74);
>> 	report_xfail("STHYI available", !has_sthyi, has_sthyi);
>> 	if (!has_sthyi)
>> 		goto exit;
>>
>> ... so that the user sees at least something if the facility is not
>> available.
> 
> Sure
> 
>>
>>> +	/* Test register/argument checking. */
>>> +	test_exception_addr();
>>> +	test_exception_reg_odd();
>>> +	test_exception_reg_equal();
>>> +	test_function_code((uint64_t) pagebuf);
>>> +
>>> +	/* Test function code 0 - CP and IFL Capacity Information */
>>> +	test_fcode0();
>>> +
>>> +exit:
>>
>> Maybe use a different label name? ... I'm normally trying to avoid to
>> use names that are part of the standard libc, e.g. the exit() function
>> in this case.
> 
> Ok, will change for all patches in the series, how about:
> test_report:

Fine for me. Or maybe simply "done:" ?

>>> +	report_prefix_pop();
>>> +	return report_summary();
>>> +}

 Thomas
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..e53cdb2
--- /dev/null
+++ b/s390x/sthyi.c
@@ -0,0 +1,210 @@ 
+/*
+ * Tests exceptions and data validity for the emulated sthyi
+ * instruction.
+ *
+ * Copyright 2017 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 * 2] __attribute__((aligned(PAGE_SIZE * 2)));
+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_odd(void)
+{
+	register uint64_t code asm("1") = 0;
+	register uint64_t dummy1 asm("2") = 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)
+{
+	register uint64_t code asm("0") = 0;
+	register uint64_t dummy1 asm("0") = 0;
+	register uint64_t addr asm("0") = 0x424242424242000;
+	register uint64_t rc asm("0") = 0;
+	int cc = 0;
+
+	asm volatile(
+		".insn	 rre,0xB2560000,0,0\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_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_odd();
+	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 * 2);
+
+	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)
+{
+	report_prefix_push("sthyi");
+
+	/* Test for availability */
+	if (!test_facility(74))
+		goto exit;
+
+	/* Test register/argument checking. */
+	test_exception_addr();
+	test_exception_reg_odd();
+	test_exception_reg_equal();
+	test_function_code((uint64_t) pagebuf);
+
+	/* Test function code 0 - CP and IFL Capacity Information */
+	test_fcode0();
+
+exit:
+	report_prefix_pop();
+	return report_summary();
+}
diff --git a/s390x/sthyi.h b/s390x/sthyi.h
new file mode 100644
index 0000000..63daaa9
--- /dev/null
+++ b/s390x/sthyi.h
@@ -0,0 +1,131 @@ 
+/*
+ * STHYI related flags and structure definitions.
+ *
+ * Copyright 2017 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