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