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$"); 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 53struct smbios_softc { 54 device_t dev; 55 struct resource * res; 56 int rid; 57 58 struct smbios_eps * eps; 59}; 60 61#define RES2EPS(res) ((struct smbios_eps *)rman_get_virtual(res)) 62#define ADDR2EPS(addr) ((struct smbios_eps *)BIOS_PADDRTOVADDR(addr)) 63 64static devclass_t smbios_devclass; 65 66static void smbios_identify (driver_t *, device_t); 67static int smbios_probe (device_t); 68static int smbios_attach (device_t); 69static int smbios_detach (device_t); 70static int smbios_modevent (module_t, int, void *); 71 72static int smbios_cksum (struct smbios_eps *); 73 74static void 75smbios_identify (driver_t *driver, device_t parent) 76{ 77 device_t child; 78 u_int32_t addr; 79 int length; 80 int rid; 81 82 if (!device_is_alive(parent)) 83 return; 84 85 addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN, 86 SMBIOS_STEP, SMBIOS_OFF); 87 if (addr != 0) { 88 rid = 0; 89 length = ADDR2EPS(addr)->length; 90 91 if (length != 0x1f) { 92 u_int8_t major, minor; 93 94 major = ADDR2EPS(addr)->major_version; 95 minor = ADDR2EPS(addr)->minor_version; 96 97 /* SMBIOS v2.1 implementation might use 0x1e. */ 98 if (length == 0x1e && major == 2 && minor == 1) 99 length = 0x1f; 100 else 101 return; 102 } 103 104 child = BUS_ADD_CHILD(parent, 5, "smbios", -1); 105 device_set_driver(child, driver); 106 bus_set_resource(child, SYS_RES_MEMORY, rid, addr, length); 107 device_set_desc(child, "System Management BIOS"); 108 } 109 110 return; 111} 112 113static int 114smbios_probe (device_t dev) 115{ 116 struct resource *res; 117 int rid; 118 int error; 119 120 error = 0; 121 rid = 0; 122 res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 123 if (res == NULL) { 124 device_printf(dev, "Unable to allocate memory resource.\n"); 125 error = ENOMEM; 126 goto bad; 127 } 128 129 if (smbios_cksum(RES2EPS(res))) { 130 device_printf(dev, "SMBIOS checksum failed.\n"); 131 error = ENXIO; 132 goto bad; 133 } 134 135bad: 136 if (res) 137 bus_release_resource(dev, SYS_RES_MEMORY, rid, res); 138 return (error); 139} 140 141static int 142smbios_attach (device_t dev) 143{ 144 struct smbios_softc *sc; 145 int error; 146 147 sc = device_get_softc(dev); 148 error = 0; 149 150 sc->dev = dev; 151 sc->rid = 0; 152 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, 153 RF_ACTIVE); 154 if (sc->res == NULL) { 155 device_printf(dev, "Unable to allocate memory resource.\n"); 156 error = ENOMEM; 157 goto bad; 158 } 159 sc->eps = RES2EPS(sc->res); 160 161 device_printf(dev, "Version: %u.%u", 162 sc->eps->major_version, sc->eps->minor_version); 163 if (bcd2bin(sc->eps->BCD_revision)) 164 printf(", BCD Revision: %u.%u", 165 bcd2bin(sc->eps->BCD_revision >> 4), 166 bcd2bin(sc->eps->BCD_revision & 0x0f)); 167 printf("\n"); 168 169 return (0); 170bad: 171 if (sc->res) 172 bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 173 return (error); 174} 175 176static int 177smbios_detach (device_t dev) 178{ 179 struct smbios_softc *sc; 180 181 sc = device_get_softc(dev); 182 183 if (sc->res) 184 bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 185 186 return (0); 187} 188 189static int 190smbios_modevent (mod, what, arg) 191 module_t mod; 192 int what; 193 void * arg; 194{ 195 device_t * devs; 196 int count; 197 int i; 198 199 switch (what) { 200 case MOD_LOAD: 201 break; 202 case MOD_UNLOAD: 203 devclass_get_devices(smbios_devclass, &devs, &count); 204 for (i = 0; i < count; i++) { 205 device_delete_child(device_get_parent(devs[i]), devs[i]); 206 } 207 break; 208 default: 209 break; 210 } 211 212 return (0); 213} 214 215static device_method_t smbios_methods[] = { 216 /* Device interface */ 217 DEVMETHOD(device_identify, smbios_identify), 218 DEVMETHOD(device_probe, smbios_probe), 219 DEVMETHOD(device_attach, smbios_attach), 220 DEVMETHOD(device_detach, smbios_detach), 221 { 0, 0 } 222}; 223 224static driver_t smbios_driver = { 225 "smbios", 226 smbios_methods, 227 sizeof(struct smbios_softc), 228}; 229 230DRIVER_MODULE(smbios, nexus, smbios_driver, smbios_devclass, smbios_modevent, 0); 231MODULE_VERSION(smbios, 1); 232 233static int 234smbios_cksum (struct smbios_eps *e) 235{ 236 u_int8_t *ptr; 237 u_int8_t cksum; 238 int i; 239 240 ptr = (u_int8_t *)e; 241 cksum = 0; 242 for (i = 0; i < e->length; i++) { 243 cksum += ptr[i]; 244 } 245 246 return (cksum); 247} 248