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 2023 Oxide Computer Company 14 */ 15 16 /* 17 * This constructs various failure cases for our SPD parsing logic and ensures 18 * that we can catch them. 19 */ 20 21 #include <err.h> 22 #include <stdlib.h> 23 #include <sys/sysmacros.h> 24 #include <libjedec.h> 25 26 typedef struct { 27 uint8_t lst_data[1024]; 28 size_t lst_len; 29 spd_error_t lst_err; 30 const char *lst_desc; 31 boolean_t (*lst_check)(nvlist_t *); 32 } libjedec_spd_test_t; 33 34 /* 35 * The test in question only specifies 0x10 bytes. This means we should have a 36 * valid errors nvl with an incomplete entry. 37 */ 38 static boolean_t 39 spd_check_short_ddr4(nvlist_t *nvl) 40 { 41 int ret; 42 uint32_t inc; 43 44 if ((ret = nvlist_lookup_uint32(nvl, SPD_KEY_INCOMPLETE, 45 &inc)) != 0) { 46 warnc(ret, "failed to lookup incomplete key"); 47 return (B_FALSE); 48 } 49 50 if (inc != 0x11) { 51 warnx("incomplete key has unexpected offset: expected %u, " 52 "found %u", 0x11, inc); 53 return (B_FALSE); 54 } 55 56 return (B_TRUE); 57 } 58 59 static boolean_t 60 spd_check_single_err(nvlist_t *nvl, const char *key, spd_error_kind_t kind) 61 { 62 int ret; 63 nvlist_t *check; 64 uint32_t code; 65 char *msg; 66 boolean_t pass = B_TRUE; 67 68 if ((ret = nvlist_lookup_nvlist(nvl, key, &check)) != 0) { 69 warnc(ret, "failed to lookup key %s in error nvlist", key); 70 dump_nvlist(nvl, 4); 71 return (B_FALSE); 72 } 73 74 if ((ret = nvlist_lookup_string(check, SPD_KEY_ERRS_MSG, &msg)) != 0) { 75 warnc(ret, "missing error message for error key %s", key); 76 dump_nvlist(check, 6); 77 pass = B_FALSE; 78 } 79 80 if ((ret = nvlist_lookup_uint32(check, SPD_KEY_ERRS_CODE, 81 &code)) != 0) { 82 warnc(ret, "missing error number for error key %s", key); 83 dump_nvlist(check, 6); 84 pass = B_FALSE; 85 } else if (code != kind) { 86 warnx("found wrong error kind for error key %s: expected 0x%x, " 87 "found 0x%x", key, kind, code); 88 pass = B_FALSE; 89 } 90 91 nvlist_free(check); 92 return (pass); 93 } 94 95 /* 96 * This goes through and checks for a number of error codes being set as 97 * expected. Note, we check that the message exists, but we don't validate its 98 * contents in any way. Because we're using all zero data, we can expect to find 99 * a number of different cases. 100 */ 101 static boolean_t 102 spd_check_misc_errors(nvlist_t *nvl) 103 { 104 int ret; 105 nvlist_t *errs; 106 boolean_t pass = B_TRUE; 107 108 if ((ret = nvlist_lookup_nvlist(nvl, SPD_KEY_ERRS, &errs)) != 0) { 109 warnc(ret, "failed to lookup errors nvlist"); 110 return (B_FALSE); 111 } 112 113 if (!spd_check_single_err(errs, SPD_KEY_MFG_DRAM_MFG_NAME, 114 SPD_ERROR_NO_XLATE) || 115 !spd_check_single_err(errs, SPD_KEY_CRC_DDR4_BASE, 116 SPD_ERROR_BAD_DATA) || 117 !spd_check_single_err(errs, SPD_KEY_MFG_MOD_PN, 118 SPD_ERROR_UNPRINT) || 119 !spd_check_single_err(errs, SPD_KEY_TRCD_MIN, SPD_ERROR_NO_XLATE)) { 120 pass = B_FALSE; 121 } 122 123 124 nvlist_free(errs); 125 return (pass); 126 } 127 128 static const libjedec_spd_test_t spd_tests[] = { 129 { .lst_data = {}, .lst_len = 0, .lst_err = LIBJEDEC_SPD_TOOSHORT, 130 .lst_desc = "Invalid SPD Data (zero length)" }, 131 { .lst_data = { 0x00, 0x10, SPD_DT_DDR_SGRAM, 0x00 }, .lst_len = 4, 132 .lst_err = LIBJEDEC_SPD_UNSUP_TYPE, .lst_desc = "Unsupported " 133 "SPD type (DDR SGRAM)" }, 134 { .lst_data = { 0x00, 0x10, 0x42, 0x00 }, .lst_len = 4, 135 .lst_err = LIBJEDEC_SPD_UNSUP_TYPE, .lst_desc = "Unknown " 136 "SPD type (0x42)" }, 137 { .lst_data = { 0x00, 0x00, SPD_DT_DDR4_SDRAM, 0x00 }, .lst_len = 4, 138 .lst_err = LIBJEDEC_SPD_UNSUP_REV, .lst_desc = "Bad DDR4 " 139 "Revision (0x00)" }, 140 { .lst_data = { 0x00, 0x54, SPD_DT_DDR4_SDRAM, 0x00 }, .lst_len = 4, 141 .lst_err = LIBJEDEC_SPD_UNSUP_REV, .lst_desc = "Bad DDR4 " 142 "Revision (0x54)" }, 143 { .lst_data = { 0x00, 0x00, SPD_DT_DDR5_SDRAM, 0x00 }, .lst_len = 4, 144 .lst_err = LIBJEDEC_SPD_UNSUP_REV, .lst_desc = "Bad DDR4 " 145 "Revision (0x00)" }, 146 { .lst_data = { 0x00, 0xb2, SPD_DT_DDR5_SDRAM, 0x00 }, .lst_len = 4, 147 .lst_err = LIBJEDEC_SPD_UNSUP_REV, .lst_desc = "Bad DDR5 " 148 "Revision (0xb2)" }, 149 { .lst_data = { 0x00, 0x10, SPD_DT_DDR5_SDRAM, 0x00 }, .lst_len = 0xc3, 150 .lst_err = LIBJEDEC_SPD_UNSUP_REV, .lst_desc = "Bad DDR5 Common " 151 "Revision (0x00)" }, 152 { .lst_data = { 0x00, 0x10, SPD_DT_DDR4_SDRAM, 0x00 }, .lst_len = 0x10, 153 .lst_err = LIBJEDEC_SPD_OK, .lst_desc = "Catch incomplete errors", 154 .lst_check = spd_check_short_ddr4 }, 155 { .lst_data = { 0x00, 0x10, SPD_DT_DDR4_SDRAM, 0x00 }, .lst_len = 0x200, 156 .lst_err = LIBJEDEC_SPD_OK, .lst_desc = "Non-fatal parsing errors", 157 .lst_check = spd_check_misc_errors }, 158 159 }; 160 161 static boolean_t 162 libjedec_spd_test(const libjedec_spd_test_t *test) 163 { 164 nvlist_t *nvl; 165 spd_error_t err; 166 boolean_t pass = B_TRUE; 167 168 nvl = libjedec_spd(test->lst_data, test->lst_len, &err); 169 if (err != test->lst_err) { 170 warnx("found mismatched error: expected 0x%x, found 0x%x", 171 test->lst_err, err); 172 pass = B_FALSE; 173 } 174 175 if (nvl != NULL) { 176 if (test->lst_err != LIBJEDEC_SPD_OK) { 177 warnx("expected fatal error (0x%x), but somehow got " 178 "an nvlist! Contents:", test->lst_err); 179 dump_nvlist(nvl, 4); 180 pass = B_FALSE; 181 } 182 } else { 183 if (test->lst_err == LIBJEDEC_SPD_OK) { 184 warnx("expected an nvlist_t, but didn't get one: " 185 "actual spd_error_t: 0x%x", err); 186 pass = B_FALSE; 187 } 188 } 189 190 if (pass && test->lst_check) { 191 pass = test->lst_check(nvl); 192 } 193 194 return (pass); 195 } 196 197 int 198 main(void) 199 { 200 int ret = EXIT_SUCCESS; 201 202 for (size_t i = 0; i < ARRAY_SIZE(spd_tests); i++) { 203 const libjedec_spd_test_t *test = &spd_tests[i]; 204 205 if (!libjedec_spd_test(test)) { 206 (void) fprintf(stderr, "TEST FAILED: %s\n", 207 test->lst_desc); 208 ret = EXIT_FAILURE; 209 } else { 210 (void) printf("TEST PASSED: %s\n", test->lst_desc); 211 } 212 } 213 214 return (ret); 215 } 216