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