1 /* $NetBSD: smbios_platform.c,v 1.1 2021/07/21 23:26:15 jmcneill 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: smbios_platform.c,v 1.1 2021/07/21 23:26:15 jmcneill 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 <dev/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_word(struct smbtable *, const char *, uint16_t, 52 const char *); 53 static void platform_add_date(struct smbtable *, const char *, int); 54 static void platform_add_uuid(struct smbtable *, const char *, 55 const uint8_t *); 56 static int platform_dmi_sysctl(SYSCTLFN_PROTO); 57 58 /* list of private DMI sysctl nodes */ 59 static const char *platform_private_nodes[] = { 60 "chassis-serial", 61 "board-serial", 62 "system-serial", 63 "system-uuid", 64 NULL 65 }; 66 67 void 68 platform_init(void) 69 { 70 struct smbtable smbios; 71 struct smbios_sys *psys; 72 struct smbios_struct_bios *pbios; 73 struct smbios_board *pboard; 74 struct smbios_chassis *pchassis; 75 struct smbios_processor *pproc; 76 struct smbios_slot *pslot; 77 int nisa, nother; 78 79 smbios.cookie = 0; 80 if (smbios_find_table(SMBIOS_TYPE_SYSTEM, &smbios)) { 81 psys = smbios.tblhdr; 82 83 platform_add(&smbios, "system-vendor", psys->vendor); 84 platform_add(&smbios, "system-product", psys->product); 85 platform_add(&smbios, "system-version", psys->version); 86 platform_add(&smbios, "system-serial", psys->serial); 87 platform_add_uuid(&smbios, "system-uuid", psys->uuid); 88 } 89 90 smbios.cookie = 0; 91 if (smbios_find_table(SMBIOS_TYPE_BIOS, &smbios)) { 92 pbios = smbios.tblhdr; 93 94 platform_add(&smbios, "bios-vendor", pbios->vendor); 95 platform_add(&smbios, "bios-version", pbios->version); 96 platform_add_date(&smbios, "bios-date", pbios->release); 97 } 98 99 smbios.cookie = 0; 100 if (smbios_find_table(SMBIOS_TYPE_BASEBOARD, &smbios)) { 101 pboard = smbios.tblhdr; 102 103 platform_add(&smbios, "board-vendor", pboard->vendor); 104 platform_add(&smbios, "board-product", pboard->product); 105 platform_add(&smbios, "board-version", pboard->version); 106 platform_add(&smbios, "board-serial", pboard->serial); 107 platform_add(&smbios, "board-asset-tag", pboard->asset); 108 } 109 110 smbios.cookie = 0; 111 if (smbios_find_table(SMBIOS_TYPE_ENCLOSURE, &smbios)) { 112 pchassis = smbios.tblhdr; 113 114 platform_add(&smbios, "chassis-vendor", pchassis->vendor); 115 platform_add(&smbios, "chassis-type", pchassis->shape); 116 platform_add(&smbios, "chassis-version", pchassis->version); 117 platform_add(&smbios, "chassis-serial", pchassis->serial); 118 platform_add(&smbios, "chassis-asset-tag", pchassis->asset); 119 } 120 121 smbios.cookie = 0; 122 if (smbios_find_table(SMBIOS_TYPE_PROCESSOR, &smbios)) { 123 pproc = smbios.tblhdr; 124 125 platform_add(&smbios, "processor-vendor", pproc->vendor); 126 platform_add(&smbios, "processor-version", pproc->version); 127 platform_add_word(&smbios, "processor-frequency", 128 pproc->curspeed, " MHz"); 129 } 130 131 smbios.cookie = 0; 132 nisa = 0; 133 nother = 0; 134 while (smbios_find_table(SMBIOS_TYPE_SLOTS, &smbios)) { 135 pslot = smbios.tblhdr; 136 switch (pslot->type) { 137 case SMBIOS_SLOT_ISA: 138 case SMBIOS_SLOT_EISA: 139 nisa++; 140 break; 141 default: 142 nother++; 143 break; 144 } 145 } 146 147 #if NISA > 0 148 if ((nother | nisa) != 0) { 149 /* Only if there seems to be good expansion slot info. */ 150 isa_set_slotcount(nisa); 151 } 152 #endif 153 } 154 155 static bool 156 platform_sysctl_is_private(const char *key) 157 { 158 unsigned int n; 159 160 for (n = 0; platform_private_nodes[n] != NULL; n++) { 161 if (strcmp(key, platform_private_nodes[n]) == 0) { 162 return true; 163 } 164 } 165 166 return false; 167 } 168 169 static void 170 platform_create_sysctl(const char *key) 171 { 172 int flags = 0, err; 173 174 if (pmf_get_platform(key) == NULL) 175 return; 176 177 /* If the key is marked private, set CTLFLAG_PRIVATE flag */ 178 if (platform_sysctl_is_private(key)) 179 flags |= CTLFLAG_PRIVATE; 180 181 err = sysctl_createv(NULL, 0, NULL, NULL, 182 CTLFLAG_READONLY | flags, CTLTYPE_STRING, 183 key, NULL, platform_dmi_sysctl, 0, NULL, 0, 184 CTL_MACHDEP, platform_dminode, CTL_CREATE, CTL_EOL); 185 if (err != 0) 186 printf("platform: sysctl_createv " 187 "(machdep.dmi.%s) failed, err = %d\n", 188 key, err); 189 } 190 191 static void 192 platform_add(struct smbtable *tbl, const char *key, int idx) 193 { 194 char tmpbuf[128]; /* XXX is this long enough? */ 195 196 if (smbios_get_string(tbl, idx, tmpbuf, 128) != NULL) { 197 /* add to platform dictionary */ 198 pmf_set_platform(key, tmpbuf); 199 200 /* create sysctl node */ 201 platform_create_sysctl(key); 202 } 203 } 204 205 static void 206 platform_add_word(struct smbtable *tbl, const char *key, uint16_t val, 207 const char *suf) 208 { 209 char tmpbuf[128]; /* XXX is this long enough? */ 210 211 if (snprintf(tmpbuf, sizeof(tmpbuf), "%u%s", val, suf)) { 212 /* add to platform dictionary */ 213 pmf_set_platform(key, tmpbuf); 214 215 /* create sysctl node */ 216 platform_create_sysctl(key); 217 } 218 } 219 220 static int 221 platform_scan_date(char *buf, unsigned int *month, unsigned int *day, 222 unsigned int *year) 223 { 224 char *p, *s; 225 226 s = buf; 227 p = strchr(s, '/'); 228 if (p) *p = '\0'; 229 *month = strtoul(s, NULL, 10); 230 if (!p) return 1; 231 232 s = p + 1; 233 p = strchr(s, '/'); 234 if (p) *p = '\0'; 235 *day = strtoul(s, NULL, 10); 236 if (!p) return 2; 237 238 s = p + 1; 239 *year = strtoul(s, NULL, 10); 240 return 3; 241 } 242 243 static void 244 platform_add_date(struct smbtable *tbl, const char *key, int idx) 245 { 246 unsigned int month, day, year; 247 char tmpbuf[128], datestr[9]; 248 249 if (smbios_get_string(tbl, idx, tmpbuf, 128) == NULL) 250 return; 251 if (platform_scan_date(tmpbuf, &month, &day, &year) != 3) 252 return; 253 if (month == 0 || month > 12 || day == 0 || day > 31) 254 return; 255 if (year > 9999) 256 return; 257 if (year < 70) 258 year += 2000; 259 else if (year < 100) 260 year += 1900; 261 snprintf(datestr, sizeof(datestr), "%04u%02u%02u", year, month, day); 262 pmf_set_platform(key, datestr); 263 platform_create_sysctl(key); 264 } 265 266 static void 267 platform_add_uuid(struct smbtable *tbl, const char *key, const uint8_t *buf) 268 { 269 struct uuid uuid; 270 char tmpbuf[UUID_STR_LEN]; 271 272 uuid_dec_le(buf, &uuid); 273 uuid_snprintf(tmpbuf, sizeof(tmpbuf), &uuid); 274 275 pmf_set_platform(key, tmpbuf); 276 platform_create_sysctl(key); 277 } 278 279 static int 280 platform_dmi_sysctl(SYSCTLFN_ARGS) 281 { 282 struct sysctlnode node; 283 const char *v; 284 int err = 0; 285 286 node = *rnode; 287 288 v = pmf_get_platform(node.sysctl_name); 289 if (v == NULL) 290 return ENOENT; 291 292 node.sysctl_data = __UNCONST(v); 293 err = sysctl_lookup(SYSCTLFN_CALL(&node)); 294 if (err || newp == NULL) 295 return err; 296 297 return 0; 298 } 299 300 SYSCTL_SETUP(sysctl_dmi_setup, "sysctl machdep.dmi subtree setup") 301 { 302 const struct sysctlnode *rnode; 303 int err; 304 305 err = sysctl_createv(clog, 0, NULL, &rnode, 306 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", 307 NULL, NULL, 0, NULL, 0, 308 CTL_MACHDEP, CTL_EOL); 309 if (err) 310 return; 311 312 err = sysctl_createv(clog, 0, &rnode, &rnode, 313 CTLFLAG_PERMANENT, CTLTYPE_NODE, "dmi", 314 SYSCTL_DESCR("DMI table information"), 315 NULL, 0, NULL, 0, 316 CTL_CREATE, CTL_EOL); 317 if (err) 318 return; 319 320 platform_dminode = rnode->sysctl_num; 321 } 322