17534ac7aSMatthew N. Dodd /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 383ef78beSPedro F. Giffuni * 47534ac7aSMatthew N. Dodd * Copyright (c) 2003 Matthew N. Dodd <winter@jurai.net> 57534ac7aSMatthew N. Dodd * All rights reserved. 67534ac7aSMatthew N. Dodd * 77534ac7aSMatthew N. Dodd * Redistribution and use in source and binary forms, with or without 87534ac7aSMatthew N. Dodd * modification, are permitted provided that the following conditions 97534ac7aSMatthew N. Dodd * are met: 107534ac7aSMatthew N. Dodd * 1. Redistributions of source code must retain the above copyright 117534ac7aSMatthew N. Dodd * notice, this list of conditions and the following disclaimer. 127534ac7aSMatthew N. Dodd * 2. Redistributions in binary form must reproduce the above copyright 137534ac7aSMatthew N. Dodd * notice, this list of conditions and the following disclaimer in the 147534ac7aSMatthew N. Dodd * documentation and/or other materials provided with the distribution. 157534ac7aSMatthew N. Dodd * 167534ac7aSMatthew N. Dodd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 177534ac7aSMatthew N. Dodd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 187534ac7aSMatthew N. Dodd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 197534ac7aSMatthew N. Dodd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 207534ac7aSMatthew N. Dodd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 217534ac7aSMatthew N. Dodd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 227534ac7aSMatthew N. Dodd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 237534ac7aSMatthew N. Dodd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 247534ac7aSMatthew N. Dodd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 257534ac7aSMatthew N. Dodd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 267534ac7aSMatthew N. Dodd * SUCH DAMAGE. 277534ac7aSMatthew N. Dodd */ 287534ac7aSMatthew N. Dodd 297534ac7aSMatthew N. Dodd #include <sys/param.h> 307534ac7aSMatthew N. Dodd #include <sys/systm.h> 317534ac7aSMatthew N. Dodd #include <sys/kernel.h> 32954c5baeSKevin Lo #include <sys/malloc.h> 337534ac7aSMatthew N. Dodd 347534ac7aSMatthew N. Dodd #include <sys/module.h> 357534ac7aSMatthew N. Dodd #include <sys/bus.h> 367534ac7aSMatthew N. Dodd #include <sys/conf.h> 377534ac7aSMatthew N. Dodd 387534ac7aSMatthew N. Dodd #include <machine/bus.h> 397534ac7aSMatthew N. Dodd #include <machine/resource.h> 407534ac7aSMatthew N. Dodd #include <sys/rman.h> 417534ac7aSMatthew N. Dodd 4296aa4252SMatthew N. Dodd /* And all this for BIOS_PADDRTOVADDR() */ 4396aa4252SMatthew N. Dodd #include <vm/vm.h> 44092a5c45SJohn Baldwin #include <vm/vm_param.h> 4596aa4252SMatthew N. Dodd #include <vm/pmap.h> 4696aa4252SMatthew N. Dodd #include <machine/md_var.h> 4796aa4252SMatthew N. Dodd #include <machine/pc/bios.h> 4896aa4252SMatthew N. Dodd 497534ac7aSMatthew N. Dodd #include <machine/smapi.h> 5096aa4252SMatthew N. Dodd 5196aa4252SMatthew N. Dodd #define SMAPI_START 0xf0000 5296aa4252SMatthew N. Dodd #define SMAPI_STEP 0x10 5396aa4252SMatthew N. Dodd #define SMAPI_OFF 0 5496aa4252SMatthew N. Dodd #define SMAPI_LEN 4 5596aa4252SMatthew N. Dodd #define SMAPI_SIG "$SMB" 5696aa4252SMatthew N. Dodd 5796aa4252SMatthew N. Dodd #define RES2HDR(res) ((struct smapi_bios_header *)rman_get_virtual(res)) 58a954a4fbSMatthew N. Dodd #define ADDR2HDR(addr) ((struct smapi_bios_header *)BIOS_PADDRTOVADDR(addr)) 5996aa4252SMatthew N. Dodd 6096aa4252SMatthew N. Dodd struct smapi_softc { 6189c9c53dSPoul-Henning Kamp struct cdev * cdev; 6296aa4252SMatthew N. Dodd device_t dev; 6396aa4252SMatthew N. Dodd struct resource * res; 6496aa4252SMatthew N. Dodd int rid; 6596aa4252SMatthew N. Dodd 6696aa4252SMatthew N. Dodd u_int32_t smapi32_entry; 6796aa4252SMatthew N. Dodd 6896aa4252SMatthew N. Dodd struct smapi_bios_header *header; 6996aa4252SMatthew N. Dodd }; 707534ac7aSMatthew N. Dodd 71cba6ce36SMatthew N. Dodd extern u_long smapi32_offset; 72cba6ce36SMatthew N. Dodd extern u_short smapi32_segment; 737534ac7aSMatthew N. Dodd 747534ac7aSMatthew N. Dodd static d_ioctl_t smapi_ioctl; 757534ac7aSMatthew N. Dodd 767534ac7aSMatthew N. Dodd static struct cdevsw smapi_cdevsw = { 77dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 787ac40f5fSPoul-Henning Kamp .d_ioctl = smapi_ioctl, 797ac40f5fSPoul-Henning Kamp .d_name = "smapi", 80e1da986bSKonstantin Belousov .d_flags = D_NEEDGIANT, 817534ac7aSMatthew N. Dodd }; 827534ac7aSMatthew N. Dodd 8396aa4252SMatthew N. Dodd static void smapi_identify(driver_t *, device_t); 8496aa4252SMatthew N. Dodd static int smapi_probe(device_t); 8596aa4252SMatthew N. Dodd static int smapi_attach(device_t); 8696aa4252SMatthew N. Dodd static int smapi_detach(device_t); 8796aa4252SMatthew N. Dodd static int smapi_modevent(module_t, int, void *); 887534ac7aSMatthew N. Dodd 8996aa4252SMatthew N. Dodd static int smapi_header_cksum(struct smapi_bios_header *); 907534ac7aSMatthew N. Dodd 9196aa4252SMatthew N. Dodd extern int smapi32(struct smapi_bios_parameter *, 9296aa4252SMatthew N. Dodd struct smapi_bios_parameter *); 9396aa4252SMatthew N. Dodd extern int smapi32_new(u_long, u_short, 9496aa4252SMatthew N. Dodd struct smapi_bios_parameter *, 9596aa4252SMatthew N. Dodd struct smapi_bios_parameter *); 967534ac7aSMatthew N. Dodd 977534ac7aSMatthew N. Dodd static int 9809605c18SWarner Losh smapi_ioctl (struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 997534ac7aSMatthew N. Dodd { 100d4f988e1SJohn Baldwin struct smapi_softc *sc = dev->si_drv1; 1017534ac7aSMatthew N. Dodd int error; 1027534ac7aSMatthew N. Dodd 1037534ac7aSMatthew N. Dodd switch (cmd) { 1047534ac7aSMatthew N. Dodd case SMAPIOGHEADER: 1057534ac7aSMatthew N. Dodd bcopy((caddr_t)sc->header, data, 1067534ac7aSMatthew N. Dodd sizeof(struct smapi_bios_header)); 1077534ac7aSMatthew N. Dodd error = 0; 1087534ac7aSMatthew N. Dodd break; 1097534ac7aSMatthew N. Dodd case SMAPIOCGFUNCTION: 1107534ac7aSMatthew N. Dodd smapi32_offset = sc->smapi32_entry; 111cba6ce36SMatthew N. Dodd error = smapi32((struct smapi_bios_parameter *)data, 1127534ac7aSMatthew N. Dodd (struct smapi_bios_parameter *)data); 1137534ac7aSMatthew N. Dodd break; 1147534ac7aSMatthew N. Dodd default: 1157534ac7aSMatthew N. Dodd error = ENOTTY; 1167534ac7aSMatthew N. Dodd } 1177534ac7aSMatthew N. Dodd 1187534ac7aSMatthew N. Dodd return (error); 1197534ac7aSMatthew N. Dodd } 1207534ac7aSMatthew N. Dodd 12196aa4252SMatthew N. Dodd static int 12296aa4252SMatthew N. Dodd smapi_header_cksum (struct smapi_bios_header *header) 1237534ac7aSMatthew N. Dodd { 12496aa4252SMatthew N. Dodd u_int8_t *ptr; 12596aa4252SMatthew N. Dodd u_int8_t cksum; 12696aa4252SMatthew N. Dodd int i; 12796aa4252SMatthew N. Dodd 12896aa4252SMatthew N. Dodd ptr = (u_int8_t *)header; 12996aa4252SMatthew N. Dodd cksum = 0; 13096aa4252SMatthew N. Dodd for (i = 0; i < header->length; i++) { 13196aa4252SMatthew N. Dodd cksum += ptr[i]; 13296aa4252SMatthew N. Dodd } 13396aa4252SMatthew N. Dodd 13496aa4252SMatthew N. Dodd return (cksum); 13596aa4252SMatthew N. Dodd } 13696aa4252SMatthew N. Dodd 13796aa4252SMatthew N. Dodd static void 13896aa4252SMatthew N. Dodd smapi_identify (driver_t *driver, device_t parent) 13996aa4252SMatthew N. Dodd { 14096aa4252SMatthew N. Dodd device_t child; 14196aa4252SMatthew N. Dodd u_int32_t addr; 14296aa4252SMatthew N. Dodd int length; 14396aa4252SMatthew N. Dodd int rid; 14496aa4252SMatthew N. Dodd 14596aa4252SMatthew N. Dodd if (!device_is_alive(parent)) 14696aa4252SMatthew N. Dodd return; 14796aa4252SMatthew N. Dodd 14896aa4252SMatthew N. Dodd addr = bios_sigsearch(SMAPI_START, SMAPI_SIG, SMAPI_LEN, 14996aa4252SMatthew N. Dodd SMAPI_STEP, SMAPI_OFF); 15096aa4252SMatthew N. Dodd if (addr != 0) { 15196aa4252SMatthew N. Dodd rid = 0; 152a954a4fbSMatthew N. Dodd length = ADDR2HDR(addr)->length; 15396aa4252SMatthew N. Dodd 154*a05a6804SWarner Losh child = BUS_ADD_CHILD(parent, 5, "smapi", DEVICE_UNIT_ANY); 15596aa4252SMatthew N. Dodd device_set_driver(child, driver); 15696aa4252SMatthew N. Dodd bus_set_resource(child, SYS_RES_MEMORY, rid, addr, length); 15796aa4252SMatthew N. Dodd device_set_desc(child, "SMAPI BIOS"); 15896aa4252SMatthew N. Dodd } 15996aa4252SMatthew N. Dodd 16096aa4252SMatthew N. Dodd return; 16196aa4252SMatthew N. Dodd } 16296aa4252SMatthew N. Dodd 16396aa4252SMatthew N. Dodd static int 16496aa4252SMatthew N. Dodd smapi_probe (device_t dev) 16596aa4252SMatthew N. Dodd { 16696aa4252SMatthew N. Dodd struct resource *res; 16796aa4252SMatthew N. Dodd int rid; 16896aa4252SMatthew N. Dodd int error; 16996aa4252SMatthew N. Dodd 17096aa4252SMatthew N. Dodd error = 0; 17196aa4252SMatthew N. Dodd rid = 0; 1725f96beb9SNate Lawson res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 17396aa4252SMatthew N. Dodd if (res == NULL) { 17496aa4252SMatthew N. Dodd device_printf(dev, "Unable to allocate memory resource.\n"); 17596aa4252SMatthew N. Dodd error = ENOMEM; 17696aa4252SMatthew N. Dodd goto bad; 17796aa4252SMatthew N. Dodd } 17896aa4252SMatthew N. Dodd 17996aa4252SMatthew N. Dodd if (smapi_header_cksum(RES2HDR(res))) { 18096aa4252SMatthew N. Dodd device_printf(dev, "SMAPI header checksum failed.\n"); 18196aa4252SMatthew N. Dodd error = ENXIO; 18296aa4252SMatthew N. Dodd goto bad; 18396aa4252SMatthew N. Dodd } 18496aa4252SMatthew N. Dodd 18596aa4252SMatthew N. Dodd bad: 18696aa4252SMatthew N. Dodd if (res) 18796aa4252SMatthew N. Dodd bus_release_resource(dev, SYS_RES_MEMORY, rid, res); 18896aa4252SMatthew N. Dodd return (error); 18996aa4252SMatthew N. Dodd } 19096aa4252SMatthew N. Dodd 19196aa4252SMatthew N. Dodd static int 19296aa4252SMatthew N. Dodd smapi_attach (device_t dev) 19396aa4252SMatthew N. Dodd { 194d4f988e1SJohn Baldwin struct make_dev_args args; 19596aa4252SMatthew N. Dodd struct smapi_softc *sc; 19696aa4252SMatthew N. Dodd int error; 19796aa4252SMatthew N. Dodd 19896aa4252SMatthew N. Dodd sc = device_get_softc(dev); 19996aa4252SMatthew N. Dodd error = 0; 20096aa4252SMatthew N. Dodd 20196aa4252SMatthew N. Dodd sc->dev = dev; 20296aa4252SMatthew N. Dodd sc->rid = 0; 2035f96beb9SNate Lawson sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, 2045f96beb9SNate Lawson RF_ACTIVE); 20596aa4252SMatthew N. Dodd if (sc->res == NULL) { 20696aa4252SMatthew N. Dodd device_printf(dev, "Unable to allocate memory resource.\n"); 20796aa4252SMatthew N. Dodd error = ENOMEM; 20896aa4252SMatthew N. Dodd goto bad; 20996aa4252SMatthew N. Dodd } 21096aa4252SMatthew N. Dodd sc->header = (struct smapi_bios_header *)rman_get_virtual(sc->res); 21196aa4252SMatthew N. Dodd sc->smapi32_entry = (u_int32_t)BIOS_PADDRTOVADDR( 21296aa4252SMatthew N. Dodd sc->header->prot32_segment + 21396aa4252SMatthew N. Dodd sc->header->prot32_offset); 2147534ac7aSMatthew N. Dodd 215d4f988e1SJohn Baldwin make_dev_args_init(&args); 216d4f988e1SJohn Baldwin args.mda_devsw = &smapi_cdevsw; 217d4f988e1SJohn Baldwin args.mda_uid = UID_ROOT; 218d4f988e1SJohn Baldwin args.mda_gid = GID_WHEEL; 219d4f988e1SJohn Baldwin args.mda_mode = 0600; 220d4f988e1SJohn Baldwin args.mda_si_drv1 = sc; 221d4f988e1SJohn Baldwin error = make_dev_s(&args, &sc->cdev, "%s%d", 2227534ac7aSMatthew N. Dodd smapi_cdevsw.d_name, 2237534ac7aSMatthew N. Dodd device_get_unit(sc->dev)); 224d4f988e1SJohn Baldwin if (error != 0) 225d4f988e1SJohn Baldwin goto bad; 2267534ac7aSMatthew N. Dodd 22796aa4252SMatthew N. Dodd device_printf(dev, "Version: %d.%02d, Length: %d, Checksum: 0x%02x\n", 22896aa4252SMatthew N. Dodd bcd2bin(sc->header->version_major), 22996aa4252SMatthew N. Dodd bcd2bin(sc->header->version_minor), 23096aa4252SMatthew N. Dodd sc->header->length, 23196aa4252SMatthew N. Dodd sc->header->checksum); 23296aa4252SMatthew N. Dodd device_printf(dev, "Information=0x%b\n", 23396aa4252SMatthew N. Dodd sc->header->information, 23496aa4252SMatthew N. Dodd "\020" 23596aa4252SMatthew N. Dodd "\001REAL_VM86" 23696aa4252SMatthew N. Dodd "\002PROTECTED_16" 23796aa4252SMatthew N. Dodd "\003PROTECTED_32"); 23896aa4252SMatthew N. Dodd 23996aa4252SMatthew N. Dodd if (bootverbose) { 24096aa4252SMatthew N. Dodd if (sc->header->information & SMAPI_REAL_VM86) 24196aa4252SMatthew N. Dodd device_printf(dev, "Real/VM86 mode: Segment 0x%04x, Offset 0x%04x\n", 24296aa4252SMatthew N. Dodd sc->header->real16_segment, 24396aa4252SMatthew N. Dodd sc->header->real16_offset); 24496aa4252SMatthew N. Dodd if (sc->header->information & SMAPI_PROT_16BIT) 24596aa4252SMatthew N. Dodd device_printf(dev, "16-bit Protected mode: Segment 0x%08x, Offset 0x%04x\n", 24696aa4252SMatthew N. Dodd sc->header->prot16_segment, 24796aa4252SMatthew N. Dodd sc->header->prot16_offset); 24896aa4252SMatthew N. Dodd if (sc->header->information & SMAPI_PROT_32BIT) 24996aa4252SMatthew N. Dodd device_printf(dev, "32-bit Protected mode: Segment 0x%08x, Offset 0x%08x\n", 25096aa4252SMatthew N. Dodd sc->header->prot32_segment, 25196aa4252SMatthew N. Dodd sc->header->prot32_offset); 2527534ac7aSMatthew N. Dodd } 2537534ac7aSMatthew N. Dodd 25496aa4252SMatthew N. Dodd return (0); 25596aa4252SMatthew N. Dodd bad: 25696aa4252SMatthew N. Dodd if (sc->res) 25796aa4252SMatthew N. Dodd bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 25896aa4252SMatthew N. Dodd return (error); 25996aa4252SMatthew N. Dodd } 26096aa4252SMatthew N. Dodd 26196aa4252SMatthew N. Dodd static int 26296aa4252SMatthew N. Dodd smapi_detach (device_t dev) 2637534ac7aSMatthew N. Dodd { 26496aa4252SMatthew N. Dodd struct smapi_softc *sc; 26596aa4252SMatthew N. Dodd 26696aa4252SMatthew N. Dodd sc = device_get_softc(dev); 2677534ac7aSMatthew N. Dodd 2687534ac7aSMatthew N. Dodd destroy_dev(sc->cdev); 26996aa4252SMatthew N. Dodd 27096aa4252SMatthew N. Dodd if (sc->res) 27196aa4252SMatthew N. Dodd bus_release_resource(dev, SYS_RES_MEMORY, sc->rid, sc->res); 27296aa4252SMatthew N. Dodd 2737534ac7aSMatthew N. Dodd return (0); 2747534ac7aSMatthew N. Dodd } 27596aa4252SMatthew N. Dodd 27696aa4252SMatthew N. Dodd static device_method_t smapi_methods[] = { 27796aa4252SMatthew N. Dodd /* Device interface */ 27896aa4252SMatthew N. Dodd DEVMETHOD(device_identify, smapi_identify), 27996aa4252SMatthew N. Dodd DEVMETHOD(device_probe, smapi_probe), 28096aa4252SMatthew N. Dodd DEVMETHOD(device_attach, smapi_attach), 28196aa4252SMatthew N. Dodd DEVMETHOD(device_detach, smapi_detach), 28296aa4252SMatthew N. Dodd { 0, 0 } 28396aa4252SMatthew N. Dodd }; 28496aa4252SMatthew N. Dodd 28596aa4252SMatthew N. Dodd static driver_t smapi_driver = { 28696aa4252SMatthew N. Dodd "smapi", 28796aa4252SMatthew N. Dodd smapi_methods, 28896aa4252SMatthew N. Dodd sizeof(struct smapi_softc), 28996aa4252SMatthew N. Dodd }; 29096aa4252SMatthew N. Dodd 2915ad42f80SJohn Baldwin static int 2925ad42f80SJohn Baldwin smapi_modevent (module_t mod, int what, void *arg) 2935ad42f80SJohn Baldwin { 2945ad42f80SJohn Baldwin device_t * devs; 2955ad42f80SJohn Baldwin int count; 2965ad42f80SJohn Baldwin int i; 2975ad42f80SJohn Baldwin 2985ad42f80SJohn Baldwin switch (what) { 2995ad42f80SJohn Baldwin case MOD_LOAD: 3005ad42f80SJohn Baldwin break; 3015ad42f80SJohn Baldwin case MOD_UNLOAD: 3025ad42f80SJohn Baldwin devclass_get_devices(devclass_find(smapi_driver.name), &devs, 3035ad42f80SJohn Baldwin &count); 3045ad42f80SJohn Baldwin for (i = 0; i < count; i++) { 3055ad42f80SJohn Baldwin device_delete_child(device_get_parent(devs[i]), devs[i]); 3065ad42f80SJohn Baldwin } 3075ad42f80SJohn Baldwin free(devs, M_TEMP); 3085ad42f80SJohn Baldwin break; 3095ad42f80SJohn Baldwin default: 3105ad42f80SJohn Baldwin break; 3115ad42f80SJohn Baldwin } 3125ad42f80SJohn Baldwin 3135ad42f80SJohn Baldwin return (0); 3145ad42f80SJohn Baldwin } 3155ad42f80SJohn Baldwin 3160f3a50d5SJohn Baldwin DRIVER_MODULE(smapi, nexus, smapi_driver, smapi_modevent, NULL); 31796aa4252SMatthew N. Dodd MODULE_VERSION(smapi, 1); 318