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  * Basic testing of the SMBIOS 3.2 Slot extensions.
19  */
20 
21 #include "smbios_test.h"
22 
23 static const char *smbios_test_name = "The One Slot";
24 static uint8_t smbios_slot_bus = 0x42;
25 static uint8_t smbios_slot_df = 0x23;
26 static uint8_t smbios_slot_info = 0x65;
27 static uint16_t smbios_slot_pitch = 0x12af;
28 
29 static size_t smbios_slot_34_contlen = offsetof(smb_slot_cont_t, smbsl_height);
30 
31 static void
smbios_test_slot_fill(smb_slot_t * slot)32 smbios_test_slot_fill(smb_slot_t *slot)
33 {
34 	bzero(slot, sizeof (smb_slot_t));
35 	slot->smbsl_hdr.smbh_type = SMB_TYPE_SLOT;
36 	slot->smbsl_hdr.smbh_len = sizeof (smb_slot_t);
37 	slot->smbsl_name = 1;
38 	slot->smbsl_type = SMB_SLT_PCIE3G16;
39 	slot->smbsl_width = SMB_SLW_16X;
40 	slot->smbsl_length = SMB_SLL_SHORT;
41 	slot->smbsl_id = htole16(1);
42 	slot->smbsl_ch1 = SMB_SLCH1_33V;
43 	slot->smbsl_ch2 = SMB_SLCH2_PME;
44 	slot->smbsl_sg = htole16(1);
45 	slot->smbsl_bus = smbios_slot_bus;
46 	slot->smbsl_df = smbios_slot_df;
47 	slot->smbsl_dbw = SMB_SLW_16X;
48 }
49 
50 boolean_t
smbios_test_slot_mktable(smbios_test_table_t * table)51 smbios_test_slot_mktable(smbios_test_table_t *table)
52 {
53 	smb_slot_t slot;
54 	smb_slot_peer_t peers[2];
55 
56 	smbios_test_slot_fill(&slot);
57 
58 	slot.smbsl_hdr.smbh_len += sizeof (peers);
59 	slot.smbsl_npeers = 2;
60 
61 	peers[0].smbspb_group_no = htole16(1);
62 	peers[0].smbspb_bus = 0x42;
63 	peers[0].smbspb_df = 0x42;
64 	peers[0].smbspb_width = SMB_SLW_8X;
65 
66 	peers[1].smbspb_group_no = htole16(1);
67 	peers[1].smbspb_bus = 0x23;
68 	peers[1].smbspb_df = 0x31;
69 	peers[1].smbspb_width = SMB_SLW_8X;
70 
71 	(void) smbios_test_table_append(table, &slot, sizeof (slot));
72 	smbios_test_table_append_raw(table, peers, sizeof (peers));
73 	smbios_test_table_append_string(table, smbios_test_name);
74 	smbios_test_table_str_fini(table);
75 
76 	smbios_test_table_append_eot(table);
77 
78 	return (B_TRUE);
79 }
80 
81 static boolean_t
smbios_test_slot_mktable_nopeers(smbios_test_table_t * table,boolean_t is_35)82 smbios_test_slot_mktable_nopeers(smbios_test_table_t *table, boolean_t is_35)
83 {
84 	smb_slot_t slot;
85 	smb_slot_cont_t cont;
86 	size_t contlen;
87 
88 	if (is_35) {
89 		contlen = sizeof (cont);
90 	} else {
91 		contlen = smbios_slot_34_contlen;
92 	}
93 
94 	smbios_test_slot_fill(&slot);
95 	slot.smbsl_hdr.smbh_len = SMB_SLOT_CONT_START + contlen;
96 
97 	cont.smbsl_info = smbios_slot_info;
98 	cont.smbsl_pwidth = SMB_SLW_32X;
99 	cont.smbsl_pitch = htole16(smbios_slot_pitch);
100 	cont.smbsl_height = SMB_SLHT_LP;
101 
102 	(void) smbios_test_table_append(table, &slot, sizeof (slot));
103 	smbios_test_table_append_raw(table, &cont, contlen);
104 	smbios_test_table_append_string(table, smbios_test_name);
105 	smbios_test_table_str_fini(table);
106 	smbios_test_table_append_eot(table);
107 
108 	return (B_TRUE);
109 }
110 
111 /*
112  * 3.4 introduced additional data after peers. This version constructs a variant
113  * with no peers.
114  */
115 boolean_t
smbios_test_slot_mktable_34_nopeers(smbios_test_table_t * table)116 smbios_test_slot_mktable_34_nopeers(smbios_test_table_t *table)
117 {
118 	return (smbios_test_slot_mktable_nopeers(table, B_FALSE));
119 }
120 
121 boolean_t
smbios_test_slot_mktable_35(smbios_test_table_t * table)122 smbios_test_slot_mktable_35(smbios_test_table_t *table)
123 {
124 	return (smbios_test_slot_mktable_nopeers(table, B_TRUE));
125 }
126 
127 boolean_t
smbios_test_slot_mktable_34_peers(smbios_test_table_t * table)128 smbios_test_slot_mktable_34_peers(smbios_test_table_t *table)
129 {
130 	smb_slot_t slot;
131 	smb_slot_cont_t cont;
132 	smb_slot_peer_t peers[1];
133 
134 	smbios_test_slot_fill(&slot);
135 	slot.smbsl_npeers = 1;
136 	slot.smbsl_hdr.smbh_len = SMB_SLOT_CONT_START + 5 * slot.smbsl_npeers +
137 	    smbios_slot_34_contlen;
138 
139 	peers[0].smbspb_group_no = htole16(1);
140 	peers[0].smbspb_bus = 0x42;
141 	peers[0].smbspb_df = 0x9a;
142 	peers[0].smbspb_width = SMB_SLW_8X;
143 
144 	cont.smbsl_info = smbios_slot_info;
145 	cont.smbsl_pwidth = SMB_SLW_32X;
146 	cont.smbsl_pitch = htole16(smbios_slot_pitch);
147 
148 	(void) smbios_test_table_append(table, &slot, sizeof (slot));
149 	smbios_test_table_append_raw(table, peers, sizeof (peers));
150 	smbios_test_table_append_raw(table, &cont, smbios_slot_34_contlen);
151 	smbios_test_table_append_string(table, smbios_test_name);
152 	smbios_test_table_str_fini(table);
153 	smbios_test_table_append_eot(table);
154 	return (B_TRUE);
155 }
156 
157 static boolean_t
smbios_test_slot_common(smbios_slot_t * slot)158 smbios_test_slot_common(smbios_slot_t *slot)
159 {
160 	uint_t errs = 0;
161 
162 	if (strcmp(slot->smbl_name, smbios_test_name) != 0) {
163 		warnx("slot name mismatch, expected %s, found %s",
164 		    smbios_test_name, slot->smbl_name);
165 		errs++;
166 	}
167 
168 	if (slot->smbl_type != SMB_SLT_PCIE3G16) {
169 		warnx("incorrect slot type, found %u", slot->smbl_type);
170 		errs++;
171 	}
172 
173 	if (slot->smbl_width != SMB_SLW_16X) {
174 		warnx("incorrect slot width, found %u", slot->smbl_width);
175 		errs++;
176 	}
177 
178 	if (slot->smbl_length != SMB_SLL_SHORT) {
179 		warnx("incorrect slot length, found %u", slot->smbl_length);
180 		errs++;
181 	}
182 
183 	if (slot->smbl_dbw != SMB_SLW_16X) {
184 		warnx("incorrect slot data bus width, found %u",
185 		    slot->smbl_dbw);
186 		errs++;
187 	}
188 
189 	if (slot->smbl_bus != smbios_slot_bus) {
190 		warnx("incorrect slot bus id, found 0x%x\n", slot->smbl_bus);
191 	}
192 
193 	if (slot->smbl_df != smbios_slot_df) {
194 		warnx("incorrect slot df id, found 0x%x\n", slot->smbl_df);
195 	}
196 
197 	if (errs > 0) {
198 		return (B_FALSE);
199 	}
200 
201 	return (B_TRUE);
202 }
203 
204 boolean_t
smbios_test_slot_verify(smbios_hdl_t * hdl)205 smbios_test_slot_verify(smbios_hdl_t *hdl)
206 {
207 	smbios_struct_t sp;
208 	smbios_slot_t slot;
209 	uint_t npeers;
210 	smbios_slot_peer_t *peers;
211 	uint_t errs = 0;
212 
213 	if (smbios_lookup_type(hdl, SMB_TYPE_SLOT, &sp) == -1) {
214 		warnx("failed to lookup SMBIOS slot: %s",
215 		    smbios_errmsg(smbios_errno(hdl)));
216 		return (B_FALSE);
217 	}
218 
219 	if (smbios_info_slot(hdl, sp.smbstr_id, &slot) != 0) {
220 		warnx("failed to get SMBIOS slot info: %s",
221 		    smbios_errmsg(smbios_errno(hdl)));
222 		return (B_FALSE);
223 	}
224 
225 	if (!smbios_test_slot_common(&slot)) {
226 		errs++;
227 	}
228 
229 	if (slot.smbl_npeers != 2) {
230 		warnx("incorrect number of slot peers, found %u",
231 		    slot.smbl_npeers);
232 		errs++;
233 	}
234 
235 	if (smbios_info_slot_peers(hdl, sp.smbstr_id, &npeers, &peers) != 0) {
236 		warnx("failed to get SMBIOS peer info: %s",
237 		    smbios_errmsg(smbios_errno(hdl)));
238 		return (B_FALSE);
239 	}
240 
241 	if (npeers != 2) {
242 		warnx("got wrong number of slot peers: %u", npeers);
243 		return (B_FALSE);
244 	}
245 
246 	if (peers[0].smblp_group != 1) {
247 		warnx("incorrect group for peer 0: %u", peers[0].smblp_group);
248 		errs++;
249 	}
250 
251 	if (peers[0].smblp_data_width != SMB_SLW_8X) {
252 		warnx("incorrect data width for peer 0: %u",
253 		    peers[0].smblp_data_width);
254 		errs++;
255 	}
256 
257 	if (peers[0].smblp_device != (0x42 >> 3)) {
258 		warnx("incorrect PCI device for peer 0: %u",
259 		    peers[0].smblp_device);
260 		errs++;
261 	}
262 
263 	if (peers[0].smblp_function != (0x42 & 0x7)) {
264 		warnx("incorrect PCI function for peer 0: %u",
265 		    peers[0].smblp_function);
266 		errs++;
267 	}
268 
269 	if (peers[1].smblp_group != 1) {
270 		warnx("incorrect group for peer 1: %u", peers[1].smblp_group);
271 		errs++;
272 	}
273 
274 	if (peers[1].smblp_device != (0x31 >> 3)) {
275 		warnx("incorrect PCI device for peer 1: %u",
276 		    peers[1].smblp_device);
277 		errs++;
278 	}
279 
280 	if (peers[1].smblp_function != (0x31 & 0x7)) {
281 		warnx("incorrect PCI function for peer 1: %u",
282 		    peers[1].smblp_function);
283 		errs++;
284 	}
285 
286 	if (peers[1].smblp_data_width != SMB_SLW_8X) {
287 		warnx("incorrect data width for peer 1: %u",
288 		    peers[1].smblp_data_width);
289 		errs++;
290 	}
291 
292 	smbios_info_slot_peers_free(hdl, npeers, peers);
293 
294 	if (slot.smbl_info != 0) {
295 		warnx("found wrong slot info: 0x%x", slot.smbl_info);
296 		errs++;
297 	}
298 
299 	if (slot.smbl_pwidth != 0) {
300 		warnx("found wrong slot physical width: 0x%x",
301 		    slot.smbl_pwidth);
302 		errs++;
303 	}
304 
305 	if (slot.smbl_pitch != 0) {
306 		warnx("found wrong slot pitch: 0x%x", slot.smbl_pitch);
307 		errs++;
308 	}
309 
310 	if (errs > 0) {
311 		return (B_FALSE);
312 	}
313 
314 	return (B_TRUE);
315 }
316 
317 static boolean_t
smbios_test_slot_common_nopeers(smbios_hdl_t * hdl,smbios_struct_t * sp,smbios_slot_t * slot)318 smbios_test_slot_common_nopeers(smbios_hdl_t *hdl, smbios_struct_t *sp,
319     smbios_slot_t *slot)
320 {
321 	uint_t errs = 0;
322 	uint_t npeers;
323 	smbios_slot_peer_t *peers;
324 
325 	if (slot->smbl_npeers != 0) {
326 		warnx("incorrect number of slot peers, found %u",
327 		    slot->smbl_npeers);
328 		errs++;
329 	}
330 
331 	if (smbios_info_slot_peers(hdl, sp->smbstr_id, &npeers, &peers) != 0) {
332 		warnx("failed to get SMBIOS peer info: %s",
333 		    smbios_errmsg(smbios_errno(hdl)));
334 		return (B_FALSE);
335 	}
336 
337 	if (npeers != 0) {
338 		warnx("got wrong number of slot peers: %u", npeers);
339 		errs++;
340 	}
341 
342 	if (peers != NULL) {
343 		warnx("expected NULL peers pointer, but found %p", peers);
344 		errs++;
345 	}
346 
347 	smbios_info_slot_peers_free(hdl, npeers, peers);
348 
349 	if (slot->smbl_info != smbios_slot_info) {
350 		warnx("found wrong slot info: 0x%x, expected 0x%x",
351 		    slot->smbl_info, smbios_slot_info);
352 		errs++;
353 	}
354 
355 	if (slot->smbl_pwidth != SMB_SLW_32X) {
356 		warnx("found wrong slot physical width: 0x%x, expected 0x%x",
357 		    slot->smbl_pwidth, SMB_SLW_32X);
358 		errs++;
359 	}
360 
361 	if (slot->smbl_pitch != smbios_slot_pitch) {
362 		warnx("found wrong slot pitch: 0x%x, expected 0x%x",
363 		    slot->smbl_pitch, smbios_slot_pitch);
364 		errs++;
365 	}
366 
367 	return (errs == 0);
368 }
369 
370 boolean_t
smbios_test_slot_verify_34_nopeers(smbios_hdl_t * hdl)371 smbios_test_slot_verify_34_nopeers(smbios_hdl_t *hdl)
372 {
373 	smbios_struct_t sp;
374 	smbios_slot_t slot;
375 	uint_t errs = 0;
376 
377 	if (smbios_lookup_type(hdl, SMB_TYPE_SLOT, &sp) == -1) {
378 		warnx("failed to lookup SMBIOS slot: %s",
379 		    smbios_errmsg(smbios_errno(hdl)));
380 		return (B_FALSE);
381 	}
382 
383 	if (smbios_info_slot(hdl, sp.smbstr_id, &slot) != 0) {
384 		warnx("failed to get SMBIOS slot info: %s",
385 		    smbios_errmsg(smbios_errno(hdl)));
386 		return (B_FALSE);
387 	}
388 
389 	if (!smbios_test_slot_common(&slot)) {
390 		errs++;
391 	}
392 
393 	if (!smbios_test_slot_common_nopeers(hdl, &sp, &slot)) {
394 		errs++;
395 	}
396 
397 	if (errs > 0) {
398 		return (B_FALSE);
399 	}
400 
401 	return (B_TRUE);
402 }
403 
404 /*
405  * This is a variant of smbios_test_slot_verify_34_nopeers() that specifically
406  * uses an older library version and ensures that we don't overrun the
407  * smbios_slot_t.
408  */
409 boolean_t
smbios_test_slot_verify_34_overrun(smbios_hdl_t * hdl)410 smbios_test_slot_verify_34_overrun(smbios_hdl_t *hdl)
411 {
412 	smbios_struct_t sp;
413 	smbios_slot_t slot;
414 	uint_t errs = 0;
415 
416 	/*
417 	 * We purposefully set the values that are part of SMBIOS 3.5+ to bad
418 	 * values to make sure that we don't end up zeroing them.
419 	 */
420 	slot.smbl_height = 0xba;
421 
422 	if (smbios_lookup_type(hdl, SMB_TYPE_SLOT, &sp) == -1) {
423 		warnx("failed to lookup SMBIOS slot: %s",
424 		    smbios_errmsg(smbios_errno(hdl)));
425 		return (B_FALSE);
426 	}
427 
428 	if (smbios_info_slot(hdl, sp.smbstr_id, &slot) != 0) {
429 		warnx("failed to get SMBIOS slot info: %s",
430 		    smbios_errmsg(smbios_errno(hdl)));
431 		return (B_FALSE);
432 	}
433 
434 	if (slot.smbl_height != 0xba) {
435 		warnx("smbios 3.4 slot structure was overrun, smbl_height "
436 		    "unexpectedly set to 0x%x", slot.smbl_height);
437 		errs++;
438 	}
439 
440 	if (!smbios_test_slot_common(&slot)) {
441 		errs++;
442 	}
443 
444 	if (!smbios_test_slot_common_nopeers(hdl, &sp, &slot)) {
445 		errs++;
446 	}
447 
448 	if (errs > 0) {
449 		return (B_FALSE);
450 	}
451 
452 	return (B_TRUE);
453 }
454 
455 boolean_t
smbios_test_slot_verify_35(smbios_hdl_t * hdl)456 smbios_test_slot_verify_35(smbios_hdl_t *hdl)
457 {
458 	smbios_struct_t sp;
459 	smbios_slot_t slot;
460 	uint_t errs = 0;
461 
462 	if (smbios_lookup_type(hdl, SMB_TYPE_SLOT, &sp) == -1) {
463 		warnx("failed to lookup SMBIOS slot: %s",
464 		    smbios_errmsg(smbios_errno(hdl)));
465 		return (B_FALSE);
466 	}
467 
468 	if (smbios_info_slot(hdl, sp.smbstr_id, &slot) != 0) {
469 		warnx("failed to get SMBIOS slot info: %s",
470 		    smbios_errmsg(smbios_errno(hdl)));
471 		return (B_FALSE);
472 	}
473 
474 	if (!smbios_test_slot_common(&slot)) {
475 		errs++;
476 	}
477 
478 	if (!smbios_test_slot_common_nopeers(hdl, &sp, &slot)) {
479 		errs++;
480 	}
481 
482 	if (slot.smbl_height != SMB_SLHT_LP) {
483 		warnx("found wrong slot height: 0x%x, expected 0x%x",
484 		    slot.smbl_height, SMB_SLHT_LP);
485 		errs++;
486 	}
487 
488 	if (errs > 0) {
489 		return (B_FALSE);
490 	}
491 
492 	return (B_TRUE);
493 }
494 
495 boolean_t
smbios_test_slot_verify_34_peers(smbios_hdl_t * hdl)496 smbios_test_slot_verify_34_peers(smbios_hdl_t *hdl)
497 {
498 	smbios_struct_t sp;
499 	smbios_slot_t slot;
500 	uint_t npeers;
501 	smbios_slot_peer_t *peers;
502 	uint_t errs = 0;
503 
504 	if (smbios_lookup_type(hdl, SMB_TYPE_SLOT, &sp) == -1) {
505 		warnx("failed to lookup SMBIOS slot: %s",
506 		    smbios_errmsg(smbios_errno(hdl)));
507 		return (B_FALSE);
508 	}
509 
510 	if (smbios_info_slot(hdl, sp.smbstr_id, &slot) != 0) {
511 		warnx("failed to get SMBIOS slot info: %s",
512 		    smbios_errmsg(smbios_errno(hdl)));
513 		return (B_FALSE);
514 	}
515 
516 	if (!smbios_test_slot_common(&slot)) {
517 		errs++;
518 	}
519 
520 	if (slot.smbl_npeers != 1) {
521 		warnx("incorrect number of slot peers, found %u",
522 		    slot.smbl_npeers);
523 		errs++;
524 	}
525 
526 	if (smbios_info_slot_peers(hdl, sp.smbstr_id, &npeers, &peers) != 0) {
527 		warnx("failed to get SMBIOS peer info: %s",
528 		    smbios_errmsg(smbios_errno(hdl)));
529 		return (B_FALSE);
530 	}
531 
532 	if (npeers != 1) {
533 		warnx("got wrong number of slot peers: %u", npeers);
534 		errs++;
535 	}
536 
537 	if (peers[0].smblp_group != 1) {
538 		warnx("incorrect group for peer 0: %u", peers[0].smblp_group);
539 		errs++;
540 	}
541 
542 	if (peers[0].smblp_data_width != SMB_SLW_8X) {
543 		warnx("incorrect data width for peer 0: %u",
544 		    peers[0].smblp_data_width);
545 		errs++;
546 	}
547 
548 	if (peers[0].smblp_bus != 0x42) {
549 		warnx("incorrect PCI bus for peer 0: %u",
550 		    peers[0].smblp_bus);
551 		errs++;
552 	}
553 
554 	if (peers[0].smblp_device != (0x9a >> 3)) {
555 		warnx("incorrect PCI device for peer 0: %u",
556 		    peers[0].smblp_device);
557 		errs++;
558 	}
559 
560 	if (peers[0].smblp_function != (0x9a & 0x7)) {
561 		warnx("incorrect PCI function for peer 0: %u",
562 		    peers[0].smblp_function);
563 		errs++;
564 	}
565 
566 	smbios_info_slot_peers_free(hdl, npeers, peers);
567 
568 	if (slot.smbl_info != smbios_slot_info) {
569 		warnx("found wrong slot info: 0x%x, expected 0x%x",
570 		    slot.smbl_info, smbios_slot_info);
571 		errs++;
572 	}
573 
574 	if (slot.smbl_pwidth != SMB_SLW_32X) {
575 		warnx("found wrong slot physical width: 0x%x, expected 0x%x",
576 		    slot.smbl_pwidth, SMB_SLW_32X);
577 		errs++;
578 	}
579 
580 	if (slot.smbl_pitch != smbios_slot_pitch) {
581 		warnx("found wrong slot pitch: 0x%x, expected 0x%x",
582 		    slot.smbl_pitch, smbios_slot_pitch);
583 		errs++;
584 	}
585 
586 	if (errs > 0) {
587 		return (B_FALSE);
588 	}
589 
590 	return (B_TRUE);
591 }
592