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