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
47static int platform_dminode = CTL_EOL;
48
49void platform_init(void); /* XXX */
50static void platform_add(struct smbtable *, const char *, int);
51static void platform_add_date(struct smbtable *, const char *, int);
52static void platform_add_uuid(struct smbtable *, const char *,
53 const uint8_t *);
54static int platform_dmi_sysctl(SYSCTLFN_PROTO);
55static void platform_print(void);
56
57/* list of private DMI sysctl nodes */
58static const char *platform_private_nodes[] = {
59 "board-serial",
60 "system-serial",
61 "system-uuid",
62 NULL
63};
64
65void
66platform_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
132static void
133platform_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
154static bool
155platform_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
168static void
169platform_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
190static void
191platform_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
204static int
205platform_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
227static void
228platform_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
250static void
251platform_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
263static int
264platform_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
284SYSCTL_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