xref: /illumos-gate/usr/src/test/util-tests/tests/libjedec/hex2spd/libjedec_hex2spd.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
1*8119dad8SRobert Mustacchi /*
2*8119dad8SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*8119dad8SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*8119dad8SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*8119dad8SRobert Mustacchi  * 1.0 of the CDDL.
6*8119dad8SRobert Mustacchi  *
7*8119dad8SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*8119dad8SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*8119dad8SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*8119dad8SRobert Mustacchi  */
11*8119dad8SRobert Mustacchi 
12*8119dad8SRobert Mustacchi /*
13*8119dad8SRobert Mustacchi  * Copyright 2024 Oxide Computer Company
14*8119dad8SRobert Mustacchi  */
15*8119dad8SRobert Mustacchi 
16*8119dad8SRobert Mustacchi /*
17*8119dad8SRobert Mustacchi  * This test goes through and converts data files that are in a semi-custom hex
18*8119dad8SRobert Mustacchi  * format that represent DIMMs into binary data and then tries to parse the SPD
19*8119dad8SRobert Mustacchi  * data. It then looks at specific fields and flags from within them. Each
20*8119dad8SRobert Mustacchi  * module is expected to be parsed error free.
21*8119dad8SRobert Mustacchi  *
22*8119dad8SRobert Mustacchi  * Tests are organized in files around the DDR module type generation, e.g.
23*8119dad8SRobert Mustacchi  * DDR3, DDR4, and DDR5 are all found in different directories. SPD information
24*8119dad8SRobert Mustacchi  * that we use has been taken from a combination of dumping data from actual
25*8119dad8SRobert Mustacchi  * modules and transforming them, transforming tables that are distributed by
26*8119dad8SRobert Mustacchi  * vendors in datasheets and supplemental, and manually creating SPD
27*8119dad8SRobert Mustacchi  * information based on information in datasheets. In particular, LPDDR in its
28*8119dad8SRobert Mustacchi  * solder package does not actually include SPD information directly and
29*8119dad8SRobert Mustacchi  * therefore we have translated it. This is what Intel and AMD have recommended
30*8119dad8SRobert Mustacchi  * and do for their LPDDR bootstrapping.
31*8119dad8SRobert Mustacchi  */
32*8119dad8SRobert Mustacchi 
33*8119dad8SRobert Mustacchi #include <stdlib.h>
34*8119dad8SRobert Mustacchi #include <stdio.h>
35*8119dad8SRobert Mustacchi #include <err.h>
36*8119dad8SRobert Mustacchi #include <strings.h>
37*8119dad8SRobert Mustacchi #include <errno.h>
38*8119dad8SRobert Mustacchi #include <sys/sysmacros.h>
39*8119dad8SRobert Mustacchi #include <libnvpair.h>
40*8119dad8SRobert Mustacchi #include <libjedec.h>
41*8119dad8SRobert Mustacchi #include <stdbool.h>
42*8119dad8SRobert Mustacchi #include <limits.h>
43*8119dad8SRobert Mustacchi #include <sys/debug.h>
44*8119dad8SRobert Mustacchi 
45*8119dad8SRobert Mustacchi #include "libjedec_hex2spd.h"
46*8119dad8SRobert Mustacchi 
47*8119dad8SRobert Mustacchi /*
48*8119dad8SRobert Mustacchi  * Default directory for data.
49*8119dad8SRobert Mustacchi  */
50*8119dad8SRobert Mustacchi #define	SPD_DATA_DIR	"/opt/util-tests/tests/hex2spd"
51*8119dad8SRobert Mustacchi 
52*8119dad8SRobert Mustacchi /*
53*8119dad8SRobert Mustacchi  * Maximum size we'll tolerate for a file. This corresponds to the largest DDR5
54*8119dad8SRobert Mustacchi  * SPD.
55*8119dad8SRobert Mustacchi  */
56*8119dad8SRobert Mustacchi #define	SPD_MAX	2048
57*8119dad8SRobert Mustacchi 
58*8119dad8SRobert Mustacchi static const hex2spd_test_t *hex2spd_tests[] = {
59*8119dad8SRobert Mustacchi 	&micron_ddr4_rdimm,
60*8119dad8SRobert Mustacchi 	&samsung_ddr4_lrdimm,
61*8119dad8SRobert Mustacchi 	&advantech_ddr4_sodimm,
62*8119dad8SRobert Mustacchi 	&advantech_ddr4_udimm,
63*8119dad8SRobert Mustacchi 	&micron_ddr5_rdimm,
64*8119dad8SRobert Mustacchi 	&advantech_ddr5_rdimm,
65*8119dad8SRobert Mustacchi 	&micron_lp4,
66*8119dad8SRobert Mustacchi 	&nanya_lp3,
67*8119dad8SRobert Mustacchi 	&micron_lp5,
68*8119dad8SRobert Mustacchi 	&fake_lp5_camm2,
69*8119dad8SRobert Mustacchi 	&samsung_ddr3_rdimm,
70*8119dad8SRobert Mustacchi 	&micron_ddr3_lrdimm
71*8119dad8SRobert Mustacchi };
72*8119dad8SRobert Mustacchi 
73*8119dad8SRobert Mustacchi /*
74*8119dad8SRobert Mustacchi  * Logic to convert an ASCII file with Hex to SPD data. Each byte of data is
75*8119dad8SRobert Mustacchi  * expected to be 2 hex digits conventionally arranged 16 bytes across. At any
76*8119dad8SRobert Mustacchi  * point we encounter a '#' character, we treat that as a comment and ignore the
77*8119dad8SRobert Mustacchi  * rest of the line. A line will start with an address followed by a ':'.
78*8119dad8SRobert Mustacchi  */
79*8119dad8SRobert Mustacchi static void *
hex2spd(const char * path,uint32_t * lenp)80*8119dad8SRobert Mustacchi hex2spd(const char *path, uint32_t *lenp)
81*8119dad8SRobert Mustacchi {
82*8119dad8SRobert Mustacchi 	char *buf = NULL;
83*8119dad8SRobert Mustacchi 	size_t buflen = 0;
84*8119dad8SRobert Mustacchi 	uint8_t *out = malloc(SPD_MAX);
85*8119dad8SRobert Mustacchi 	uint32_t outlen = 0, curline = 0;
86*8119dad8SRobert Mustacchi 	FILE *f;
87*8119dad8SRobert Mustacchi 
88*8119dad8SRobert Mustacchi 	f = fopen(path, "r");
89*8119dad8SRobert Mustacchi 	if (f == NULL) {
90*8119dad8SRobert Mustacchi 		warnx("INTERNAL TEST ERROR: failed to find test file %s",
91*8119dad8SRobert Mustacchi 		    path);
92*8119dad8SRobert Mustacchi 		free(out);
93*8119dad8SRobert Mustacchi 		return (NULL);
94*8119dad8SRobert Mustacchi 	}
95*8119dad8SRobert Mustacchi 
96*8119dad8SRobert Mustacchi 	if (out == NULL) {
97*8119dad8SRobert Mustacchi 		err(EXIT_FAILURE, "failed to allocate %u bytes for buffer",
98*8119dad8SRobert Mustacchi 		    SPD_MAX);
99*8119dad8SRobert Mustacchi 	}
100*8119dad8SRobert Mustacchi 
101*8119dad8SRobert Mustacchi 	while (getline(&buf, &buflen, f) != -1) {
102*8119dad8SRobert Mustacchi 		char *comment, *colon;
103*8119dad8SRobert Mustacchi 		unsigned long dataoff;
104*8119dad8SRobert Mustacchi 
105*8119dad8SRobert Mustacchi 		curline++;
106*8119dad8SRobert Mustacchi 
107*8119dad8SRobert Mustacchi 		if ((comment = strchr(buf, '#')) != NULL) {
108*8119dad8SRobert Mustacchi 			*comment = '\0';
109*8119dad8SRobert Mustacchi 		}
110*8119dad8SRobert Mustacchi 
111*8119dad8SRobert Mustacchi 		if (*buf == '\0')
112*8119dad8SRobert Mustacchi 			continue;
113*8119dad8SRobert Mustacchi 
114*8119dad8SRobert Mustacchi 		/*
115*8119dad8SRobert Mustacchi 		 * First grab out a line offset marker. This should be in order,
116*8119dad8SRobert Mustacchi 		 * but future us may be end up wanting to skip lots of zeros of
117*8119dad8SRobert Mustacchi 		 * course.
118*8119dad8SRobert Mustacchi 		 */
119*8119dad8SRobert Mustacchi 		errno = 0;
120*8119dad8SRobert Mustacchi 		dataoff = strtoul(buf, &colon, 16);
121*8119dad8SRobert Mustacchi 		if (errno != 0 || *colon != ':' || *(colon + 1) != ' ') {
122*8119dad8SRobert Mustacchi 			errx(EXIT_FAILURE, "failed to parse address part of "
123*8119dad8SRobert Mustacchi 			    "line %u", curline);
124*8119dad8SRobert Mustacchi 		}
125*8119dad8SRobert Mustacchi 
126*8119dad8SRobert Mustacchi 		if (dataoff >= SPD_MAX || dataoff % 0x10 != 0) {
127*8119dad8SRobert Mustacchi 			errx(EXIT_FAILURE, "line %u parsed data offset %lu is "
128*8119dad8SRobert Mustacchi 			    "invalid", curline, dataoff);
129*8119dad8SRobert Mustacchi 		}
130*8119dad8SRobert Mustacchi 
131*8119dad8SRobert Mustacchi 		/*
132*8119dad8SRobert Mustacchi 		 * We've got the starting data offset. Now go ahead and parse
133*8119dad8SRobert Mustacchi 		 * all the actual data that's in here. We use the max power way.
134*8119dad8SRobert Mustacchi 		 */
135*8119dad8SRobert Mustacchi 		if (sscanf(colon + 2, "%02x %02x %02x %02x %02x %02x %02x %02x "
136*8119dad8SRobert Mustacchi 		    "%02x %02x %02x %02x %02x %02x %02x %02x",
137*8119dad8SRobert Mustacchi 		    &out[dataoff + 0], &out[dataoff + 1], &out[dataoff + 2],
138*8119dad8SRobert Mustacchi 		    &out[dataoff + 3], &out[dataoff + 4], &out[dataoff + 5],
139*8119dad8SRobert Mustacchi 		    &out[dataoff + 6], &out[dataoff + 7], &out[dataoff + 8],
140*8119dad8SRobert Mustacchi 		    &out[dataoff + 9], &out[dataoff + 10], &out[dataoff + 11],
141*8119dad8SRobert Mustacchi 		    &out[dataoff + 12], &out[dataoff + 13], &out[dataoff + 14],
142*8119dad8SRobert Mustacchi 		    &out[dataoff + 15]) != 16) {
143*8119dad8SRobert Mustacchi 			errx(EXIT_FAILURE, "failed to parse data from line %u",
144*8119dad8SRobert Mustacchi 			    curline);
145*8119dad8SRobert Mustacchi 		}
146*8119dad8SRobert Mustacchi 
147*8119dad8SRobert Mustacchi 		outlen = MAX(outlen, dataoff + 16);
148*8119dad8SRobert Mustacchi 	}
149*8119dad8SRobert Mustacchi 
150*8119dad8SRobert Mustacchi 	*lenp = outlen;
151*8119dad8SRobert Mustacchi 	VERIFY0(fclose(f));
152*8119dad8SRobert Mustacchi 	return (out);
153*8119dad8SRobert Mustacchi }
154*8119dad8SRobert Mustacchi 
155*8119dad8SRobert Mustacchi static bool
hex2spd_test_one(const char * dir,const hex2spd_test_t * test)156*8119dad8SRobert Mustacchi hex2spd_test_one(const char *dir, const hex2spd_test_t *test)
157*8119dad8SRobert Mustacchi {
158*8119dad8SRobert Mustacchi 	char path[PATH_MAX];
159*8119dad8SRobert Mustacchi 	void *data;
160*8119dad8SRobert Mustacchi 	uint32_t dlen;
161*8119dad8SRobert Mustacchi 	nvlist_t *nvl;
162*8119dad8SRobert Mustacchi 	spd_error_t spd_err;
163*8119dad8SRobert Mustacchi 	bool ret = true;
164*8119dad8SRobert Mustacchi 
165*8119dad8SRobert Mustacchi 	if (snprintf(path, sizeof (path), "%s/%s.spd", dir, test->ht_file) >=
166*8119dad8SRobert Mustacchi 	    sizeof (path)) {
167*8119dad8SRobert Mustacchi 		errx(EXIT_FAILURE, "INTERNAL TEST ERROR: constructing test "
168*8119dad8SRobert Mustacchi 		    "path for %s would have overflowed internal buffer",
169*8119dad8SRobert Mustacchi 		    test->ht_file);
170*8119dad8SRobert Mustacchi 	}
171*8119dad8SRobert Mustacchi 
172*8119dad8SRobert Mustacchi 	data = hex2spd(path, &dlen);
173*8119dad8SRobert Mustacchi 	if (data == NULL) {
174*8119dad8SRobert Mustacchi 		return (false);
175*8119dad8SRobert Mustacchi 	}
176*8119dad8SRobert Mustacchi 
177*8119dad8SRobert Mustacchi 	nvl = libjedec_spd(data, dlen, &spd_err);
178*8119dad8SRobert Mustacchi 	free(data);
179*8119dad8SRobert Mustacchi 	if (spd_err != LIBJEDEC_SPD_OK) {
180*8119dad8SRobert Mustacchi 		warnx("TEST FAILURE: failed to parse %s: 0x%x", path, spd_err);
181*8119dad8SRobert Mustacchi 		return (false);
182*8119dad8SRobert Mustacchi 	}
183*8119dad8SRobert Mustacchi 	(void) printf("TEST PASSED: initially parsed %s\n", test->ht_file);
184*8119dad8SRobert Mustacchi 
185*8119dad8SRobert Mustacchi 	/*
186*8119dad8SRobert Mustacchi 	 * Verify there are no errors in this data. This means that we shouldn't
187*8119dad8SRobert Mustacchi 	 * find the errors key or the incomplete key.
188*8119dad8SRobert Mustacchi 	 */
189*8119dad8SRobert Mustacchi 	if (nvlist_exists(nvl, SPD_KEY_ERRS)) {
190*8119dad8SRobert Mustacchi 		warnx("TEST FAILED: %s contains errors:", test->ht_file);
191*8119dad8SRobert Mustacchi 		dump_nvlist(nvl, 0);
192*8119dad8SRobert Mustacchi 		ret = false;
193*8119dad8SRobert Mustacchi 	}
194*8119dad8SRobert Mustacchi 
195*8119dad8SRobert Mustacchi 	if (nvlist_exists(nvl, SPD_KEY_INCOMPLETE)) {
196*8119dad8SRobert Mustacchi 		ret = false;
197*8119dad8SRobert Mustacchi 		warnx("TEST FAILED: %s flagged as incomplete:", test->ht_file);
198*8119dad8SRobert Mustacchi 		dump_nvlist(nvl, 0);
199*8119dad8SRobert Mustacchi 	}
200*8119dad8SRobert Mustacchi 
201*8119dad8SRobert Mustacchi 	for (const hex2spd_spd_t *spd = &test->ht_checks[0];
202*8119dad8SRobert Mustacchi 	    spd->hs_key != NULL; spd++) {
203*8119dad8SRobert Mustacchi 		int nvret;
204*8119dad8SRobert Mustacchi 		uint_t nents;
205*8119dad8SRobert Mustacchi 		uint8_t *u8a;
206*8119dad8SRobert Mustacchi 		uint32_t u32, *u32a;
207*8119dad8SRobert Mustacchi 		uint64_t u64, *u64a;
208*8119dad8SRobert Mustacchi 		boolean_t *ba;
209*8119dad8SRobert Mustacchi 		char *str;
210*8119dad8SRobert Mustacchi 		bool pass;
211*8119dad8SRobert Mustacchi 
212*8119dad8SRobert Mustacchi 		switch (spd->hs_type) {
213*8119dad8SRobert Mustacchi 		case DATA_TYPE_UINT32:
214*8119dad8SRobert Mustacchi 			nvret = nvlist_lookup_uint32(nvl, spd->hs_key, &u32);
215*8119dad8SRobert Mustacchi 			if (nvret != 0) {
216*8119dad8SRobert Mustacchi 				warnc(nvret, "TEST FAILED: %s: failed to "
217*8119dad8SRobert Mustacchi 				    "lookup key %s", test->ht_file,
218*8119dad8SRobert Mustacchi 				    spd->hs_key);
219*8119dad8SRobert Mustacchi 				ret = false;
220*8119dad8SRobert Mustacchi 			} else if (u32 != spd->hs_val.hs_u32) {
221*8119dad8SRobert Mustacchi 				warnx("TEST FAILED: %s: key %s: found value "
222*8119dad8SRobert Mustacchi 				    "0x%x, but expected 0x%x", test->ht_file,
223*8119dad8SRobert Mustacchi 				    spd->hs_key, u32, spd->hs_val.hs_u32);
224*8119dad8SRobert Mustacchi 				ret = false;
225*8119dad8SRobert Mustacchi 			} else {
226*8119dad8SRobert Mustacchi 				(void) printf("TEST PASSED: %s: key %s data "
227*8119dad8SRobert Mustacchi 				    "matches\n", test->ht_file, spd->hs_key);
228*8119dad8SRobert Mustacchi 			}
229*8119dad8SRobert Mustacchi 			break;
230*8119dad8SRobert Mustacchi 		case DATA_TYPE_UINT64:
231*8119dad8SRobert Mustacchi 			nvret = nvlist_lookup_uint64(nvl, spd->hs_key, &u64);
232*8119dad8SRobert Mustacchi 			if (nvret != 0) {
233*8119dad8SRobert Mustacchi 				warnc(nvret, "TEST FAILED: %s: failed to "
234*8119dad8SRobert Mustacchi 				    "lookup key %s", test->ht_file,
235*8119dad8SRobert Mustacchi 				    spd->hs_key);
236*8119dad8SRobert Mustacchi 				ret = false;
237*8119dad8SRobert Mustacchi 			} else if (u64 != spd->hs_val.hs_u64) {
238*8119dad8SRobert Mustacchi 				warnx("TEST FAILED: %s: key %s: found value "
239*8119dad8SRobert Mustacchi 				    "0x%" PRIx64 ", but expected 0x%" PRIx64,
240*8119dad8SRobert Mustacchi 				    test->ht_file, spd->hs_key, u64,
241*8119dad8SRobert Mustacchi 				    spd->hs_val.hs_u64);
242*8119dad8SRobert Mustacchi 				ret = false;
243*8119dad8SRobert Mustacchi 			} else {
244*8119dad8SRobert Mustacchi 				(void) printf("TEST PASSED: %s: key %s data "
245*8119dad8SRobert Mustacchi 				    "matches\n", test->ht_file, spd->hs_key);
246*8119dad8SRobert Mustacchi 			}
247*8119dad8SRobert Mustacchi 			break;
248*8119dad8SRobert Mustacchi 		case DATA_TYPE_STRING:
249*8119dad8SRobert Mustacchi 			nvret = nvlist_lookup_string(nvl, spd->hs_key, &str);
250*8119dad8SRobert Mustacchi 			if (nvret != 0) {
251*8119dad8SRobert Mustacchi 				warnc(nvret, "TEST FAILED: %s: failed to "
252*8119dad8SRobert Mustacchi 				    "lookup key %s", test->ht_file,
253*8119dad8SRobert Mustacchi 				    spd->hs_key);
254*8119dad8SRobert Mustacchi 				ret = false;
255*8119dad8SRobert Mustacchi 			} else if (strcmp(str, spd->hs_val.hs_str) != 0) {
256*8119dad8SRobert Mustacchi 				warnx("TEST FAILED: %s: key %s: found value "
257*8119dad8SRobert Mustacchi 				    "%s, but expected %s", test->ht_file,
258*8119dad8SRobert Mustacchi 				    spd->hs_key, str, spd->hs_val.hs_str);
259*8119dad8SRobert Mustacchi 				ret = false;
260*8119dad8SRobert Mustacchi 			} else {
261*8119dad8SRobert Mustacchi 				(void) printf("TEST PASSED: %s: key %s data "
262*8119dad8SRobert Mustacchi 				    "matches\n", test->ht_file, spd->hs_key);
263*8119dad8SRobert Mustacchi 			}
264*8119dad8SRobert Mustacchi 			break;
265*8119dad8SRobert Mustacchi 		case DATA_TYPE_UINT8_ARRAY:
266*8119dad8SRobert Mustacchi 			nvret = nvlist_lookup_uint8_array(nvl, spd->hs_key,
267*8119dad8SRobert Mustacchi 			    &u8a, &nents);
268*8119dad8SRobert Mustacchi 			if (nvret != 0) {
269*8119dad8SRobert Mustacchi 				warnc(nvret, "TEST FAILED: %s: failed to "
270*8119dad8SRobert Mustacchi 				    "lookup key %s", test->ht_file,
271*8119dad8SRobert Mustacchi 				    spd->hs_key);
272*8119dad8SRobert Mustacchi 				ret = false;
273*8119dad8SRobert Mustacchi 				break;
274*8119dad8SRobert Mustacchi 			}
275*8119dad8SRobert Mustacchi 
276*8119dad8SRobert Mustacchi 			if (nents != spd->hs_val.hs_u8a.ha_nval) {
277*8119dad8SRobert Mustacchi 				warnx("TEST FAILED: %s: key %s array has 0x%x "
278*8119dad8SRobert Mustacchi 				    "values, but expected 0x%x values",
279*8119dad8SRobert Mustacchi 				    test->ht_file, spd->hs_key, nents,
280*8119dad8SRobert Mustacchi 				    spd->hs_val.hs_u8a.ha_nval);
281*8119dad8SRobert Mustacchi 				ret = false;
282*8119dad8SRobert Mustacchi 				break;
283*8119dad8SRobert Mustacchi 			}
284*8119dad8SRobert Mustacchi 
285*8119dad8SRobert Mustacchi 			pass = true;
286*8119dad8SRobert Mustacchi 			for (uint_t i = 0; i < nents; i++) {
287*8119dad8SRobert Mustacchi 				uint8_t targ = spd->hs_val.hs_u8a.ha_vals[i];
288*8119dad8SRobert Mustacchi 				if (u8a[i] != targ) {
289*8119dad8SRobert Mustacchi 					warnx("TEST FAILED: %s: key %s: entry "
290*8119dad8SRobert Mustacchi 					    "[%u] has value 0x%x, but expected "
291*8119dad8SRobert Mustacchi 					    "0x%x", test->ht_file, spd->hs_key,
292*8119dad8SRobert Mustacchi 					    i, u8a[i], targ);
293*8119dad8SRobert Mustacchi 					ret = false;
294*8119dad8SRobert Mustacchi 					pass = false;
295*8119dad8SRobert Mustacchi 				}
296*8119dad8SRobert Mustacchi 			}
297*8119dad8SRobert Mustacchi 
298*8119dad8SRobert Mustacchi 			if (pass) {
299*8119dad8SRobert Mustacchi 				(void) printf("TEST PASSED: %s: key %s data "
300*8119dad8SRobert Mustacchi 				    "matches\n", test->ht_file, spd->hs_key);
301*8119dad8SRobert Mustacchi 			}
302*8119dad8SRobert Mustacchi 			break;
303*8119dad8SRobert Mustacchi 		case DATA_TYPE_UINT32_ARRAY:
304*8119dad8SRobert Mustacchi 			nvret = nvlist_lookup_uint32_array(nvl, spd->hs_key,
305*8119dad8SRobert Mustacchi 			    &u32a, &nents);
306*8119dad8SRobert Mustacchi 			if (nvret != 0) {
307*8119dad8SRobert Mustacchi 				warnc(nvret, "TEST FAILED: %s: failed to "
308*8119dad8SRobert Mustacchi 				    "lookup key %s", test->ht_file,
309*8119dad8SRobert Mustacchi 				    spd->hs_key);
310*8119dad8SRobert Mustacchi 				ret = false;
311*8119dad8SRobert Mustacchi 				break;
312*8119dad8SRobert Mustacchi 			}
313*8119dad8SRobert Mustacchi 
314*8119dad8SRobert Mustacchi 			if (nents != spd->hs_val.hs_u32a.ha_nval) {
315*8119dad8SRobert Mustacchi 				warnx("TEST FAILED: %s: key %s array has 0x%x "
316*8119dad8SRobert Mustacchi 				    "values, but expected 0x%x values",
317*8119dad8SRobert Mustacchi 				    test->ht_file, spd->hs_key, nents,
318*8119dad8SRobert Mustacchi 				    spd->hs_val.hs_u32a.ha_nval);
319*8119dad8SRobert Mustacchi 				ret = false;
320*8119dad8SRobert Mustacchi 				break;
321*8119dad8SRobert Mustacchi 			}
322*8119dad8SRobert Mustacchi 
323*8119dad8SRobert Mustacchi 			pass = true;
324*8119dad8SRobert Mustacchi 			for (uint_t i = 0; i < nents; i++) {
325*8119dad8SRobert Mustacchi 				uint32_t targ = spd->hs_val.hs_u32a.ha_vals[i];
326*8119dad8SRobert Mustacchi 				if (u32a[i] != targ) {
327*8119dad8SRobert Mustacchi 					warnx("TEST FAILED: %s: key %s: entry "
328*8119dad8SRobert Mustacchi 					    "[%u] has value 0x%x, but expected "
329*8119dad8SRobert Mustacchi 					    "0x%x", test->ht_file, spd->hs_key,
330*8119dad8SRobert Mustacchi 					    i, u32a[i], targ);
331*8119dad8SRobert Mustacchi 					ret = false;
332*8119dad8SRobert Mustacchi 					pass = false;
333*8119dad8SRobert Mustacchi 				}
334*8119dad8SRobert Mustacchi 			}
335*8119dad8SRobert Mustacchi 
336*8119dad8SRobert Mustacchi 			if (pass) {
337*8119dad8SRobert Mustacchi 				(void) printf("TEST PASSED: %s: key %s data "
338*8119dad8SRobert Mustacchi 				    "matches\n", test->ht_file, spd->hs_key);
339*8119dad8SRobert Mustacchi 			}
340*8119dad8SRobert Mustacchi 			break;
341*8119dad8SRobert Mustacchi 		case DATA_TYPE_UINT64_ARRAY:
342*8119dad8SRobert Mustacchi 			nvret = nvlist_lookup_uint64_array(nvl, spd->hs_key,
343*8119dad8SRobert Mustacchi 			    &u64a, &nents);
344*8119dad8SRobert Mustacchi 			if (nvret != 0) {
345*8119dad8SRobert Mustacchi 				warnc(nvret, "TEST FAILED: %s: failed to "
346*8119dad8SRobert Mustacchi 				    "lookup key %s", test->ht_file,
347*8119dad8SRobert Mustacchi 				    spd->hs_key);
348*8119dad8SRobert Mustacchi 				ret = false;
349*8119dad8SRobert Mustacchi 				break;
350*8119dad8SRobert Mustacchi 			}
351*8119dad8SRobert Mustacchi 
352*8119dad8SRobert Mustacchi 			if (nents != spd->hs_val.hs_u64a.ha_nval) {
353*8119dad8SRobert Mustacchi 				warnx("TEST FAILED: %s: key %s array has 0x%x "
354*8119dad8SRobert Mustacchi 				    "values, but expected 0x%x values",
355*8119dad8SRobert Mustacchi 				    test->ht_file, spd->hs_key, nents,
356*8119dad8SRobert Mustacchi 				    spd->hs_val.hs_u64a.ha_nval);
357*8119dad8SRobert Mustacchi 				ret = false;
358*8119dad8SRobert Mustacchi 				break;
359*8119dad8SRobert Mustacchi 			}
360*8119dad8SRobert Mustacchi 
361*8119dad8SRobert Mustacchi 			pass = true;
362*8119dad8SRobert Mustacchi 			for (uint_t i = 0; i < nents; i++) {
363*8119dad8SRobert Mustacchi 				uint64_t targ = spd->hs_val.hs_u64a.ha_vals[i];
364*8119dad8SRobert Mustacchi 				if (u64a[i] != targ) {
365*8119dad8SRobert Mustacchi 					warnx("TEST FAILED: %s: key %s: entry "
366*8119dad8SRobert Mustacchi 					    "[%u] has value 0x%" PRIx64 ", but "
367*8119dad8SRobert Mustacchi 					    "expected 0x%" PRIx64,
368*8119dad8SRobert Mustacchi 					    test->ht_file, spd->hs_key, i,
369*8119dad8SRobert Mustacchi 					    u64a[i], targ);
370*8119dad8SRobert Mustacchi 					ret = false;
371*8119dad8SRobert Mustacchi 					pass = false;
372*8119dad8SRobert Mustacchi 				}
373*8119dad8SRobert Mustacchi 			}
374*8119dad8SRobert Mustacchi 
375*8119dad8SRobert Mustacchi 			if (pass) {
376*8119dad8SRobert Mustacchi 				(void) printf("TEST PASSED: %s: key %s data "
377*8119dad8SRobert Mustacchi 				    "matches\n", test->ht_file, spd->hs_key);
378*8119dad8SRobert Mustacchi 			}
379*8119dad8SRobert Mustacchi 			break;
380*8119dad8SRobert Mustacchi 
381*8119dad8SRobert Mustacchi 		case DATA_TYPE_BOOLEAN:
382*8119dad8SRobert Mustacchi 			nvret = nvlist_lookup_boolean(nvl, spd->hs_key);
383*8119dad8SRobert Mustacchi 			if (spd->hs_val.hs_bool) {
384*8119dad8SRobert Mustacchi 				if (nvret != 0) {
385*8119dad8SRobert Mustacchi 					warnc(nvret, "TEST FAILED: %s: failed "
386*8119dad8SRobert Mustacchi 					    "to lookup key %s", test->ht_file,
387*8119dad8SRobert Mustacchi 					    spd->hs_key);
388*8119dad8SRobert Mustacchi 					ret = false;
389*8119dad8SRobert Mustacchi 				} else {
390*8119dad8SRobert Mustacchi 					(void) printf("TEST PASSED: %s: key %s "
391*8119dad8SRobert Mustacchi 					    "data matches\n", test->ht_file,
392*8119dad8SRobert Mustacchi 					    spd->hs_key);
393*8119dad8SRobert Mustacchi 				}
394*8119dad8SRobert Mustacchi 			} else {
395*8119dad8SRobert Mustacchi 				if (nvret == 0) {
396*8119dad8SRobert Mustacchi 					warnc(nvret, "TEST FAILED: %s: "
397*8119dad8SRobert Mustacchi 					    "successfully lookup up key %s, "
398*8119dad8SRobert Mustacchi 					    "but expected it not to be present",
399*8119dad8SRobert Mustacchi 					    test->ht_file, spd->hs_key);
400*8119dad8SRobert Mustacchi 					ret = false;
401*8119dad8SRobert Mustacchi 				} else if (nvret != ENOENT) {
402*8119dad8SRobert Mustacchi 					warnx("TEST FAILED: %s: failed to "
403*8119dad8SRobert Mustacchi 					    "lookup key %s, but got %s not "
404*8119dad8SRobert Mustacchi 					    "ENOENT", test->ht_file,
405*8119dad8SRobert Mustacchi 					    spd->hs_key,
406*8119dad8SRobert Mustacchi 					    strerrorname_np(nvret));
407*8119dad8SRobert Mustacchi 					ret = false;
408*8119dad8SRobert Mustacchi 				} else {
409*8119dad8SRobert Mustacchi 					(void) printf("TEST PASSED: %s: key %s "
410*8119dad8SRobert Mustacchi 					    "data matches\n", test->ht_file,
411*8119dad8SRobert Mustacchi 					    spd->hs_key);
412*8119dad8SRobert Mustacchi 				}
413*8119dad8SRobert Mustacchi 			}
414*8119dad8SRobert Mustacchi 			break;
415*8119dad8SRobert Mustacchi 		case DATA_TYPE_BOOLEAN_ARRAY:
416*8119dad8SRobert Mustacchi 			nvret = nvlist_lookup_boolean_array(nvl, spd->hs_key,
417*8119dad8SRobert Mustacchi 			    &ba, &nents);
418*8119dad8SRobert Mustacchi 			if (nvret != 0) {
419*8119dad8SRobert Mustacchi 				warnc(nvret, "TEST FAILED: %s: failed to "
420*8119dad8SRobert Mustacchi 				    "lookup key %s", test->ht_file,
421*8119dad8SRobert Mustacchi 				    spd->hs_key);
422*8119dad8SRobert Mustacchi 				ret = false;
423*8119dad8SRobert Mustacchi 				break;
424*8119dad8SRobert Mustacchi 			}
425*8119dad8SRobert Mustacchi 
426*8119dad8SRobert Mustacchi 			if (nents != spd->hs_val.hs_ba.ha_nval) {
427*8119dad8SRobert Mustacchi 				warnx("TEST FAILED: %s: key %s array has 0x%x "
428*8119dad8SRobert Mustacchi 				    "values, but expected 0x%x values",
429*8119dad8SRobert Mustacchi 				    test->ht_file, spd->hs_key, nents,
430*8119dad8SRobert Mustacchi 				    spd->hs_val.hs_u32a.ha_nval);
431*8119dad8SRobert Mustacchi 				ret = false;
432*8119dad8SRobert Mustacchi 				break;
433*8119dad8SRobert Mustacchi 			}
434*8119dad8SRobert Mustacchi 
435*8119dad8SRobert Mustacchi 			pass = true;
436*8119dad8SRobert Mustacchi 			for (uint_t i = 0; i < nents; i++) {
437*8119dad8SRobert Mustacchi 				boolean_t targ = spd->hs_val.hs_ba.ha_vals[i];
438*8119dad8SRobert Mustacchi 				if (ba[i] != targ) {
439*8119dad8SRobert Mustacchi 					warnx("TEST FAILED: %s: key %s: entry "
440*8119dad8SRobert Mustacchi 					    "[%u] is %s, but expected %s",
441*8119dad8SRobert Mustacchi 					    test->ht_file, spd->hs_key, i,
442*8119dad8SRobert Mustacchi 					    ba[i] ? "true" : "false",
443*8119dad8SRobert Mustacchi 					    targ ? "true" : "false");
444*8119dad8SRobert Mustacchi 					ret = false;
445*8119dad8SRobert Mustacchi 					pass = false;
446*8119dad8SRobert Mustacchi 				}
447*8119dad8SRobert Mustacchi 			}
448*8119dad8SRobert Mustacchi 
449*8119dad8SRobert Mustacchi 			if (pass) {
450*8119dad8SRobert Mustacchi 				(void) printf("TEST PASSED: %s: key %s data "
451*8119dad8SRobert Mustacchi 				    "matches\n", test->ht_file, spd->hs_key);
452*8119dad8SRobert Mustacchi 			}
453*8119dad8SRobert Mustacchi 			break;
454*8119dad8SRobert Mustacchi 		default:
455*8119dad8SRobert Mustacchi 			warnx("TEST FAILURE: %s: key %s has unsupported "
456*8119dad8SRobert Mustacchi 			    "data type 0x%x", test->ht_file, spd->hs_key,
457*8119dad8SRobert Mustacchi 			    spd->hs_type);
458*8119dad8SRobert Mustacchi 			ret = false;
459*8119dad8SRobert Mustacchi 			break;
460*8119dad8SRobert Mustacchi 		}
461*8119dad8SRobert Mustacchi 	}
462*8119dad8SRobert Mustacchi 
463*8119dad8SRobert Mustacchi 	nvlist_free(nvl);
464*8119dad8SRobert Mustacchi 	return (ret);
465*8119dad8SRobert Mustacchi }
466*8119dad8SRobert Mustacchi 
467*8119dad8SRobert Mustacchi int
main(void)468*8119dad8SRobert Mustacchi main(void)
469*8119dad8SRobert Mustacchi {
470*8119dad8SRobert Mustacchi 	int ret = EXIT_SUCCESS;
471*8119dad8SRobert Mustacchi 	const char *dir;
472*8119dad8SRobert Mustacchi 
473*8119dad8SRobert Mustacchi 	dir = getenv("HEX2SPD_DIR");
474*8119dad8SRobert Mustacchi 	if (dir == NULL) {
475*8119dad8SRobert Mustacchi 		dir = SPD_DATA_DIR;
476*8119dad8SRobert Mustacchi 	}
477*8119dad8SRobert Mustacchi 
478*8119dad8SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(hex2spd_tests); i++) {
479*8119dad8SRobert Mustacchi 		if (!hex2spd_test_one(dir, hex2spd_tests[i]))
480*8119dad8SRobert Mustacchi 			ret = EXIT_FAILURE;
481*8119dad8SRobert Mustacchi 	}
482*8119dad8SRobert Mustacchi 
483*8119dad8SRobert Mustacchi 	if (ret == EXIT_SUCCESS) {
484*8119dad8SRobert Mustacchi 		(void) printf("All tests passed successfully!\n");
485*8119dad8SRobert Mustacchi 	}
486*8119dad8SRobert Mustacchi 	return (ret);
487*8119dad8SRobert Mustacchi }
488