1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019 Joyent, Inc.
14  */
15 
16 #include <string.h>
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <err.h>
21 
22 #include "imc_test.h"
23 
24 /*
25  * Test runner for the IMC driver and its decoder. This operates by creating
26  * fake topologies and then building a copy of the decoder into this.
27  */
28 
29 static void
30 imc_print(const char *fmt, ...)
31 {
32 	va_list ap;
33 
34 	va_start(ap, fmt);
35 	(void) vfprintf(stdout, fmt, ap);
36 	va_end(ap);
37 }
38 
39 static const char *
40 imc_test_strerror(imc_decode_failure_t fail)
41 {
42 	switch (fail) {
43 	case IMC_DECODE_F_NONE:
44 		return ("Actually succeeded");
45 	case IMC_DECODE_F_LEGACY_RANGE:
46 		return ("Asked to decode legacy address");
47 	case IMC_DECODE_F_BAD_SOCKET:
48 		return ("BAD socket data");
49 	case IMC_DECODE_F_BAD_SAD:
50 		return ("BAD SAD data");
51 	case IMC_DECODE_F_OUTSIDE_DRAM:
52 		return ("Address not DRAM");
53 	case IMC_DECODE_F_NO_SAD_RULE:
54 		return ("No valid SAD rule");
55 	case IMC_DECODE_F_BAD_SAD_INTERLEAVE:
56 		return ("SAD bad interleave target");
57 	case IMC_DECODE_F_BAD_REMOTE_MC_ROUTE:
58 		return ("SAD MC_ROUTE refers to non-existent socket");
59 	case IMC_DECODE_F_SAD_SEARCH_LOOP:
60 		return ("SAD search looped");
61 	case IMC_DECODE_F_SAD_BAD_MOD:
62 		return ("SAD has a bad mod rule");
63 	case IMC_DECODE_F_SAD_BAD_SOCKET:
64 		return ("SAD has a bad Socket target");
65 	case IMC_DECODE_F_SAD_BAD_TAD:
66 		return ("SAD has a bad TAD target");
67 	case IMC_DECODE_F_NO_TAD_RULE:
68 		return ("No valid TAD rule");
69 	case IMC_DECODE_F_TAD_3_ILEAVE:
70 		return ("Unsupported 3-way channel interleave");
71 	case IMC_DECODE_F_TAD_BAD_TARGET_INDEX:
72 		return ("Bad TAD target index");
73 	case IMC_DECODE_F_BAD_CHANNEL_ID:
74 		return ("Bad channel ID");
75 	case IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET:
76 		return ("Bad channel tad offset");
77 	case IMC_DECODE_F_NO_RIR_RULE:
78 		return ("No valid rank interleave rule");
79 	case IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET:
80 		return ("Bad rank interleave target");
81 	case IMC_DECODE_F_BAD_DIMM_INDEX:
82 		return ("Bad DIMM target index");
83 	case IMC_DECODE_F_DIMM_NOT_PRESENT:
84 		return ("DIMM not present");
85 	case IMC_DECODE_F_BAD_DIMM_RANK:
86 		return ("Bad DIMM rank");
87 	case IMC_DECODE_F_CHANOFF_UNDERFLOW:
88 		return ("Channel address offset calculation underflow");
89 	case IMC_DECODE_F_RANKOFF_UNDERFLOW:
90 		return ("Rank address offset calculation underflow");
91 	default:
92 		return ("<unknown>");
93 	}
94 }
95 
96 static const char *
97 imc_test_strenum(imc_decode_failure_t fail)
98 {
99 	switch (fail) {
100 	case IMC_DECODE_F_NONE:
101 		return ("IMC_DECODE_F_NONE");
102 	case IMC_DECODE_F_LEGACY_RANGE:
103 		return ("IMC_DECODE_F_LEGACY_RANGE");
104 	case IMC_DECODE_F_BAD_SOCKET:
105 		return ("IMC_DECODE_F_BAD_SOCKET");
106 	case IMC_DECODE_F_BAD_SAD:
107 		return ("IMC_DECODE_F_BAD_SAD");
108 	case IMC_DECODE_F_OUTSIDE_DRAM:
109 		return ("IMC_DECODE_F_OUTSIDE_DRAM");
110 	case IMC_DECODE_F_NO_SAD_RULE:
111 		return ("IMC_DECODE_F_NO_SAD_RULE");
112 	case IMC_DECODE_F_BAD_SAD_INTERLEAVE:
113 		return ("IMC_DECODE_F_BAD_SAD_INTERLEAVE");
114 	case IMC_DECODE_F_BAD_REMOTE_MC_ROUTE:
115 		return ("IMC_DECODE_F_BAD_REMOTE_MC_ROUTE");
116 	case IMC_DECODE_F_SAD_SEARCH_LOOP:
117 		return ("IMC_DECODE_F_SAD_SEARCH_LOOP");
118 	case IMC_DECODE_F_SAD_BAD_MOD:
119 		return ("IMC_DECODE_F_SAD_BAD_MOD");
120 	case IMC_DECODE_F_SAD_BAD_SOCKET:
121 		return ("IMC_DECODE_F_SAD_BAD_SOCKET");
122 	case IMC_DECODE_F_SAD_BAD_TAD:
123 		return ("IMC_DECODE_F_SAD_BAD_TAD");
124 	case IMC_DECODE_F_NO_TAD_RULE:
125 		return ("IMC_DECODE_F_NO_TAD_RULE");
126 	case IMC_DECODE_F_TAD_3_ILEAVE:
127 		return ("IMC_DECODE_F_TAD_3_ILEAVE");
128 	case IMC_DECODE_F_TAD_BAD_TARGET_INDEX:
129 		return ("IMC_DECODE_F_TAD_BAD_TARGET_INDEX");
130 	case IMC_DECODE_F_BAD_CHANNEL_ID:
131 		return ("IMC_DECODE_F_BAD_CHANNEL_ID");
132 	case IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET:
133 		return ("IMC_DECODE_F_BAD_CHANNEL_TAD_OFFSET");
134 	case IMC_DECODE_F_NO_RIR_RULE:
135 		return ("IMC_DECODE_F_NO_RIR_RULE");
136 	case IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET:
137 		return ("IMC_DECODE_F_BAD_RIR_ILEAVE_TARGET");
138 	case IMC_DECODE_F_BAD_DIMM_INDEX:
139 		return ("IMC_DECODE_F_BAD_DIMM_INDEX");
140 	case IMC_DECODE_F_DIMM_NOT_PRESENT:
141 		return ("IMC_DECODE_F_DIMM_NOT_PRESENT");
142 	case IMC_DECODE_F_BAD_DIMM_RANK:
143 		return ("IMC_DECODE_F_BAD_DIMM_RANK");
144 	case IMC_DECODE_F_CHANOFF_UNDERFLOW:
145 		return ("IMC_DECODE_F_CHANOFF_UNDERFLOW");
146 	case IMC_DECODE_F_RANKOFF_UNDERFLOW:
147 		return ("IMC_DECODE_F_RANKOFF_UNDERFLOW");
148 	default:
149 		return ("<unknown>");
150 	}
151 }
152 
153 static uint_t
154 imc_test_run_one(const imc_test_case_t *test)
155 {
156 	imc_decode_state_t dec;
157 	boolean_t pass;
158 
159 	imc_print("Running test: %s\n", test->itc_desc);
160 	imc_print("\tDecoding address: 0x%" PRIx64 "\n", test->itc_pa);
161 
162 	(void) memset(&dec, '\0', sizeof (dec));
163 	pass = imc_decode_pa(test->itc_imc, test->itc_pa, &dec);
164 	if (pass && !test->itc_pass) {
165 		imc_print("\tdecode unexpectedly succeeded\n");
166 		imc_print("\texpected error '%s' (%s/0x%x)\n",
167 		    imc_test_strerror(test->itc_fail),
168 		    imc_test_strenum(test->itc_fail),
169 		    test->itc_fail);
170 		imc_print("\t\tdecoded socket: %u\n", dec.ids_nodeid);
171 		imc_print("\t\tdecoded tad: %u\n", dec.ids_tadid);
172 		imc_print("\t\tdecoded channel: %u\n",
173 		    dec.ids_channelid);
174 		imc_print("\t\tdecoded channel address: 0x%" PRIx64 "\n",
175 		    dec.ids_chanaddr);
176 		imc_print("\t\tdecoded rank: %u\n", dec.ids_rankid);
177 		imc_print("\t\tdecoded rank address: 0x%" PRIx64 "\n",
178 		    dec.ids_rankaddr);
179 		imc_print("\ttest failed\n");
180 
181 		return (1);
182 	} else if (pass) {
183 		uint_t err = 0;
184 
185 		if (test->itc_nodeid != UINT32_MAX &&
186 		    test->itc_nodeid != dec.ids_nodeid) {
187 			imc_print("\tsocket mismatch\n"
188 			    "\t\texpected %u\n\t\tfound %u\n",
189 			    test->itc_nodeid, dec.ids_nodeid);
190 			err |= 1;
191 		}
192 
193 		if (test->itc_tadid != UINT32_MAX &&
194 		    test->itc_tadid != dec.ids_tadid) {
195 			imc_print("\tTAD mismatch\n"
196 			    "\t\texpected %u\n\t\tfound %u\n",
197 			    test->itc_tadid, dec.ids_tadid);
198 			err |= 1;
199 		}
200 
201 		if (test->itc_channelid != UINT32_MAX &&
202 		    test->itc_channelid != dec.ids_channelid) {
203 			imc_print("\tchannel mismatch\n"
204 			    "\t\texpected %u\n\t\tfound %u\n",
205 			    test->itc_channelid, dec.ids_channelid);
206 			err |= 1;
207 		}
208 
209 		if (test->itc_chanaddr != UINT64_MAX &&
210 		    test->itc_chanaddr != dec.ids_chanaddr) {
211 			imc_print("\tchannel address mismatch\n"
212 			    "\t\texpected 0x%" PRIx64 "\n\t\t"
213 			    "found 0x%" PRIx64 "\n",
214 			    test->itc_chanaddr, dec.ids_chanaddr);
215 			err |= 1;
216 		}
217 
218 		if (test->itc_dimmid != UINT32_MAX &&
219 		    test->itc_dimmid != dec.ids_dimmid) {
220 			imc_print("\tDIMM mismatch\n"
221 			    "\t\texpected %u\n\t\tfound %u\n",
222 			    test->itc_dimmid, dec.ids_dimmid);
223 			err |= 1;
224 		}
225 
226 		if (test->itc_rankid != UINT32_MAX &&
227 		    test->itc_rankid != dec.ids_rankid) {
228 			imc_print("\trank mismatch\n"
229 			    "\t\texpected %u\n\t\tfound %u\n",
230 			    test->itc_rankid, dec.ids_rankid);
231 			err |= 1;
232 		}
233 
234 		if (test->itc_rankaddr != UINT64_MAX &&
235 		    test->itc_rankaddr != dec.ids_rankaddr) {
236 			imc_print("\trank address mismatch\n"
237 			    "\t\texpected 0x%" PRIx64 "\n\t\t"
238 			    "found 0x%" PRIx64 "\n",
239 			    test->itc_rankaddr, dec.ids_rankaddr);
240 			err |= 1;
241 		}
242 
243 		if (err) {
244 			imc_print("\tDecoding failed\n");
245 		} else {
246 			imc_print("\tDecoded successfully\n");
247 		}
248 
249 		return (err);
250 	} else if (!pass && !test->itc_pass) {
251 		if (dec.ids_fail != test->itc_fail) {
252 			imc_print("\terror mismatch\n"
253 			    "\t\texpected '%s' (%s/0x%x)\n\t\tfound '%s' "
254 			    "(%s/0x%x)\n", imc_test_strerror(test->itc_fail),
255 			    imc_test_strenum(test->itc_fail), test->itc_fail,
256 			    imc_test_strerror(dec.ids_fail),
257 			    imc_test_strenum(dec.ids_fail), dec.ids_fail);
258 			return (1);
259 		}
260 
261 		imc_print("\tCorrect decoding error generated\n");
262 		return (0);
263 	} else {
264 		imc_print("\tdecode failed with '%s' (%s/0x%x)\n",
265 		    imc_test_strerror(dec.ids_fail),
266 		    imc_test_strenum(dec.ids_fail),
267 		    dec.ids_fail);
268 		if (test->itc_nodeid != UINT32_MAX) {
269 			imc_print("\t\texpected socket: %u\n",
270 			    test->itc_nodeid);
271 		}
272 
273 		if (test->itc_tadid != UINT32_MAX) {
274 			imc_print("\t\texpected tad: %u\n", test->itc_tadid);
275 		}
276 
277 		if (test->itc_channelid != UINT32_MAX) {
278 			imc_print("\t\texpected channel: %u\n",
279 			    test->itc_channelid);
280 		}
281 
282 		if (test->itc_chanaddr != UINT64_MAX) {
283 			imc_print("\t\texpected channel address: 0x%" PRIx64
284 			    "\n", test->itc_chanaddr);
285 		}
286 
287 		if (test->itc_rankid != UINT32_MAX) {
288 			imc_print("\t\texpected rank: %u\n",
289 			    test->itc_rankid);
290 		}
291 
292 		if (test->itc_rankaddr != UINT64_MAX) {
293 			imc_print("\t\texpected rank address: 0x%" PRIx64 "\n",
294 			    test->itc_rankaddr);
295 		}
296 
297 		imc_print("\tdecode failed, expected pass\n");
298 
299 		return (1);
300 	}
301 }
302 
303 static void
304 imc_test_run(const imc_test_case_t *tests, uint_t *ntests, uint_t *nfail)
305 {
306 	while (tests[0].itc_desc != NULL) {
307 		*nfail += imc_test_run_one(tests);
308 		*ntests += 1;
309 		tests++;
310 	}
311 }
312 
313 int
314 main(int argc, char *argv[])
315 {
316 	uint_t ntests = 0, nfail = 0;
317 	int i;
318 
319 	if (argc > 1) {
320 		for (i = 1; i < argc; i++) {
321 			if (strcmp(argv[i], "basic") == 0) {
322 				imc_test_run(imc_test_basics, &ntests, &nfail);
323 			} else if (strcmp(argv[i], "badaddr") == 0) {
324 				imc_test_run(imc_test_badaddr, &ntests, &nfail);
325 			} else if (strcmp(argv[i], "sad") == 0) {
326 				imc_test_run(imc_test_sad, &ntests, &nfail);
327 			} else if (strcmp(argv[i], "skx_loop") == 0) {
328 				imc_test_run(imc_test_skx_loop, &ntests,
329 				    &nfail);
330 			} else if (strcmp(argv[i], "tad") == 0) {
331 				imc_test_run(imc_test_tad, &ntests, &nfail);
332 			} else if (strcmp(argv[i], "rir") == 0) {
333 				imc_test_run(imc_test_rir, &ntests, &nfail);
334 			} else if (strcmp(argv[i], "fail") == 0) {
335 				imc_test_run(imc_test_fail, &ntests, &nfail);
336 			} else {
337 				errx(EXIT_FAILURE, "Unknown test argument %s",
338 				    argv[i]);
339 			}
340 		}
341 	} else {
342 		imc_test_run(imc_test_basics, &ntests, &nfail);
343 		imc_test_run(imc_test_badaddr, &ntests, &nfail);
344 		imc_test_run(imc_test_skx_loop, &ntests, &nfail);
345 		imc_test_run(imc_test_rir, &ntests, &nfail);
346 		imc_test_run(imc_test_tad, &ntests, &nfail);
347 		imc_test_run(imc_test_sad, &ntests, &nfail);
348 		imc_test_run(imc_test_fail, &ntests, &nfail);
349 	}
350 
351 	imc_print("%u/%u tests passed\n", ntests - nfail, ntests);
352 	return (nfail > 0);
353 }
354