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