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 * 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 * 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 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 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 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 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 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 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