xref: /openbsd/sys/arch/arm64/stand/efiboot/smbios.c (revision 4befd8f0)
1 /*	$OpenBSD: smbios.c,v 1.1 2022/12/07 23:04:26 patrick Exp $	*/
2 /*
3  * Copyright (c) 2006 Gordon Willem Klok <gklok@cogeco.ca>
4  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 
21 #include <machine/smbiosvar.h>
22 
23 #include <lib/libkern/libkern.h>
24 #include <stand/boot/cmd.h>
25 
26 #include "libsa.h"
27 
28 #undef DPRINTF
29 #if defined(SMBIOSDEBUG)
30 #define DPRINTF(x...)	do { printf(x); } while(0)
31 #else
32 #define DPRINTF(x...)
33 #endif
34 
35 struct smbios_entry smbios_entry;
36 
37 const char *smbios_uninfo[] = {
38 	"System",
39 	"Not ",
40 	"To be",
41 	"SYS-"
42 };
43 
44 char smbios_bios_date[64];
45 char smbios_board_vendor[64];
46 char smbios_board_prod[64];
47 char smbios_board_serial[64];
48 
49 void smbios_info(void);
50 char *fixstring(char *);
51 
52 char *hw_vendor, *hw_prod, *hw_ver, *hw_serial;
53 
54 void
smbios_init(void * smbios)55 smbios_init(void *smbios)
56 {
57 	struct smbios_struct_bios *sb;
58 	struct smbtable bios;
59 	char scratch[64];
60 	char *sminfop;
61 	uint64_t addr;
62 
63 	if (smbios == NULL)
64 		return;
65 
66 	if (strncmp(smbios, "_SM_", 4) == 0) {
67 		struct smbhdr *hdr = smbios;
68 		uint8_t *p, checksum = 0;
69 		int i;
70 
71 		if (hdr->len != sizeof(*hdr))
72 			return;
73 		for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++)
74 			checksum += p[i];
75 		if (checksum != 0)
76 			return;
77 
78 		DPRINTF("SMBIOS %d.%d", hdr->majrev, hdr->minrev);
79 
80 		smbios_entry.len = hdr->size;
81 		smbios_entry.mjr = hdr->majrev;
82 		smbios_entry.min = hdr->minrev;
83 		smbios_entry.count = hdr->count;
84 
85 		addr = hdr->addr;
86 	} else if (strncmp(smbios, "_SM3_", 5) == 0) {
87 		struct smb3hdr *hdr = smbios;
88 		uint8_t *p, checksum = 0;
89 		int i;
90 
91 		if (hdr->len != sizeof(*hdr) || hdr->epr != 0x01)
92 			return;
93 		for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++)
94 			checksum += p[i];
95 		if (checksum != 0)
96 			return;
97 
98 		DPRINTF("SMBIOS %d.%d.%d", hdr->majrev, hdr->minrev,
99 		    hdr->docrev);
100 
101 		smbios_entry.len = hdr->size;
102 		smbios_entry.mjr = hdr->majrev;
103 		smbios_entry.min = hdr->minrev;
104 		smbios_entry.count = -1;
105 
106 		addr = hdr->addr;
107 	} else {
108 		DPRINTF("Unsupported SMBIOS entry point\n");
109 		return;
110 	}
111 
112 	smbios_entry.addr = (uint8_t *)addr;
113 
114 	bios.cookie = 0;
115 	if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) {
116 		sb = bios.tblhdr;
117 		DPRINTF("SMBIOS:");
118 		if ((smbios_get_string(&bios, sb->vendor,
119 		    scratch, sizeof(scratch))) != NULL)
120 			DPRINTF(" vendor %s",
121 			    fixstring(scratch));
122 		if ((smbios_get_string(&bios, sb->version,
123 		    scratch, sizeof(scratch))) != NULL)
124 			DPRINTF(" version \"%s\"",
125 			    fixstring(scratch));
126 		if ((smbios_get_string(&bios, sb->release,
127 		    scratch, sizeof(scratch))) != NULL) {
128 			sminfop = fixstring(scratch);
129 			if (sminfop != NULL) {
130 				strlcpy(smbios_bios_date,
131 				    sminfop,
132 				    sizeof(smbios_bios_date));
133 				DPRINTF(" date %s", sminfop);
134 			}
135 		}
136 
137 		smbios_info();
138 		DPRINTF("\n");
139 	}
140 
141 	return;
142 }
143 
144 /*
145  * smbios_find_table() takes a caller supplied smbios struct type and
146  * a pointer to a handle (struct smbtable) returning one if the structure
147  * is successfully located and zero otherwise. Callers should take care
148  * to initialize the cookie field of the smbtable structure to zero before
149  * the first invocation of this function.
150  * Multiple tables of the same type can be located by repeatedly calling
151  * smbios_find_table with the same arguments.
152  */
153 int
smbios_find_table(uint8_t type,struct smbtable * st)154 smbios_find_table(uint8_t type, struct smbtable *st)
155 {
156 	uint8_t *va, *end;
157 	struct smbtblhdr *hdr;
158 	int ret = 0, tcount = 1;
159 
160 	va = smbios_entry.addr;
161 	end = va + smbios_entry.len;
162 
163 	/*
164 	 * The cookie field of the smtable structure is used to locate
165 	 * multiple instances of a table of an arbitrary type. Following the
166 	 * successful location of a table, the type is encoded as bits 0:7 of
167 	 * the cookie value, the offset in terms of the number of structures
168 	 * preceding that referenced by the handle is encoded in bits 15:31.
169 	 */
170 	if ((st->cookie & 0xfff) == type && st->cookie >> 16) {
171 		if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) {
172 			hdr = st->hdr;
173 			if (hdr->type == type) {
174 				va = (uint8_t *)hdr + hdr->size;
175 				for (; va + 1 < end; va++)
176 					if (*va == 0 && *(va + 1) == 0)
177 						break;
178 				va += 2;
179 				tcount = st->cookie >> 16;
180 			}
181 		}
182 	}
183 	for (; va + sizeof(struct smbtblhdr) < end &&
184 	    tcount <= smbios_entry.count; tcount++) {
185 		hdr = (struct smbtblhdr *)va;
186 		if (hdr->type == type) {
187 			ret = 1;
188 			st->hdr = hdr;
189 			st->tblhdr = va + sizeof(struct smbtblhdr);
190 			st->cookie = (tcount + 1) << 16 | type;
191 			break;
192 		}
193 		if (hdr->type == SMBIOS_TYPE_EOT)
194 			break;
195 		va += hdr->size;
196 		for (; va + 1 < end; va++)
197 			if (*va == 0 && *(va + 1) == 0)
198 				break;
199 		va += 2;
200 	}
201 	return ret;
202 }
203 
204 char *
smbios_get_string(struct smbtable * st,uint8_t indx,char * dest,size_t len)205 smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len)
206 {
207 	uint8_t *va, *end;
208 	char *ret = NULL;
209 	int i;
210 
211 	va = (uint8_t *)st->hdr + st->hdr->size;
212 	end = smbios_entry.addr + smbios_entry.len;
213 	for (i = 1; va < end && i < indx && *va; i++)
214 		while (*va++)
215 			;
216 	if (i == indx) {
217 		if (va + len < end) {
218 			ret = dest;
219 			memcpy(ret, va, len);
220 			ret[len - 1] = '\0';
221 		}
222 	}
223 
224 	return ret;
225 }
226 
227 char *
fixstring(char * s)228 fixstring(char *s)
229 {
230 	char *p, *e;
231 #if 0
232 	int i;
233 
234 	for (i = 0; i < nitems(smbios_uninfo); i++)
235 		if ((strncasecmp(s, smbios_uninfo[i],
236 		    strlen(smbios_uninfo[i]))) == 0)
237 			return NULL;
238 #endif
239 	/*
240 	 * Remove leading and trailing whitespace
241 	 */
242 	for (p = s; *p == ' '; p++)
243 		;
244 	/*
245 	 * Special case entire string is whitespace
246 	 */
247 	if (p == s + strlen(s))
248 		return NULL;
249 	for (e = s + strlen(s) - 1; e > s && *e == ' '; e--)
250 		;
251 	if (p > s || e < s + strlen(s) - 1) {
252 		memmove(s, p, e - p + 1);
253 		s[e - p + 1] = '\0';
254 	}
255 
256 	return s;
257 }
258 
259 void
smbios_info(void)260 smbios_info(void)
261 {
262 	char *sminfop, sminfo[64];
263 	struct smbtable stbl, btbl;
264 	struct smbios_sys *sys;
265 	struct smbios_board *board;
266 	int infolen, havebb;
267 	char *p;
268 
269 	if (smbios_entry.mjr < 2)
270 		return;
271 	/*
272 	 * According to the spec the system table among others is required,
273 	 * if it is not we do not bother with this smbios implementation.
274 	 */
275 	stbl.cookie = btbl.cookie = 0;
276 	if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl))
277 		return;
278 	havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl);
279 
280 	sys = (struct smbios_sys *)stbl.tblhdr;
281 	if (havebb) {
282 		board = (struct smbios_board *)btbl.tblhdr;
283 
284 		sminfop = NULL;
285 		if ((p = smbios_get_string(&btbl, board->vendor,
286 		    sminfo, sizeof(sminfo))) != NULL)
287 			sminfop = fixstring(p);
288 		if (sminfop)
289 			strlcpy(smbios_board_vendor, sminfop,
290 			    sizeof(smbios_board_vendor));
291 
292 		sminfop = NULL;
293 		if ((p = smbios_get_string(&btbl, board->product,
294 		    sminfo, sizeof(sminfo))) != NULL)
295 			sminfop = fixstring(p);
296 		if (sminfop)
297 			strlcpy(smbios_board_prod, sminfop,
298 			    sizeof(smbios_board_prod));
299 
300 		sminfop = NULL;
301 		if ((p = smbios_get_string(&btbl, board->serial,
302 		    sminfo, sizeof(sminfo))) != NULL)
303 			sminfop = fixstring(p);
304 		if (sminfop)
305 			strlcpy(smbios_board_serial, sminfop,
306 			    sizeof(smbios_board_serial));
307 	}
308 	/*
309 	 * Some smbios implementations have no system vendor or
310 	 * product strings, some have very uninformative data which is
311 	 * harder to work around and we must rely upon various
312 	 * heuristics to detect this. In both cases we attempt to fall
313 	 * back on the base board information in the perhaps naive
314 	 * belief that motherboard vendors will supply this
315 	 * information.
316 	 */
317 	sminfop = NULL;
318 	if ((p = smbios_get_string(&stbl, sys->vendor, sminfo,
319 	    sizeof(sminfo))) != NULL)
320 		sminfop = fixstring(p);
321 	if (sminfop == NULL) {
322 		if (havebb) {
323 			if ((p = smbios_get_string(&btbl, board->vendor,
324 			    sminfo, sizeof(sminfo))) != NULL)
325 				sminfop = fixstring(p);
326 		}
327 	}
328 	if (sminfop) {
329 		infolen = strlen(sminfop) + 1;
330 		hw_vendor = alloc(infolen);
331 		if (hw_vendor)
332 			strlcpy(hw_vendor, sminfop, infolen);
333 		sminfop = NULL;
334 	}
335 	if ((p = smbios_get_string(&stbl, sys->product, sminfo,
336 	    sizeof(sminfo))) != NULL)
337 		sminfop = fixstring(p);
338 	if (sminfop == NULL) {
339 		if (havebb) {
340 			if ((p = smbios_get_string(&btbl, board->product,
341 			    sminfo, sizeof(sminfo))) != NULL)
342 				sminfop = fixstring(p);
343 		}
344 	}
345 	if (sminfop) {
346 		infolen = strlen(sminfop) + 1;
347 		hw_prod = alloc(infolen);
348 		if (hw_prod)
349 			strlcpy(hw_prod, sminfop, infolen);
350 		sminfop = NULL;
351 	}
352 	if (hw_vendor != NULL && hw_prod != NULL)
353 		DPRINTF("\nSMBIOS: %s %s", hw_vendor, hw_prod);
354 	if ((p = smbios_get_string(&stbl, sys->version, sminfo,
355 	    sizeof(sminfo))) != NULL)
356 		sminfop = fixstring(p);
357 	if (sminfop) {
358 		infolen = strlen(sminfop) + 1;
359 		hw_ver = alloc(infolen);
360 		if (hw_ver)
361 			strlcpy(hw_ver, sminfop, infolen);
362 		sminfop = NULL;
363 	}
364 	if ((p = smbios_get_string(&stbl, sys->serial, sminfo,
365 	    sizeof(sminfo))) != NULL)
366 		sminfop = fixstring(p);
367 	if (sminfop) {
368 		infolen = strlen(sminfop) + 1;
369 		hw_serial = alloc(infolen);
370 		if (hw_serial)
371 			strlcpy(hw_serial, sminfop, infolen);
372 	}
373 }
374