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 struct uuid efi_smbios = EFI_TABLE_SMBIOS; 80 struct uuid 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 int 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, SMBIOS_SIG, SMBIOS_LEN, 107 SMBIOS_STEP, SMBIOS_OFF); 108 #endif 109 110 if (addr != 0) { 111 ptr = pmap_mapbios(addr, map_size); 112 if (ptr == NULL) 113 return; 114 if (map_size == sizeof (*eps3)) { 115 eps3 = ptr; 116 length = eps3->length; 117 if (memcmp(eps3->anchor_string, 118 SMBIOS3_SIG, SMBIOS3_LEN) != 0) { 119 printf("smbios3: corrupt sig %s found\n", 120 eps3->anchor_string); 121 return; 122 } 123 } else { 124 eps = ptr; 125 length = eps->length; 126 if (memcmp(eps->anchor_string, 127 SMBIOS_SIG, SMBIOS_LEN) != 0) { 128 printf("smbios: corrupt sig %s found\n", 129 eps->anchor_string); 130 return; 131 } 132 } 133 if (length != map_size) { 134 u_int8_t major, minor; 135 136 major = eps->major_version; 137 minor = eps->minor_version; 138 139 /* SMBIOS v2.1 implementation might use 0x1e. */ 140 if (length == 0x1e && major == 2 && minor == 1) { 141 length = 0x1f; 142 } else { 143 pmap_unmapbios(eps, map_size); 144 return; 145 } 146 } 147 148 child = BUS_ADD_CHILD(parent, 5, "smbios", -1); 149 device_set_driver(child, driver); 150 151 /* smuggle the phys addr into probe and attach */ 152 bus_set_resource(child, SYS_RES_MEMORY, 0, addr, length); 153 device_set_desc(child, "System Management BIOS"); 154 pmap_unmapbios(ptr, map_size); 155 } 156 157 return; 158 } 159 160 static int 161 smbios_probe (device_t dev) 162 { 163 vm_paddr_t pa; 164 vm_size_t size; 165 void *va; 166 int error; 167 168 error = 0; 169 170 pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); 171 size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0); 172 va = pmap_mapbios(pa, size); 173 if (va == NULL) { 174 device_printf(dev, "Unable to map memory.\n"); 175 return (ENOMEM); 176 } 177 178 if (smbios_cksum(va)) { 179 device_printf(dev, "SMBIOS checksum failed.\n"); 180 error = ENXIO; 181 } 182 183 pmap_unmapbios(va, size); 184 return (error); 185 } 186 187 static int 188 smbios_attach (device_t dev) 189 { 190 struct smbios_softc *sc; 191 void *va; 192 vm_paddr_t pa; 193 vm_size_t size; 194 195 sc = device_get_softc(dev); 196 sc->dev = dev; 197 pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); 198 size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0); 199 va = pmap_mapbios(pa, size); 200 if (va == NULL) { 201 device_printf(dev, "Unable to map memory.\n"); 202 return (ENOMEM); 203 } 204 sc->is_eps3 = smbios_eps3(va); 205 206 if (sc->is_eps3) { 207 sc->eps3 = va; 208 device_printf(dev, "Version: %u.%u", 209 sc->eps3->major_version, sc->eps3->minor_version); 210 } else { 211 sc->eps = va; 212 device_printf(dev, "Version: %u.%u", 213 sc->eps->major_version, sc->eps->minor_version); 214 if (bcd2bin(sc->eps->BCD_revision)) 215 printf(", BCD Revision: %u.%u", 216 bcd2bin(sc->eps->BCD_revision >> 4), 217 bcd2bin(sc->eps->BCD_revision & 0x0f)); 218 } 219 printf("\n"); 220 return (0); 221 } 222 223 static int 224 smbios_detach (device_t dev) 225 { 226 struct smbios_softc *sc; 227 vm_size_t size; 228 void *va; 229 230 sc = device_get_softc(dev); 231 va = (sc->is_eps3 ? (void *)sc->eps3 : (void *)sc->eps); 232 if (sc->is_eps3) 233 va = sc->eps3; 234 else 235 va = sc->eps; 236 size = bus_get_resource_count(dev, SYS_RES_MEMORY, 0); 237 238 if (va != NULL) 239 pmap_unmapbios(va, size); 240 241 return (0); 242 } 243 244 static int 245 smbios_modevent (module_t mod, int what, void *arg) 246 { 247 device_t * devs; 248 int count; 249 int i; 250 251 switch (what) { 252 case MOD_LOAD: 253 break; 254 case MOD_UNLOAD: 255 devclass_get_devices(devclass_find("smbios"), &devs, &count); 256 for (i = 0; i < count; i++) { 257 device_delete_child(device_get_parent(devs[i]), devs[i]); 258 } 259 free(devs, M_TEMP); 260 break; 261 default: 262 break; 263 } 264 265 return (0); 266 } 267 268 static device_method_t smbios_methods[] = { 269 /* Device interface */ 270 DEVMETHOD(device_identify, smbios_identify), 271 DEVMETHOD(device_probe, smbios_probe), 272 DEVMETHOD(device_attach, smbios_attach), 273 DEVMETHOD(device_detach, smbios_detach), 274 { 0, 0 } 275 }; 276 277 static driver_t smbios_driver = { 278 "smbios", 279 smbios_methods, 280 sizeof(struct smbios_softc), 281 }; 282 283 DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_modevent, NULL); 284 #ifdef ARCH_MAY_USE_EFI 285 MODULE_DEPEND(smbios, efirt, 1, 1, 1); 286 #endif 287 MODULE_VERSION(smbios, 1); 288 289 290 static bool 291 smbios_eps3 (void *v) 292 { 293 struct smbios3_eps *e; 294 295 e = (struct smbios3_eps *)v; 296 return (memcmp(e->anchor_string, SMBIOS3_SIG, SMBIOS3_LEN) == 0); 297 } 298 299 static int 300 smbios_cksum (void *v) 301 { 302 struct smbios3_eps *eps3; 303 struct smbios_eps *eps; 304 u_int8_t *ptr; 305 u_int8_t cksum; 306 u_int8_t length; 307 int i; 308 309 if (smbios_eps3(v)) { 310 eps3 = (struct smbios3_eps *)v; 311 length = eps3->length; 312 } else { 313 eps = (struct smbios_eps *)v; 314 length = eps->length; 315 } 316 ptr = (u_int8_t *)v; 317 cksum = 0; 318 for (i = 0; i < length; i++) { 319 cksum += ptr[i]; 320 } 321 322 return (cksum); 323 } 324