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/* 31112553Smdodd * VPD decoder for IBM systems (Thinkpads) 32112553Smdodd * http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-45120 33112553Smdodd */ 34112553Smdodd 35112553Smdodd#include <sys/param.h> 36112553Smdodd#include <sys/systm.h> 37112553Smdodd#include <sys/kernel.h> 38112553Smdodd#include <sys/socket.h> 39112553Smdodd#include <sys/sysctl.h> 40112553Smdodd 41112553Smdodd#include <sys/module.h> 42112553Smdodd#include <sys/bus.h> 43112553Smdodd 44112553Smdodd#include <machine/bus.h> 45112553Smdodd#include <machine/resource.h> 46112553Smdodd#include <sys/rman.h> 47112553Smdodd 48112553Smdodd#include <vm/vm.h> 49130312Sjhb#include <vm/vm_param.h> 50112553Smdodd#include <vm/pmap.h> 51112553Smdodd#include <machine/md_var.h> 52112553Smdodd#include <machine/pc/bios.h> 53112553Smdodd 54112553Smdodd/* 55112553Smdodd * Vital Product Data 56112553Smdodd */ 57112553Smdoddstruct vpd { 58112553Smdodd u_int16_t Header; /* 0x55AA */ 59112553Smdodd u_int8_t Signature[3]; /* Always 'VPD' */ 60112553Smdodd u_int8_t Length; /* Sructure Length */ 61112553Smdodd 62112553Smdodd u_int8_t Reserved[7]; /* Reserved */ 63112553Smdodd 64112553Smdodd u_int8_t BuildID[9]; /* BIOS Build ID */ 65112553Smdodd u_int8_t BoxSerial[7]; /* Box Serial Number */ 66112553Smdodd u_int8_t PlanarSerial[11]; /* Motherboard Serial Number */ 67112553Smdodd u_int8_t MachType[7]; /* Machine Type/Model */ 68112553Smdodd u_int8_t Checksum; /* Checksum */ 69112553Smdodd} __packed; 70112553Smdodd 71112553Smdoddstruct vpd_softc { 72112553Smdodd device_t dev; 73112553Smdodd struct resource * res; 74112553Smdodd int rid; 75112553Smdodd 76112553Smdodd struct vpd * vpd; 77112553Smdodd 78112553Smdodd struct sysctl_ctx_list ctx; 79112553Smdodd 80112553Smdodd char BuildID[10]; 81112553Smdodd char BoxSerial[8]; 82112553Smdodd char PlanarSerial[12]; 83112553Smdodd char MachineType[5]; 84112553Smdodd char MachineModel[4]; 85112553Smdodd}; 86112553Smdodd 87112553Smdodd#define VPD_START 0xf0000 88112553Smdodd#define VPD_STEP 0x10 89112553Smdodd#define VPD_OFF 2 90112553Smdodd#define VPD_LEN 3 91112553Smdodd#define VPD_SIG "VPD" 92112553Smdodd 93112553Smdodd#define RES2VPD(res) ((struct vpd *)rman_get_virtual(res)) 94112553Smdodd#define ADDR2VPD(addr) ((struct vpd *)BIOS_PADDRTOVADDR(addr)) 95112553Smdodd 96112553Smdoddstatic devclass_t vpd_devclass; 97112553Smdodd 98112553Smdoddstatic void vpd_identify (driver_t *, device_t); 99112553Smdoddstatic int vpd_probe (device_t); 100112553Smdoddstatic int vpd_attach (device_t); 101112553Smdoddstatic int vpd_detach (device_t); 102112553Smdoddstatic int vpd_modevent (module_t, int, void *); 103112553Smdodd 104112553Smdoddstatic int vpd_cksum (struct vpd *); 105112553Smdodd 106248085Smariusstatic SYSCTL_NODE(_hw, OID_AUTO, vpd, CTLFLAG_RD, NULL, NULL); 107248085Smariusstatic SYSCTL_NODE(_hw_vpd, OID_AUTO, machine, CTLFLAG_RD, NULL, NULL); 108248085Smariusstatic SYSCTL_NODE(_hw_vpd_machine, OID_AUTO, type, CTLFLAG_RD, NULL, NULL); 109248085Smariusstatic SYSCTL_NODE(_hw_vpd_machine, OID_AUTO, model, CTLFLAG_RD, NULL, NULL); 110248085Smariusstatic SYSCTL_NODE(_hw_vpd, OID_AUTO, build_id, CTLFLAG_RD, NULL, NULL); 111248085Smariusstatic SYSCTL_NODE(_hw_vpd, OID_AUTO, serial, CTLFLAG_RD, NULL, NULL); 112248085Smariusstatic SYSCTL_NODE(_hw_vpd_serial, OID_AUTO, box, CTLFLAG_RD, NULL, NULL); 113248085Smariusstatic SYSCTL_NODE(_hw_vpd_serial, OID_AUTO, planar, CTLFLAG_RD, NULL, NULL); 114112553Smdodd 115112553Smdoddstatic void 116112553Smdoddvpd_identify (driver_t *driver, device_t parent) 117112553Smdodd{ 118112553Smdodd device_t child; 119112553Smdodd u_int32_t addr; 120112553Smdodd int length; 121112553Smdodd int rid; 122112553Smdodd 123112553Smdodd if (!device_is_alive(parent)) 124112553Smdodd return; 125112553Smdodd 126112553Smdodd addr = bios_sigsearch(VPD_START, VPD_SIG, VPD_LEN, VPD_STEP, VPD_OFF); 127112553Smdodd if (addr != 0) { 128112553Smdodd rid = 0; 129112553Smdodd length = ADDR2VPD(addr)->Length; 130112553Smdodd 131167742Sjhb child = BUS_ADD_CHILD(parent, 5, "vpd", -1); 132112553Smdodd device_set_driver(child, driver); 133112553Smdodd bus_set_resource(child, SYS_RES_MEMORY, rid, addr, length); 134112553Smdodd device_set_desc(child, "Vital Product Data Area"); 135112553Smdodd } 136112553Smdodd 137112553Smdodd return; 138112553Smdodd} 139112553Smdodd 140112553Smdoddstatic int 141112553Smdoddvpd_probe (device_t dev) 142112553Smdodd{ 143112553Smdodd struct resource *res; 144112553Smdodd int rid; 145112553Smdodd int error; 146112553Smdodd 147112553Smdodd error = 0; 148112553Smdodd rid = 0; 149127135Snjl res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 150112553Smdodd if (res == NULL) { 151112553Smdodd device_printf(dev, "Unable to allocate memory resource.\n"); 152112553Smdodd error = ENOMEM; 153112553Smdodd goto bad; 154112553Smdodd } 155112553Smdodd 156134581Smdodd if (vpd_cksum(RES2VPD(res))) 157134581Smdodd device_printf(dev, "VPD checksum failed. BIOS update may be required.\n"); 158112553Smdodd 159112553Smdoddbad: 160112553Smdodd if (res) 161112553Smdodd bus_release_resource(dev, SYS_RES_MEMORY, rid, res); 162112553Smdodd return (error); 163112553Smdodd} 164112553Smdodd 165112553Smdoddstatic int 166112553Smdoddvpd_attach (device_t dev) 167112553Smdodd{ 168112553Smdodd struct vpd_softc *sc; 169112553Smdodd char unit[4]; 170112553Smdodd int error; 171112553Smdodd 172112553Smdodd sc = device_get_softc(dev); 173112553Smdodd error = 0; 174112553Smdodd 175112553Smdodd sc->dev = dev; 176112553Smdodd sc->rid = 0; 177127135Snjl sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, 178127135Snjl RF_ACTIVE); 179112553Smdodd if (sc->res == NULL) { 180112553Smdodd device_printf(dev, "Unable to allocate memory resource.\n"); 181112553Smdodd error = ENOMEM; 182112553Smdodd goto bad; 183112553Smdodd } 184112553Smdodd sc->vpd = RES2VPD(sc->res); 185112553Smdodd 186112553Smdodd snprintf(unit, sizeof(unit), "%d", device_get_unit(sc->dev)); 187112553Smdodd snprintf(sc->MachineType, 5, "%.4s", sc->vpd->MachType); 188112553Smdodd snprintf(sc->MachineModel, 4, "%.3s", sc->vpd->MachType+4); 189112553Smdodd snprintf(sc->BuildID, 10, "%.9s", sc->vpd->BuildID); 190112553Smdodd snprintf(sc->BoxSerial, 8, "%.7s", sc->vpd->BoxSerial); 191112553Smdodd snprintf(sc->PlanarSerial, 12, "%.11s", sc->vpd->PlanarSerial); 192112553Smdodd 193112553Smdodd sysctl_ctx_init(&sc->ctx); 194112553Smdodd SYSCTL_ADD_STRING(&sc->ctx, 195112553Smdodd SYSCTL_STATIC_CHILDREN(_hw_vpd_machine_type), OID_AUTO, 196112553Smdodd unit, CTLFLAG_RD|CTLFLAG_DYN, sc->MachineType, 0, NULL); 197112553Smdodd SYSCTL_ADD_STRING(&sc->ctx, 198112553Smdodd SYSCTL_STATIC_CHILDREN(_hw_vpd_machine_model), OID_AUTO, 199112553Smdodd unit, CTLFLAG_RD|CTLFLAG_DYN, sc->MachineModel, 0, NULL); 200112553Smdodd SYSCTL_ADD_STRING(&sc->ctx, 201112553Smdodd SYSCTL_STATIC_CHILDREN(_hw_vpd_build_id), OID_AUTO, 202112553Smdodd unit, CTLFLAG_RD|CTLFLAG_DYN, sc->BuildID, 0, NULL); 203112553Smdodd SYSCTL_ADD_STRING(&sc->ctx, 204112553Smdodd SYSCTL_STATIC_CHILDREN(_hw_vpd_serial_box), OID_AUTO, 205112553Smdodd unit, CTLFLAG_RD|CTLFLAG_DYN, sc->BoxSerial, 0, NULL); 206112553Smdodd SYSCTL_ADD_STRING(&sc->ctx, 207112553Smdodd SYSCTL_STATIC_CHILDREN(_hw_vpd_serial_planar), OID_AUTO, 208112553Smdodd unit, CTLFLAG_RD|CTLFLAG_DYN, sc->PlanarSerial, 0, NULL); 209112553Smdodd 210112553Smdodd device_printf(dev, "Machine Type: %.4s, Model: %.3s, Build ID: %.9s\n", 211112553Smdodd sc->MachineType, sc->MachineModel, sc->BuildID); 212112553Smdodd device_printf(dev, "Box Serial: %.7s, Planar Serial: %.11s\n", 213112553Smdodd sc->BoxSerial, sc->PlanarSerial); 214112553Smdodd 215112553Smdodd return (0); 216112553Smdoddbad: 217112553Smdodd if (sc->res) 218112553Smdodd bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 219112553Smdodd return (error); 220112553Smdodd} 221112553Smdodd 222112553Smdoddstatic int 223112553Smdoddvpd_detach (device_t dev) 224112553Smdodd{ 225112553Smdodd struct vpd_softc *sc; 226112553Smdodd 227112553Smdodd sc = device_get_softc(dev); 228112553Smdodd 229112553Smdodd if (sc->res) 230112553Smdodd bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 231112553Smdodd 232112553Smdodd sysctl_ctx_free(&sc->ctx); 233112553Smdodd 234112553Smdodd return (0); 235112553Smdodd} 236112553Smdodd 237112553Smdoddstatic int 238112553Smdoddvpd_modevent (mod, what, arg) 239112553Smdodd module_t mod; 240112553Smdodd int what; 241112553Smdodd void * arg; 242112553Smdodd{ 243112553Smdodd device_t * devs; 244112553Smdodd int count; 245112553Smdodd int i; 246112553Smdodd 247112553Smdodd switch (what) { 248112553Smdodd case MOD_LOAD: 249112553Smdodd break; 250112553Smdodd case MOD_UNLOAD: 251112553Smdodd devclass_get_devices(vpd_devclass, &devs, &count); 252112553Smdodd for (i = 0; i < count; i++) { 253112553Smdodd device_delete_child(device_get_parent(devs[i]), devs[i]); 254112553Smdodd } 255112553Smdodd break; 256112553Smdodd default: 257112553Smdodd break; 258112553Smdodd } 259112553Smdodd 260112553Smdodd return (0); 261112553Smdodd} 262112553Smdodd 263112553Smdoddstatic device_method_t vpd_methods[] = { 264112553Smdodd /* Device interface */ 265112553Smdodd DEVMETHOD(device_identify, vpd_identify), 266112553Smdodd DEVMETHOD(device_probe, vpd_probe), 267112553Smdodd DEVMETHOD(device_attach, vpd_attach), 268112553Smdodd DEVMETHOD(device_detach, vpd_detach), 269112553Smdodd { 0, 0 } 270112553Smdodd}; 271112553Smdodd 272112553Smdoddstatic driver_t vpd_driver = { 273112553Smdodd "vpd", 274112553Smdodd vpd_methods, 275112553Smdodd sizeof(struct vpd_softc), 276112553Smdodd}; 277112553Smdodd 278112553SmdoddDRIVER_MODULE(vpd, nexus, vpd_driver, vpd_devclass, vpd_modevent, 0); 279112553SmdoddMODULE_VERSION(vpd, 1); 280112553Smdodd 281134581Smdodd/* 282134581Smdodd * Perform a checksum over the VPD structure, starting with 283134581Smdodd * the BuildID. (Jean Delvare <khali@linux-fr.org>) 284134581Smdodd */ 285112553Smdoddstatic int 286112553Smdoddvpd_cksum (struct vpd *v) 287112553Smdodd{ 288112553Smdodd u_int8_t *ptr; 289112553Smdodd u_int8_t cksum; 290112553Smdodd int i; 291112553Smdodd 292112553Smdodd ptr = (u_int8_t *)v; 293112553Smdodd cksum = 0; 294134581Smdodd for (i = offsetof(struct vpd, BuildID); i < v->Length ; i++) 295112553Smdodd cksum += ptr[i]; 296112553Smdodd return (cksum); 297112553Smdodd} 298