diff options
Diffstat (limited to 'stand/i386/libi386/biospnp.c')
-rw-r--r-- | stand/i386/libi386/biospnp.c | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/stand/i386/libi386/biospnp.c b/stand/i386/libi386/biospnp.c new file mode 100644 index 0000000000000..30e55fc893d0e --- /dev/null +++ b/stand/i386/libi386/biospnp.c @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * PnP BIOS enumerator. + */ + +#include <stand.h> +#include <machine/stdarg.h> +#include <bootstrap.h> +#include <isapnp.h> +#include <btxv86.h> + + +static int biospnp_init(void); +static void biospnp_enumerate(void); + +struct pnphandler biospnphandler = +{ + "PnP BIOS", + biospnp_enumerate +}; + +struct pnp_ICstructure +{ + u_int8_t pnp_signature[4]; + u_int8_t pnp_version; + u_int8_t pnp_length; + u_int16_t pnp_BIOScontrol; + u_int8_t pnp_checksum; + u_int32_t pnp_eventflag; + u_int16_t pnp_rmip; + u_int16_t pnp_rmcs; + u_int16_t pnp_pmip; + u_int32_t pnp_pmcs; + u_int8_t pnp_OEMdev[4]; + u_int16_t pnp_rmds; + u_int32_t pnp_pmds; +} __packed; + +struct pnp_devNode +{ + u_int16_t dn_size; + u_int8_t dn_handle; + u_int8_t dn_id[4]; + u_int8_t dn_type[3]; + u_int16_t dn_attrib; + u_int8_t dn_data[1]; +} __packed; + +struct pnp_isaConfiguration +{ + u_int8_t ic_revision; + u_int8_t ic_nCSN; + u_int16_t ic_rdport; + u_int16_t ic_reserved; +} __packed; + +static struct pnp_ICstructure *pnp_Icheck = NULL; +static u_int16_t pnp_NumNodes; +static u_int16_t pnp_NodeSize; + +static void biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn); +static int biospnp_call(int func, const char *fmt, ...); + +#define vsegofs(vptr) (((u_int32_t)VTOPSEG(vptr) << 16) + VTOPOFF(vptr)) + +typedef void v86bios_t(u_int32_t, u_int32_t, u_int32_t, u_int32_t); +v86bios_t *v86bios = (v86bios_t *)v86int; + +#define biospnp_f00(NumNodes, NodeSize) biospnp_call(0x00, "ll", NumNodes, NodeSize) +#define biospnp_f01(Node, devNodeBuffer, Control) biospnp_call(0x01, "llw", Node, devNodeBuffer, Control) +#define biospnp_f40(Configuration) biospnp_call(0x40, "l", Configuration) + +/* PnP BIOS return codes */ +#define PNP_SUCCESS 0x00 +#define PNP_FUNCTION_NOT_SUPPORTED 0x80 + +/* + * Initialisation: locate the PnP BIOS, test that we can call it. + * Returns nonzero if the PnP BIOS is not usable on this system. + */ +static int +biospnp_init(void) +{ + struct pnp_isaConfiguration icfg; + char *sigptr; + int result; + + /* Search for the $PnP signature */ + pnp_Icheck = NULL; + for (sigptr = PTOV(0xf0000); sigptr < PTOV(0xfffff); sigptr += 16) + if (!bcmp(sigptr, "$PnP", 4)) { + pnp_Icheck = (struct pnp_ICstructure *)sigptr; + break; + } + + /* No signature, no BIOS */ + if (pnp_Icheck == NULL) + return(1); + + /* + * Fetch the system table parameters as a test of the BIOS + */ + result = biospnp_f00(vsegofs(&pnp_NumNodes), vsegofs(&pnp_NodeSize)); + if (result != PNP_SUCCESS) { + return(1); + } + + /* + * Look for the PnP ISA configuration table + */ + result = biospnp_f40(vsegofs(&icfg)); + switch (result) { + case PNP_SUCCESS: + /* If the BIOS found some PnP devices, take its hint for the read port */ + if ((icfg.ic_revision == 1) && (icfg.ic_nCSN > 0)) + isapnp_readport = icfg.ic_rdport; + break; + case PNP_FUNCTION_NOT_SUPPORTED: + /* The BIOS says there is no ISA bus (should we trust that this works?) */ + printf("PnP BIOS claims no ISA bus\n"); + isapnp_readport = -1; + break; + } + return(0); +} + +static void +biospnp_enumerate(void) +{ + u_int8_t Node; + struct pnp_devNode *devNodeBuffer; + int result; + struct pnpinfo *pi; + int count; + + /* Init/check state */ + if (biospnp_init()) + return; + + devNodeBuffer = (struct pnp_devNode *)alloca(pnp_NodeSize); + Node = 0; + count = 1000; + while((Node != 0xff) && (count-- > 0)) { + result = biospnp_f01(vsegofs(&Node), vsegofs(devNodeBuffer), 0x1); + if (result != PNP_SUCCESS) { + printf("PnP BIOS node %d: error 0x%x\n", Node, result); + } else { + pi = pnp_allocinfo(); + pnp_addident(pi, pnp_eisaformat(devNodeBuffer->dn_id)); + biospnp_scanresdata(pi, devNodeBuffer); + pnp_addinfo(pi); + } + } +} + +/* + * Scan the resource data in the node's data area for compatible device IDs + * and descriptions. + */ +static void +biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn) +{ + u_int tag, i, rlen, dlen; + u_int8_t *p; + char *str; + + p = dn->dn_data; /* point to resource data */ + dlen = dn->dn_size - (p - (u_int8_t *)dn); /* length of resource data */ + + for (i = 0; i < dlen; i+= rlen) { + tag = p[i]; + i++; + if (PNP_RES_TYPE(tag) == 0) { + rlen = PNP_SRES_LEN(tag); + /* small resource */ + switch (PNP_SRES_NUM(tag)) { + + case COMP_DEVICE_ID: + /* got a compatible device ID */ + pnp_addident(pi, pnp_eisaformat(p + i)); + break; + + case END_TAG: + return; + } + } else { + /* large resource */ + rlen = *(u_int16_t *)(p + i); + i += sizeof(u_int16_t); + + switch(PNP_LRES_NUM(tag)) { + + case ID_STRING_ANSI: + str = malloc(rlen + 1); + bcopy(p + i, str, rlen); + str[rlen] = 0; + if (pi->pi_desc == NULL) { + pi->pi_desc = str; + } else { + free(str); + } + break; + } + } + } +} + + +/* + * Make a 16-bit realmode PnP BIOS call. + * + * The first argument passed is the function number, the last is the + * BIOS data segment selector. Intermediate arguments may be 16 or + * 32 bytes in length, and are described by the format string. + * + * Arguments to the BIOS functions must be packed on the stack, hence + * this evil. + */ +static int +biospnp_call(int func, const char *fmt, ...) +{ + va_list ap; + const char *p; + u_int8_t *argp; + u_int32_t args[4]; + u_int32_t i; + + /* function number first */ + argp = (u_int8_t *)args; + *(u_int16_t *)argp = func; + argp += sizeof(u_int16_t); + + /* take args according to format */ + va_start(ap, fmt); + for (p = fmt; *p != 0; p++) { + switch(*p) { + + case 'w': + i = va_arg(ap, u_int); + *(u_int16_t *)argp = i; + argp += sizeof(u_int16_t); + break; + + case 'l': + i = va_arg(ap, u_int32_t); + *(u_int32_t *)argp = i; + argp += sizeof(u_int32_t); + break; + } + } + va_end(ap); + + /* BIOS segment last */ + *(u_int16_t *)argp = pnp_Icheck->pnp_rmds; + argp += sizeof(u_int16_t); + + /* prepare for call */ + v86.ctl = V86_ADDR | V86_CALLF; + v86.addr = ((u_int32_t)pnp_Icheck->pnp_rmcs << 16) + pnp_Icheck->pnp_rmip; + + /* call with packed stack and return */ + v86bios(args[0], args[1], args[2], args[3]); + return(v86.eax & 0xffff); +} |