xref: /openbsd-src/sys/arch/arm64/dev/smbios.c (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
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 
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 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
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
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 *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 
154 fail:
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  */
167 int
168 smbios_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 
218 char *
219 smbios_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 
241 char *
242 fixstring(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 
271 void
272 smbios_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