xref: /kvm-unit-tests/s390x/sthyi.c (revision 3ac97f8fc847d05d0a5555aefd34e2cac26fdc0c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Tests exceptions and data validity for the emulated sthyi
4  * instruction.
5  *
6  * Copyright 2018 IBM Corp.
7  *
8  * Authors:
9  *    Janosch Frank <frankja@linux.vnet.ibm.com>
10  */
11 #include <libcflat.h>
12 #include <asm/asm-offsets.h>
13 #include <asm/interrupt.h>
14 #include <asm/page.h>
15 #include <asm/facility.h>
16 
17 #include "sthyi.h"
18 
19 static uint8_t pagebuf[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
20 static char null_buf[32] = {};
21 
sthyi(uint64_t vaddr,uint64_t fcode,uint64_t * rc,unsigned int r1,unsigned int r2)22 static inline int sthyi(uint64_t vaddr, uint64_t fcode, uint64_t *rc,
23 			unsigned int r1, unsigned int r2)
24 {
25 	register uint64_t code asm("0") = fcode;
26 	register uint64_t addr asm("2") = vaddr;
27 	register uint64_t rc3 asm("3") = 42;
28 	int cc = 0;
29 
30 	asm volatile(
31 		".insn   rre,0xB2560000,%[r1],%[r2]\n"
32 		"ipm     %[cc]\n"
33 		"srl     %[cc],28\n"
34 		: [cc] "=d" (cc), "+d" (rc3)
35 		: [code] "d" (code), [addr] "a" (addr), [r1] "i" (r1), [r2] "i" (r2)
36 		: "memory", "cc");
37 	if (rc)
38 		*rc = rc3;
39 	return cc;
40 }
41 
test_exception_addr(void)42 static void test_exception_addr(void)
43 {
44 	report_prefix_push("Illegal address check");
45 	expect_pgm_int();
46 	sthyi(42042, 0, NULL, 0, 2);
47 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
48 	report_prefix_pop();
49 }
50 
test_exception_reg_odd(void)51 static void test_exception_reg_odd(void)
52 {
53 	report_prefix_push("Register check odd R1");
54 	expect_pgm_int();
55 	sthyi((uint64_t)pagebuf, 0, NULL, 1, 2);
56 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
57 	report_prefix_pop();
58 	report_prefix_push("Register check odd R2");
59 	expect_pgm_int();
60 	sthyi((uint64_t)pagebuf, 0, NULL, 0, 3);
61 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
62 	report_prefix_pop();
63 }
64 
test_exception_reg_equal(void)65 static void test_exception_reg_equal(void)
66 {
67 	report_prefix_push("Register check equal");
68 	expect_pgm_int();
69 	sthyi((uint64_t)pagebuf, 0, NULL, 0, 0);
70 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
71 	report_prefix_pop();
72 }
73 
test_function_code(uint64_t addr)74 static void test_function_code(uint64_t addr)
75 {
76 	uint64_t urc = 0;
77 	int cc = sthyi((uint64_t)pagebuf, 42, &urc, 0, 2);
78 
79 	report(cc == 3 && urc == CODE_UNSUPP, "Illegal fcode");
80 }
81 
test_fcode0_hdr(struct sthyi_hdr_sctn * hdr)82 static void test_fcode0_hdr(struct sthyi_hdr_sctn *hdr)
83 {
84 	report_prefix_push("Header");
85 
86 	report(hdr->INFHDLN >= sizeof(*hdr) && !(hdr->INFHDLN % 8), "length");
87 	report((hdr->INFMLEN >= sizeof(struct sthyi_mach_sctn) && !(hdr->INFMLEN % 8)),
88 	       "Machine sctn length");
89 	report((hdr->INFPLEN >= sizeof(struct sthyi_par_sctn) && !(hdr->INFPLEN % 8)),
90 	       "Partition section length");
91 
92 	report(hdr->INFMOFF >= hdr->INFHDLN, "Machine offset");
93 	report(hdr->INFPOFF >= hdr->INFHDLN, "Partition offset");
94 	report_prefix_pop();
95 }
96 
test_fcode0_mach(struct sthyi_mach_sctn * mach)97 static void test_fcode0_mach(struct sthyi_mach_sctn *mach)
98 {
99 	int sum = mach->INFMSCPS + mach->INFMDCPS + mach->INFMSIFL + mach->INFMDIFL;
100 
101 	report_prefix_push("Machine");
102 	if (mach->INFMVAL1 & MACH_ID_VLD) {
103 		report(memcmp(mach->INFMTYPE, null_buf, sizeof(mach->INFMTYPE)),
104 		       "type");
105 		report(memcmp(mach->INFMMANU, null_buf, sizeof(mach->INFMMANU)),
106 		       "manufacturer");
107 		report(memcmp(mach->INFMSEQ, null_buf, sizeof(mach->INFMSEQ)),
108 		       "sequence");
109 		report(memcmp(mach->INFMPMAN, null_buf, sizeof(mach->INFMPMAN)),
110 		       "plant");
111 	}
112 
113 	if (mach->INFMVAL1 & MACH_NAME_VLD)
114 		report(memcmp(mach->INFMNAME, null_buf, sizeof(mach->INFMNAME)),
115 		       "name");
116 
117 	if (mach->INFMVAL1 & MACH_CNT_VLD)
118 		report(sum, "core counts");
119 	report_prefix_pop();
120 }
121 
test_fcode0_par(struct sthyi_par_sctn * par)122 static void test_fcode0_par(struct sthyi_par_sctn *par)
123 {
124 	int sum = par->INFPSCPS + par->INFPDCPS + par->INFPSIFL + par->INFPDIFL;
125 
126 	report_prefix_push("Partition");
127 	if (par->INFPVAL1 & PART_CNT_VLD)
128 		report(sum, "core counts");
129 
130 	if (par->INFPVAL1 & PART_STSI_SUC) {
131 		report(memcmp(par->INFPPNAM, null_buf, sizeof(par->INFPPNAM)),
132 		       "name");
133 	}
134 	report_prefix_pop();
135 }
136 
test_fcode0(void)137 static void test_fcode0(void)
138 {
139 	struct sthyi_hdr_sctn *hdr;
140 	struct sthyi_mach_sctn *mach;
141 	struct sthyi_par_sctn *par;
142 	uint64_t rc = 42;
143 	int cc;
144 
145 	/* Zero destination memory. */
146 	memset(pagebuf, 0, PAGE_SIZE);
147 
148 	report_prefix_push("fcode 0");
149 	cc = sthyi((uint64_t)pagebuf, 0, &rc, 0, 2);
150 	hdr = (void *)pagebuf;
151 	mach = (void *)pagebuf + hdr->INFMOFF;
152 	par = (void *)pagebuf + hdr->INFPOFF;
153 
154 	report(cc == 0 && rc == CODE_SUCCES, "r2 + 1 == 0");
155 	test_fcode0_hdr(hdr);
156 	test_fcode0_mach(mach);
157 	test_fcode0_par(par);
158 	report_prefix_pop();
159 }
160 
main(void)161 int main(void)
162 {
163 	bool has_sthyi = test_facility(74);
164 
165 	report_prefix_push("sthyi");
166 
167 	/* Test for availability */
168 	if (!has_sthyi) {
169 		report_skip("STHYI is not available");
170 		goto done;
171 	}
172 
173 	/* Test register/argument checking. */
174 	report_prefix_push("Instruction");
175 	test_exception_addr();
176 	test_exception_reg_odd();
177 	test_exception_reg_equal();
178 	test_function_code((uint64_t) pagebuf);
179 	report_prefix_pop();
180 
181 	/* Test function code 0 - CP and IFL Capacity Information */
182 	test_fcode0();
183 
184 done:
185 	report_prefix_pop();
186 	return report_summary();
187 }
188