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 Robert Mustacchi
14  * Copyright 2023 Oxide Computer Company
15  */
16 
17 /*
18  * Basic testing of the SMBIOS 3.3 memory device extensions. We test these in a
19  * few different ways:
20  *
21  * 1. Using a 3.2 table with a 3.2 library to make sure we get the old fields.
22  * We also need to verify that we don't clobber memory in this case.
23  * 2. Using a 3.2 table with a 3.3 library to make sure we get the new fields.
24  * populated with the corresponding 3.2 values.
25  * 3. Using a 3.3 table with only the old values as valid.
26  * 4. Using a 3.3 table with both the old and new values as valid.
27  * memory.
28  *
29  * We also test the 3.7 extensions in two ways:
30  *
31  * 1. Using a 3.2 table with a 3.7 library to make sure that the new fields are
32  * properly set to the right spec mandated unknown values.
33  * 2. Using a 3.7 table with a 3.7 library.
34  */
35 
36 #include <stdlib.h>
37 #include "smbios_test.h"
38 
39 static const uint16_t smbios_memdevice_speed = 0xdeed;
40 static const uint16_t smbios_memdevice_clkspeed = 0xf00f;
41 static const uint32_t smbios_memdevice_extspeed = 0xbaddeed;
42 static const uint32_t smbios_memdevice_extclkspeed = 0xbadf00f;
43 static const uint16_t smbios_memdevice_pmic0_mfg = 0x1234;
44 static const uint16_t smbios_memdevice_pmic0_rev = 0x5600;
45 static const uint16_t smbios_memdevice_rcd_mfg = 0x4321;
46 static const uint16_t smbios_memdevice_rcd_rev = 0x6500;
47 
48 /*
49  * Fixed sizes from older versions.
50  */
51 static const size_t smbios_memdevice_len_v3p2 = 0x54;
52 static const size_t smbios_memdevice_len_v3p3 = 0x5c;
53 
54 /*
55  * Fill in the basics of a single memory device. Callers need to fill in the
56  * speed, extspeed, clkspeed, and extclkspeed members.
57  */
58 static void
smbios_test_memdevice_fill(smb_memdevice_t * mem)59 smbios_test_memdevice_fill(smb_memdevice_t *mem)
60 {
61 	mem->smbmdev_hdr.smbh_type = SMB_TYPE_MEMDEVICE;
62 	mem->smbmdev_hdr.smbh_len = sizeof (smb_memdevice_t);
63 
64 	mem->smbmdev_array = 0xffff;
65 	mem->smbmdev_error = htole16(0xfffe);
66 	mem->smbmdev_twidth = 64;
67 	mem->smbmdev_dwidth = 64;
68 	mem->smbmdev_size = 0x7fff;
69 	mem->smbmdev_form = SMB_MDFF_FBDIMM;
70 	mem->smbmdev_set = 0;
71 	mem->smbmdev_dloc = 0;
72 	mem->smbmdev_bloc = 0;
73 	mem->smbmdev_type = SMB_MDT_DDR4;
74 	mem->smbmdev_manufacturer = 0;
75 	mem->smbmdev_asset = 0;
76 	mem->smbmdev_part = 0;
77 	mem->smbmdev_attrs = 2;
78 	mem->smbmdev_extsize = htole32(0x123456);
79 	mem->smbmdev_minvolt = 0;
80 	mem->smbmdev_maxvolt = 0;
81 	mem->smbmdev_confvolt = 0;
82 	mem->smbmdev_memtech = 0;
83 	mem->smbmdev_opmode = 1 << 3;
84 	mem->smbmdev_fwver = 0;
85 	mem->smbmdev_modulemfgid = 0;
86 	mem->smbmdev_moduleprodid = 0;
87 	mem->smbmdev_memsysmfgid = 0;
88 	mem->smbmdev_memsysprodid = 0;
89 	mem->smbmdev_nvsize = htole64(UINT64_MAX);
90 	mem->smbmdev_volsize = htole64(UINT64_MAX);
91 	mem->smbmdev_cachesize = htole64(UINT64_MAX);
92 	mem->smbmdev_logicalsize = htole64(UINT64_MAX);
93 }
94 
95 boolean_t
smbios_test_memdevice_mktable_32(smbios_test_table_t * table)96 smbios_test_memdevice_mktable_32(smbios_test_table_t *table)
97 {
98 	smb_memdevice_t mem;
99 
100 	smbios_test_memdevice_fill(&mem);
101 	mem.smbmdev_speed = htole16(smbios_memdevice_speed);
102 	mem.smbmdev_clkspeed = htole16(smbios_memdevice_clkspeed);
103 	mem.smbmdev_extspeed = htole32(0);
104 	mem.smbmdev_extclkspeed = htole32(0);
105 
106 	mem.smbmdev_hdr.smbh_len = smbios_memdevice_len_v3p2;
107 	(void) smbios_test_table_append(table, &mem, smbios_memdevice_len_v3p2);
108 	smbios_test_table_append_eot(table);
109 
110 	return (B_TRUE);
111 }
112 
113 boolean_t
smbios_test_memdevice_mktable_33(smbios_test_table_t * table)114 smbios_test_memdevice_mktable_33(smbios_test_table_t *table)
115 {
116 	smb_memdevice_t mem;
117 
118 	smbios_test_memdevice_fill(&mem);
119 	mem.smbmdev_speed = htole16(smbios_memdevice_speed);
120 	mem.smbmdev_clkspeed = htole16(smbios_memdevice_clkspeed);
121 	mem.smbmdev_extspeed = htole32(0);
122 	mem.smbmdev_extclkspeed = htole32(0);
123 
124 	mem.smbmdev_hdr.smbh_len = smbios_memdevice_len_v3p3;
125 	(void) smbios_test_table_append(table, &mem, smbios_memdevice_len_v3p3);
126 	smbios_test_table_append_eot(table);
127 
128 	return (B_TRUE);
129 }
130 
131 boolean_t
smbios_test_memdevice_mktable_33ext(smbios_test_table_t * table)132 smbios_test_memdevice_mktable_33ext(smbios_test_table_t *table)
133 {
134 	smb_memdevice_t mem;
135 
136 	smbios_test_memdevice_fill(&mem);
137 	mem.smbmdev_speed = htole16(0xffff);
138 	mem.smbmdev_clkspeed = htole16(0xffff);
139 	mem.smbmdev_extspeed = htole32(smbios_memdevice_extspeed);
140 	mem.smbmdev_extclkspeed = htole32(smbios_memdevice_extclkspeed);
141 
142 	mem.smbmdev_hdr.smbh_len = smbios_memdevice_len_v3p3;
143 	(void) smbios_test_table_append(table, &mem, smbios_memdevice_len_v3p3);
144 	smbios_test_table_append_eot(table);
145 
146 	return (B_TRUE);
147 }
148 
149 boolean_t
smbios_test_memdevice_mktable_37(smbios_test_table_t * table)150 smbios_test_memdevice_mktable_37(smbios_test_table_t *table)
151 {
152 	smb_memdevice_t mem;
153 
154 	smbios_test_memdevice_fill(&mem);
155 	mem.smbmdev_speed = htole16(0xffff);
156 	mem.smbmdev_clkspeed = htole16(0xffff);
157 	mem.smbmdev_extspeed = htole32(smbios_memdevice_extspeed);
158 	mem.smbmdev_extclkspeed = htole32(smbios_memdevice_extclkspeed);
159 
160 	mem.smbmdev_pmic0mfgid = htole16(smbios_memdevice_pmic0_mfg);
161 	mem.smbmdev_pmic0rev = htole16(smbios_memdevice_pmic0_rev);
162 	mem.smbmdev_rcdmfgid = htole16(smbios_memdevice_rcd_mfg);
163 	mem.smbmdev_rcdrev = htole16(smbios_memdevice_rcd_rev);
164 
165 	(void) smbios_test_table_append(table, &mem, sizeof (mem));
166 	smbios_test_table_append_eot(table);
167 	return (B_TRUE);
168 }
169 
170 static boolean_t
smbios_test_memdevice_verify_common(smbios_memdevice_t * mem)171 smbios_test_memdevice_verify_common(smbios_memdevice_t *mem)
172 {
173 	boolean_t ret = B_TRUE;
174 
175 	if (mem->smbmd_dwidth != 64) {
176 		warnx("found wrong dwidth: %u", mem->smbmd_dwidth);
177 		ret = B_FALSE;
178 	}
179 
180 	if (mem->smbmd_twidth != 64) {
181 		warnx("found wrong twidth: %u", mem->smbmd_twidth);
182 		ret = B_FALSE;
183 	}
184 
185 	if (mem->smbmd_form != SMB_MDFF_FBDIMM) {
186 		warnx("found wrong form: %u", mem->smbmd_form);
187 		ret = B_FALSE;
188 	}
189 
190 	if (mem->smbmd_size != 0x123456ULL * 1024 * 1024) {
191 		warnx("found wrong size: %" PRIu64, mem->smbmd_size);
192 		ret = B_FALSE;
193 	}
194 
195 	return (ret);
196 }
197 
198 boolean_t
smbios_test_memdevice_verify_32(smbios_hdl_t * hdl)199 smbios_test_memdevice_verify_32(smbios_hdl_t *hdl)
200 {
201 	smbios_struct_t sp;
202 	smbios_memdevice_t mem;
203 	boolean_t ret = B_TRUE;
204 	uint64_t rval;
205 
206 	/*
207 	 * We expect that the SMBIOS 3.2 memory device values should not be
208 	 * touched here. As such we set them to a random value to verify and
209 	 * verify that it hasn't been set.
210 	 */
211 	arc4random_buf(&rval, sizeof (rval));
212 	mem.smbmd_extspeed = rval;
213 	mem.smbmd_extclkspeed = rval;
214 
215 	if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
216 		warnx("failed to lookup SMBIOS memory device: %s",
217 		    smbios_errmsg(smbios_errno(hdl)));
218 		return (B_FALSE);
219 	}
220 
221 	if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
222 		warnx("failed to get SMBIOS memory device info: %s",
223 		    smbios_errmsg(smbios_errno(hdl)));
224 		return (B_FALSE);
225 	}
226 
227 	if (mem.smbmd_extspeed != rval || mem.smbmd_extclkspeed != rval) {
228 		warnx("smbios_memdevice_t had its memory cloberred!");
229 		return (B_FALSE);
230 	}
231 
232 	if (!smbios_test_memdevice_verify_common(&mem)) {
233 		return (B_FALSE);
234 	}
235 
236 	if (mem.smbmd_speed != smbios_memdevice_speed) {
237 		warnx("found wrong device speed: %u", mem.smbmd_speed);
238 		ret = B_FALSE;
239 	}
240 
241 	if (mem.smbmd_clkspeed != smbios_memdevice_clkspeed) {
242 		warnx("found wrong device clkspeed: %u", mem.smbmd_clkspeed);
243 		ret = B_FALSE;
244 	}
245 
246 	return (ret);
247 }
248 
249 /*
250  * This is a variant of smbios_test_memdevice_verify_32(), but instead of using
251  * an SMBIOS 3.2 library, we use an SMBIOS 3.3 handle. This means that we expect
252  * the extended values to be populated with the base values.
253  */
254 boolean_t
smbios_test_memdevice_verify_32_33(smbios_hdl_t * hdl)255 smbios_test_memdevice_verify_32_33(smbios_hdl_t *hdl)
256 {
257 	smbios_struct_t sp;
258 	smbios_memdevice_t mem;
259 	boolean_t ret = B_TRUE;
260 
261 	if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
262 		warnx("failed to lookup SMBIOS memory device: %s",
263 		    smbios_errmsg(smbios_errno(hdl)));
264 		return (B_FALSE);
265 	}
266 
267 	if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
268 		warnx("failed to get SMBIOS memory device info: %s",
269 		    smbios_errmsg(smbios_errno(hdl)));
270 		return (B_FALSE);
271 	}
272 
273 	if (!smbios_test_memdevice_verify_common(&mem)) {
274 		return (B_FALSE);
275 	}
276 
277 	if (mem.smbmd_speed != smbios_memdevice_speed) {
278 		warnx("found wrong device speed: %u", mem.smbmd_speed);
279 		ret = B_FALSE;
280 	}
281 
282 	if (mem.smbmd_clkspeed != smbios_memdevice_clkspeed) {
283 		warnx("found wrong device clkspeed: %u", mem.smbmd_clkspeed);
284 		ret = B_FALSE;
285 	}
286 
287 	if (mem.smbmd_extspeed != smbios_memdevice_speed) {
288 		warnx("found wrong device speed: %" PRIu64, mem.smbmd_extspeed);
289 		ret = B_FALSE;
290 	}
291 
292 	if (mem.smbmd_extclkspeed != smbios_memdevice_clkspeed) {
293 		warnx("found wrong device clkspeed: %" PRIu64,
294 		    mem.smbmd_extclkspeed);
295 		ret = B_FALSE;
296 	}
297 
298 	return (ret);
299 }
300 
301 /*
302  * This is similar to the 3.2/3.3 variant above except we're checking the newer
303  * 3.7 fields related to the PMIC0 and RCD.
304  */
305 boolean_t
smbios_test_memdevice_verify_32_37(smbios_hdl_t * hdl)306 smbios_test_memdevice_verify_32_37(smbios_hdl_t *hdl)
307 {
308 	smbios_struct_t sp;
309 	smbios_memdevice_t mem;
310 	boolean_t ret = B_TRUE;
311 
312 	if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
313 		warnx("failed to lookup SMBIOS memory device: %s",
314 		    smbios_errmsg(smbios_errno(hdl)));
315 		return (B_FALSE);
316 	}
317 
318 	if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
319 		warnx("failed to get SMBIOS memory device info: %s",
320 		    smbios_errmsg(smbios_errno(hdl)));
321 		return (B_FALSE);
322 	}
323 
324 	if (!smbios_test_memdevice_verify_32_33(hdl)) {
325 		ret = B_FALSE;
326 	}
327 
328 	if (mem.smbmd_pmic0_mfgid != SMB_MD_MFG_UNKNOWN) {
329 		warnx("found wrong PMIC0 mfg id: 0x%x", mem.smbmd_pmic0_mfgid);
330 		ret = B_FALSE;
331 	}
332 
333 	if (mem.smbmd_pmic0_rev != SMB_MD_REV_UNKNOWN) {
334 		warnx("found wrong PMIC0 revision: 0x%x", mem.smbmd_pmic0_rev);
335 		ret = B_FALSE;
336 	}
337 
338 	if (mem.smbmd_rcd_mfgid != SMB_MD_MFG_UNKNOWN) {
339 		warnx("found wrong RCD mfg id: 0x%x", mem.smbmd_rcd_mfgid);
340 		ret = B_FALSE;
341 	}
342 
343 	if (mem.smbmd_rcd_rev != SMB_MD_REV_UNKNOWN) {
344 		warnx("found wrong RCD revision: 0x%x", mem.smbmd_rcd_rev);
345 		ret = B_FALSE;
346 	}
347 
348 	return (ret);
349 }
350 
351 boolean_t
smbios_test_memdevice_verify_33(smbios_hdl_t * hdl)352 smbios_test_memdevice_verify_33(smbios_hdl_t *hdl)
353 {
354 	smbios_struct_t sp;
355 	smbios_memdevice_t mem;
356 	boolean_t ret = B_TRUE;
357 
358 	if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
359 		warnx("failed to lookup SMBIOS memory device: %s",
360 		    smbios_errmsg(smbios_errno(hdl)));
361 		return (B_FALSE);
362 	}
363 
364 	if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
365 		warnx("failed to get SMBIOS memory device info: %s",
366 		    smbios_errmsg(smbios_errno(hdl)));
367 		return (B_FALSE);
368 	}
369 
370 	if (!smbios_test_memdevice_verify_common(&mem)) {
371 		return (B_FALSE);
372 	}
373 
374 	if (mem.smbmd_speed != smbios_memdevice_speed) {
375 		warnx("found wrong device speed: %u", mem.smbmd_speed);
376 		ret = B_FALSE;
377 	}
378 
379 	if (mem.smbmd_clkspeed != smbios_memdevice_clkspeed) {
380 		warnx("found wrong device clkspeed: %u", mem.smbmd_clkspeed);
381 		ret = B_FALSE;
382 	}
383 
384 	if (mem.smbmd_extspeed != smbios_memdevice_speed) {
385 		warnx("found wrong device speed: %" PRIu64, mem.smbmd_extspeed);
386 		ret = B_FALSE;
387 	}
388 
389 	if (mem.smbmd_extclkspeed != smbios_memdevice_clkspeed) {
390 		warnx("found wrong device clkspeed: %" PRIu64,
391 		    mem.smbmd_extclkspeed);
392 		ret = B_FALSE;
393 	}
394 
395 	return (ret);
396 }
397 
398 boolean_t
smbios_test_memdevice_verify_33ext(smbios_hdl_t * hdl)399 smbios_test_memdevice_verify_33ext(smbios_hdl_t *hdl)
400 {
401 	smbios_struct_t sp;
402 	smbios_memdevice_t mem;
403 	boolean_t ret = B_TRUE;
404 
405 	if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
406 		warnx("failed to lookup SMBIOS memory device: %s",
407 		    smbios_errmsg(smbios_errno(hdl)));
408 		return (B_FALSE);
409 	}
410 
411 	if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
412 		warnx("failed to get SMBIOS memory device info: %s",
413 		    smbios_errmsg(smbios_errno(hdl)));
414 		return (B_FALSE);
415 	}
416 
417 	if (!smbios_test_memdevice_verify_common(&mem)) {
418 		return (B_FALSE);
419 	}
420 
421 	if (mem.smbmd_speed != 0xffff) {
422 		warnx("found wrong device speed: %u", mem.smbmd_speed);
423 		ret = B_FALSE;
424 	}
425 
426 	if (mem.smbmd_clkspeed != 0xffff) {
427 		warnx("found wrong device clkspeed: %u", mem.smbmd_clkspeed);
428 		ret = B_FALSE;
429 	}
430 
431 	if (mem.smbmd_extspeed != smbios_memdevice_extspeed) {
432 		warnx("found wrong device speed: %" PRIu64, mem.smbmd_extspeed);
433 		ret = B_FALSE;
434 	}
435 
436 	if (mem.smbmd_extclkspeed != smbios_memdevice_extclkspeed) {
437 		warnx("found wrong device clkspeed: %" PRIu64,
438 		    mem.smbmd_extclkspeed);
439 		ret = B_FALSE;
440 	}
441 
442 	return (ret);
443 }
444 
445 /*
446  * Note, the 3.7 table is based upon 3.3ext so we use that for checking the
447  * first chunk of this.
448  */
449 boolean_t
smbios_test_memdevice_verify_37(smbios_hdl_t * hdl)450 smbios_test_memdevice_verify_37(smbios_hdl_t *hdl)
451 {
452 	smbios_struct_t sp;
453 	smbios_memdevice_t mem;
454 	boolean_t ret = B_TRUE;
455 
456 	if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
457 		warnx("failed to lookup SMBIOS memory device: %s",
458 		    smbios_errmsg(smbios_errno(hdl)));
459 		return (B_FALSE);
460 	}
461 
462 	if (smbios_info_memdevice(hdl, sp.smbstr_id, &mem) != 0) {
463 		warnx("failed to get SMBIOS memory device info: %s",
464 		    smbios_errmsg(smbios_errno(hdl)));
465 		return (B_FALSE);
466 	}
467 
468 	if (!smbios_test_memdevice_verify_33ext(hdl)) {
469 		ret = B_FALSE;
470 	}
471 
472 	if (mem.smbmd_pmic0_mfgid != smbios_memdevice_pmic0_mfg) {
473 		warnx("found wrong PMIC0 mfg id: 0x%x", mem.smbmd_pmic0_mfgid);
474 		ret = B_FALSE;
475 	}
476 
477 	if (mem.smbmd_pmic0_rev != smbios_memdevice_pmic0_rev) {
478 		warnx("found wrong PMIC0 revision: 0x%x", mem.smbmd_pmic0_rev);
479 		ret = B_FALSE;
480 	}
481 
482 	if (mem.smbmd_rcd_mfgid != smbios_memdevice_rcd_mfg) {
483 		warnx("found wrong RCD mfg id: 0x%x", mem.smbmd_rcd_mfgid);
484 		ret = B_FALSE;
485 	}
486 
487 	if (mem.smbmd_rcd_rev != smbios_memdevice_rcd_rev) {
488 		warnx("found wrong RCD revision: 0x%x", mem.smbmd_rcd_rev);
489 		ret = B_FALSE;
490 	}
491 
492 	return (ret);
493 }
494