1 /* $NetBSD: platform.c,v 1.15 2014/03/26 08:04:19 christos Exp $ */
2 
3 /*-
4  * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "isa.h"
30 
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: platform.c,v 1.15 2014/03/26 08:04:19 christos Exp $");
33 
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/sysctl.h>
38 #include <sys/uuid.h>
39 #include <sys/pmf.h>
40 
41 #if NISA > 0
42 #include <dev/isa/isavar.h>
43 #endif
44 
45 #include <arch/x86/include/smbiosvar.h>
46 
47 static int platform_dminode = CTL_EOL;
48 
49 void		platform_init(void);	/* XXX */
50 static void	platform_add(struct smbtable *, const char *, int);
51 static void	platform_add_date(struct smbtable *, const char *, int);
52 static void	platform_add_uuid(struct smbtable *, const char *,
53 				  const uint8_t *);
54 static int	platform_dmi_sysctl(SYSCTLFN_PROTO);
55 static void	platform_print(void);
56 
57 /* list of private DMI sysctl nodes */
58 static const char *platform_private_nodes[] = {
59 	"board-serial",
60 	"system-serial",
61 	"system-uuid",
62 	NULL
63 };
64 
65 void
platform_init(void)66 platform_init(void)
67 {
68 	struct smbtable smbios;
69 	struct smbios_sys *psys;
70 	struct smbios_struct_bios *pbios;
71 	struct smbios_board *pboard;
72 	struct smbios_slot *pslot;
73 	int nisa, nother;
74 
75 	smbios.cookie = 0;
76 	if (smbios_find_table(SMBIOS_TYPE_SYSTEM, &smbios)) {
77 		psys = smbios.tblhdr;
78 
79 		platform_add(&smbios, "system-vendor", psys->vendor);
80 		platform_add(&smbios, "system-product", psys->product);
81 		platform_add(&smbios, "system-version", psys->version);
82 		platform_add(&smbios, "system-serial", psys->serial);
83 		platform_add_uuid(&smbios, "system-uuid", psys->uuid);
84 	}
85 
86 	smbios.cookie = 0;
87 	if (smbios_find_table(SMBIOS_TYPE_BIOS, &smbios)) {
88 		pbios = smbios.tblhdr;
89 
90 		platform_add(&smbios, "bios-vendor", pbios->vendor);
91 		platform_add(&smbios, "bios-version", pbios->version);
92 		platform_add_date(&smbios, "bios-date", pbios->release);
93 	}
94 
95 	smbios.cookie = 0;
96 	if (smbios_find_table(SMBIOS_TYPE_BASEBOARD, &smbios)) {
97 		pboard = smbios.tblhdr;
98 
99 		platform_add(&smbios, "board-vendor", pboard->vendor);
100 		platform_add(&smbios, "board-product", pboard->product);
101 		platform_add(&smbios, "board-version", pboard->version);
102 		platform_add(&smbios, "board-serial", pboard->serial);
103 		platform_add(&smbios, "board-asset-tag", pboard->asset);
104 	}
105 
106 	smbios.cookie = 0;
107 	nisa = 0;
108 	nother = 0;
109 	while (smbios_find_table(SMBIOS_TYPE_SLOTS, &smbios)) {
110 		pslot = smbios.tblhdr;
111 		switch (pslot->type) {
112 		case SMBIOS_SLOT_ISA:
113 		case SMBIOS_SLOT_EISA:
114 			nisa++;
115 			break;
116 		default:
117 			nother++;
118 			break;
119 		}
120 	}
121 
122 #if NISA > 0
123 	if ((nother | nisa) != 0) {
124 		/* Only if there seems to be good expansion slot info. */
125 		isa_set_slotcount(nisa);
126 	}
127 #endif
128 
129 	platform_print();
130 }
131 
132 static void
platform_print(void)133 platform_print(void)
134 {
135 	const char *vend, *prod, *ver;
136 
137 	vend = pmf_get_platform("system-vendor");
138 	prod = pmf_get_platform("system-product");
139 	ver = pmf_get_platform("system-version");
140 
141 	if (vend == NULL)
142 		aprint_verbose("Generic");
143 	else
144 		aprint_verbose("%s", vend);
145 	if (prod == NULL)
146 		aprint_verbose(" PC");
147 	else
148 		aprint_verbose(" %s", prod);
149 	if (ver != NULL)
150 		aprint_verbose(" (%s)", ver);
151 	aprint_verbose("\n");
152 }
153 
154 static bool
platform_sysctl_is_private(const char * key)155 platform_sysctl_is_private(const char *key)
156 {
157 	unsigned int n;
158 
159 	for (n = 0; platform_private_nodes[n] != NULL; n++) {
160 		if (strcmp(key, platform_private_nodes[n]) == 0) {
161 			return true;
162 		}
163 	}
164 
165 	return false;
166 }
167 
168 static void
platform_create_sysctl(const char * key)169 platform_create_sysctl(const char *key)
170 {
171 	int flags = 0, err;
172 
173 	if (pmf_get_platform(key) == NULL)
174 		return;
175 
176 	/* If the key is marked private, set CTLFLAG_PRIVATE flag */
177 	if (platform_sysctl_is_private(key))
178 		flags |= CTLFLAG_PRIVATE;
179 
180 	err = sysctl_createv(NULL, 0, NULL, NULL,
181 	    CTLFLAG_READONLY | flags, CTLTYPE_STRING,
182 	    key, NULL, platform_dmi_sysctl, 0, NULL, 0,
183 	    CTL_MACHDEP, platform_dminode, CTL_CREATE, CTL_EOL);
184 	if (err != 0)
185 		printf("platform: sysctl_createv "
186 		    "(machdep.dmi.%s) failed, err = %d\n",
187 		    key, err);
188 }
189 
190 static void
platform_add(struct smbtable * tbl,const char * key,int idx)191 platform_add(struct smbtable *tbl, const char *key, int idx)
192 {
193 	char tmpbuf[128]; /* XXX is this long enough? */
194 
195 	if (smbios_get_string(tbl, idx, tmpbuf, 128) != NULL) {
196 		/* add to platform dictionary */
197 		pmf_set_platform(key, tmpbuf);
198 
199 		/* create sysctl node */
200 		platform_create_sysctl(key);
201 	}
202 }
203 
204 static int
platform_scan_date(char * buf,unsigned int * month,unsigned int * day,unsigned int * year)205 platform_scan_date(char *buf, unsigned int *month, unsigned int *day,
206     unsigned int *year)
207 {
208 	char *p, *s;
209 
210 	s = buf;
211 	p = strchr(s, '/');
212 	if (p) *p = '\0';
213 	*month = strtoul(s, NULL, 10);
214 	if (!p) return 1;
215 
216 	s = p + 1;
217 	p = strchr(s, '/');
218 	if (p) *p = '\0';
219 	*day = strtoul(s, NULL, 10);
220 	if (!p) return 2;
221 
222 	s = p + 1;
223 	*year = strtoul(s, NULL, 10);
224 	return 3;
225 }
226 
227 static void
platform_add_date(struct smbtable * tbl,const char * key,int idx)228 platform_add_date(struct smbtable *tbl, const char *key, int idx)
229 {
230 	unsigned int month, day, year;
231 	char tmpbuf[128], datestr[9];
232 
233 	if (smbios_get_string(tbl, idx, tmpbuf, 128) == NULL)
234 		return;
235 	if (platform_scan_date(tmpbuf, &month, &day, &year) != 3)
236 		return;
237 	if (month == 0 || month > 12 || day == 0 || day > 31)
238 		return;
239 	if (year > 9999)
240 		return;
241 	if (year < 70)
242 		year += 2000;
243 	else if (year < 100)
244 		year += 1900;
245 	snprintf(datestr, sizeof(datestr), "%04u%02u%02u", year, month, day);
246 	pmf_set_platform(key, datestr);
247 	platform_create_sysctl(key);
248 }
249 
250 static void
platform_add_uuid(struct smbtable * tbl,const char * key,const uint8_t * buf)251 platform_add_uuid(struct smbtable *tbl, const char *key, const uint8_t *buf)
252 {
253 	struct uuid uuid;
254 	char tmpbuf[UUID_STR_LEN];
255 
256 	uuid_dec_le(buf, &uuid);
257 	uuid_snprintf(tmpbuf, sizeof(tmpbuf), &uuid);
258 
259 	pmf_set_platform(key, tmpbuf);
260 	platform_create_sysctl(key);
261 }
262 
263 static int
platform_dmi_sysctl(SYSCTLFN_ARGS)264 platform_dmi_sysctl(SYSCTLFN_ARGS)
265 {
266 	struct sysctlnode node;
267 	const char *v;
268 	int err = 0;
269 
270 	node = *rnode;
271 
272 	v = pmf_get_platform(node.sysctl_name);
273 	if (v == NULL)
274 		return ENOENT;
275 
276 	node.sysctl_data = __UNCONST(v);
277 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
278 	if (err || newp == NULL)
279 		return err;
280 
281 	return 0;
282 }
283 
284 SYSCTL_SETUP(sysctl_dmi_setup, "sysctl machdep.dmi subtree setup")
285 {
286 	const struct sysctlnode *rnode;
287 	int err;
288 
289 	err = sysctl_createv(clog, 0, NULL, &rnode,
290 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep",
291 	    NULL, NULL, 0, NULL, 0,
292 	    CTL_MACHDEP, CTL_EOL);
293 	if (err)
294 		return;
295 
296 	err = sysctl_createv(clog, 0, &rnode, &rnode,
297 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "dmi",
298 	    SYSCTL_DESCR("DMI table information"),
299 	    NULL, 0, NULL, 0,
300 	    CTL_CREATE, CTL_EOL);
301 	if (err)
302 		return;
303 
304 	platform_dminode = rnode->sysctl_num;
305 }
306