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