xref: /illumos-gate/usr/src/test/util-tests/tests/smbios/smbios.c (revision 89fbfe0d2fbdaef52447ae1ca77634c69a3cf220)
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 (c) 2018, Joyent, Inc.
14  * Copyright 2021 Oxide Computer Company
15  */
16 
17 /*
18  * Primordial SMBIOS test suite. At the moment, the purpose of this is just to
19  * test the recent SMBIOS 3.2 additions specific to the variable length slots.
20  * This should be evolved into a much fuller test suite.
21  */
22 
23 #include <umem.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <unistd.h>
29 #include "smbios_test.h"
30 
31 static int test_dirfd = -1;
32 
33 const char *
34 _umem_debug_init(void)
35 {
36 	return ("default,verbose");
37 }
38 
39 const char *
40 _umem_logging_init(void)
41 {
42 	return ("fail,contents");
43 }
44 
45 smbios_test_table_t *
46 smbios_test_table_init(smbios_entry_point_t type, uint_t version)
47 {
48 	smbios_test_table_t *table;
49 
50 	if (type != SMBIOS_ENTRY_POINT_30) {
51 		abort();
52 	}
53 
54 	table = umem_zalloc(sizeof (smbios_test_table_t), UMEM_DEFAULT);
55 	if (table == NULL) {
56 		return (NULL);
57 	}
58 
59 	table->stt_data = umem_zalloc(SMBIOS_TEST_ALLOC_SIZE, UMEM_DEFAULT);
60 	if (table->stt_data == NULL) {
61 		umem_free(table, sizeof (smbios_test_table_t));
62 		return (NULL);
63 	}
64 	table->stt_buflen = SMBIOS_TEST_ALLOC_SIZE;
65 	table->stt_type = type;
66 	table->stt_version = version;
67 	table->stt_nextid = 1;
68 
69 	return (table);
70 }
71 
72 static void *
73 smbios_test_table_append_common(smbios_test_table_t *table, const void *buf,
74     size_t len)
75 {
76 	void *start;
77 
78 	if (SIZE_MAX - table->stt_offset < len)
79 		abort();
80 
81 	if (len + table->stt_offset >= table->stt_buflen) {
82 		void *newbuf;
83 		size_t newlen = table->stt_buflen + SMBIOS_TEST_ALLOC_SIZE;
84 
85 		while (len + table->stt_offset >= newlen) {
86 			newlen += SMBIOS_TEST_ALLOC_SIZE;
87 		}
88 
89 		newbuf = umem_zalloc(newlen, UMEM_DEFAULT);
90 		if (newbuf == NULL) {
91 			err(EXIT_FAILURE, "failed to umem_zalloc for %lu bytes",
92 			    newlen);
93 		}
94 
95 		(void) memcpy(newbuf, table->stt_data, table->stt_buflen);
96 		umem_free(table->stt_data, table->stt_buflen);
97 		table->stt_data = newbuf;
98 		table->stt_buflen = newlen;
99 	}
100 
101 	start = (void *)((uintptr_t)table->stt_data + table->stt_offset);
102 	(void) memcpy(start, buf, len);
103 	table->stt_offset += len;
104 
105 	return (start);
106 }
107 
108 void
109 smbios_test_table_append_raw(smbios_test_table_t *table, const void *buf,
110     size_t len)
111 {
112 	(void) smbios_test_table_append_common(table, buf, len);
113 }
114 
115 void
116 smbios_test_table_append_string(smbios_test_table_t *table, const char *str)
117 {
118 	size_t len = strlen(str) + 1;
119 	(void) smbios_test_table_append_common(table, str, len);
120 }
121 
122 void
123 smbios_test_table_str_fini(smbios_test_table_t *table)
124 {
125 	const uint8_t endstring = 0;
126 
127 	smbios_test_table_append_raw(table, &endstring, sizeof (endstring));
128 }
129 
130 uint16_t
131 smbios_test_table_append(smbios_test_table_t *table, const void *buf,
132     size_t len)
133 {
134 	smb_header_t *hdr;
135 	uint16_t id;
136 
137 	hdr = smbios_test_table_append_common(table, buf, len);
138 	table->stt_nents++;
139 
140 	id = table->stt_nextid;
141 	hdr->smbh_hdl = htole16(table->stt_nextid);
142 	table->stt_nextid++;
143 
144 	return (id);
145 }
146 
147 void
148 smbios_test_table_append_eot(smbios_test_table_t *table)
149 {
150 	smb_header_t eot;
151 	uint8_t endstring = 0;
152 
153 	bzero(&eot, sizeof (eot));
154 	eot.smbh_type = SMB_TYPE_EOT;
155 	eot.smbh_len = 4;
156 	(void) smbios_test_table_append(table, &eot, sizeof (eot));
157 	(void) smbios_test_table_append_raw(table, &endstring,
158 	    sizeof (endstring));
159 	smbios_test_table_append_raw(table, &endstring,
160 	    sizeof (endstring));
161 	smbios_test_table_append_raw(table, &endstring,
162 	    sizeof (endstring));
163 
164 }
165 
166 static uint8_t
167 smbios_test_table_checksum(const uint8_t *buf, size_t len)
168 {
169 	uint8_t sum;
170 	size_t i;
171 
172 	for (i = 0, sum = 0; i < len; i++) {
173 		sum += buf[i];
174 	}
175 
176 	if (sum == 0)
177 		return (0);
178 
179 	return ((uint8_t)(0x100 - sum));
180 }
181 
182 static void
183 smbios_test_table_snapshot(smbios_test_table_t *table, smbios_entry_t **entryp,
184     void **bufp, size_t *lenp)
185 {
186 	smbios_30_entry_t *ent30;
187 
188 	switch (table->stt_type) {
189 	case SMBIOS_ENTRY_POINT_30:
190 		ent30 = &table->stt_entry.ep30;
191 
192 		(void) memcpy(ent30->smbe_eanchor, SMB3_ENTRY_EANCHOR,
193 		    sizeof (ent30->smbe_eanchor));
194 		ent30->smbe_ecksum = 0;
195 		ent30->smbe_elen = sizeof (*ent30);
196 		ent30->smbe_major = (table->stt_version >> 8) & 0xff;
197 		ent30->smbe_minor = table->stt_version & 0xff;
198 		ent30->smbe_docrev = 0;
199 		ent30->smbe_revision = 1;
200 		ent30->smbe_reserved = 0;
201 		ent30->smbe_stlen = htole32(table->stt_offset);
202 		ent30->smbe_staddr = htole64(P2ROUNDUP(sizeof (*ent30), 16));
203 
204 		ent30->smbe_ecksum = smbios_test_table_checksum((void *)ent30,
205 		    sizeof (*ent30));
206 		break;
207 	default:
208 		abort();
209 	}
210 
211 	*entryp = &table->stt_entry;
212 	*bufp = table->stt_data;
213 	*lenp = table->stt_offset;
214 }
215 
216 static void
217 smbios_test_table_fini(smbios_test_table_t *table)
218 {
219 	if (table == NULL) {
220 		return;
221 	}
222 
223 	if (table->stt_data != NULL) {
224 		umem_free(table->stt_data, table->stt_buflen);
225 	}
226 
227 	umem_free(table, sizeof (smbios_test_table_t));
228 }
229 
230 static const smbios_test_t smbios_tests[] = {
231 	{
232 	    .st_entry = SMBIOS_ENTRY_POINT_30,
233 	    .st_tvers = SMB_VERSION,
234 	    .st_libvers = 0xffff,
235 	    .st_mktable = smbios_test_badvers_mktable,
236 	    .st_desc = "bad library version"
237 	}, {
238 	    .st_entry = SMBIOS_ENTRY_POINT_30,
239 	    .st_tvers = SMB_VERSION,
240 	    .st_libvers = 0,
241 	    .st_mktable = smbios_test_badvers_mktable,
242 	    .st_desc = "bad library version (zeros)"
243 	}, {
244 	    .st_entry = SMBIOS_ENTRY_POINT_30,
245 	    .st_tvers = SMB_VERSION,
246 	    .st_libvers = SMB_VERSION,
247 	    .st_mktable = smbios_test_slot_mktable,
248 	    .st_canopen = B_TRUE,
249 	    .st_verify = smbios_test_verify_badids,
250 	    .st_desc = "smbios_info_* with bad id"
251 	}, {
252 	    .st_entry = SMBIOS_ENTRY_POINT_30,
253 	    .st_tvers = SMB_VERSION,
254 	    .st_libvers = SMB_VERSION,
255 	    .st_mktable = smbios_test_slot_mktable,
256 	    .st_canopen = B_TRUE,
257 	    .st_verify = smbios_test_verify_strings,
258 	    .st_desc = "smbios string functions"
259 	}, {
260 	    .st_entry = SMBIOS_ENTRY_POINT_30,
261 	    .st_tvers = SMB_VERSION_32,
262 	    .st_libvers = SMB_VERSION,
263 	    .st_mktable = smbios_test_slot_mktable,
264 	    .st_canopen = B_TRUE,
265 	    .st_verify = smbios_test_slot_verify,
266 	    .st_desc = "slot 3.2"
267 	}, {
268 	    .st_entry = SMBIOS_ENTRY_POINT_30,
269 	    .st_tvers = SMB_VERSION_34,
270 	    .st_libvers = SMB_VERSION,
271 	    .st_mktable = smbios_test_slot_mktable_34_nopeers,
272 	    .st_canopen = B_TRUE,
273 	    .st_verify = smbios_test_slot_verify_34_nopeers,
274 	    .st_desc = "slot 3.4 without peers"
275 	}, {
276 	    .st_entry = SMBIOS_ENTRY_POINT_30,
277 	    .st_tvers = SMB_VERSION_34,
278 	    .st_libvers = SMB_VERSION,
279 	    .st_mktable = smbios_test_slot_mktable_34_peers,
280 	    .st_canopen = B_TRUE,
281 	    .st_verify = smbios_test_slot_verify_34_peers,
282 	    .st_desc = "slot 3.4 with peers"
283 	}, {
284 	    .st_entry = SMBIOS_ENTRY_POINT_30,
285 	    .st_tvers = SMB_VERSION_35,
286 	    .st_libvers = SMB_VERSION_34,
287 	    .st_mktable = smbios_test_slot_mktable_35,
288 	    .st_canopen = B_TRUE,
289 	    .st_verify = smbios_test_slot_verify_34_overrun,
290 	    .st_desc = "slot 3.5 against 3.4 lib"
291 	}, {
292 	    .st_entry = SMBIOS_ENTRY_POINT_30,
293 	    .st_tvers = SMB_VERSION_35,
294 	    .st_libvers = SMB_VERSION,
295 	    .st_mktable = smbios_test_slot_mktable_35,
296 	    .st_canopen = B_TRUE,
297 	    .st_verify = smbios_test_slot_verify_35,
298 	    .st_desc = "slot 3.5"
299 	}, {
300 	    .st_entry = SMBIOS_ENTRY_POINT_30,
301 	    .st_tvers = SMB_VERSION_32,
302 	    .st_libvers = SMB_VERSION_32,
303 	    .st_mktable = smbios_test_memdevice_mktable_32,
304 	    .st_canopen = B_TRUE,
305 	    .st_verify = smbios_test_memdevice_verify_32,
306 	    .st_desc = "memory device 3.2 % 3.2"
307 	}, {
308 	    .st_entry = SMBIOS_ENTRY_POINT_30,
309 	    .st_tvers = SMB_VERSION_32,
310 	    .st_libvers = SMB_VERSION_33,
311 	    .st_mktable = smbios_test_memdevice_mktable_32,
312 	    .st_canopen = B_TRUE,
313 	    .st_verify = smbios_test_memdevice_verify_32_33,
314 	    .st_desc = "memory device 3.2 % 3.3"
315 	}, {
316 	    .st_entry = SMBIOS_ENTRY_POINT_30,
317 	    .st_tvers = SMB_VERSION_33,
318 	    .st_libvers = SMB_VERSION_33,
319 	    .st_mktable = smbios_test_memdevice_mktable_33,
320 	    .st_canopen = B_TRUE,
321 	    .st_verify = smbios_test_memdevice_verify_33,
322 	    .st_desc = "memory device 3.3"
323 	}, {
324 	    .st_entry = SMBIOS_ENTRY_POINT_30,
325 	    .st_tvers = SMB_VERSION_33,
326 	    .st_libvers = SMB_VERSION_33,
327 	    .st_mktable = smbios_test_memdevice_mktable_33ext,
328 	    .st_canopen = B_TRUE,
329 	    .st_verify = smbios_test_memdevice_verify_33ext,
330 	    .st_desc = "memory device 3.3"
331 	}, {
332 	    .st_entry = SMBIOS_ENTRY_POINT_30,
333 	    .st_tvers = SMB_VERSION,
334 	    .st_libvers = SMB_VERSION,
335 	    .st_mktable = smbios_test_pinfo_mktable_amd64,
336 	    .st_canopen = B_TRUE,
337 	    .st_verify = smbios_test_pinfo_verify_amd64,
338 	    .st_desc = "processor additional information - amd64"
339 	}, {
340 	    .st_entry = SMBIOS_ENTRY_POINT_30,
341 	    .st_tvers = SMB_VERSION,
342 	    .st_libvers = SMB_VERSION,
343 	    .st_mktable = smbios_test_pinfo_mktable_riscv,
344 	    .st_canopen = B_TRUE,
345 	    .st_verify = smbios_test_pinfo_verify_riscv,
346 	    .st_desc = "processor additional information - riscv"
347 	}, {
348 	    .st_entry = SMBIOS_ENTRY_POINT_30,
349 	    .st_tvers = SMB_VERSION,
350 	    .st_libvers = SMB_VERSION,
351 	    .st_mktable = smbios_test_pinfo_mktable_invlen1,
352 	    .st_canopen = B_TRUE,
353 	    .st_verify = smbios_test_pinfo_verify_invlen1,
354 	    .st_desc = "processor additional information - bad table length 1"
355 	}, {
356 	    .st_entry = SMBIOS_ENTRY_POINT_30,
357 	    .st_tvers = SMB_VERSION,
358 	    .st_libvers = SMB_VERSION,
359 	    .st_mktable = smbios_test_pinfo_mktable_invlen2,
360 	    .st_canopen = B_TRUE,
361 	    .st_verify = smbios_test_pinfo_verify_invlen2,
362 	    .st_desc = "processor additional information - bad table length 2"
363 	}, {
364 	    .st_entry = SMBIOS_ENTRY_POINT_30,
365 	    .st_tvers = SMB_VERSION,
366 	    .st_libvers = SMB_VERSION,
367 	    .st_mktable = smbios_test_pinfo_mktable_invlen3,
368 	    .st_canopen = B_TRUE,
369 	    .st_verify = smbios_test_pinfo_verify_invlen3,
370 	    .st_desc = "processor additional information - bad table length 3"
371 	}, {
372 	    .st_entry = SMBIOS_ENTRY_POINT_30,
373 	    .st_tvers = SMB_VERSION,
374 	    .st_libvers = SMB_VERSION,
375 	    .st_mktable = smbios_test_pinfo_mktable_invlen4,
376 	    .st_canopen = B_TRUE,
377 	    .st_verify = smbios_test_pinfo_verify_invlen4,
378 	    .st_desc = "processor additional information - bad table length 4"
379 	}, {
380 	    .st_entry = SMBIOS_ENTRY_POINT_30,
381 	    .st_tvers = SMB_VERSION,
382 	    .st_libvers = SMB_VERSION,
383 	    .st_mktable = smbios_test_memdevice_mktable_32,
384 	    .st_canopen = B_TRUE,
385 	    .st_verify = smbios_test_pinfo_verify_badtype,
386 	    .st_desc = "processor additional information - bad type"
387 	}, {
388 	    .st_entry = SMBIOS_ENTRY_POINT_30,
389 	    .st_tvers = SMB_VERSION,
390 	    .st_libvers = SMB_VERSION,
391 	    .st_mktable = smbios_test_strprop_mktable_invlen1,
392 	    .st_canopen = B_TRUE,
393 	    .st_verify = smbios_test_strprop_verify_invlen1,
394 	    .st_desc = "string property - bad table length 1"
395 	}, {
396 	    .st_entry = SMBIOS_ENTRY_POINT_30,
397 	    .st_tvers = SMB_VERSION,
398 	    .st_libvers = SMB_VERSION,
399 	    .st_mktable = smbios_test_strprop_mktable_invlen2,
400 	    .st_canopen = B_TRUE,
401 	    .st_verify = smbios_test_strprop_verify_invlen2,
402 	    .st_desc = "string property - bad table length 2"
403 	}, {
404 	    .st_entry = SMBIOS_ENTRY_POINT_30,
405 	    .st_tvers = SMB_VERSION,
406 	    .st_libvers = SMB_VERSION,
407 	    .st_mktable = smbios_test_memdevice_mktable_32,
408 	    .st_canopen = B_TRUE,
409 	    .st_verify = smbios_test_strprop_verify_badtype,
410 	    .st_desc = "string property - bad type"
411 	}, {
412 	    .st_entry = SMBIOS_ENTRY_POINT_30,
413 	    .st_tvers = SMB_VERSION,
414 	    .st_libvers = SMB_VERSION,
415 	    .st_mktable = smbios_test_strprop_mktable_basic,
416 	    .st_canopen = B_TRUE,
417 	    .st_verify = smbios_test_strprop_verify_basic,
418 	    .st_desc = "string property - basic"
419 	}, {
420 	    .st_entry = SMBIOS_ENTRY_POINT_30,
421 	    .st_tvers = SMB_VERSION,
422 	    .st_libvers = SMB_VERSION,
423 	    .st_mktable = smbios_test_strprop_mktable_badstr,
424 	    .st_canopen = B_TRUE,
425 	    .st_verify = smbios_test_strprop_verify_badstr,
426 	    .st_desc = "string property - bad string"
427 	}, {
428 	    .st_entry = SMBIOS_ENTRY_POINT_30,
429 	    .st_tvers = SMB_VERSION,
430 	    .st_libvers = SMB_VERSION,
431 	    .st_mktable = smbios_test_fwinfo_mktable_invlen_base,
432 	    .st_canopen = B_TRUE,
433 	    .st_verify = smbios_test_fwinfo_verify_invlen_base,
434 	    .st_desc = "firmware inventory - bad base length"
435 	}, {
436 	    .st_entry = SMBIOS_ENTRY_POINT_30,
437 	    .st_tvers = SMB_VERSION,
438 	    .st_libvers = SMB_VERSION,
439 	    .st_mktable = smbios_test_fwinfo_mktable_invlen_comps,
440 	    .st_canopen = B_TRUE,
441 	    .st_verify = smbios_test_fwinfo_verify_invlen_comps,
442 	    .st_desc = "firmware inventory - bad comp length"
443 	}, {
444 	    .st_entry = SMBIOS_ENTRY_POINT_30,
445 	    .st_tvers = SMB_VERSION,
446 	    .st_libvers = SMB_VERSION,
447 	    .st_mktable = smbios_test_memdevice_mktable_32,
448 	    .st_canopen = B_TRUE,
449 	    .st_verify = smbios_test_fwinfo_verify_badtype,
450 	    .st_desc = "firmware inventory - bad type"
451 	}, {
452 	    .st_entry = SMBIOS_ENTRY_POINT_30,
453 	    .st_tvers = SMB_VERSION,
454 	    .st_libvers = SMB_VERSION,
455 	    .st_mktable = smbios_test_fwinfo_mktable_nocomps,
456 	    .st_canopen = B_TRUE,
457 	    .st_verify = smbios_test_fwinfo_verify_nocomps,
458 	    .st_desc = "firmware inventory - no components"
459 	}, {
460 	    .st_entry = SMBIOS_ENTRY_POINT_30,
461 	    .st_tvers = SMB_VERSION,
462 	    .st_libvers = SMB_VERSION,
463 	    .st_mktable = smbios_test_fwinfo_mktable_comps,
464 	    .st_canopen = B_TRUE,
465 	    .st_verify = smbios_test_fwinfo_verify_comps,
466 	    .st_desc = "firmware inventory - components"
467 	}, {
468 	    .st_entry = SMBIOS_ENTRY_POINT_30,
469 	    .st_tvers = SMB_VERSION_24,
470 	    .st_libvers = SMB_VERSION,
471 	    .st_mktable = smbios_test_chassis_mktable_invlen_base,
472 	    .st_canopen = B_TRUE,
473 	    .st_verify = smbios_test_chassis_verify_invlen,
474 	    .st_desc = "chassis - bad length (2.4 table)"
475 	}, {
476 	    .st_entry = SMBIOS_ENTRY_POINT_30,
477 	    .st_tvers = SMB_VERSION,
478 	    .st_libvers = SMB_VERSION,
479 	    .st_mktable = smbios_test_chassis_mktable_invlen_base,
480 	    .st_canopen = B_TRUE,
481 	    .st_verify = smbios_test_chassis_verify_invlen,
482 	    .st_desc = "chassis - bad length (latest version)"
483 	}, {
484 	    .st_entry = SMBIOS_ENTRY_POINT_30,
485 	    .st_tvers = SMB_VERSION,
486 	    .st_libvers = SMB_VERSION,
487 	    .st_mktable = smbios_test_chassis_mktable_base,
488 	    .st_canopen = B_TRUE,
489 	    .st_verify = smbios_test_chassis_verify_invlen,
490 	    .st_desc = "chassis - bad length, expect sku"
491 	}, {
492 	    .st_entry = SMBIOS_ENTRY_POINT_30,
493 	    .st_tvers = SMB_VERSION_24,
494 	    .st_libvers = SMB_VERSION,
495 	    .st_mktable = smbios_test_chassis_mktable_base,
496 	    .st_canopen = B_TRUE,
497 	    .st_verify = smbios_test_chassis_verify_base,
498 	    .st_desc = "chassis - basic 2.4 version"
499 	}, {
500 	    .st_entry = SMBIOS_ENTRY_POINT_30,
501 	    .st_tvers = SMB_VERSION,
502 	    .st_libvers = SMB_VERSION,
503 	    .st_mktable = smbios_test_chassis_mktable_sku_nocomps,
504 	    .st_canopen = B_TRUE,
505 	    .st_verify = smbios_test_chassis_verify_sku_nocomps,
506 	    .st_desc = "chassis - sku, but no components"
507 	}, {
508 	    .st_entry = SMBIOS_ENTRY_POINT_30,
509 	    .st_tvers = SMB_VERSION_24,
510 	    .st_libvers = SMB_VERSION,
511 	    .st_mktable = smbios_test_chassis_mktable_comps,
512 	    .st_canopen = B_TRUE,
513 	    .st_verify = smbios_test_chassis_verify_comps,
514 	    .st_desc = "chassis - 2.4 version with comps"
515 	}, {
516 	    .st_entry = SMBIOS_ENTRY_POINT_30,
517 	    .st_tvers = SMB_VERSION,
518 	    .st_libvers = SMB_VERSION,
519 	    .st_mktable = smbios_test_chassis_mktable_sku_nocomps,
520 	    .st_canopen = B_TRUE,
521 	    .st_verify = smbios_test_chassis_verify_sku_nocomps,
522 	    .st_desc = "chassis - sku + comps"
523 	}
524 };
525 
526 static boolean_t
527 smbios_test_run_one(const smbios_test_t *test)
528 {
529 	smbios_test_table_t *table = NULL;
530 	smbios_hdl_t *hdl = NULL;
531 	void *buf;
532 	size_t len;
533 	smbios_entry_t *entry;
534 	int err = 0;
535 	boolean_t ret = B_FALSE;
536 
537 	table = smbios_test_table_init(test->st_entry, test->st_tvers);
538 	if (!test->st_mktable(table)) {
539 		goto out;
540 	}
541 
542 	smbios_test_table_snapshot(table, &entry, &buf, &len);
543 	hdl = smbios_bufopen(entry, buf, len, test->st_libvers, SMB_FL_DEBUG,
544 	    &err);
545 	if (test->st_canopen) {
546 		if (hdl == NULL) {
547 			warnx("failed to create table for test %s: %s",
548 			    test->st_desc, smbios_errmsg(err));
549 			goto out;
550 		}
551 	} else {
552 		if (hdl != NULL) {
553 			warnx("accidentally created table for test %s, "
554 			    "expected failure", test->st_desc);
555 		} else {
556 			ret = B_TRUE;
557 		}
558 		goto out;
559 	}
560 
561 	if (test->st_verify(hdl)) {
562 		ret = B_TRUE;
563 	}
564 
565 	if (hdl != NULL && test_dirfd > -1) {
566 		int fd;
567 		char fname[PATH_MAX];
568 
569 		(void) snprintf(fname, sizeof (fname), "%s.smbios",
570 		    test->st_desc);
571 		fd = openat(test_dirfd, fname, O_RDWR | O_CREAT, 0644);
572 		if (fd < 0) {
573 			warn("failed to dump test %s, failed to open output "
574 			    "file", test->st_desc);
575 		} else {
576 			if (smbios_write(hdl, fd) != 0) {
577 				warnx("failed to dump test %s: %s",
578 				    test->st_desc,
579 				    smbios_errmsg(smbios_errno(hdl)));
580 			} else {
581 				(void) close(fd);
582 			}
583 		}
584 	}
585 out:
586 	if (hdl != NULL) {
587 		smbios_close(hdl);
588 	}
589 
590 	if (table != NULL) {
591 		smbios_test_table_fini(table);
592 	}
593 
594 	if (ret) {
595 		(void) printf("TEST PASSED: %s\n", test->st_desc);
596 	} else {
597 		(void) printf("TEST FAILED: %s\n", test->st_desc);
598 	}
599 
600 	return (ret);
601 }
602 
603 int
604 main(int argc, char *argv[])
605 {
606 	int ret = 0, c;
607 	size_t i;
608 	const char *outdir = NULL;
609 
610 	while ((c = getopt(argc, argv, ":d:")) != -1) {
611 		switch (c) {
612 		case 'd':
613 			outdir = optarg;
614 			break;
615 		case '?':
616 			errx(EXIT_FAILURE, "unknown option: -%c", optopt);
617 		case ':':
618 			errx(EXIT_FAILURE, "-%c requires an operand", optopt);
619 		}
620 	}
621 
622 	if (outdir != NULL) {
623 		if ((test_dirfd = open(outdir, O_RDONLY)) < 0) {
624 			err(EXIT_FAILURE, "failed to open %s", outdir);
625 		}
626 	}
627 
628 	for (i = 0; i < ARRAY_SIZE(smbios_tests); i++) {
629 		if (!smbios_test_run_one(&smbios_tests[i])) {
630 			ret = 1;
631 		}
632 	}
633 
634 	if (ret == 0) {
635 		(void) printf("All tests passed successfully\n");
636 	}
637 
638 	return (ret);
639 }
640