smbios.c revision 196403
1/*- 2 * Copyright (c) 2003 Matthew N. Dodd <winter@jurai.net> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/i386/bios/smbios.c 196403 2009-08-20 19:17:53Z jhb $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/kernel.h> 33#include <sys/socket.h> 34 35#include <sys/module.h> 36#include <sys/bus.h> 37 38#include <machine/bus.h> 39#include <machine/resource.h> 40#include <sys/rman.h> 41 42#include <vm/vm.h> 43#include <vm/vm_param.h> 44#include <vm/pmap.h> 45#include <machine/md_var.h> 46#include <machine/pc/bios.h> 47 48/* 49 * System Management BIOS Reference Specification, v2.4 Final 50 * http://www.dmtf.org/standards/published_documents/DSP0134.pdf 51 */ 52 53/* 54 * SMBIOS Entry Point Structure 55 */ 56struct smbios_eps { 57 u_int8_t Anchor[4]; /* '_SM_' */ 58 u_int8_t Checksum; 59 u_int8_t Length; 60 61 u_int8_t SMBIOS_Major; 62 u_int8_t SMBIOS_Minor; 63 u_int16_t Max_Size; 64 u_int8_t Revision; 65 u_int8_t Formatted_Area[5]; 66 67 u_int8_t Intermediate_Anchor[5]; /* '_DMI_' */ 68 u_int8_t Intermediate_Checksum; 69 70 u_int16_t Structure_Table_Length; 71 u_int32_t Structure_Table_Address; 72 u_int16_t Structure_Count; 73 74 u_int8_t SMBIOS_BCD_Revision; 75} __packed; 76 77struct smbios_softc { 78 device_t dev; 79 struct resource * res; 80 int rid; 81 82 struct smbios_eps * eps; 83}; 84 85#define SMBIOS_START 0xf0000 86#define SMBIOS_STEP 0x10 87#define SMBIOS_OFF 0 88#define SMBIOS_LEN 4 89#define SMBIOS_SIG "_SM_" 90 91#define RES2EPS(res) ((struct smbios_eps *)rman_get_virtual(res)) 92#define ADDR2EPS(addr) ((struct smbios_eps *)BIOS_PADDRTOVADDR(addr)) 93 94static devclass_t smbios_devclass; 95 96static void smbios_identify (driver_t *, device_t); 97static int smbios_probe (device_t); 98static int smbios_attach (device_t); 99static int smbios_detach (device_t); 100static int smbios_modevent (module_t, int, void *); 101 102static int smbios_cksum (struct smbios_eps *); 103 104static void 105smbios_identify (driver_t *driver, device_t parent) 106{ 107 device_t child; 108 u_int32_t addr; 109 int length; 110 int rid; 111 112 if (!device_is_alive(parent)) 113 return; 114 115 addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN, 116 SMBIOS_STEP, SMBIOS_OFF); 117 if (addr != 0) { 118 rid = 0; 119 length = ADDR2EPS(addr)->Length; 120 121 if (length != 0x1f) { 122 u_int8_t major, minor; 123 124 major = ADDR2EPS(addr)->SMBIOS_Major; 125 minor = ADDR2EPS(addr)->SMBIOS_Minor; 126 127 /* SMBIOS v2.1 implementation might use 0x1e. */ 128 if (length == 0x1e && major == 2 && minor == 1) 129 length = 0x1f; 130 else 131 return; 132 } 133 134 child = BUS_ADD_CHILD(parent, 5, "smbios", -1); 135 device_set_driver(child, driver); 136 bus_set_resource(child, SYS_RES_MEMORY, rid, addr, length); 137 device_set_desc(child, "System Management BIOS"); 138 } 139 140 return; 141} 142 143static int 144smbios_probe (device_t dev) 145{ 146 struct resource *res; 147 int rid; 148 int error; 149 150 error = 0; 151 rid = 0; 152 res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 153 if (res == NULL) { 154 device_printf(dev, "Unable to allocate memory resource.\n"); 155 error = ENOMEM; 156 goto bad; 157 } 158 159 if (smbios_cksum(RES2EPS(res))) { 160 device_printf(dev, "SMBIOS checksum failed.\n"); 161 error = ENXIO; 162 goto bad; 163 } 164 165bad: 166 if (res) 167 bus_release_resource(dev, SYS_RES_MEMORY, rid, res); 168 return (error); 169} 170 171static int 172smbios_attach (device_t dev) 173{ 174 struct smbios_softc *sc; 175 int error; 176 177 sc = device_get_softc(dev); 178 error = 0; 179 180 sc->dev = dev; 181 sc->rid = 0; 182 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, 183 RF_ACTIVE); 184 if (sc->res == NULL) { 185 device_printf(dev, "Unable to allocate memory resource.\n"); 186 error = ENOMEM; 187 goto bad; 188 } 189 sc->eps = RES2EPS(sc->res); 190 191 device_printf(dev, "Version: %u.%u", 192 sc->eps->SMBIOS_Major, sc->eps->SMBIOS_Minor); 193 if (bcd2bin(sc->eps->SMBIOS_BCD_Revision)) 194 printf(", BCD Revision: %u.%u", 195 bcd2bin(sc->eps->SMBIOS_BCD_Revision >> 4), 196 bcd2bin(sc->eps->SMBIOS_BCD_Revision & 0x0f)); 197 printf("\n"); 198 199 return (0); 200bad: 201 if (sc->res) 202 bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 203 return (error); 204} 205 206static int 207smbios_detach (device_t dev) 208{ 209 struct smbios_softc *sc; 210 211 sc = device_get_softc(dev); 212 213 if (sc->res) 214 bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 215 216 return (0); 217} 218 219static int 220smbios_modevent (mod, what, arg) 221 module_t mod; 222 int what; 223 void * arg; 224{ 225 device_t * devs; 226 int count; 227 int i; 228 229 switch (what) { 230 case MOD_LOAD: 231 break; 232 case MOD_UNLOAD: 233 devclass_get_devices(smbios_devclass, &devs, &count); 234 for (i = 0; i < count; i++) { 235 device_delete_child(device_get_parent(devs[i]), devs[i]); 236 } 237 break; 238 default: 239 break; 240 } 241 242 return (0); 243} 244 245static device_method_t smbios_methods[] = { 246 /* Device interface */ 247 DEVMETHOD(device_identify, smbios_identify), 248 DEVMETHOD(device_probe, smbios_probe), 249 DEVMETHOD(device_attach, smbios_attach), 250 DEVMETHOD(device_detach, smbios_detach), 251 { 0, 0 } 252}; 253 254static driver_t smbios_driver = { 255 "smbios", 256 smbios_methods, 257 sizeof(struct smbios_softc), 258}; 259 260DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_devclass, smbios_modevent, 0); 261MODULE_VERSION(smbios, 1); 262 263static int 264smbios_cksum (struct smbios_eps *e) 265{ 266 u_int8_t *ptr; 267 u_int8_t cksum; 268 int i; 269 270 ptr = (u_int8_t *)e; 271 cksum = 0; 272 for (i = 0; i < e->Length; i++) { 273 cksum += ptr[i]; 274 } 275 276 return (cksum); 277} 278