xref: /openbsd-src/sys/arch/arm64/dev/smbios.c (revision cad45e2ea1017ceb0aa58ce568e740475c2b24ed)
1 /*	$OpenBSD: smbios.c,v 1.8 2022/10/29 20:35:50 kettenis 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 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/systm.h>
23 
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 #include <machine/smbiosvar.h>
27 
28 #include <dev/ofw/fdt.h>
29 
30 struct smbios_entry smbios_entry;
31 
32 const char *smbios_uninfo[] = {
33 	"System",
34 	"Not ",
35 	"To be",
36 	"SYS-"
37 };
38 
39 char smbios_bios_date[64];
40 char smbios_board_vendor[64];
41 char smbios_board_prod[64];
42 char smbios_board_serial[64];
43 
44 void smbios_info(char *);
45 char *fixstring(char *);
46 
47 struct smbios_softc {
48 	struct device	sc_dev;
49 	bus_space_tag_t	sc_iot;
50 };
51 
52 int	smbios_match(struct device *, void *, void *);
53 void	smbios_attach(struct device *, struct device *, void *);
54 
55 const struct cfattach smbios_ca = {
56 	sizeof(struct device), smbios_match, smbios_attach
57 };
58 
59 struct cfdriver smbios_cd = {
60 	NULL, "smbios", DV_DULL
61 };
62 
63 int
smbios_match(struct device * parent,void * match,void * aux)64 smbios_match(struct device *parent, void *match, void *aux)
65 {
66 	struct fdt_attach_args *faa = aux;
67 
68 	return (strcmp(faa->fa_name, "smbios") == 0);
69 }
70 
71 void
smbios_attach(struct device * parent,struct device * self,void * aux)72 smbios_attach(struct device *parent, struct device *self, void *aux)
73 {
74 	struct smbios_softc *sc = (struct smbios_softc *)self;
75 	struct fdt_attach_args *faa = aux;
76 	struct smbios_struct_bios *sb;
77 	struct smbtable bios;
78 	char scratch[64];
79 	char sig[5];
80 	char *sminfop;
81 	bus_addr_t addr;
82 	bus_size_t size;
83 	bus_space_handle_t ioh;
84 
85 	sc->sc_iot = faa->fa_iot;
86 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, sizeof(sig),
87 	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) {
88 		printf(": can't map SMBIOS entry point structure\n");
89 		return;
90 	}
91 	bus_space_read_region_1(sc->sc_iot, ioh, 0, sig, sizeof(sig));
92 	bus_space_unmap(sc->sc_iot, ioh, sizeof(sig));
93 
94 	if (strncmp(sig, "_SM_", 4) == 0) {
95 		struct smbhdr *hdr;
96 		uint8_t *p, checksum = 0;
97 		int i;
98 
99 		if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, sizeof(*hdr),
100 		    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) {
101 			printf(": can't map SMBIOS entry point structure\n");
102 			return;
103 		}
104 
105 		hdr = bus_space_vaddr(sc->sc_iot, ioh);
106 		if (hdr->len != sizeof(*hdr)) {
107 			bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
108 			printf("\n");
109 			return;
110 		}
111 		for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++)
112 			checksum += p[i];
113 		if (checksum != 0) {
114 			bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
115 			printf("\n");
116 			return;
117 		}
118 
119 		printf(": SMBIOS %d.%d", hdr->majrev, hdr->minrev);
120 
121 		smbios_entry.len = hdr->size;
122 		smbios_entry.mjr = hdr->majrev;
123 		smbios_entry.min = hdr->minrev;
124 		smbios_entry.count = hdr->count;
125 
126 		addr = hdr->addr;
127 		size = hdr->size;
128 		bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
129 	} else if (strncmp(sig, "_SM3_", 5) == 0) {
130 		struct smb3hdr *hdr;
131 		uint8_t *p, checksum = 0;
132 		int i;
133 
134 		if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, sizeof(*hdr),
135 		    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) {
136 			printf(": can't map SMBIOS entry point structure\n");
137 			return;
138 		}
139 
140 		hdr = bus_space_vaddr(sc->sc_iot, ioh);
141 		if (hdr->len != sizeof(*hdr) || hdr->epr != 0x01) {
142 			bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
143 			printf("\n");
144 			return;
145 		}
146 		for (i = 0, p = (uint8_t *)hdr; i < hdr->len; i++)
147 			checksum += p[i];
148 		if (checksum != 0) {
149 			bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
150 			printf("\n");
151 			return;
152 		}
153 
154 		printf(": SMBIOS %d.%d.%d", hdr->majrev, hdr->minrev,
155 		    hdr->docrev);
156 
157 		smbios_entry.len = hdr->size;
158 		smbios_entry.mjr = hdr->majrev;
159 		smbios_entry.min = hdr->minrev;
160 		smbios_entry.count = -1;
161 
162 		addr = hdr->addr;
163 		size = hdr->size;
164 		bus_space_unmap(sc->sc_iot, ioh, sizeof(*hdr));
165 	} else {
166 		printf(": unsupported SMBIOS entry point\n");
167 		return;
168 	}
169 
170 	if (bus_space_map(sc->sc_iot, addr, size,
171 	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) {
172 		printf(": can't map SMBIOS structure table\n");
173 		return;
174 	}
175 	smbios_entry.addr = bus_space_vaddr(sc->sc_iot, ioh);
176 
177 	bios.cookie = 0;
178 	if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) {
179 		sb = bios.tblhdr;
180 		printf("\n%s:", sc->sc_dev.dv_xname);
181 		if ((smbios_get_string(&bios, sb->vendor,
182 		    scratch, sizeof(scratch))) != NULL)
183 			printf(" vendor %s",
184 			    fixstring(scratch));
185 		if ((smbios_get_string(&bios, sb->version,
186 		    scratch, sizeof(scratch))) != NULL)
187 			printf(" version \"%s\"",
188 			    fixstring(scratch));
189 		if ((smbios_get_string(&bios, sb->release,
190 		    scratch, sizeof(scratch))) != NULL) {
191 			sminfop = fixstring(scratch);
192 			if (sminfop != NULL) {
193 				strlcpy(smbios_bios_date,
194 				    sminfop,
195 				    sizeof(smbios_bios_date));
196 				printf(" date %s", sminfop);
197 			}
198 		}
199 
200 		smbios_info(sc->sc_dev.dv_xname);
201 	}
202 
203 	bus_space_unmap(sc->sc_iot, ioh, size);
204 
205 	printf("\n");
206 	return;
207 }
208 
209 /*
210  * smbios_find_table() takes a caller supplied smbios struct type and
211  * a pointer to a handle (struct smbtable) returning one if the structure
212  * is successfully located and zero otherwise. Callers should take care
213  * to initialize the cookie field of the smbtable structure to zero before
214  * the first invocation of this function.
215  * Multiple tables of the same type can be located by repeatedly calling
216  * smbios_find_table with the same arguments.
217  */
218 int
smbios_find_table(uint8_t type,struct smbtable * st)219 smbios_find_table(uint8_t type, struct smbtable *st)
220 {
221 	uint8_t *va, *end;
222 	struct smbtblhdr *hdr;
223 	int ret = 0, tcount = 1;
224 
225 	va = smbios_entry.addr;
226 	end = va + smbios_entry.len;
227 
228 	/*
229 	 * The cookie field of the smtable structure is used to locate
230 	 * multiple instances of a table of an arbitrary type. Following the
231 	 * successful location of a table, the type is encoded as bits 0:7 of
232 	 * the cookie value, the offset in terms of the number of structures
233 	 * preceding that referenced by the handle is encoded in bits 15:31.
234 	 */
235 	if ((st->cookie & 0xfff) == type && st->cookie >> 16) {
236 		if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) {
237 			hdr = st->hdr;
238 			if (hdr->type == type) {
239 				va = (uint8_t *)hdr + hdr->size;
240 				for (; va + 1 < end; va++)
241 					if (*va == 0 && *(va + 1) == 0)
242 						break;
243 				va += 2;
244 				tcount = st->cookie >> 16;
245 			}
246 		}
247 	}
248 	for (; va + sizeof(struct smbtblhdr) < end &&
249 	    tcount <= smbios_entry.count; tcount++) {
250 		hdr = (struct smbtblhdr *)va;
251 		if (hdr->type == type) {
252 			ret = 1;
253 			st->hdr = hdr;
254 			st->tblhdr = va + sizeof(struct smbtblhdr);
255 			st->cookie = (tcount + 1) << 16 | type;
256 			break;
257 		}
258 		if (hdr->type == SMBIOS_TYPE_EOT)
259 			break;
260 		va += hdr->size;
261 		for (; va + 1 < end; va++)
262 			if (*va == 0 && *(va + 1) == 0)
263 				break;
264 		va += 2;
265 	}
266 	return ret;
267 }
268 
269 char *
smbios_get_string(struct smbtable * st,uint8_t indx,char * dest,size_t len)270 smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len)
271 {
272 	uint8_t *va, *end;
273 	char *ret = NULL;
274 	int i;
275 
276 	va = (uint8_t *)st->hdr + st->hdr->size;
277 	end = smbios_entry.addr + smbios_entry.len;
278 	for (i = 1; va < end && i < indx && *va; i++)
279 		while (*va++)
280 			;
281 	if (i == indx) {
282 		if (va + len < end) {
283 			ret = dest;
284 			memcpy(ret, va, len);
285 			ret[len - 1] = '\0';
286 		}
287 	}
288 
289 	return ret;
290 }
291 
292 char *
fixstring(char * s)293 fixstring(char *s)
294 {
295 	char *p, *e;
296 	int i;
297 
298 	for (i = 0; i < nitems(smbios_uninfo); i++)
299 		if ((strncasecmp(s, smbios_uninfo[i],
300 		    strlen(smbios_uninfo[i]))) == 0)
301 			return NULL;
302 	/*
303 	 * Remove leading and trailing whitespace
304 	 */
305 	for (p = s; *p == ' '; p++)
306 		;
307 	/*
308 	 * Special case entire string is whitespace
309 	 */
310 	if (p == s + strlen(s))
311 		return NULL;
312 	for (e = s + strlen(s) - 1; e > s && *e == ' '; e--)
313 		;
314 	if (p > s || e < s + strlen(s) - 1) {
315 		memmove(s, p, e - p + 1);
316 		s[e - p + 1] = '\0';
317 	}
318 
319 	return s;
320 }
321 
322 void
smbios_info(char * str)323 smbios_info(char *str)
324 {
325 	char *sminfop, sminfo[64];
326 	struct smbtable stbl, btbl;
327 	struct smbios_sys *sys;
328 	struct smbios_board *board;
329 	int i, infolen, uuidf, havebb;
330 	char *p;
331 
332 	if (smbios_entry.mjr < 2)
333 		return;
334 	/*
335 	 * According to the spec the system table among others is required,
336 	 * if it is not we do not bother with this smbios implementation.
337 	 */
338 	stbl.cookie = btbl.cookie = 0;
339 	if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl))
340 		return;
341 	havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl);
342 
343 	sys = (struct smbios_sys *)stbl.tblhdr;
344 	if (havebb) {
345 		board = (struct smbios_board *)btbl.tblhdr;
346 
347 		sminfop = NULL;
348 		if ((p = smbios_get_string(&btbl, board->vendor,
349 		    sminfo, sizeof(sminfo))) != NULL)
350 			sminfop = fixstring(p);
351 		if (sminfop)
352 			strlcpy(smbios_board_vendor, sminfop,
353 			    sizeof(smbios_board_vendor));
354 
355 		sminfop = NULL;
356 		if ((p = smbios_get_string(&btbl, board->product,
357 		    sminfo, sizeof(sminfo))) != NULL)
358 			sminfop = fixstring(p);
359 		if (sminfop)
360 			strlcpy(smbios_board_prod, sminfop,
361 			    sizeof(smbios_board_prod));
362 
363 		sminfop = NULL;
364 		if ((p = smbios_get_string(&btbl, board->serial,
365 		    sminfo, sizeof(sminfo))) != NULL)
366 			sminfop = fixstring(p);
367 		if (sminfop)
368 			strlcpy(smbios_board_serial, sminfop,
369 			    sizeof(smbios_board_serial));
370 	}
371 	/*
372 	 * Some smbios implementations have no system vendor or
373 	 * product strings, some have very uninformative data which is
374 	 * harder to work around and we must rely upon various
375 	 * heuristics to detect this. In both cases we attempt to fall
376 	 * back on the base board information in the perhaps naive
377 	 * belief that motherboard vendors will supply this
378 	 * information.
379 	 */
380 	sminfop = NULL;
381 	if ((p = smbios_get_string(&stbl, sys->vendor, sminfo,
382 	    sizeof(sminfo))) != NULL)
383 		sminfop = fixstring(p);
384 	if (sminfop == NULL) {
385 		if (havebb) {
386 			if ((p = smbios_get_string(&btbl, board->vendor,
387 			    sminfo, sizeof(sminfo))) != NULL)
388 				sminfop = fixstring(p);
389 		}
390 	}
391 	if (sminfop) {
392 		infolen = strlen(sminfop) + 1;
393 		hw_vendor = malloc(infolen, M_DEVBUF, M_NOWAIT);
394 		if (hw_vendor)
395 			strlcpy(hw_vendor, sminfop, infolen);
396 		sminfop = NULL;
397 	}
398 	if ((p = smbios_get_string(&stbl, sys->product, sminfo,
399 	    sizeof(sminfo))) != NULL)
400 		sminfop = fixstring(p);
401 	if (sminfop == NULL) {
402 		if (havebb) {
403 			if ((p = smbios_get_string(&btbl, board->product,
404 			    sminfo, sizeof(sminfo))) != NULL)
405 				sminfop = fixstring(p);
406 		}
407 	}
408 	if (sminfop) {
409 		infolen = strlen(sminfop) + 1;
410 		hw_prod = malloc(infolen, M_DEVBUF, M_NOWAIT);
411 		if (hw_prod)
412 			strlcpy(hw_prod, sminfop, infolen);
413 		sminfop = NULL;
414 	}
415 	if (hw_vendor != NULL && hw_prod != NULL)
416 		printf("\n%s: %s %s", str, hw_vendor, hw_prod);
417 	if ((p = smbios_get_string(&stbl, sys->version, sminfo,
418 	    sizeof(sminfo))) != NULL)
419 		sminfop = fixstring(p);
420 	if (sminfop) {
421 		infolen = strlen(sminfop) + 1;
422 		hw_ver = malloc(infolen, M_DEVBUF, M_NOWAIT);
423 		if (hw_ver)
424 			strlcpy(hw_ver, sminfop, infolen);
425 		sminfop = NULL;
426 	}
427 	if ((p = smbios_get_string(&stbl, sys->serial, sminfo,
428 	    sizeof(sminfo))) != NULL)
429 		sminfop = fixstring(p);
430 	if (sminfop) {
431 		infolen = strlen(sminfop) + 1;
432 		for (i = 0; i < infolen - 1; i++)
433 			enqueue_randomness(sminfop[i]);
434 		hw_serial = malloc(infolen, M_DEVBUF, M_NOWAIT);
435 		if (hw_serial)
436 			strlcpy(hw_serial, sminfop, infolen);
437 	}
438 	if (smbios_entry.mjr > 2 || (smbios_entry.mjr == 2 &&
439 	    smbios_entry.min >= 1)) {
440 		/*
441 		 * If the uuid value is all 0xff the uuid is present but not
442 		 * set, if its all 0 then the uuid isn't present at all.
443 		 */
444 		uuidf = SMBIOS_UUID_NPRESENT|SMBIOS_UUID_NSET;
445 		for (i = 0; i < sizeof(sys->uuid); i++) {
446 			if (sys->uuid[i] != 0xff)
447 				uuidf &= ~SMBIOS_UUID_NSET;
448 			if (sys->uuid[i] != 0)
449 				uuidf &= ~SMBIOS_UUID_NPRESENT;
450 		}
451 
452 		if (uuidf & SMBIOS_UUID_NPRESENT)
453 			hw_uuid = NULL;
454 		else if (uuidf & SMBIOS_UUID_NSET)
455 			hw_uuid = "Not Set";
456 		else {
457 			for (i = 0; i < sizeof(sys->uuid); i++)
458 				enqueue_randomness(sys->uuid[i]);
459 			hw_uuid = malloc(SMBIOS_UUID_REPLEN, M_DEVBUF,
460 			    M_NOWAIT);
461 			if (hw_uuid) {
462 				snprintf(hw_uuid, SMBIOS_UUID_REPLEN,
463 				    SMBIOS_UUID_REP,
464 				    sys->uuid[0], sys->uuid[1], sys->uuid[2],
465 				    sys->uuid[3], sys->uuid[4], sys->uuid[5],
466 				    sys->uuid[6], sys->uuid[7], sys->uuid[8],
467 				    sys->uuid[9], sys->uuid[10], sys->uuid[11],
468 				    sys->uuid[12], sys->uuid[13], sys->uuid[14],
469 				    sys->uuid[15]);
470 			}
471 		}
472 	}
473 }
474