blob: d8dfc8545dfc6fe8297aa3e7890b24ea2843e017 [file] [log] [blame] [edit]
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Tests exceptions and data validity for the emulated sthyi
* instruction.
*
* Copyright 2018 IBM Corp.
*
* Authors:
* Janosch Frank <frankja@linux.vnet.ibm.com>
*/
#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 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();
}