From: Janosch Frank <frankja@xxxxxxxxxxxxxxxxxx> 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> Reviewed-by: Thomas Huth <thuth@xxxxxxxxxx> Signed-off-by: Thomas Huth <thuth@xxxxxxxxxx> --- s390x/Makefile | 1 + s390x/sthyi.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++ s390x/sthyi.h | 131 ++++++++++++++++++++++++++++++++++++++++ s390x/unittests.cfg | 3 + 4 files changed, 304 insertions(+) create mode 100644 s390x/sthyi.c create mode 100644 s390x/sthyi.h diff --git a/s390x/Makefile b/s390x/Makefile index fe92f65..f68d8a7 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..0b1b4ec --- /dev/null +++ b/s390x/sthyi.c @@ -0,0 +1,169 @@ +/* + * 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))); +static char null_buf[32] = {}; + +static inline int sthyi(uint64_t vaddr, uint64_t fcode, uint64_t *rc, + unsigned int r1, unsigned int r2) +{ + register uint64_t code asm("0") = fcode; + register uint64_t addr asm("2") = vaddr; + register uint64_t rc3 asm("3") = 0; + int cc = 0; + + asm volatile(".insn rre,0xB2560000,%[r1],%[r2]\n" + "ipm %[cc]\n" + "srl %[cc],28\n" + : [cc] "=d" (cc) + : [code] "d" (code), [addr] "a" (addr), [r1] "i" (r1), + [r2] "i" (r2) + : "memory", "cc", "r3"); + if (rc) + *rc = rc3; + return cc; +} + +static void test_exception_addr(void) +{ + report_prefix_push("Illegal address check"); + expect_pgm_int(); + sthyi(42042, 0, NULL, 0, 2); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); +} + +static void test_exception_reg_odd(void) +{ + report_prefix_push("Register check odd"); + expect_pgm_int(); + sthyi((uint64_t)pagebuf, 0, NULL, 1, 2); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + expect_pgm_int(); + sthyi((uint64_t)pagebuf, 0, NULL, 0, 3); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); +} + +static void test_exception_reg_equal(void) +{ + report_prefix_push("Register check equal"); + expect_pgm_int(); + sthyi((uint64_t)pagebuf, 0, NULL, 0, 0); + check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); + report_prefix_pop(); +} + +static void test_function_code(uint64_t addr) +{ + uint64_t urc = 0; + int cc = sthyi((uint64_t)pagebuf, 42, &urc, 0, 2); + + 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, NULL, 0, 2); + 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(); + 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(); + +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@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. + */ +#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..258189e 100644 --- a/s390x/unittests.cfg +++ b/s390x/unittests.cfg @@ -34,3 +34,6 @@ file = sieve.elf groups = selftest # can take fairly long when KVM is nested inside z/VM timeout = 600 + +[sthyi] +file = sthyi.elf -- 1.8.3.1