1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2003 Matthew N. Dodd <winter@jurai.net> 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/kernel.h> 32 #include <sys/malloc.h> 33 #include <sys/socket.h> 34 #include <sys/efi.h> 35 36 #include <sys/module.h> 37 #include <sys/bus.h> 38 39 #include <machine/bus.h> 40 #include <machine/resource.h> 41 #include <sys/rman.h> 42 43 #include <vm/vm.h> 44 #include <vm/vm_param.h> 45 #include <vm/pmap.h> 46 #include <machine/md_var.h> 47 #if defined(__amd64__) || defined(__i386__) 48 #include <machine/pc/bios.h> 49 #endif 50 #include <dev/smbios/smbios.h> 51 52 /* 53 * System Management BIOS Reference Specification, v2.4 Final 54 * http://www.dmtf.org/standards/published_documents/DSP0134.pdf 55 */ 56 57 struct smbios_softc { 58 device_t dev; 59 union { 60 struct smbios_eps * eps; 61 struct smbios3_eps * eps3; 62 }; 63 bool is_eps3; 64 }; 65 66 static void smbios_identify (driver_t *, device_t); 67 static int smbios_probe (device_t); 68 static int smbios_attach (device_t); 69 static int smbios_detach (device_t); 70 static int smbios_modevent (module_t, int, void *); 71 72 static int smbios_cksum (void *); 73 static bool smbios_eps3 (void *); 74 75 static void 76 smbios_identify (driver_t *driver, device_t parent) 77 { 78 #ifdef ARCH_MAY_USE_EFI 79 efi_guid_t efi_smbios = EFI_TABLE_SMBIOS; 80 efi_guid_t efi_smbios3 = EFI_TABLE_SMBIOS3; 81 void *addr_efi; 82 #endif 83 struct smbios_eps *eps; 84 struct smbios3_eps *eps3; 85 void *ptr; 86 device_t child; 87 vm_paddr_t addr = 0; 88 size_t map_size = sizeof(*eps); 89 uint8_t length; 90 91 if (!device_is_alive(parent)) 92 return; 93 94 #ifdef ARCH_MAY_USE_EFI 95 if (!efi_get_table(&efi_smbios3, &addr_efi)) { 96 addr = (vm_paddr_t)addr_efi; 97 map_size = sizeof(*eps3); 98 } else if (!efi_get_table(&efi_smbios, &addr_efi)) { 99 addr = (vm_paddr_t)addr_efi; 100 } 101 102 #endif 103 104 #if defined(__amd64__) || defined(__i386__) 105 if (addr == 0) { 106 addr = bios_sigsearch(SMBIOS_START, SMBIOS3_SIG, SMBIOS3_LEN, 107 SMBIOS_STEP, SMBIOS_OFF); 108 if (addr != 0) 109 map_size = sizeof(*eps3); 110 else 111 addr = bios_sigsearch(SMBIOS_START, 112 SMBIOS_SIG, SMBIOS_LEN, SMBIOS_STEP, SMBIOS_OFF); 113 } 114 #endif 115 116 if (addr == 0) 117 return; 118 119 ptr = pmap_mapbios(addr, map_size); 120 if (ptr == NULL) { 121 printf("smbios: Unable to map memory.\n"); 122 return; 123 } 124 if (map_size == sizeof(*eps3)) { 125 eps3 = ptr; 126 length = eps3->length; 127 if (memcmp(eps3->anchor_string, SMBIOS3_SIG, SMBIOS3_LEN) != 0) 128 goto corrupt_sig; 129 } else { 130 eps = ptr; 131 length = eps->length; 132 if (memcmp(eps->anchor_string, SMBIOS_SIG, SMBIOS_LEN) != 0) 133 goto corrupt_sig; 134 } 135 if (length != map_size) { 136 /* 137 * SMBIOS v2.1 implementations might use 0x1e because the 138 * standard was then erroneous. 139 */ 140 if (length == 0x1e && map_size == sizeof(*eps) && 141 eps->major_version == 2 && eps->minor_version == 1) 142 length = map_size; 143 else { 144 printf("smbios: %s-bit Entry Point: Invalid length: " 145 "Got %hhu, expected %zu\n", 146 map_size == sizeof(*eps3) ? "64" : "32", 147 length, map_size); 148 goto unmap_return; 149 } 150 } 151 152 child = BUS_ADD_CHILD(parent, 5, "smbios", DEVICE_UNIT_ANY); 153 device_set_driver(child, driver); 154 155 /* smuggle the phys addr into probe and attach */ 156 bus_set_resource(child, SYS_RES_MEMORY, 0, addr, length); 157 device_set_desc(child, "System Management BIOS"); 158 159 unmap_return: 160 pmap_unmapbios(ptr, map_size); 161 return; 162 163 corrupt_sig: 164 { 165 const char *sig; 166 const char *table_ver_str; 167 size_t i, end; 168 169 if (map_size == sizeof(*eps3)) { 170 sig = eps3->anchor_string; 171 table_ver_str = "64"; 172 end = SMBIOS3_LEN; 173 } else { 174 sig = eps->anchor_string; 175 table_ver_str = "32"; 176 end = SMBIOS_LEN; 177 } 178 179 /* Space after ':' printed by the loop. */ 180 printf("smbios: %s-bit Entry Point: Corrupt signature (hex):", 181 table_ver_str); 182 for (i = 0; i < end; ++i) 183 printf(" %02hhx", sig[i]); 184 printf("\n"); 185 } 186 goto unmap_return; 187 } 188 189 static int 190 smbios_probe (device_t dev) 191 { 192 vm_paddr_t pa; 193 vm_size_t size; 194 void *va; 195 int error; 196 197 error = 0; 198 199 pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); 200 size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0); 201 va = pmap_mapbios(pa, size); 202 if (va == NULL) { 203 device_printf(dev, "Unable to map memory.\n"); 204 return (ENOMEM); 205 } 206 207 if (smbios_cksum(va)) { 208 device_printf(dev, "SMBIOS checksum failed.\n"); 209 error = ENXIO; 210 } 211 212 pmap_unmapbios(va, size); 213 return (error); 214 } 215 216 static int 217 smbios_attach (device_t dev) 218 { 219 struct smbios_softc *sc; 220 void *va; 221 vm_paddr_t pa; 222 vm_size_t size; 223 224 sc = device_get_softc(dev); 225 sc->dev = dev; 226 pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); 227 size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0); 228 va = pmap_mapbios(pa, size); 229 if (va == NULL) { 230 device_printf(dev, "Unable to map memory.\n"); 231 return (ENOMEM); 232 } 233 sc->is_eps3 = smbios_eps3(va); 234 235 if (sc->is_eps3) { 236 sc->eps3 = va; 237 device_printf(dev, "Entry point: v3 (64-bit), Version: %u.%u\n", 238 sc->eps3->major_version, sc->eps3->minor_version); 239 if (bootverbose) 240 device_printf(dev, 241 "Docrev: %u, Entry Point Revision: %u\n", 242 sc->eps3->docrev, sc->eps3->entry_point_revision); 243 } else { 244 const struct smbios_eps *const eps = va; 245 const uint8_t bcd = eps->BCD_revision; 246 247 sc->eps = va; 248 device_printf(dev, "Entry point: v2.1 (32-bit), Version: %u.%u", 249 eps->major_version, eps->minor_version); 250 if (bcd < LIBKERN_LEN_BCD2BIN && bcd2bin(bcd) != 0) 251 printf(", BCD Revision: %u.%u\n", 252 bcd2bin(bcd >> 4), bcd2bin(bcd & 0x0f)); 253 else 254 printf("\n"); 255 if (bootverbose) 256 device_printf(dev, "Entry Point Revision: %u\n", 257 eps->entry_point_revision); 258 } 259 return (0); 260 } 261 262 static int 263 smbios_detach (device_t dev) 264 { 265 struct smbios_softc *sc; 266 vm_size_t size; 267 void *va; 268 269 sc = device_get_softc(dev); 270 va = (sc->is_eps3 ? (void *)sc->eps3 : (void *)sc->eps); 271 if (sc->is_eps3) 272 va = sc->eps3; 273 else 274 va = sc->eps; 275 size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0); 276 277 if (va != NULL) 278 pmap_unmapbios(va, size); 279 280 return (0); 281 } 282 283 static int 284 smbios_modevent (module_t mod, int what, void *arg) 285 { 286 device_t * devs; 287 int count; 288 int i; 289 290 switch (what) { 291 case MOD_LOAD: 292 break; 293 case MOD_UNLOAD: 294 devclass_get_devices(devclass_find("smbios"), &devs, &count); 295 for (i = 0; i < count; i++) { 296 device_delete_child(device_get_parent(devs[i]), devs[i]); 297 } 298 free(devs, M_TEMP); 299 break; 300 default: 301 break; 302 } 303 304 return (0); 305 } 306 307 static device_method_t smbios_methods[] = { 308 /* Device interface */ 309 DEVMETHOD(device_identify, smbios_identify), 310 DEVMETHOD(device_probe, smbios_probe), 311 DEVMETHOD(device_attach, smbios_attach), 312 DEVMETHOD(device_detach, smbios_detach), 313 { 0, 0 } 314 }; 315 316 static driver_t smbios_driver = { 317 "smbios", 318 smbios_methods, 319 sizeof(struct smbios_softc), 320 }; 321 322 DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_modevent, NULL); 323 #ifdef ARCH_MAY_USE_EFI 324 MODULE_DEPEND(smbios, efirt, 1, 1, 1); 325 #endif 326 MODULE_VERSION(smbios, 1); 327 328 329 static bool 330 smbios_eps3 (void *v) 331 { 332 struct smbios3_eps *e; 333 334 e = (struct smbios3_eps *)v; 335 return (memcmp(e->anchor_string, SMBIOS3_SIG, SMBIOS3_LEN) == 0); 336 } 337 338 static int 339 smbios_cksum (void *v) 340 { 341 const u_int8_t *ptr; 342 u_int8_t cksum; 343 u_int8_t length; 344 int i; 345 346 if (smbios_eps3(v)) { 347 const struct smbios3_eps *eps3 = v; 348 349 length = eps3->length; 350 } else { 351 const struct smbios_eps *eps = v; 352 353 length = eps->length; 354 } 355 ptr = v; 356 cksum = 0; 357 for (i = 0; i < length; i++) 358 cksum += ptr[i]; 359 360 return (cksum); 361 } 362