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 2024 Oxide Computer Company
14  */
15 
16 /*
17  * Test SMBIOS Type 40 Additional Information. We try to cover a variety of
18  * cases with and without entries, entries with and without additional data, and
19  * several invalid length entries. Nothing currently checks that handles are
20  * meaningful beyond that they are replicated.
21  */
22 
23 #include "smbios_test.h"
24 
25 static const uint16_t smbios_addinfo_ent0_hdl = 0x7777;
26 static const uint8_t smbios_addinfo_ent0_off = 0x97;
27 static const char *smbios_addinfo_ent0_str = "Sephiroth";
28 static const uint32_t smbios_addinfo_ent0_data = 9999;
29 static const uint16_t smbios_addinfo_ent1_hdl = 0x1234;
30 static const uint8_t smbios_addinfo_ent1_off = 4;
31 static const char *smbios_addinfo_ent1_str = "Himmel";
32 static const uint16_t smbios_addinfo_ent2_hdl = 0x4321;
33 static const uint8_t smbios_addinfo_ent2_off = 0xfe;
34 static const char *smbios_addinfo_ent2_str = "Knights of the Round";
35 static const char *smbios_addinfo_ent2_data = "Galahad, Gawain, Lancelot";
36 
37 static boolean_t
smbios_test_addinfo_verify_base(smbios_hdl_t * hdl,smbios_struct_t * sp,uint_t exp)38 smbios_test_addinfo_verify_base(smbios_hdl_t *hdl, smbios_struct_t *sp,
39     uint_t exp)
40 {
41 	uint_t nents;
42 	boolean_t ret = B_TRUE;
43 	smbios_addinfo_ent_t *ent;
44 
45 	if (smbios_lookup_type(hdl, SMB_TYPE_ADDINFO, sp) == -1) {
46 		warnx("failed to lookup SMBIOS addinfo: %s",
47 		    smbios_errmsg(smbios_errno(hdl)));
48 		return (B_FALSE);
49 	}
50 
51 	if (smbios_info_addinfo_nents(hdl, sp->smbstr_id, &nents) != 0) {
52 		warnx("failed to get additional information entry count: %s",
53 		    smbios_errmsg(smbios_errno(hdl)));
54 		return (B_FALSE);
55 	}
56 
57 	if (nents != exp) {
58 		warnx("additional information entry mismatch: expected 0x%x, "
59 		    "found 0x%x", exp, nents);
60 		ret = B_FALSE;
61 	}
62 
63 	if (smbios_info_addinfo_ent(hdl, sp->smbstr_id, exp, &ent) != -1) {
64 		warnx("incorrectly parsed non-existent entity");
65 		smbios_info_addinfo_ent_free(hdl, ent);
66 		ret = B_FALSE;
67 	} else if (smbios_errno(hdl) != ESMB_REQVAL) {
68 		warnx("encountered wrong error for addinfo ent, expected: "
69 		    "0x%x, found: 0x%x", ESMB_REQVAL, smbios_errno(hdl));
70 		ret = B_FALSE;
71 	}
72 
73 	return (ret);
74 }
75 
76 /*
77  * Basic entry without valid entries. Strictly speaking this may be illegal per
78  * the spec.
79  */
80 boolean_t
smbios_test_addinfo_mktable_noent(smbios_test_table_t * table)81 smbios_test_addinfo_mktable_noent(smbios_test_table_t *table)
82 {
83 	smb_addinfo_t add;
84 
85 	add.smbai_hdr.smbh_type = SMB_TYPE_ADDINFO;
86 	add.smbai_hdr.smbh_len = sizeof (add);
87 	add.smbai_nents = 0;
88 
89 	(void) smbios_test_table_append(table, &add, sizeof (add));
90 	smbios_test_table_append_eot(table);
91 
92 	return (B_TRUE);
93 }
94 
95 boolean_t
smbios_test_addinfo_verify_noent(smbios_hdl_t * hdl)96 smbios_test_addinfo_verify_noent(smbios_hdl_t *hdl)
97 {
98 	smbios_struct_t sp;
99 
100 	return (smbios_test_addinfo_verify_base(hdl, &sp, 0));
101 }
102 
103 /*
104  * Complex case with three entries, each with varying data and strings.
105  */
106 boolean_t
smbios_test_addinfo_mktable_ents(smbios_test_table_t * table)107 smbios_test_addinfo_mktable_ents(smbios_test_table_t *table)
108 {
109 	smb_addinfo_t add;
110 	smb_addinfo_ent_t ent0, ent1, ent2;
111 	size_t slen;
112 
113 	add.smbai_hdr.smbh_type = SMB_TYPE_ADDINFO;
114 	add.smbai_hdr.smbh_len = sizeof (add);
115 	add.smbai_nents = 3;
116 
117 	ent0.smbaie_len = sizeof (smb_addinfo_ent_t) +
118 	    sizeof (smbios_addinfo_ent0_data);
119 	ent0.smbaie_rhdl = htole16(smbios_addinfo_ent0_hdl);
120 	ent0.smbaie_off = smbios_addinfo_ent0_off;
121 	ent0.smbaie_str = 1;
122 	add.smbai_hdr.smbh_len += ent0.smbaie_len;
123 
124 	ent1.smbaie_len = sizeof (smb_addinfo_ent_t);
125 	ent1.smbaie_rhdl = htole16(smbios_addinfo_ent1_hdl);
126 	ent1.smbaie_off = smbios_addinfo_ent1_off;
127 	ent1.smbaie_str = 2;
128 	add.smbai_hdr.smbh_len += ent1.smbaie_len;
129 
130 	slen = strlen(smbios_addinfo_ent2_data) + 1;
131 	ent2.smbaie_len = sizeof (smb_addinfo_ent_t) + slen;
132 	ent2.smbaie_rhdl = htole16(smbios_addinfo_ent2_hdl);
133 	ent2.smbaie_off = smbios_addinfo_ent2_off;
134 	ent2.smbaie_str = 3;
135 	add.smbai_hdr.smbh_len += ent2.smbaie_len;
136 
137 	(void) smbios_test_table_append(table, &add, sizeof (add));
138 	(void) smbios_test_table_append_raw(table, &ent0, sizeof (ent0));
139 	(void) smbios_test_table_append_raw(table, &smbios_addinfo_ent0_data,
140 	    sizeof (smbios_addinfo_ent0_data));
141 	(void) smbios_test_table_append_raw(table, &ent1, sizeof (ent1));
142 	(void) smbios_test_table_append_raw(table, &ent2, sizeof (ent2));
143 	(void) smbios_test_table_append_raw(table, smbios_addinfo_ent2_data,
144 	    slen);
145 	smbios_test_table_append_string(table, smbios_addinfo_ent0_str);
146 	smbios_test_table_append_string(table, smbios_addinfo_ent1_str);
147 	smbios_test_table_append_string(table, smbios_addinfo_ent2_str);
148 	smbios_test_table_str_fini(table);
149 	smbios_test_table_append_eot(table);
150 
151 	return (B_TRUE);
152 }
153 
154 boolean_t
smbios_test_addinfo_verify_ents(smbios_hdl_t * hdl)155 smbios_test_addinfo_verify_ents(smbios_hdl_t *hdl)
156 {
157 	smbios_struct_t sp;
158 	boolean_t ret = B_TRUE;
159 	smbios_addinfo_ent_t *ent;
160 
161 	if (!smbios_test_addinfo_verify_base(hdl, &sp, 3)) {
162 		return (B_FALSE);
163 	}
164 
165 	if (smbios_info_addinfo_ent(hdl, sp.smbstr_id, 0, &ent) != 0) {
166 		warnx("failed to lookup additional entry 0: %s",
167 		    smbios_errmsg(smbios_errno(hdl)));
168 		return (B_FALSE);
169 	}
170 
171 	if (ent->smbai_ref != smbios_addinfo_ent0_hdl) {
172 		warnx("entry 0 mismatch, found unexpected reference handle: "
173 		    "0x%lx", ent->smbai_ref);
174 		ret = B_FALSE;
175 	}
176 	if (ent->smbai_ref_off != smbios_addinfo_ent0_off) {
177 		warnx("entry 0 mismatch, found unexpected reference offset: "
178 		    "0x%x", ent->smbai_ref_off);
179 		ret = B_FALSE;
180 	}
181 	if (ent->smbai_dlen != sizeof (smbios_addinfo_ent0_data)) {
182 		warnx("entry 0 mismatch, found unexpected data length: 0x%x",
183 		    ent->smbai_dlen);
184 		ret = B_FALSE;
185 	}
186 	if (memcmp(ent->smbai_data, &smbios_addinfo_ent0_data,
187 	    ent->smbai_dlen) != 0) {
188 		warnx("entry 0 mismatch, additional data mismatched");
189 		ret = B_FALSE;
190 	}
191 	smbios_info_addinfo_ent_free(hdl, ent);
192 
193 	if (smbios_info_addinfo_ent(hdl, sp.smbstr_id, 1, &ent) != 0) {
194 		warnx("failed to lookup additional entry 1: %s",
195 		    smbios_errmsg(smbios_errno(hdl)));
196 		return (B_FALSE);
197 	}
198 
199 	if (ent->smbai_ref != smbios_addinfo_ent1_hdl) {
200 		warnx("entry 1 mismatch, found unexpected reference handle: "
201 		    "0x%lx", ent->smbai_ref);
202 		ret = B_FALSE;
203 	}
204 	if (ent->smbai_ref_off != smbios_addinfo_ent1_off) {
205 		warnx("entry 1 mismatch, found unexpected reference offset: "
206 		    "0x%x", ent->smbai_ref_off);
207 		ret = B_FALSE;
208 	}
209 	if (ent->smbai_dlen != 0) {
210 		warnx("entry 1 mismatch, found unexpected data length: 0x%x",
211 		    ent->smbai_dlen);
212 		ret = B_FALSE;
213 	}
214 	if (ent->smbai_data != NULL) {
215 		warnx("entry 1 mismatch, found unexpected data pointer");
216 		ret = B_FALSE;
217 	}
218 	smbios_info_addinfo_ent_free(hdl, ent);
219 
220 	if (smbios_info_addinfo_ent(hdl, sp.smbstr_id, 2, &ent) != 0) {
221 		warnx("failed to lookup additional entry 2: %s",
222 		    smbios_errmsg(smbios_errno(hdl)));
223 		return (B_FALSE);
224 	}
225 
226 	if (ent->smbai_ref != smbios_addinfo_ent2_hdl) {
227 		warnx("entry 2 mismatch, found unexpected reference handle: "
228 		    "0x%lx", ent->smbai_ref);
229 		ret = B_FALSE;
230 	}
231 	if (ent->smbai_ref_off != smbios_addinfo_ent2_off) {
232 		warnx("entry 2 mismatch, found unexpected reference offset: "
233 		    "0x%x", ent->smbai_ref_off);
234 		ret = B_FALSE;
235 	}
236 	if (ent->smbai_dlen != strlen(smbios_addinfo_ent2_data) + 1) {
237 		warnx("entry 2 mismatch, found unexpected data length: 0x%x",
238 		    ent->smbai_dlen);
239 		ret = B_FALSE;
240 	}
241 	if (memcmp(ent->smbai_data, smbios_addinfo_ent2_data,
242 	    ent->smbai_dlen) != 0) {
243 		warnx("entry 2 mismatch, additional data mismatched");
244 		ret = B_FALSE;
245 	}
246 	smbios_info_addinfo_ent_free(hdl, ent);
247 
248 	return (ret);
249 }
250 
251 /*
252  * Generate a table that's too short to get basic info.
253  */
254 boolean_t
smbios_test_addinfo_mktable_invlen_base(smbios_test_table_t * table)255 smbios_test_addinfo_mktable_invlen_base(smbios_test_table_t *table)
256 {
257 	smb_header_t hdr;
258 
259 	hdr.smbh_type = SMB_TYPE_ADDINFO;
260 	hdr.smbh_len = sizeof (hdr);
261 
262 	(void) smbios_test_table_append(table, &hdr, sizeof (hdr));
263 	smbios_test_table_append_eot(table);
264 
265 	return (B_TRUE);
266 }
267 
268 boolean_t
smbios_test_addinfo_verify_invlen_base(smbios_hdl_t * hdl)269 smbios_test_addinfo_verify_invlen_base(smbios_hdl_t *hdl)
270 {
271 	smbios_struct_t sp;
272 	uint_t nents;
273 
274 	if (smbios_lookup_type(hdl, SMB_TYPE_ADDINFO, &sp) == -1) {
275 		warnx("failed to lookup SMBIOS addinfo: %s",
276 		    smbios_errmsg(smbios_errno(hdl)));
277 		return (B_FALSE);
278 	}
279 
280 	if (smbios_info_addinfo_nents(hdl, sp.smbstr_id, &nents) != -1) {
281 		warnx("accidentally parsed invalid addinfo information as "
282 		    "valid");
283 		return (B_FALSE);
284 	}
285 
286 	if (smbios_errno(hdl) != ESMB_SHORT) {
287 		warnx("encountered wrong error for addinfo, expected: "
288 		    "0x%x, found: 0x%x", ESMB_SHORT, smbios_errno(hdl));
289 		return (B_FALSE);
290 	}
291 
292 	return (B_TRUE);
293 }
294 
295 /*
296  * A table that's long enough to have valid entries, but too short for the first
297  * entry.
298  */
299 boolean_t
smbios_test_addinfo_mktable_invlen_ent(smbios_test_table_t * table)300 smbios_test_addinfo_mktable_invlen_ent(smbios_test_table_t *table)
301 {
302 	smb_addinfo_t add;
303 	smb_addinfo_ent_t ent = { 0 };
304 	size_t entoff = offsetof(smb_addinfo_ent_t, smbaie_rhdl);
305 
306 	add.smbai_hdr.smbh_type = SMB_TYPE_ADDINFO;
307 	add.smbai_hdr.smbh_len = sizeof (add) + entoff;
308 	add.smbai_nents = 1;
309 
310 	(void) smbios_test_table_append(table, &add, sizeof (add));
311 	(void) smbios_test_table_append_raw(table, &ent, entoff);
312 	smbios_test_table_append_eot(table);
313 
314 	return (B_TRUE);
315 }
316 
317 boolean_t
smbios_test_addinfo_verify_invlen_ent(smbios_hdl_t * hdl)318 smbios_test_addinfo_verify_invlen_ent(smbios_hdl_t *hdl)
319 {
320 	smbios_struct_t sp;
321 	smbios_addinfo_ent_t *ent;
322 	boolean_t ret = B_TRUE;
323 
324 	if (!smbios_test_addinfo_verify_base(hdl, &sp, 1)) {
325 		return (B_FALSE);
326 	}
327 
328 	if (smbios_info_addinfo_ent(hdl, sp.smbstr_id, 0, &ent) != -1) {
329 		warnx("incorrectly parsed additional information entry 0: "
330 		    "expected bad length");
331 		ret = B_FALSE;
332 	} else if (smbios_errno(hdl) != ESMB_SHORT) {
333 		warnx("encountered wrong error for addinfo ent, expected: "
334 		    "0x%x, found: 0x%x", ESMB_SHORT, smbios_errno(hdl));
335 		ret = B_FALSE;
336 	}
337 
338 	return (ret);
339 }
340 
341 /*
342  * Make sure even if we parse the first entity correctly, we fail on the second
343  * one being too short.
344  */
345 boolean_t
smbios_test_addinfo_mktable_invlen_multient(smbios_test_table_t * table)346 smbios_test_addinfo_mktable_invlen_multient(smbios_test_table_t *table)
347 {
348 	smb_addinfo_t add;
349 	smb_addinfo_ent_t ent0, ent1 = { 0 };
350 	size_t entoff = offsetof(smb_addinfo_ent_t, smbaie_rhdl);
351 
352 	add.smbai_hdr.smbh_type = SMB_TYPE_ADDINFO;
353 	add.smbai_hdr.smbh_len = sizeof (add);
354 	add.smbai_nents = 2;
355 
356 	ent0.smbaie_len = sizeof (smb_addinfo_ent_t) +
357 	    sizeof (smbios_addinfo_ent0_data);
358 	ent0.smbaie_rhdl = htole16(smbios_addinfo_ent0_hdl);
359 	ent0.smbaie_off = smbios_addinfo_ent0_off;
360 	ent0.smbaie_str = 1;
361 	add.smbai_hdr.smbh_len += ent0.smbaie_len;
362 
363 	ent1.smbaie_len = sizeof (smb_addinfo_ent_t);
364 	add.smbai_hdr.smbh_len += entoff;
365 
366 	(void) smbios_test_table_append(table, &add, sizeof (add));
367 	(void) smbios_test_table_append_raw(table, &ent0, sizeof (ent0));
368 	(void) smbios_test_table_append_raw(table, &smbios_addinfo_ent0_data,
369 	    sizeof (smbios_addinfo_ent0_data));
370 	(void) smbios_test_table_append_raw(table, &ent1, entoff);
371 
372 	smbios_test_table_append_string(table, smbios_addinfo_ent0_str);
373 	smbios_test_table_str_fini(table);
374 	smbios_test_table_append_eot(table);
375 
376 
377 	(void) smbios_test_table_append(table, &add, sizeof (add));
378 	(void) smbios_test_table_append_raw(table, &ent1, entoff);
379 	smbios_test_table_append_eot(table);
380 
381 	return (B_TRUE);
382 }
383 
384 boolean_t
smbios_test_addinfo_verify_invlen_multient(smbios_hdl_t * hdl)385 smbios_test_addinfo_verify_invlen_multient(smbios_hdl_t *hdl)
386 {
387 	smbios_struct_t sp;
388 	smbios_addinfo_ent_t *ent;
389 	boolean_t ret = B_TRUE;
390 
391 	if (!smbios_test_addinfo_verify_base(hdl, &sp, 2)) {
392 		return (B_FALSE);
393 	}
394 
395 	if (smbios_info_addinfo_ent(hdl, sp.smbstr_id, 1, &ent) != -1) {
396 		warnx("incorrectly parsed additional information entry 1: "
397 		    "expected bad length");
398 		ret = B_FALSE;
399 	} else if (smbios_errno(hdl) != ESMB_SHORT) {
400 		warnx("encountered wrong error for addinfo ent, expected: "
401 		    "0x%x, found: 0x%x", ESMB_SHORT, smbios_errno(hdl));
402 		ret = B_FALSE;
403 	}
404 
405 	if (smbios_info_addinfo_ent(hdl, sp.smbstr_id, 0, &ent) != 0) {
406 		warnx("failed to lookup additional entry 0: %s",
407 		    smbios_errmsg(smbios_errno(hdl)));
408 		return (B_FALSE);
409 	}
410 
411 	if (ent->smbai_ref != smbios_addinfo_ent0_hdl) {
412 		warnx("entry 0 mismatch, found unexpected reference handle: "
413 		    "0x%lx", ent->smbai_ref);
414 		ret = B_FALSE;
415 	}
416 	if (ent->smbai_ref_off != smbios_addinfo_ent0_off) {
417 		warnx("entry 0 mismatch, found unexpected reference offset: "
418 		    "0x%x", ent->smbai_ref_off);
419 		ret = B_FALSE;
420 	}
421 	if (ent->smbai_dlen != sizeof (smbios_addinfo_ent0_data)) {
422 		warnx("entry 0 mismatch, found unexpected data length: 0x%x",
423 		    ent->smbai_dlen);
424 		ret = B_FALSE;
425 	}
426 	if (memcmp(ent->smbai_data, &smbios_addinfo_ent0_data,
427 	    ent->smbai_dlen) != 0) {
428 		warnx("entry 0 mismatch, additional data mismatched");
429 		ret = B_FALSE;
430 	}
431 	smbios_info_addinfo_ent_free(hdl, ent);
432 
433 	return (ret);
434 }
435 
436 /*
437  * Make sure we get the case where the length of the entity is longer than the
438  * table.
439  */
440 boolean_t
smbios_test_addinfo_mktable_invlen_entdata(smbios_test_table_t * table)441 smbios_test_addinfo_mktable_invlen_entdata(smbios_test_table_t *table)
442 {
443 	smb_addinfo_t add;
444 	smb_addinfo_ent_t ent;
445 
446 	add.smbai_hdr.smbh_type = SMB_TYPE_ADDINFO;
447 	add.smbai_hdr.smbh_len = sizeof (add) + sizeof (ent);
448 	add.smbai_nents = 1;
449 
450 	(void) memset(&ent, 0, sizeof (ent));
451 	ent.smbaie_len = sizeof (ent) + 3;
452 
453 	(void) smbios_test_table_append(table, &add, sizeof (add));
454 	(void) smbios_test_table_append_raw(table, &ent, sizeof (ent));
455 	smbios_test_table_append_eot(table);
456 
457 	return (B_TRUE);
458 }
459 
460 boolean_t
smbios_test_addinfo_verify_invlen_entdata(smbios_hdl_t * hdl)461 smbios_test_addinfo_verify_invlen_entdata(smbios_hdl_t *hdl)
462 {
463 	smbios_struct_t sp;
464 	smbios_addinfo_ent_t *ent;
465 	boolean_t ret = B_TRUE;
466 
467 	if (!smbios_test_addinfo_verify_base(hdl, &sp, 1)) {
468 		return (B_FALSE);
469 	}
470 
471 	if (smbios_info_addinfo_ent(hdl, sp.smbstr_id, 0, &ent) != -1) {
472 		warnx("incorrectly parsed additional information entry 0: "
473 		    "expected bad length");
474 		ret = B_FALSE;
475 	} else if (smbios_errno(hdl) != ESMB_CORRUPT) {
476 		warnx("encountered wrong error for addinfo ent, expected: "
477 		    "0x%x, found: 0x%x", ESMB_CORRUPT, smbios_errno(hdl));
478 		ret = B_FALSE;
479 	}
480 
481 	return (ret);
482 }
483