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/cdefs.h> 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/malloc.h> 34 #include <sys/socket.h> 35 #include <sys/efi.h> 36 37 #include <sys/module.h> 38 #include <sys/bus.h> 39 40 #include <machine/bus.h> 41 #include <machine/resource.h> 42 #include <sys/rman.h> 43 44 #include <vm/vm.h> 45 #include <vm/vm_param.h> 46 #include <vm/pmap.h> 47 #include <machine/md_var.h> 48 #if defined(__amd64__) || defined(__i386__) 49 #include <machine/pc/bios.h> 50 #endif 51 #include <dev/smbios/smbios.h> 52 53 /* 54 * System Management BIOS Reference Specification, v2.4 Final 55 * http://www.dmtf.org/standards/published_documents/DSP0134.pdf 56 */ 57 58 struct smbios_softc { 59 device_t dev; 60 union { 61 struct smbios_eps * eps; 62 struct smbios3_eps * eps3; 63 }; 64 bool is_eps3; 65 }; 66 67 static void smbios_identify (driver_t *, device_t); 68 static int smbios_probe (device_t); 69 static int smbios_attach (device_t); 70 static int smbios_detach (device_t); 71 static int smbios_modevent (module_t, int, void *); 72 73 static int smbios_cksum (void *); 74 static bool smbios_eps3 (void *); 75 76 static void 77 smbios_identify (driver_t *driver, device_t parent) 78 { 79 #ifdef ARCH_MAY_USE_EFI 80 struct uuid efi_smbios = EFI_TABLE_SMBIOS; 81 struct uuid efi_smbios3 = EFI_TABLE_SMBIOS3; 82 void *addr_efi; 83 #endif 84 struct smbios_eps *eps; 85 struct smbios3_eps *eps3; 86 void *ptr; 87 device_t child; 88 vm_paddr_t addr = 0; 89 size_t map_size = sizeof (*eps); 90 int length; 91 92 if (!device_is_alive(parent)) 93 return; 94 95 #ifdef ARCH_MAY_USE_EFI 96 if (!efi_get_table(&efi_smbios3, &addr_efi)) { 97 addr = (vm_paddr_t)addr_efi; 98 map_size = sizeof (*eps3); 99 } else if (!efi_get_table(&efi_smbios, &addr_efi)) { 100 addr = (vm_paddr_t)addr_efi; 101 } 102 103 #endif 104 105 #if defined(__amd64__) || defined(__i386__) 106 if (addr == 0) 107 addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN, 108 SMBIOS_STEP, SMBIOS_OFF); 109 #endif 110 111 if (addr != 0) { 112 ptr = pmap_mapbios(addr, map_size); 113 if (ptr == NULL) 114 return; 115 if (map_size == sizeof (*eps3)) { 116 eps3 = ptr; 117 length = eps3->length; 118 if (memcmp(eps3->anchor_string, 119 SMBIOS3_SIG, SMBIOS3_LEN) != 0) { 120 printf("smbios3: corrupt sig %s found\n", 121 eps3->anchor_string); 122 return; 123 } 124 } else { 125 eps = ptr; 126 length = eps->length; 127 if (memcmp(eps->anchor_string, 128 SMBIOS_SIG, SMBIOS_LEN) != 0) { 129 printf("smbios: corrupt sig %s found\n", 130 eps->anchor_string); 131 return; 132 } 133 } 134 if (length != map_size) { 135 u_int8_t major, minor; 136 137 major = eps->major_version; 138 minor = eps->minor_version; 139 140 /* SMBIOS v2.1 implementation might use 0x1e. */ 141 if (length == 0x1e && major == 2 && minor == 1) { 142 length = 0x1f; 143 } else { 144 pmap_unmapbios(eps, map_size); 145 return; 146 } 147 } 148 149 child = BUS_ADD_CHILD(parent, 5, "smbios", -1); 150 device_set_driver(child, driver); 151 152 /* smuggle the phys addr into probe and attach */ 153 bus_set_resource(child, SYS_RES_MEMORY, 0, addr, length); 154 device_set_desc(child, "System Management BIOS"); 155 pmap_unmapbios(ptr, map_size); 156 } 157 158 return; 159 } 160 161 static int 162 smbios_probe (device_t dev) 163 { 164 vm_paddr_t pa; 165 vm_size_t size; 166 void *va; 167 int error; 168 169 error = 0; 170 171 pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); 172 size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0); 173 va = pmap_mapbios(pa, size); 174 if (va == NULL) { 175 device_printf(dev, "Unable to map memory.\n"); 176 return (ENOMEM); 177 } 178 179 if (smbios_cksum(va)) { 180 device_printf(dev, "SMBIOS checksum failed.\n"); 181 error = ENXIO; 182 } 183 184 pmap_unmapbios(va, size); 185 return (error); 186 } 187 188 static int 189 smbios_attach (device_t dev) 190 { 191 struct smbios_softc *sc; 192 void *va; 193 vm_paddr_t pa; 194 vm_size_t size; 195 196 sc = device_get_softc(dev); 197 sc->dev = dev; 198 pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); 199 size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0); 200 va = pmap_mapbios(pa, size); 201 if (va == NULL) { 202 device_printf(dev, "Unable to map memory.\n"); 203 return (ENOMEM); 204 } 205 sc->is_eps3 = smbios_eps3(va); 206 207 if (sc->is_eps3) { 208 sc->eps3 = va; 209 device_printf(dev, "Version: %u.%u", 210 sc->eps3->major_version, sc->eps3->minor_version); 211 } else { 212 sc->eps = va; 213 device_printf(dev, "Version: %u.%u", 214 sc->eps->major_version, sc->eps->minor_version); 215 if (bcd2bin(sc->eps->BCD_revision)) 216 printf(", BCD Revision: %u.%u", 217 bcd2bin(sc->eps->BCD_revision >> 4), 218 bcd2bin(sc->eps->BCD_revision & 0x0f)); 219 } 220 printf("\n"); 221 return (0); 222 } 223 224 static int 225 smbios_detach (device_t dev) 226 { 227 struct smbios_softc *sc; 228 vm_size_t size; 229 void *va; 230 231 sc = device_get_softc(dev); 232 va = (sc->is_eps3 ? (void *)sc->eps3 : (void *)sc->eps); 233 if (sc->is_eps3) 234 va = sc->eps3; 235 else 236 va = sc->eps; 237 size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0); 238 239 if (va != NULL) 240 pmap_unmapbios(va, size); 241 242 return (0); 243 } 244 245 static int 246 smbios_modevent (module_t mod, int what, void *arg) 247 { 248 device_t * devs; 249 int count; 250 int i; 251 252 switch (what) { 253 case MOD_LOAD: 254 break; 255 case MOD_UNLOAD: 256 devclass_get_devices(devclass_find("smbios"), &devs, &count); 257 for (i = 0; i < count; i++) { 258 device_delete_child(device_get_parent(devs[i]), devs[i]); 259 } 260 free(devs, M_TEMP); 261 break; 262 default: 263 break; 264 } 265 266 return (0); 267 } 268 269 static device_method_t smbios_methods[] = { 270 /* Device interface */ 271 DEVMETHOD(device_identify, smbios_identify), 272 DEVMETHOD(device_probe, smbios_probe), 273 DEVMETHOD(device_attach, smbios_attach), 274 DEVMETHOD(device_detach, smbios_detach), 275 { 0, 0 } 276 }; 277 278 static driver_t smbios_driver = { 279 "smbios", 280 smbios_methods, 281 sizeof(struct smbios_softc), 282 }; 283 284 DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_modevent, NULL); 285 #ifdef ARCH_MAY_USE_EFI 286 MODULE_DEPEND(smbios, efirt, 1, 1, 1); 287 #endif 288 MODULE_VERSION(smbios, 1); 289 290 291 static bool 292 smbios_eps3 (void *v) 293 { 294 struct smbios3_eps *e; 295 296 e = (struct smbios3_eps *)v; 297 return (memcmp(e->anchor_string, SMBIOS3_SIG, SMBIOS3_LEN) == 0); 298 } 299 300 static int 301 smbios_cksum (void *v) 302 { 303 struct smbios3_eps *eps3; 304 struct smbios_eps *eps; 305 u_int8_t *ptr; 306 u_int8_t cksum; 307 u_int8_t length; 308 int i; 309 310 if (smbios_eps3(v)) { 311 eps3 = (struct smbios3_eps *)v; 312 length = eps3->length; 313 } else { 314 eps = (struct smbios_eps *)v; 315 length = eps->length; 316 } 317 ptr = (u_int8_t *)v; 318 cksum = 0; 319 for (i = 0; i < length; i++) { 320 cksum += ptr[i]; 321 } 322 323 return (cksum); 324 } 325