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 2022 Oxide Computer Company
14 */
15
16 /*
17 * Test the memory decoding and normalization features at the heart of the
18 * zen_umc(4D) driver.
19 */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <inttypes.h>
24 #include <err.h>
25 #include <stdlib.h>
26 #include <sys/sysmacros.h>
27
28 #include "zen_umc_test.h"
29
30 static const char *
zen_umc_test_strerror(zen_umc_decode_failure_t fail)31 zen_umc_test_strerror(zen_umc_decode_failure_t fail)
32 {
33 switch (fail) {
34 case ZEN_UMC_DECODE_F_NONE:
35 return ("Actually succeeded");
36 case ZEN_UMC_DECODE_F_OUTSIDE_DRAM:
37 return ("Address outside of DRAM");
38 case ZEN_UMC_DECODE_F_NO_DF_RULE:
39 return ("Address didn't find a DF rule that matched");
40 case ZEN_UMC_DECODE_F_ILEAVE_UNDERFLOW:
41 return ("Interleave adjustments caused PA to underflow");
42 case ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP:
43 return ("Unsupported channel interleave");
44 case ZEN_UMC_DECODE_F_COD_BAD_ILEAVE:
45 return ("Unsupported interleave settings for COD hash");
46 case ZEN_UMC_DECODE_F_NPS_BAD_ILEAVE:
47 return ("Unsupported interleave settings for NPS hash");
48 case ZEN_UMC_DECODE_F_BAD_REMAP_SET:
49 return ("Remap ruleset was invalid");
50 case ZEN_UMC_DECODE_F_BAD_REMAP_ENTRY:
51 return ("Remap entry was invalid");
52 case ZEN_UMC_DECODE_F_REMAP_HAS_BAD_COMP:
53 return ("Remap entry is not a valid component ID");
54 case ZEN_UMC_DECODE_F_CANNOT_MAP_FABID:
55 return ("Failed to find target fabric ID");
56 case ZEN_UMC_DECODE_F_UMC_DOESNT_HAVE_PA:
57 return ("Target UMC does not have a DRAM rule for PA");
58 case ZEN_UMC_DECODE_F_CALC_NORM_UNDERFLOW:
59 return ("Address normalization underflowed");
60 case ZEN_UMC_DECODE_F_NO_CS_BASE_MATCH:
61 return ("No chip-select matched normal address");
62 default:
63 return ("<unknown>");
64 }
65 }
66
67 static const char *
zen_umc_test_strenum(zen_umc_decode_failure_t fail)68 zen_umc_test_strenum(zen_umc_decode_failure_t fail)
69 {
70 switch (fail) {
71 case ZEN_UMC_DECODE_F_NONE:
72 return ("ZEN_UMC_DECODE_F_NONE");
73 case ZEN_UMC_DECODE_F_OUTSIDE_DRAM:
74 return ("ZEN_UMC_DECODE_F_OUTSIDE_DRAM");
75 case ZEN_UMC_DECODE_F_NO_DF_RULE:
76 return ("ZEN_UMC_DECODE_F_NO_DF_RULE");
77 case ZEN_UMC_DECODE_F_ILEAVE_UNDERFLOW:
78 return ("ZEN_UMC_DECODE_F_ILEAVE_UNDERFLOW");
79 case ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP:
80 return ("ZEN_UMC_DECODE_F_CHAN_ILEAVE_NOTSUP");
81 case ZEN_UMC_DECODE_F_COD_BAD_ILEAVE:
82 return ("ZEN_UMC_DECODE_F_COD_BAD_ILEAVE");
83 case ZEN_UMC_DECODE_F_NPS_BAD_ILEAVE:
84 return ("ZEN_UMC_DECODE_F_NPS_BAD_ILEAVE");
85 case ZEN_UMC_DECODE_F_BAD_REMAP_SET:
86 return ("ZEN_UMC_DECODE_F_BAD_REMAP_SET");
87 case ZEN_UMC_DECODE_F_BAD_REMAP_ENTRY:
88 return ("ZEN_UMC_DECODE_F_BAD_REMAP_ENTRY");
89 case ZEN_UMC_DECODE_F_REMAP_HAS_BAD_COMP:
90 return ("ZEN_UMC_DECODE_F_REMAP_HAS_BAD_COMP");
91 case ZEN_UMC_DECODE_F_CANNOT_MAP_FABID:
92 return ("ZEN_UMC_DECODE_F_CANNOT_MAP_FABID");
93 case ZEN_UMC_DECODE_F_UMC_DOESNT_HAVE_PA:
94 return ("ZEN_UMC_DECODE_F_UMC_DOESNT_HAVE_PA");
95 case ZEN_UMC_DECODE_F_CALC_NORM_UNDERFLOW:
96 return ("ZEN_UMC_DECODE_F_CALC_NORM_UNDERFLOW");
97 case ZEN_UMC_DECODE_F_NO_CS_BASE_MATCH:
98 return ("ZEN_UMC_DECODE_F_NO_CS_BASE_MATCH");
99 default:
100 return ("<unknown>");
101 }
102 }
103
104 static boolean_t
zen_umc_test_fabric_one(const umc_fabric_test_t * test)105 zen_umc_test_fabric_one(const umc_fabric_test_t *test)
106 {
107 boolean_t ret = B_TRUE;
108
109 (void) printf("Running test: %s\n", test->uft_desc);
110 if (test->uft_compose) {
111 uint32_t fab, sock, die, comp;
112 boolean_t rtt = B_TRUE;
113 boolean_t valid;
114
115 valid = zen_fabric_id_valid_parts(test->uft_decomp,
116 test->uft_sock_id, test->uft_die_id, test->uft_comp_id);
117 if (!valid) {
118 if (test->uft_valid) {
119 (void) printf("\tInvalid fabric ID parts "
120 "found\n");
121 return (B_FALSE);
122 }
123
124 (void) printf("\tTEST PASSED: Invalid Fabric parts "
125 "detected\n");
126 return (B_TRUE);
127 } else {
128 if (!test->uft_valid) {
129 (void) printf("\tFabric ID parts validated, "
130 "but expected failure\n");
131 return (B_FALSE);
132 }
133 }
134 zen_fabric_id_compose(test->uft_decomp, test->uft_sock_id,
135 test->uft_die_id, test->uft_comp_id, &fab);
136 if (fab != test->uft_fabric_id) {
137 (void) printf("\tFabric ID mismatch\n"
138 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
139 test->uft_fabric_id, fab);
140 ret = B_FALSE;
141 } else {
142 (void) printf("\tTEST PASSED: Fabric ID composition\n");
143 }
144
145 zen_fabric_id_decompose(test->uft_decomp, fab, &sock, &die,
146 &comp);
147 if (sock != test->uft_sock_id) {
148 (void) printf("\tRound-trip socket mismatch\n"
149 "\t\texpected %u\n\t\tfound %u\n",
150 test->uft_sock_id, sock);
151 ret = rtt = B_FALSE;
152 }
153
154 if (die != test->uft_die_id) {
155 (void) printf("\tRound-trip die mismatch\n"
156 "\t\texpected %u\n\t\tfound %u\n",
157 test->uft_die_id, die);
158 ret = rtt = B_FALSE;
159 }
160
161 if (comp != test->uft_comp_id) {
162 (void) printf("\tRound-trip comp mismatch\n"
163 "\t\texpected %u\n\t\tfound %u\n",
164 test->uft_comp_id, comp);
165 ret = rtt = B_FALSE;
166 }
167
168 if (rtt) {
169 (void) printf("\tTEST PASSED: Round-trip Fabric ID "
170 "decomposition\n");
171 }
172 } else {
173 uint32_t fab, sock, die, comp;
174 boolean_t valid;
175
176 valid = zen_fabric_id_valid_fabid(test->uft_decomp,
177 test->uft_fabric_id);
178 if (!valid) {
179 if (test->uft_valid) {
180 (void) printf("\tInvalid fabric ID found\n");
181 return (B_FALSE);
182 }
183
184 (void) printf("\tTEST PASSED: Successfully found "
185 "invalid fabric ID\n");
186 return (B_TRUE);
187 } else {
188 if (!test->uft_valid) {
189 (void) printf("\tFabric ID validated, "
190 "but expected to find an invalid one\n");
191 return (B_FALSE);
192 }
193 }
194 zen_fabric_id_decompose(test->uft_decomp, test->uft_fabric_id,
195 &sock, &die, &comp);
196 if (sock != test->uft_sock_id) {
197 (void) printf("\tsocket mismatch\n"
198 "\t\texpected %u\n\t\tfound %u\n",
199 test->uft_sock_id, sock);
200 ret = B_FALSE;
201 }
202
203 if (die != test->uft_die_id) {
204 (void) printf("\tdie mismatch\n"
205 "\t\texpected %u\n\t\tfound %u\n",
206 test->uft_die_id, die);
207 ret = B_FALSE;
208 }
209
210 if (comp != test->uft_comp_id) {
211 (void) printf("\tcomp mismatch\n"
212 "\t\texpected %u\n\t\tfound %u\n",
213 test->uft_comp_id, comp);
214 ret = B_FALSE;
215 }
216
217 if (ret) {
218 (void) printf("\tTEST PASSED: Fabric ID "
219 "Decomposition\n");
220 }
221
222 zen_fabric_id_compose(test->uft_decomp, sock, die, comp, &fab);
223 if (fab != test->uft_fabric_id) {
224 (void) printf("\tFabric ID mismatch on round trip\n"
225 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
226 test->uft_fabric_id, fab);
227 ret = B_FALSE;
228 } else {
229 (void) printf("\tTEST PASSED: Round-trip Fabric ID "
230 "composition\n");
231 }
232 }
233
234 return (ret);
235 }
236
237 static boolean_t
zen_umc_test_decode_one(const umc_decode_test_t * test)238 zen_umc_test_decode_one(const umc_decode_test_t *test)
239 {
240 boolean_t pass;
241 zen_umc_decoder_t dec;
242
243 (void) printf("Running test: %s\n", test->udt_desc);
244 (void) printf("\tDecoding address: 0x%" PRIx64 "\n", test->udt_pa);
245 memset(&dec, '\0', sizeof (dec));
246
247 pass = zen_umc_decode_pa(test->udt_umc, test->udt_pa, &dec);
248 if (pass && !test->udt_pass) {
249 uint32_t sock, die, comp;
250
251 zen_fabric_id_decompose(&test->udt_umc->umc_decomp,
252 dec.dec_targ_fabid, &sock, &die, &comp);
253
254 (void) printf("\tdecode unexpectedly succeeded\n");
255 (void) printf("\texpected error '%s' (%s/0x%x)\n",
256 zen_umc_test_strerror(test->udt_fail),
257 zen_umc_test_strenum(test->udt_fail),
258 test->udt_fail);
259 (void) printf("\t\tdecoded socket: 0x%x\n", sock);
260 (void) printf("\t\tdecoded die: 0x%x\n", die);
261 (void) printf("\t\tdecoded component: 0x%x\n", comp);
262 (void) printf("\t\tnormal address: 0x%" PRIx64 "\n",
263 dec.dec_norm_addr);
264 (void) printf("\t\tdecoded dimm: 0x%x\n", dec.dec_dimm_no);
265 (void) printf("\t\tdecoded row: 0x%x\n", dec.dec_dimm_row);
266 (void) printf("\t\tdecoded column: 0x%x\n", dec.dec_dimm_col);
267 (void) printf("\t\tdecoded bank: 0x%x\n", dec.dec_dimm_bank);
268 (void) printf("\t\tdecoded bank group: 0x%x\n",
269 dec.dec_dimm_bank_group);
270 (void) printf("\t\tdecoded rm: 0x%x\n", dec.dec_dimm_rm);
271 (void) printf("\t\tdecoded cs: 0x%x\n", dec.dec_dimm_csno);
272 (void) printf("\ttest failed\n");
273 return (B_FALSE);
274 } else if (pass) {
275 uint32_t sock, die, comp;
276 boolean_t success = B_TRUE;
277
278 zen_fabric_id_decompose(&test->udt_umc->umc_decomp,
279 dec.dec_targ_fabid, &sock, &die, &comp);
280 if (test->udt_sock != UINT8_MAX &&
281 test->udt_sock != sock) {
282 (void) printf("\tsocket mismatch\n"
283 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
284 test->udt_sock, sock);
285 success = B_FALSE;
286 }
287
288 if (test->udt_die != UINT8_MAX &&
289 test->udt_die != die) {
290 (void) printf("\tdie mismatch\n"
291 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
292 test->udt_die, die);
293 success = B_FALSE;
294 }
295
296 if (test->udt_comp != UINT8_MAX &&
297 test->udt_comp != comp) {
298 (void) printf("\tcomp mismatch\n"
299 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
300 test->udt_comp, comp);
301 success = B_FALSE;
302 }
303
304 if (test->udt_norm_addr != UINT64_MAX &&
305 test->udt_norm_addr != dec.dec_norm_addr) {
306 (void) printf("\tnormalized address mismatch\n"
307 "\t\texpected 0x%" PRIx64 "\n"
308 "\t\tfound 0x%" PRIx64 "\n",
309 test->udt_norm_addr, dec.dec_norm_addr);
310 success = B_FALSE;
311 }
312
313 if (test->udt_dimm_no != UINT32_MAX &&
314 test->udt_dimm_no != dec.dec_dimm_no) {
315 (void) printf("\tDIMM number mismatch\n"
316 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
317 test->udt_dimm_no, dec.dec_dimm_no);
318 success = B_FALSE;
319 }
320
321 if (test->udt_dimm_col != UINT32_MAX &&
322 test->udt_dimm_col != dec.dec_dimm_col) {
323 (void) printf("\tcolumn mismatch\n"
324 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
325 test->udt_dimm_col, dec.dec_dimm_col);
326 success = B_FALSE;
327 }
328
329 if (test->udt_dimm_row != UINT32_MAX &&
330 test->udt_dimm_row != dec.dec_dimm_row) {
331 (void) printf("\trow mismatch\n"
332 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
333 test->udt_dimm_row, dec.dec_dimm_row);
334 success = B_FALSE;
335 }
336
337 if (test->udt_dimm_bank != UINT8_MAX &&
338 test->udt_dimm_bank != dec.dec_dimm_bank) {
339 (void) printf("\tbank mismatch\n"
340 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
341 test->udt_dimm_bank, dec.dec_dimm_bank);
342 success = B_FALSE;
343 }
344
345 if (test->udt_dimm_bank_group != UINT8_MAX &&
346 test->udt_dimm_bank_group != dec.dec_dimm_bank_group) {
347 (void) printf("\tbank group mismatch\n"
348 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
349 test->udt_dimm_bank_group, dec.dec_dimm_bank_group);
350 success = B_FALSE;
351 }
352
353 if (test->udt_dimm_subchan != UINT8_MAX &&
354 test->udt_dimm_subchan != dec.dec_dimm_subchan) {
355 (void) printf("\tsub-channel mismatch\n"
356 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
357 test->udt_dimm_subchan, dec.dec_dimm_subchan);
358 success = B_FALSE;
359 }
360
361 if (test->udt_dimm_rm != UINT8_MAX &&
362 test->udt_dimm_rm != dec.dec_dimm_rm) {
363 (void) printf("\tRM mismatch\n"
364 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
365 test->udt_dimm_rm, dec.dec_dimm_rm);
366 success = B_FALSE;
367 }
368
369 if (test->udt_dimm_cs != UINT8_MAX &&
370 test->udt_dimm_cs != dec.dec_dimm_csno) {
371 (void) printf("\tCS mismatch\n"
372 "\t\texpected 0x%x\n\t\tfound 0x%x\n",
373 test->udt_dimm_cs, dec.dec_dimm_csno);
374 success = B_FALSE;
375 }
376
377 if (success) {
378 (void) printf("\tTEST PASSED: Successfully decoded "
379 "PA\n");
380 } else {
381 (void) printf("\tTEST FAILED!\n");
382 }
383 return (success);
384 } else if (!pass && !test->udt_pass) {
385 if (dec.dec_fail != test->udt_fail) {
386 (void) printf("\terror mismatch\n"
387 "\t\texpected '%s' (%s/0x%x)\n"
388 "\t\tfound '%s' (%s/0x%x)\n",
389 zen_umc_test_strerror(test->udt_fail),
390 zen_umc_test_strenum(test->udt_fail),
391 test->udt_fail,
392 zen_umc_test_strerror(dec.dec_fail),
393 zen_umc_test_strenum(dec.dec_fail),
394 dec.dec_fail);
395 return (B_FALSE);
396 }
397
398 (void) printf("\tTEST PASSED: Correct error generated\n");
399 return (B_TRUE);
400 } else {
401 (void) printf("\tdecode failed with error '%s' (%s/0x%x)\n",
402 zen_umc_test_strerror(dec.dec_fail),
403 zen_umc_test_strenum(dec.dec_fail),
404 dec.dec_fail);
405
406 if (test->udt_norm_addr != UINT64_MAX) {
407 (void) printf("\t\texpected normal address: "
408 "0x%" PRIx64 "\n", test->udt_norm_addr);
409 }
410
411 if (test->udt_sock != UINT8_MAX) {
412 (void) printf("\t\texpected socket: 0x%x\n",
413 test->udt_sock);
414 }
415
416 if (test->udt_die != UINT8_MAX) {
417 (void) printf("\t\texpected die: 0x%x\n",
418 test->udt_die);
419 }
420
421 if (test->udt_comp != UINT8_MAX) {
422 (void) printf("\t\texpected comp: 0x%x\n",
423 test->udt_comp);
424 }
425
426 if (test->udt_dimm_no != UINT32_MAX) {
427 (void) printf("\t\texpected DIMM number: 0x%x\n",
428 test->udt_dimm_no);
429 }
430
431 if (test->udt_dimm_col != UINT32_MAX) {
432 (void) printf("\t\texpected column: 0x%x\n",
433 test->udt_dimm_col);
434 }
435
436 if (test->udt_dimm_row != UINT32_MAX) {
437 (void) printf("\t\texpected row: 0x%x\n",
438 test->udt_dimm_row);
439 }
440
441 if (test->udt_dimm_bank != UINT8_MAX) {
442 (void) printf("\t\texpected bank: 0x%x\n",
443 test->udt_dimm_bank);
444 }
445
446 if (test->udt_dimm_bank_group != UINT8_MAX) {
447 (void) printf("\t\texpected bank group: 0x%x\n",
448 test->udt_dimm_bank_group);
449 }
450
451 if (test->udt_dimm_subchan != UINT8_MAX) {
452 (void) printf("\t\texpected sub-channel: 0x%x\n",
453 test->udt_dimm_subchan);
454 }
455
456 if (test->udt_dimm_rm != UINT8_MAX) {
457 (void) printf("\t\texpected RM: 0x%x\n",
458 test->udt_dimm_rm);
459 }
460
461 if (test->udt_dimm_cs != UINT8_MAX) {
462 (void) printf("\t\texpected CS: 0x%x\n",
463 test->udt_dimm_cs);
464 }
465
466 return (B_FALSE);
467 }
468 }
469
470 static void
zen_umc_test_fabric(const umc_fabric_test_t * tests,uint_t * ntests,uint_t * nfail)471 zen_umc_test_fabric(const umc_fabric_test_t *tests, uint_t *ntests,
472 uint_t *nfail)
473 {
474 for (uint_t i = 0; tests[i].uft_desc != NULL; i++) {
475 if (!zen_umc_test_fabric_one(&tests[i]))
476 *nfail += 1;
477 *ntests += 1;
478 }
479 }
480
481 static void
zen_umc_test_decode(const umc_decode_test_t * tests,uint_t * ntests,uint_t * nfail)482 zen_umc_test_decode(const umc_decode_test_t *tests, uint_t *ntests,
483 uint_t *nfail)
484 {
485 for (uint_t i = 0; tests[i].udt_desc != NULL; i++) {
486 if (!zen_umc_test_decode_one(&tests[i]))
487 *nfail += 1;
488 *ntests += 1;
489 }
490 }
491
492 typedef struct zen_umc_test_set {
493 const char *set_name;
494 const umc_decode_test_t *set_test;
495 } zen_umc_test_set_t;
496
497 static const zen_umc_test_set_t zen_umc_test_set[] = {
498 { "basic", zen_umc_test_basics },
499 { "channel", zen_umc_test_chans },
500 { "cod", zen_umc_test_cod },
501 { "errors", zen_umc_test_errors },
502 { "hole", zen_umc_test_hole },
503 { "ilv", zen_umc_test_ilv },
504 { "multi", zen_umc_test_multi },
505 { "nps", zen_umc_test_nps },
506 { "remap", zen_umc_test_remap },
507 };
508
509 static void
zen_umc_test_selected(int argc,char * argv[],uint_t * ntests,uint_t * nfail)510 zen_umc_test_selected(int argc, char *argv[], uint_t *ntests, uint_t *nfail)
511 {
512 for (int i = 1; i < argc; i++) {
513 boolean_t ran = B_FALSE;
514
515 if (strcmp(argv[i], "fabric_ids") == 0) {
516 zen_umc_test_fabric(zen_umc_test_fabric_ids, ntests,
517 nfail);
518 continue;
519 }
520
521 for (uint_t t = 0; t < ARRAY_SIZE(zen_umc_test_set); t++) {
522 const zen_umc_test_set_t *s = &zen_umc_test_set[t];
523
524 if (strcmp(s->set_name, argv[i]) == 0) {
525 zen_umc_test_decode(s->set_test, ntests, nfail);
526 ran = B_TRUE;
527 break;
528 }
529 }
530
531 if (!ran) {
532 errx(EXIT_FAILURE, "Unknown test suite: %s", argv[i]);
533 }
534 }
535 }
536
537 int
main(int argc,char * argv[])538 main(int argc, char *argv[])
539 {
540 uint_t ntests = 0, nfail = 0;
541
542 if (argc > 1) {
543 zen_umc_test_selected(argc, argv, &ntests, &nfail);
544 } else {
545 zen_umc_test_fabric(zen_umc_test_fabric_ids, &ntests, &nfail);
546 for (uint_t i = 0; i < ARRAY_SIZE(zen_umc_test_set); i++) {
547 zen_umc_test_decode(zen_umc_test_set[i].set_test,
548 &ntests, &nfail);
549 }
550 }
551 (void) printf("%u/%u tests passed\n", ntests - nfail, ntests);
552 return (nfail > 0);
553 }
554