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