1112553Smdodd/*- 2112553Smdodd * Copyright (c) 2003 Matthew N. Dodd <winter@jurai.net> 3112553Smdodd * All rights reserved. 4112553Smdodd * 5112553Smdodd * Redistribution and use in source and binary forms, with or without 6112553Smdodd * modification, are permitted provided that the following conditions 7112553Smdodd * are met: 8112553Smdodd * 1. Redistributions of source code must retain the above copyright 9112553Smdodd * notice, this list of conditions and the following disclaimer. 10112553Smdodd * 2. Redistributions in binary form must reproduce the above copyright 11112553Smdodd * notice, this list of conditions and the following disclaimer in the 12112553Smdodd * documentation and/or other materials provided with the distribution. 13112553Smdodd * 14112553Smdodd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15112553Smdodd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16112553Smdodd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17112553Smdodd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18112553Smdodd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19112553Smdodd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20112553Smdodd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21112553Smdodd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22112553Smdodd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23112553Smdodd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24112553Smdodd * SUCH DAMAGE. 25112553Smdodd */ 26112553Smdodd 27115679Sobrien#include <sys/cdefs.h> 28115679Sobrien__FBSDID("$FreeBSD$"); 29115679Sobrien 30112553Smdodd#include <sys/param.h> 31112553Smdodd#include <sys/systm.h> 32112553Smdodd#include <sys/kernel.h> 33241073Skevlo#include <sys/malloc.h> 34112553Smdodd#include <sys/socket.h> 35112553Smdodd 36112553Smdodd#include <sys/module.h> 37112553Smdodd#include <sys/bus.h> 38112553Smdodd 39112553Smdodd#include <machine/bus.h> 40112553Smdodd#include <machine/resource.h> 41112553Smdodd#include <sys/rman.h> 42112553Smdodd 43112553Smdodd#include <vm/vm.h> 44130312Sjhb#include <vm/vm_param.h> 45112553Smdodd#include <vm/pmap.h> 46112553Smdodd#include <machine/md_var.h> 47112553Smdodd#include <machine/pc/bios.h> 48112553Smdodd 49112553Smdodd/* 50148217Sjkim * System Management BIOS Reference Specification, v2.4 Final 51148217Sjkim * http://www.dmtf.org/standards/published_documents/DSP0134.pdf 52148217Sjkim */ 53148217Sjkim 54112553Smdoddstruct smbios_softc { 55112553Smdodd device_t dev; 56112553Smdodd struct resource * res; 57112553Smdodd int rid; 58112553Smdodd 59112553Smdodd struct smbios_eps * eps; 60112553Smdodd}; 61112553Smdodd 62112553Smdodd#define RES2EPS(res) ((struct smbios_eps *)rman_get_virtual(res)) 63112553Smdodd#define ADDR2EPS(addr) ((struct smbios_eps *)BIOS_PADDRTOVADDR(addr)) 64112553Smdodd 65112553Smdoddstatic devclass_t smbios_devclass; 66112553Smdodd 67112553Smdoddstatic void smbios_identify (driver_t *, device_t); 68112553Smdoddstatic int smbios_probe (device_t); 69112553Smdoddstatic int smbios_attach (device_t); 70112553Smdoddstatic int smbios_detach (device_t); 71112553Smdoddstatic int smbios_modevent (module_t, int, void *); 72112553Smdodd 73112553Smdoddstatic int smbios_cksum (struct smbios_eps *); 74112553Smdodd 75112553Smdoddstatic void 76112553Smdoddsmbios_identify (driver_t *driver, device_t parent) 77112553Smdodd{ 78112553Smdodd device_t child; 79112553Smdodd u_int32_t addr; 80112553Smdodd int length; 81112553Smdodd int rid; 82112553Smdodd 83112553Smdodd if (!device_is_alive(parent)) 84112553Smdodd return; 85112553Smdodd 86112553Smdodd addr = bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN, 87112553Smdodd SMBIOS_STEP, SMBIOS_OFF); 88112553Smdodd if (addr != 0) { 89112553Smdodd rid = 0; 90241027Sjhb length = ADDR2EPS(addr)->length; 91112553Smdodd 92148217Sjkim if (length != 0x1f) { 93148217Sjkim u_int8_t major, minor; 94148217Sjkim 95241027Sjhb major = ADDR2EPS(addr)->major_version; 96241027Sjhb minor = ADDR2EPS(addr)->minor_version; 97148217Sjkim 98148217Sjkim /* SMBIOS v2.1 implementation might use 0x1e. */ 99148217Sjkim if (length == 0x1e && major == 2 && minor == 1) 100148217Sjkim length = 0x1f; 101148217Sjkim else 102148217Sjkim return; 103148217Sjkim } 104148217Sjkim 105167742Sjhb child = BUS_ADD_CHILD(parent, 5, "smbios", -1); 106112553Smdodd device_set_driver(child, driver); 107112553Smdodd bus_set_resource(child, SYS_RES_MEMORY, rid, addr, length); 108112553Smdodd device_set_desc(child, "System Management BIOS"); 109112553Smdodd } 110112553Smdodd 111112553Smdodd return; 112112553Smdodd} 113112553Smdodd 114112553Smdoddstatic int 115112553Smdoddsmbios_probe (device_t dev) 116112553Smdodd{ 117112553Smdodd struct resource *res; 118112553Smdodd int rid; 119112553Smdodd int error; 120112553Smdodd 121112553Smdodd error = 0; 122112553Smdodd rid = 0; 123127135Snjl res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 124112553Smdodd if (res == NULL) { 125112553Smdodd device_printf(dev, "Unable to allocate memory resource.\n"); 126112553Smdodd error = ENOMEM; 127112553Smdodd goto bad; 128112553Smdodd } 129112553Smdodd 130112553Smdodd if (smbios_cksum(RES2EPS(res))) { 131112553Smdodd device_printf(dev, "SMBIOS checksum failed.\n"); 132112553Smdodd error = ENXIO; 133112553Smdodd goto bad; 134112553Smdodd } 135112553Smdodd 136112553Smdoddbad: 137112553Smdodd if (res) 138112553Smdodd bus_release_resource(dev, SYS_RES_MEMORY, rid, res); 139112553Smdodd return (error); 140112553Smdodd} 141112553Smdodd 142112553Smdoddstatic int 143112553Smdoddsmbios_attach (device_t dev) 144112553Smdodd{ 145112553Smdodd struct smbios_softc *sc; 146112553Smdodd int error; 147112553Smdodd 148112553Smdodd sc = device_get_softc(dev); 149112553Smdodd error = 0; 150112553Smdodd 151112553Smdodd sc->dev = dev; 152112553Smdodd sc->rid = 0; 153127135Snjl sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, 154127135Snjl RF_ACTIVE); 155112553Smdodd if (sc->res == NULL) { 156112553Smdodd device_printf(dev, "Unable to allocate memory resource.\n"); 157112553Smdodd error = ENOMEM; 158112553Smdodd goto bad; 159112553Smdodd } 160112553Smdodd sc->eps = RES2EPS(sc->res); 161112553Smdodd 162148217Sjkim device_printf(dev, "Version: %u.%u", 163241027Sjhb sc->eps->major_version, sc->eps->minor_version); 164241027Sjhb if (bcd2bin(sc->eps->BCD_revision)) 165148217Sjkim printf(", BCD Revision: %u.%u", 166241027Sjhb bcd2bin(sc->eps->BCD_revision >> 4), 167241027Sjhb bcd2bin(sc->eps->BCD_revision & 0x0f)); 168112553Smdodd printf("\n"); 169112553Smdodd 170112553Smdodd return (0); 171112553Smdoddbad: 172112553Smdodd if (sc->res) 173112553Smdodd bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 174112553Smdodd return (error); 175112553Smdodd} 176112553Smdodd 177112553Smdoddstatic int 178112553Smdoddsmbios_detach (device_t dev) 179112553Smdodd{ 180112553Smdodd struct smbios_softc *sc; 181112553Smdodd 182112553Smdodd sc = device_get_softc(dev); 183112553Smdodd 184112553Smdodd if (sc->res) 185112553Smdodd bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 186112553Smdodd 187112553Smdodd return (0); 188112553Smdodd} 189112553Smdodd 190112553Smdoddstatic int 191112553Smdoddsmbios_modevent (mod, what, arg) 192112553Smdodd module_t mod; 193112553Smdodd int what; 194112553Smdodd void * arg; 195112553Smdodd{ 196112553Smdodd device_t * devs; 197112553Smdodd int count; 198112553Smdodd int i; 199112553Smdodd 200112553Smdodd switch (what) { 201112553Smdodd case MOD_LOAD: 202112553Smdodd break; 203112553Smdodd case MOD_UNLOAD: 204112553Smdodd devclass_get_devices(smbios_devclass, &devs, &count); 205112553Smdodd for (i = 0; i < count; i++) { 206112553Smdodd device_delete_child(device_get_parent(devs[i]), devs[i]); 207112553Smdodd } 208241066Skevlo free(devs, M_TEMP); 209112553Smdodd break; 210112553Smdodd default: 211112553Smdodd break; 212112553Smdodd } 213112553Smdodd 214112553Smdodd return (0); 215112553Smdodd} 216112553Smdodd 217112553Smdoddstatic device_method_t smbios_methods[] = { 218112553Smdodd /* Device interface */ 219112553Smdodd DEVMETHOD(device_identify, smbios_identify), 220112553Smdodd DEVMETHOD(device_probe, smbios_probe), 221112553Smdodd DEVMETHOD(device_attach, smbios_attach), 222112553Smdodd DEVMETHOD(device_detach, smbios_detach), 223112553Smdodd { 0, 0 } 224112553Smdodd}; 225112553Smdodd 226112553Smdoddstatic driver_t smbios_driver = { 227112553Smdodd "smbios", 228112553Smdodd smbios_methods, 229112553Smdodd sizeof(struct smbios_softc), 230112553Smdodd}; 231112553Smdodd 232112553SmdoddDRIVER_MODULE(smbios, nexus, smbios_driver, smbios_devclass, smbios_modevent, 0); 233112553SmdoddMODULE_VERSION(smbios, 1); 234112553Smdodd 235112553Smdoddstatic int 236112553Smdoddsmbios_cksum (struct smbios_eps *e) 237112553Smdodd{ 238112553Smdodd u_int8_t *ptr; 239112553Smdodd u_int8_t cksum; 240112553Smdodd int i; 241112553Smdodd 242112553Smdodd ptr = (u_int8_t *)e; 243112553Smdodd cksum = 0; 244241027Sjhb for (i = 0; i < e->length; i++) { 245112553Smdodd cksum += ptr[i]; 246112553Smdodd } 247112553Smdodd 248112553Smdodd return (cksum); 249112553Smdodd} 250