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 2020 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 "smbios_test.h"
25 
26 const char *
_umem_debug_init(void)27 _umem_debug_init(void)
28 {
29 	return ("default,verbose");
30 }
31 
32 const char *
_umem_logging_init(void)33 _umem_logging_init(void)
34 {
35 	return ("fail,contents");
36 }
37 
38 smbios_test_table_t *
smbios_test_table_init(smbios_entry_point_t type,uint_t version)39 smbios_test_table_init(smbios_entry_point_t type, uint_t version)
40 {
41 	smbios_test_table_t *table;
42 
43 	if (type != SMBIOS_ENTRY_POINT_30) {
44 		abort();
45 	}
46 
47 	table = umem_zalloc(sizeof (smbios_test_table_t), UMEM_DEFAULT);
48 	if (table == NULL) {
49 		return (NULL);
50 	}
51 
52 	table->stt_data = umem_zalloc(SMBIOS_TEST_ALLOC_SIZE, UMEM_DEFAULT);
53 	if (table->stt_data == NULL) {
54 		umem_free(table, sizeof (smbios_test_table_t));
55 		return (NULL);
56 	}
57 	table->stt_buflen = SMBIOS_TEST_ALLOC_SIZE;
58 	table->stt_type = type;
59 	table->stt_version = version;
60 	table->stt_nextid = 1;
61 
62 	return (table);
63 }
64 
65 static void *
smbios_test_table_append_common(smbios_test_table_t * table,const void * buf,size_t len)66 smbios_test_table_append_common(smbios_test_table_t *table, const void *buf,
67     size_t len)
68 {
69 	void *start;
70 
71 	if (SIZE_MAX - table->stt_offset < len)
72 		abort();
73 
74 	if (len + table->stt_offset >= table->stt_buflen) {
75 		void *newbuf;
76 		size_t newlen = table->stt_buflen + SMBIOS_TEST_ALLOC_SIZE;
77 
78 		while (len + table->stt_offset >= newlen) {
79 			newlen += SMBIOS_TEST_ALLOC_SIZE;
80 		}
81 
82 		newbuf = umem_zalloc(newlen, UMEM_DEFAULT);
83 		if (newbuf == NULL) {
84 			err(EXIT_FAILURE, "failed to umem_zalloc for %lu bytes",
85 			    newlen);
86 		}
87 
88 		(void) memcpy(newbuf, table->stt_data, table->stt_buflen);
89 		umem_free(table->stt_data, table->stt_buflen);
90 		table->stt_data = newbuf;
91 		table->stt_buflen = newlen;
92 	}
93 
94 	start = (void *)((uintptr_t)table->stt_data + table->stt_offset);
95 	(void) memcpy(start, buf, len);
96 	table->stt_offset += len;
97 
98 	return (start);
99 }
100 
101 void
smbios_test_table_append_raw(smbios_test_table_t * table,const void * buf,size_t len)102 smbios_test_table_append_raw(smbios_test_table_t *table, const void *buf,
103     size_t len)
104 {
105 	(void) smbios_test_table_append_common(table, buf, len);
106 }
107 
108 void
smbios_test_table_append_string(smbios_test_table_t * table,const char * str)109 smbios_test_table_append_string(smbios_test_table_t *table, const char *str)
110 {
111 	size_t len = strlen(str) + 1;
112 	(void) smbios_test_table_append_common(table, str, len);
113 }
114 
115 uint16_t
smbios_test_table_append(smbios_test_table_t * table,const void * buf,size_t len)116 smbios_test_table_append(smbios_test_table_t *table, const void *buf,
117     size_t len)
118 {
119 	smb_header_t *hdr;
120 	uint16_t id;
121 
122 	hdr = smbios_test_table_append_common(table, buf, len);
123 	table->stt_nents++;
124 
125 	id = table->stt_nextid;
126 	hdr->smbh_hdl = htole16(table->stt_nextid);
127 	table->stt_nextid++;
128 
129 	return (id);
130 }
131 
132 void
smbios_test_table_append_eot(smbios_test_table_t * table)133 smbios_test_table_append_eot(smbios_test_table_t *table)
134 {
135 	smb_header_t eot;
136 	uint8_t endstring = 0;
137 
138 	bzero(&eot, sizeof (eot));
139 	eot.smbh_type = SMB_TYPE_EOT;
140 	eot.smbh_len = 4;
141 	(void) smbios_test_table_append(table, &eot, sizeof (eot));
142 	(void) smbios_test_table_append_raw(table, &endstring,
143 	    sizeof (endstring));
144 	smbios_test_table_append_raw(table, &endstring,
145 	    sizeof (endstring));
146 	smbios_test_table_append_raw(table, &endstring,
147 	    sizeof (endstring));
148 
149 }
150 
151 static uint8_t
smbios_test_table_checksum(const uint8_t * buf,size_t len)152 smbios_test_table_checksum(const uint8_t *buf, size_t len)
153 {
154 	uint8_t sum;
155 	size_t i;
156 
157 	for (i = 0, sum = 0; i < len; i++) {
158 		sum += buf[i];
159 	}
160 
161 	if (sum == 0)
162 		return (0);
163 
164 	return ((uint8_t)(0x100 - sum));
165 }
166 
167 static void
smbios_test_table_snapshot(smbios_test_table_t * table,smbios_entry_t ** entryp,void ** bufp,size_t * lenp)168 smbios_test_table_snapshot(smbios_test_table_t *table, smbios_entry_t **entryp,
169     void **bufp, size_t *lenp)
170 {
171 	smbios_30_entry_t *ent30;
172 
173 	switch (table->stt_type) {
174 	case SMBIOS_ENTRY_POINT_30:
175 		ent30 = &table->stt_entry.ep30;
176 
177 		(void) memcpy(ent30->smbe_eanchor, SMB3_ENTRY_EANCHOR,
178 		    sizeof (ent30->smbe_eanchor));
179 		ent30->smbe_ecksum = 0;
180 		ent30->smbe_elen = sizeof (*ent30);
181 		ent30->smbe_major = (table->stt_version >> 8) & 0xff;
182 		ent30->smbe_minor = table->stt_version & 0xff;
183 		ent30->smbe_docrev = 0;
184 		ent30->smbe_revision = 1;
185 		ent30->smbe_reserved = 0;
186 		ent30->smbe_stlen = htole32(table->stt_offset);
187 		ent30->smbe_staddr = htole64(P2ROUNDUP(sizeof (*ent30), 16));
188 
189 		ent30->smbe_ecksum = smbios_test_table_checksum((void *)ent30,
190 		    sizeof (*ent30));
191 		break;
192 	default:
193 		abort();
194 	}
195 
196 	*entryp = &table->stt_entry;
197 	*bufp = table->stt_data;
198 	*lenp = table->stt_offset;
199 }
200 
201 static void
smbios_test_table_fini(smbios_test_table_t * table)202 smbios_test_table_fini(smbios_test_table_t *table)
203 {
204 	if (table == NULL) {
205 		return;
206 	}
207 
208 	if (table->stt_data != NULL) {
209 		umem_free(table->stt_data, table->stt_buflen);
210 	}
211 
212 	umem_free(table, sizeof (smbios_test_table_t));
213 }
214 
215 static const smbios_test_t smbios_tests[] = {
216 	{
217 	    .st_entry = SMBIOS_ENTRY_POINT_30,
218 	    .st_tvers = SMB_VERSION_32,
219 	    .st_libvers = SMB_VERSION,
220 	    .st_mktable = smbios_test_slot_mktable,
221 	    .st_canopen = B_TRUE,
222 	    .st_verify = smbios_test_slot_verify,
223 	    .st_desc = "slot 3.2"
224 	}, {
225 	    .st_entry = SMBIOS_ENTRY_POINT_30,
226 	    .st_tvers = SMB_VERSION_34,
227 	    .st_libvers = SMB_VERSION,
228 	    .st_mktable = smbios_test_slot_mktable_34_nopeers,
229 	    .st_canopen = B_TRUE,
230 	    .st_verify = smbios_test_slot_verify_34_nopeers,
231 	    .st_desc = "slot 3.4 without peers"
232 	}, {
233 	    .st_entry = SMBIOS_ENTRY_POINT_30,
234 	    .st_tvers = SMB_VERSION_34,
235 	    .st_libvers = SMB_VERSION,
236 	    .st_mktable = smbios_test_slot_mktable_34_peers,
237 	    .st_canopen = B_TRUE,
238 	    .st_verify = smbios_test_slot_verify_34_peers,
239 	    .st_desc = "slot 3.4 with peers"
240 	}, {
241 	    .st_entry = SMBIOS_ENTRY_POINT_30,
242 	    .st_tvers = SMB_VERSION,
243 	    .st_libvers = 0xffff,
244 	    .st_mktable = smbios_test_badvers_mktable,
245 	    .st_desc = "bad library version"
246 	}, {
247 	    .st_entry = SMBIOS_ENTRY_POINT_30,
248 	    .st_tvers = SMB_VERSION_32,
249 	    .st_libvers = SMB_VERSION_32,
250 	    .st_mktable = smbios_test_memdevice_mktable_32,
251 	    .st_canopen = B_TRUE,
252 	    .st_verify = smbios_test_memdevice_verify_32,
253 	    .st_desc = "memory device 3.2 / 3.2"
254 	}, {
255 	    .st_entry = SMBIOS_ENTRY_POINT_30,
256 	    .st_tvers = SMB_VERSION_32,
257 	    .st_libvers = SMB_VERSION_33,
258 	    .st_mktable = smbios_test_memdevice_mktable_32,
259 	    .st_canopen = B_TRUE,
260 	    .st_verify = smbios_test_memdevice_verify_32_33,
261 	    .st_desc = "memory device 3.2 / 3.3"
262 	}, {
263 	    .st_entry = SMBIOS_ENTRY_POINT_30,
264 	    .st_tvers = SMB_VERSION_33,
265 	    .st_libvers = SMB_VERSION_33,
266 	    .st_mktable = smbios_test_memdevice_mktable_33,
267 	    .st_canopen = B_TRUE,
268 	    .st_verify = smbios_test_memdevice_verify_33,
269 	    .st_desc = "memory device 3.3"
270 	}, {
271 	    .st_entry = SMBIOS_ENTRY_POINT_30,
272 	    .st_tvers = SMB_VERSION_33,
273 	    .st_libvers = SMB_VERSION_33,
274 	    .st_mktable = smbios_test_memdevice_mktable_33ext,
275 	    .st_canopen = B_TRUE,
276 	    .st_verify = smbios_test_memdevice_verify_33ext,
277 	    .st_desc = "memory device 3.3"
278 	}, {
279 	    .st_entry = SMBIOS_ENTRY_POINT_30,
280 	    .st_tvers = SMB_VERSION,
281 	    .st_libvers = SMB_VERSION,
282 	    .st_mktable = smbios_test_pinfo_mktable_amd64,
283 	    .st_canopen = B_TRUE,
284 	    .st_verify = smbios_test_pinfo_verify_amd64,
285 	    .st_desc = "processor additional information - amd64"
286 	}, {
287 	    .st_entry = SMBIOS_ENTRY_POINT_30,
288 	    .st_tvers = SMB_VERSION,
289 	    .st_libvers = SMB_VERSION,
290 	    .st_mktable = smbios_test_pinfo_mktable_riscv,
291 	    .st_canopen = B_TRUE,
292 	    .st_verify = smbios_test_pinfo_verify_riscv,
293 	    .st_desc = "processor additional information - riscv"
294 	}, {
295 	    .st_entry = SMBIOS_ENTRY_POINT_30,
296 	    .st_tvers = SMB_VERSION,
297 	    .st_libvers = SMB_VERSION,
298 	    .st_mktable = smbios_test_pinfo_mktable_invlen1,
299 	    .st_canopen = B_TRUE,
300 	    .st_verify = smbios_test_pinfo_verify_invlen1,
301 	    .st_desc = "processor additional information - bad table length 1"
302 	}, {
303 	    .st_entry = SMBIOS_ENTRY_POINT_30,
304 	    .st_tvers = SMB_VERSION,
305 	    .st_libvers = SMB_VERSION,
306 	    .st_mktable = smbios_test_pinfo_mktable_invlen2,
307 	    .st_canopen = B_TRUE,
308 	    .st_verify = smbios_test_pinfo_verify_invlen2,
309 	    .st_desc = "processor additional information - bad table length 2"
310 	}, {
311 	    .st_entry = SMBIOS_ENTRY_POINT_30,
312 	    .st_tvers = SMB_VERSION,
313 	    .st_libvers = SMB_VERSION,
314 	    .st_mktable = smbios_test_pinfo_mktable_invlen3,
315 	    .st_canopen = B_TRUE,
316 	    .st_verify = smbios_test_pinfo_verify_invlen3,
317 	    .st_desc = "processor additional information - bad table length 3"
318 	}, {
319 	    .st_entry = SMBIOS_ENTRY_POINT_30,
320 	    .st_tvers = SMB_VERSION,
321 	    .st_libvers = SMB_VERSION,
322 	    .st_mktable = smbios_test_pinfo_mktable_invlen4,
323 	    .st_canopen = B_TRUE,
324 	    .st_verify = smbios_test_pinfo_verify_invlen4,
325 	    .st_desc = "processor additional information - bad table length 4"
326 	}, {
327 	    .st_entry = SMBIOS_ENTRY_POINT_30,
328 	    .st_tvers = SMB_VERSION,
329 	    .st_libvers = SMB_VERSION,
330 	    .st_mktable = smbios_test_memdevice_mktable_32,
331 	    .st_canopen = B_TRUE,
332 	    .st_verify = smbios_test_pinfo_verify_badtype,
333 	    .st_desc = "processor additional information - bad type"
334 	},
335 
336 };
337 
338 static boolean_t
smbios_test_run_one(const smbios_test_t * test)339 smbios_test_run_one(const smbios_test_t *test)
340 {
341 	smbios_test_table_t *table = NULL;
342 	smbios_hdl_t *hdl = NULL;
343 	void *buf;
344 	size_t len;
345 	smbios_entry_t *entry;
346 	int err = 0;
347 	boolean_t ret = B_FALSE;
348 
349 	table = smbios_test_table_init(test->st_entry, test->st_tvers);
350 	if (!test->st_mktable(table)) {
351 		goto out;
352 	}
353 
354 	smbios_test_table_snapshot(table, &entry, &buf, &len);
355 	hdl = smbios_bufopen(entry, buf, len, test->st_libvers, SMB_FL_DEBUG,
356 	    &err);
357 	if (test->st_canopen) {
358 		if (hdl == NULL) {
359 			warnx("failed to create table for test %s: %s",
360 			    test->st_desc, smbios_errmsg(err));
361 			goto out;
362 		}
363 	} else {
364 		if (hdl != NULL) {
365 			warnx("accidentally created table for test %s, "
366 			    "expected failure", test->st_desc);
367 		} else {
368 			ret = B_TRUE;
369 		}
370 		goto out;
371 	}
372 
373 	if (test->st_verify(hdl)) {
374 		ret = B_TRUE;
375 	}
376 
377 out:
378 	if (hdl != NULL) {
379 		smbios_close(hdl);
380 	}
381 
382 	if (table != NULL) {
383 		smbios_test_table_fini(table);
384 	}
385 
386 	if (ret) {
387 		(void) printf("TEST PASSED: %s\n", test->st_desc);
388 	} else {
389 		(void) printf("TEST FAILED: %s\n", test->st_desc);
390 	}
391 
392 	return (ret);
393 }
394 
395 int
main(void)396 main(void)
397 {
398 	int err = 0;
399 	size_t i;
400 
401 	for (i = 0; i < ARRAY_SIZE(smbios_tests); i++) {
402 		if (!smbios_test_run_one(&smbios_tests[i])) {
403 			err = 1;
404 		}
405 	}
406 
407 	return (err);
408 }
409