/* * Tests exceptions and data validity for the emulated sthyi * instruction. * * Copyright 2018 IBM Corp. * * Authors: * Janosch Frank * * 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 #include #include #include #include #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 R1"); expect_pgm_int(); sthyi((uint64_t)pagebuf, 0, NULL, 1, 2); check_pgm_int_code(PGM_INT_CODE_SPECIFICATION); report_prefix_pop(); report_prefix_push("Register check odd R2"); 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(cc == 3 && urc == CODE_UNSUPP, "Illegal fcode"); } static void test_fcode0_hdr(struct sthyi_hdr_sctn *hdr) { report_prefix_push("Header"); report(hdr->INFHDLN >= sizeof(*hdr) && !(hdr->INFHDLN % 8), "length"); report((hdr->INFMLEN >= sizeof(struct sthyi_mach_sctn) && !(hdr->INFMLEN % 8)), "Machine sctn length"); report((hdr->INFPLEN >= sizeof(struct sthyi_par_sctn) && !(hdr->INFPLEN % 8)), "Partition section length"); report(hdr->INFMOFF >= hdr->INFHDLN, "Machine offset"); report(hdr->INFPOFF >= hdr->INFHDLN, "Partition offset"); report_prefix_pop(); } static void test_fcode0_mach(struct sthyi_mach_sctn *mach) { int sum = mach->INFMSCPS + mach->INFMDCPS + mach->INFMSIFL + mach->INFMDIFL; report_prefix_push("Machine"); if (mach->INFMVAL1 & MACH_ID_VLD) { report(memcmp(mach->INFMTYPE, null_buf, sizeof(mach->INFMTYPE)), "type"); report(memcmp(mach->INFMMANU, null_buf, sizeof(mach->INFMMANU)), "manufacturer"); report(memcmp(mach->INFMSEQ, null_buf, sizeof(mach->INFMSEQ)), "sequence"); report(memcmp(mach->INFMPMAN, null_buf, sizeof(mach->INFMPMAN)), "plant"); } if (mach->INFMVAL1 & MACH_NAME_VLD) report(memcmp(mach->INFMNAME, null_buf, sizeof(mach->INFMNAME)), "name"); if (mach->INFMVAL1 & MACH_CNT_VLD) report(sum, "core counts"); report_prefix_pop(); } static void test_fcode0_par(struct sthyi_par_sctn *par) { int sum = par->INFPSCPS + par->INFPDCPS + par->INFPSIFL + par->INFPDIFL; report_prefix_push("Partition"); if (par->INFPVAL1 & PART_CNT_VLD) report(sum, "core counts"); if (par->INFPVAL1 & PART_STSI_SUC) { report(par->INFPPNUM, "number"); report(memcmp(par->INFPPNAM, null_buf, sizeof(par->INFPPNAM)), "name"); } report_prefix_pop(); } 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); report_prefix_push("fcode 0"); 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); report_prefix_pop(); } int main(void) { bool has_sthyi = test_facility(74); report_prefix_push("sthyi"); /* Test for availability */ if (!has_sthyi) { report_skip("STHYI is not available"); goto done; } /* Test register/argument checking. */ report_prefix_push("Instruction"); test_exception_addr(); test_exception_reg_odd(); test_exception_reg_equal(); test_function_code((uint64_t) pagebuf); report_prefix_pop(); /* Test function code 0 - CP and IFL Capacity Information */ test_fcode0(); done: report_prefix_pop(); return report_summary(); }