xref: /illumos-gate/usr/src/test/util-tests/tests/smbios/smbios.c (revision 5328fc53d11d7151861fa272e4fb0248b8f0e145)
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  */
15 
16 /*
17  * Primordial SMBIOS test suite. At the moment, the purpose of this is just to
18  * test the recent SMBIOS 3.2 additions specific to the variable length slots.
19  * This should be evolved into a much fuller test suite.
20  */
21 
22 #include <smbios.h>
23 #include <umem.h>
24 #include <stdint.h>
25 #include <endian.h>
26 #include <stdio.h>
27 #include <err.h>
28 
29 #include <sys/smbios.h>
30 #include <sys/smbios_impl.h>
31 
32 static const char *smbios_test_name = "The One Slot";
33 
34 /*
35  * Number of bytes we allocate at a given time for an SMBIOS table.
36  */
37 #define	SMBIOS_TEST_ALLOC_SIZE	1024
38 
39 typedef struct smbios_test_table {
40 	smbios_entry_point_t	stt_type;
41 	void			*stt_data;
42 	size_t			stt_buflen;
43 	size_t			stt_offset;
44 	uint_t			stt_nents;
45 	uint_t			stt_version;
46 	uint_t			stt_nextid;
47 	smbios_entry_t		stt_entry;
48 } smbios_test_table_t;
49 
50 const char *
51 _umem_debug_init(void)
52 {
53 	return ("default,verbose");
54 }
55 
56 const char *
57 _umem_logging_init(void)
58 {
59 	return ("fail,contents");
60 }
61 
62 static smbios_test_table_t *
63 smbios_test_table_init(smbios_entry_point_t type, uint_t version)
64 {
65 	smbios_test_table_t *table;
66 
67 	if (type != SMBIOS_ENTRY_POINT_30) {
68 		abort();
69 	}
70 
71 	table = umem_zalloc(sizeof (smbios_test_table_t), UMEM_DEFAULT);
72 	if (table == NULL) {
73 		return (NULL);
74 	}
75 
76 	table->stt_data = umem_zalloc(SMBIOS_TEST_ALLOC_SIZE, UMEM_DEFAULT);
77 	if (table->stt_data == NULL) {
78 		umem_free(table, sizeof (smbios_test_table_t));
79 		return (NULL);
80 	}
81 	table->stt_buflen = SMBIOS_TEST_ALLOC_SIZE;
82 	table->stt_type = type;
83 	table->stt_version = version;
84 	table->stt_nextid = 1;
85 
86 	return (table);
87 }
88 
89 static void *
90 smbios_test_table_append_common(smbios_test_table_t *table, const void *buf,
91     size_t len)
92 {
93 	void *start;
94 
95 	if (SIZE_MAX - table->stt_offset < len)
96 		abort();
97 
98 	if (len + table->stt_offset >= table->stt_buflen) {
99 		void *newbuf;
100 		size_t newlen = table->stt_buflen + SMBIOS_TEST_ALLOC_SIZE;
101 
102 		while (len + table->stt_offset >= newlen) {
103 			newlen += SMBIOS_TEST_ALLOC_SIZE;
104 		}
105 
106 		newbuf = umem_zalloc(newlen, UMEM_DEFAULT);
107 		if (newbuf == NULL) {
108 			err(EXIT_FAILURE, "failed to umem_zalloc for %lu bytes",
109 			    newlen);
110 		}
111 
112 		(void) memcpy(newbuf, table->stt_data, table->stt_buflen);
113 		umem_free(table->stt_data, table->stt_buflen);
114 		table->stt_data = newbuf;
115 		table->stt_buflen = newlen;
116 	}
117 
118 	start = (void *)((uintptr_t)table->stt_data + table->stt_offset);
119 	(void) memcpy(start, buf, len);
120 	table->stt_offset += len;
121 
122 	return (start);
123 }
124 
125 static void
126 smbios_test_table_append_raw(smbios_test_table_t *table, const void *buf,
127     size_t len)
128 {
129 	(void) smbios_test_table_append_common(table, buf, len);
130 }
131 
132 static void
133 smbios_test_table_append_string(smbios_test_table_t *table, const char *str)
134 {
135 	size_t len = strlen(str) + 1;
136 	(void) smbios_test_table_append_common(table, str, len);
137 }
138 
139 static uint16_t
140 smbios_test_table_append(smbios_test_table_t *table, const void *buf,
141     size_t len)
142 {
143 	smb_header_t *hdr;
144 	uint16_t id;
145 
146 	hdr = smbios_test_table_append_common(table, buf, len);
147 	table->stt_nents++;
148 
149 	id = table->stt_nextid;
150 	hdr->smbh_hdl = htole16(table->stt_nextid);
151 	table->stt_nextid++;
152 
153 	return (id);
154 }
155 
156 static uint8_t
157 smbios_test_table_checksum(const uint8_t *buf, size_t len)
158 {
159 	uint8_t sum;
160 	size_t i;
161 
162 	for (i = 0, sum = 0; i < len; i++) {
163 		sum += buf[i];
164 	}
165 
166 	if (sum == 0)
167 		return (0);
168 
169 	return ((uint8_t)(0x100 - sum));
170 }
171 
172 static void
173 smbios_test_table_snapshot(smbios_test_table_t *table, smbios_entry_t **entryp,
174     void **bufp, size_t *lenp)
175 {
176 	smbios_30_entry_t *ent30;
177 
178 	switch (table->stt_type) {
179 	case SMBIOS_ENTRY_POINT_30:
180 		ent30 = &table->stt_entry.ep30;
181 
182 		(void) memcpy(ent30->smbe_eanchor, SMB3_ENTRY_EANCHOR,
183 		    sizeof (ent30->smbe_eanchor));
184 		ent30->smbe_ecksum = 0;
185 		ent30->smbe_elen = sizeof (*ent30);
186 		ent30->smbe_major = (table->stt_version >> 8) & 0xff;
187 		ent30->smbe_minor = table->stt_version & 0xff;
188 		ent30->smbe_docrev = 0;
189 		ent30->smbe_revision = 1;
190 		ent30->smbe_reserved = 0;
191 		ent30->smbe_stlen = htole32(table->stt_offset);
192 		ent30->smbe_staddr = htole64(P2ROUNDUP(sizeof (*ent30), 16));
193 
194 		ent30->smbe_ecksum = smbios_test_table_checksum((void *)ent30,
195 		    sizeof (*ent30));
196 		break;
197 	default:
198 		abort();
199 	}
200 
201 	*entryp = &table->stt_entry;
202 	*bufp = table->stt_data;
203 	*lenp = table->stt_offset;
204 }
205 
206 static void
207 smbios_test_table_fini(smbios_test_table_t *table)
208 {
209 	if (table == NULL) {
210 		return;
211 	}
212 
213 	if (table->stt_data != NULL) {
214 		umem_free(table->stt_data, table->stt_buflen);
215 	}
216 
217 	umem_free(table, sizeof (smbios_test_table_t));
218 }
219 
220 static void
221 smbios_test_mktable(smbios_test_table_t *table)
222 {
223 	smb_slot_t slot;
224 	smb_slot_peer_t peers[2];
225 	smb_header_t eot;
226 	uint8_t endstring = 0;
227 
228 	slot.smbsl_hdr.smbh_type = SMB_TYPE_SLOT;
229 	slot.smbsl_hdr.smbh_len = sizeof (smb_slot_t) + sizeof (peers);
230 
231 	slot.smbsl_name = 1;
232 	slot.smbsl_type = SMB_SLT_PCIE3G16;
233 	slot.smbsl_width = SMB_SLW_16X;
234 	slot.smbsl_length = SMB_SLL_SHORT;
235 	slot.smbsl_id = htole16(1);
236 	slot.smbsl_ch1 = SMB_SLCH1_33V;
237 	slot.smbsl_ch2 = SMB_SLCH2_PME;
238 	slot.smbsl_sg = htole16(1);
239 	slot.smbsl_bus = 0x42;
240 	slot.smbsl_df = 0x23;
241 	slot.smbsl_dbw = SMB_SLW_16X;
242 	slot.smbsl_npeers = 2;
243 	peers[0].smbspb_group_no = htole16(1);
244 	peers[0].smbspb_bus = 0x42;
245 	peers[0].smbspb_df = 0x42;
246 	peers[0].smbspb_width = SMB_SLW_8X;
247 
248 	peers[1].smbspb_group_no = htole16(1);
249 	peers[1].smbspb_bus = 0x23;
250 	peers[1].smbspb_df = 0x31;
251 	peers[1].smbspb_width = SMB_SLW_8X;
252 
253 	(void) smbios_test_table_append(table, &slot, sizeof (slot));
254 	(void) smbios_test_table_append_raw(table, peers, sizeof (peers));
255 	(void) smbios_test_table_append_string(table, smbios_test_name);
256 	(void) smbios_test_table_append_raw(table, &endstring,
257 	    sizeof (endstring));
258 
259 	bzero(&eot, sizeof (eot));
260 	eot.smbh_type = SMB_TYPE_EOT;
261 	eot.smbh_len = 4;
262 	(void) smbios_test_table_append(table, &eot, sizeof (eot));
263 	(void) smbios_test_table_append_raw(table, &endstring,
264 	    sizeof (endstring));
265 	(void) smbios_test_table_append_raw(table, &endstring,
266 	    sizeof (endstring));
267 }
268 
269 static void
270 smbios_test_verify_table(smbios_hdl_t *hdl)
271 {
272 	smbios_struct_t sp;
273 	smbios_slot_t slot;
274 	uint_t npeers;
275 	smbios_slot_peer_t *peers;
276 	uint_t errs = 0;
277 
278 	if (smbios_lookup_type(hdl, SMB_TYPE_SLOT, &sp) == -1) {
279 		errx(EXIT_FAILURE, "failed to lookup SMBIOS slot: %s",
280 		    smbios_errmsg(smbios_errno(hdl)));
281 	}
282 
283 	if (smbios_info_slot(hdl, sp.smbstr_id, &slot) != 0) {
284 		errx(EXIT_FAILURE, "failed to get SMBIOS slot info: %s",
285 		    smbios_errmsg(smbios_errno(hdl)));
286 	}
287 
288 	/*
289 	 * Verify everything we'd expect about the slot.
290 	 */
291 	if (strcmp(slot.smbl_name, smbios_test_name) != 0) {
292 		warnx("slot name mismatch, expected %s, found %s",
293 		    smbios_test_name, slot.smbl_name);
294 		errs++;
295 	}
296 
297 	if (slot.smbl_type != SMB_SLT_PCIE3G16) {
298 		warnx("incorrect slot type, found %u", slot.smbl_type);
299 		errs++;
300 	}
301 
302 	if (slot.smbl_width != SMB_SLW_16X) {
303 		warnx("incorrect slot width, found %u", slot.smbl_width);
304 		errs++;
305 	}
306 
307 	if (slot.smbl_length != SMB_SLL_SHORT) {
308 		warnx("incorrect slot length, found %u", slot.smbl_length);
309 		errs++;
310 	}
311 
312 	if (slot.smbl_dbw != SMB_SLW_16X) {
313 		warnx("incorrect slot data bus width, found %u", slot.smbl_dbw);
314 		errs++;
315 	}
316 
317 	if (slot.smbl_npeers != 2) {
318 		warnx("incorrect number of slot peers, found %u",
319 		    slot.smbl_npeers);
320 		errs++;
321 	}
322 
323 	if (smbios_info_slot_peers(hdl, sp.smbstr_id, &npeers, &peers) != 0) {
324 		errx(EXIT_FAILURE, "failed to get SMBIOS peer info: %s",
325 		    smbios_errmsg(smbios_errno(hdl)));
326 	}
327 
328 	if (npeers != 2) {
329 		errx(EXIT_FAILURE, "got wrong number of slot peers: %u\n",
330 		    npeers);
331 	}
332 
333 	if (peers[0].smblp_group != 1) {
334 		warnx("incorrect group for peer 0: %u", peers[0].smblp_group);
335 		errs++;
336 	}
337 
338 	if (peers[0].smblp_data_width != SMB_SLW_8X) {
339 		warnx("incorrect data width for peer 0: %u",
340 		    peers[0].smblp_data_width);
341 		errs++;
342 	}
343 
344 	if (peers[0].smblp_device != (0x42 >> 3)) {
345 		warnx("incorrect PCI device for peer 0: %u",
346 		    peers[0].smblp_device);
347 		errs++;
348 	}
349 
350 	if (peers[0].smblp_function != (0x42 & 0x7)) {
351 		warnx("incorrect PCI function for peer 0: %u",
352 		    peers[0].smblp_function);
353 		errs++;
354 	}
355 
356 	if (peers[1].smblp_group != 1) {
357 		warnx("incorrect group for peer 1: %u", peers[1].smblp_group);
358 		errs++;
359 	}
360 
361 	if (peers[1].smblp_device != (0x31 >> 3)) {
362 		warnx("incorrect PCI device for peer 1: %u",
363 		    peers[1].smblp_device);
364 		errs++;
365 	}
366 
367 	if (peers[1].smblp_function != (0x31 & 0x7)) {
368 		warnx("incorrect PCI function for peer 1: %u",
369 		    peers[1].smblp_function);
370 		errs++;
371 	}
372 
373 	if (peers[1].smblp_data_width != SMB_SLW_8X) {
374 		warnx("incorrect data width for peer 1: %u",
375 		    peers[1].smblp_data_width);
376 		errs++;
377 	}
378 
379 	smbios_info_slot_peers_free(hdl, npeers, peers);
380 
381 	if (errs > 0) {
382 		errx(EXIT_FAILURE, "encountered fatal errors");
383 	}
384 }
385 
386 int
387 main(void)
388 {
389 	void *buf;
390 	size_t len;
391 	smbios_test_table_t *table;
392 	smbios_entry_t *entry;
393 	smbios_hdl_t *hdl;
394 	int err = 0;
395 
396 	table = smbios_test_table_init(SMBIOS_ENTRY_POINT_30, SMB_VERSION_32);
397 	smbios_test_mktable(table);
398 	smbios_test_table_snapshot(table, &entry, &buf, &len);
399 
400 	hdl = smbios_bufopen(entry, buf, len, SMB_VERSION_32, SMB_FL_DEBUG,
401 	    &err);
402 	if (hdl == NULL) {
403 		errx(EXIT_FAILURE, "failed to create fake smbios table: %s",
404 		    smbios_errmsg(err));
405 	}
406 	smbios_test_verify_table(hdl);
407 	smbios_close(hdl);
408 	smbios_test_table_fini(table);
409 
410 	return (0);
411 }
412