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 *
_umem_debug_init(void)51 _umem_debug_init(void)
52 {
53 return ("default,verbose");
54 }
55
56 const char *
_umem_logging_init(void)57 _umem_logging_init(void)
58 {
59 return ("fail,contents");
60 }
61
62 static smbios_test_table_t *
smbios_test_table_init(smbios_entry_point_t type,uint_t version)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 *
smbios_test_table_append_common(smbios_test_table_t * table,const void * buf,size_t len)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
smbios_test_table_append_raw(smbios_test_table_t * table,const void * buf,size_t len)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
smbios_test_table_append_string(smbios_test_table_t * table,const char * str)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
smbios_test_table_append(smbios_test_table_t * table,const void * buf,size_t len)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
smbios_test_table_checksum(const uint8_t * buf,size_t len)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
smbios_test_table_snapshot(smbios_test_table_t * table,smbios_entry_t ** entryp,void ** bufp,size_t * lenp)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
smbios_test_table_fini(smbios_test_table_t * table)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
smbios_test_mktable(smbios_test_table_t * table)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
smbios_test_verify_table(smbios_hdl_t * hdl)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
main(void)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