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))); +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@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..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 -- 2.7.4