1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/malloc.h> 36 #include <sys/socket.h> 37 #include <sys/efi.h> 38 39 #include <sys/module.h> 40 #include <sys/bus.h> 41 42 #include <machine/bus.h> 43 #include <machine/resource.h> 44 #include <sys/rman.h> 45 46 #include <vm/vm.h> 47 #include <vm/vm_param.h> 48 #include <vm/pmap.h> 49 #include <machine/md_var.h> 50 #if defined(__amd64__) || defined(__i386__) 51 #include <machine/pc/bios.h> 52 #endif 53 #include <dev/smbios/smbios.h> 54 55 /* 56 * System Management BIOS Reference Specification, v2.4 Final 57 * http://www.dmtf.org/standards/published_documents/DSP0134.pdf 58 */ 59 60 struct smbios_softc { 61 device_t dev; 62 struct resource * res; 63 int rid; 64 65 struct smbios_eps * eps; 66 }; 67 68 #define RES2EPS(res) ((struct smbios_eps *)rman_get_virtual(res)) 69 70 static devclass_t smbios_devclass; 71 72 static void smbios_identify (driver_t *, device_t); 73 static int smbios_probe (device_t); 74 static int smbios_attach (device_t); 75 static int smbios_detach (device_t); 76 static int smbios_modevent (module_t, int, void *); 77 78 static int smbios_cksum (struct smbios_eps *); 79 80 static void 81 smbios_identify (driver_t *driver, device_t parent) 82 { 83 #ifdef ARCH_MAY_USE_EFI 84 struct uuid efi_smbios = EFI_TABLE_SMBIOS; 85 void *addr_efi; 86 #endif 87 struct smbios_eps *eps; 88 device_t child; 89 vm_paddr_t addr = 0; 90 int length; 91 int rid; 92 93 if (!device_is_alive(parent)) 94 return; 95 96 #ifdef ARCH_MAY_USE_EFI 97 if (!efi_get_table(&efi_smbios, &addr_efi)) 98 addr = (vm_paddr_t)addr_efi; 99 #endif 100 101 #if defined(__amd64__) || defined(__i386__) 102 if (addr == 0) 103 addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN, 104 SMBIOS_STEP, SMBIOS_OFF); 105 #endif 106 107 if (addr != 0) { 108 eps = pmap_mapbios(addr, 0x1f); 109 rid = 0; 110 length = eps->length; 111 112 if (length != 0x1f) { 113 u_int8_t major, minor; 114 115 major = eps->major_version; 116 minor = eps->minor_version; 117 118 /* SMBIOS v2.1 implementation might use 0x1e. */ 119 if (length == 0x1e && major == 2 && minor == 1) 120 length = 0x1f; 121 else 122 return; 123 } 124 125 child = BUS_ADD_CHILD(parent, 5, "smbios", -1); 126 device_set_driver(child, driver); 127 bus_set_resource(child, SYS_RES_MEMORY, rid, addr, length); 128 device_set_desc(child, "System Management BIOS"); 129 pmap_unmapbios((vm_offset_t)eps, 0x1f); 130 } 131 132 return; 133 } 134 135 static int 136 smbios_probe (device_t dev) 137 { 138 struct resource *res; 139 int rid; 140 int error; 141 142 error = 0; 143 rid = 0; 144 res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 145 if (res == NULL) { 146 device_printf(dev, "Unable to allocate memory resource.\n"); 147 error = ENOMEM; 148 goto bad; 149 } 150 151 if (smbios_cksum(RES2EPS(res))) { 152 device_printf(dev, "SMBIOS checksum failed.\n"); 153 error = ENXIO; 154 goto bad; 155 } 156 157 bad: 158 if (res) 159 bus_release_resource(dev, SYS_RES_MEMORY, rid, res); 160 return (error); 161 } 162 163 static int 164 smbios_attach (device_t dev) 165 { 166 struct smbios_softc *sc; 167 int error; 168 169 sc = device_get_softc(dev); 170 error = 0; 171 172 sc->dev = dev; 173 sc->rid = 0; 174 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, 175 RF_ACTIVE); 176 if (sc->res == NULL) { 177 device_printf(dev, "Unable to allocate memory resource.\n"); 178 error = ENOMEM; 179 goto bad; 180 } 181 sc->eps = RES2EPS(sc->res); 182 183 device_printf(dev, "Version: %u.%u", 184 sc->eps->major_version, sc->eps->minor_version); 185 if (bcd2bin(sc->eps->BCD_revision)) 186 printf(", BCD Revision: %u.%u", 187 bcd2bin(sc->eps->BCD_revision >> 4), 188 bcd2bin(sc->eps->BCD_revision & 0x0f)); 189 printf("\n"); 190 191 return (0); 192 bad: 193 if (sc->res) 194 bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 195 return (error); 196 } 197 198 static int 199 smbios_detach (device_t dev) 200 { 201 struct smbios_softc *sc; 202 203 sc = device_get_softc(dev); 204 205 if (sc->res) 206 bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 207 208 return (0); 209 } 210 211 static int 212 smbios_modevent (mod, what, arg) 213 module_t mod; 214 int what; 215 void * arg; 216 { 217 device_t * devs; 218 int count; 219 int i; 220 221 switch (what) { 222 case MOD_LOAD: 223 break; 224 case MOD_UNLOAD: 225 devclass_get_devices(smbios_devclass, &devs, &count); 226 for (i = 0; i < count; i++) { 227 device_delete_child(device_get_parent(devs[i]), devs[i]); 228 } 229 free(devs, M_TEMP); 230 break; 231 default: 232 break; 233 } 234 235 return (0); 236 } 237 238 static device_method_t smbios_methods[] = { 239 /* Device interface */ 240 DEVMETHOD(device_identify, smbios_identify), 241 DEVMETHOD(device_probe, smbios_probe), 242 DEVMETHOD(device_attach, smbios_attach), 243 DEVMETHOD(device_detach, smbios_detach), 244 { 0, 0 } 245 }; 246 247 static driver_t smbios_driver = { 248 "smbios", 249 smbios_methods, 250 sizeof(struct smbios_softc), 251 }; 252 253 DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_devclass, smbios_modevent, 0); 254 #ifdef ARCH_MAY_USE_EFI 255 MODULE_DEPEND(smbios, efirt, 1, 1, 1); 256 #endif 257 MODULE_VERSION(smbios, 1); 258 259 static int 260 smbios_cksum (struct smbios_eps *e) 261 { 262 u_int8_t *ptr; 263 u_int8_t cksum; 264 int i; 265 266 ptr = (u_int8_t *)e; 267 cksum = 0; 268 for (i = 0; i < e->length; i++) { 269 cksum += ptr[i]; 270 } 271 272 return (cksum); 273 } 274