140601Smsmith/*- 240601Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 340601Smsmith * All rights reserved. 440601Smsmith * 540601Smsmith * Redistribution and use in source and binary forms, with or without 640601Smsmith * modification, are permitted provided that the following conditions 740601Smsmith * are met: 840601Smsmith * 1. Redistributions of source code must retain the above copyright 940601Smsmith * notice, this list of conditions and the following disclaimer. 1040601Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1140601Smsmith * notice, this list of conditions and the following disclaimer in the 1240601Smsmith * documentation and/or other materials provided with the distribution. 1340601Smsmith * 1440601Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1540601Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1640601Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1740601Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1840601Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1940601Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2040601Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2140601Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2240601Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2340601Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2440601Smsmith * SUCH DAMAGE. 2540601Smsmith */ 2640601Smsmith 27119482Sobrien#include <sys/cdefs.h> 28119482Sobrien__FBSDID("$FreeBSD$"); 29119482Sobrien 3040601Smsmith/* 3140617Smsmith * PnP BIOS enumerator. 3240601Smsmith */ 3340601Smsmith 3440601Smsmith#include <stand.h> 3540601Smsmith#include <machine/stdarg.h> 3640601Smsmith#include <bootstrap.h> 3740601Smsmith#include <isapnp.h> 3840601Smsmith#include <btxv86.h> 3940601Smsmith 4040601Smsmith 4140601Smsmithstatic int biospnp_init(void); 4240601Smsmithstatic void biospnp_enumerate(void); 4340601Smsmith 4440601Smsmithstruct pnphandler biospnphandler = 4540601Smsmith{ 4640601Smsmith "PnP BIOS", 4740601Smsmith biospnp_enumerate 4840601Smsmith}; 4940601Smsmith 5040601Smsmithstruct pnp_ICstructure 5140601Smsmith{ 52193720Sjhb u_int8_t pnp_signature[4]; 53193720Sjhb u_int8_t pnp_version; 54193720Sjhb u_int8_t pnp_length; 55193720Sjhb u_int16_t pnp_BIOScontrol; 56193720Sjhb u_int8_t pnp_checksum; 57193720Sjhb u_int32_t pnp_eventflag; 58193720Sjhb u_int16_t pnp_rmip; 59193720Sjhb u_int16_t pnp_rmcs; 60193720Sjhb u_int16_t pnp_pmip; 61193720Sjhb u_int32_t pnp_pmcs; 62193720Sjhb u_int8_t pnp_OEMdev[4]; 63193720Sjhb u_int16_t pnp_rmds; 64193720Sjhb u_int32_t pnp_pmds; 65193720Sjhb} __packed; 6640601Smsmith 6740601Smsmithstruct pnp_devNode 6840601Smsmith{ 69193720Sjhb u_int16_t dn_size; 70193720Sjhb u_int8_t dn_handle; 71193720Sjhb u_int8_t dn_id[4]; 72193720Sjhb u_int8_t dn_type[3]; 73193720Sjhb u_int16_t dn_attrib; 74193720Sjhb u_int8_t dn_data[1]; 75193720Sjhb} __packed; 7640601Smsmith 7740601Smsmithstruct pnp_isaConfiguration 7840601Smsmith{ 79193720Sjhb u_int8_t ic_revision; 80193720Sjhb u_int8_t ic_nCSN; 81193720Sjhb u_int16_t ic_rdport; 82193720Sjhb u_int16_t ic_reserved; 83193720Sjhb} __packed; 8440601Smsmith 8540601Smsmithstatic struct pnp_ICstructure *pnp_Icheck = NULL; 8640601Smsmithstatic u_int16_t pnp_NumNodes; 8740601Smsmithstatic u_int16_t pnp_NodeSize; 8840601Smsmith 8940601Smsmithstatic void biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn); 9064187Sjhbstatic int biospnp_call(int func, const char *fmt, ...); 9140601Smsmith 9240601Smsmith#define vsegofs(vptr) (((u_int32_t)VTOPSEG(vptr) << 16) + VTOPOFF(vptr)) 9340601Smsmith 9464187Sjhbtypedef void v86bios_t(u_int32_t, u_int32_t, u_int32_t, u_int32_t); 9564187Sjhbv86bios_t *v86bios = (v86bios_t *)v86int; 9664187Sjhb 9740601Smsmith#define biospnp_f00(NumNodes, NodeSize) biospnp_call(0x00, "ll", NumNodes, NodeSize) 9840601Smsmith#define biospnp_f01(Node, devNodeBuffer, Control) biospnp_call(0x01, "llw", Node, devNodeBuffer, Control) 9940601Smsmith#define biospnp_f40(Configuration) biospnp_call(0x40, "l", Configuration) 10040601Smsmith 10140601Smsmith/* PnP BIOS return codes */ 10240601Smsmith#define PNP_SUCCESS 0x00 10340601Smsmith#define PNP_FUNCTION_NOT_SUPPORTED 0x80 10440601Smsmith 10540601Smsmith/* 10640601Smsmith * Initialisation: locate the PnP BIOS, test that we can call it. 10740601Smsmith * Returns nonzero if the PnP BIOS is not usable on this system. 10840601Smsmith */ 10940601Smsmithstatic int 11040601Smsmithbiospnp_init(void) 11140601Smsmith{ 11240601Smsmith struct pnp_isaConfiguration icfg; 11340601Smsmith char *sigptr; 11440601Smsmith int result; 11540601Smsmith 11640601Smsmith /* Search for the $PnP signature */ 11740601Smsmith pnp_Icheck = NULL; 11840601Smsmith for (sigptr = PTOV(0xf0000); sigptr < PTOV(0xfffff); sigptr += 16) 11940601Smsmith if (!bcmp(sigptr, "$PnP", 4)) { 12040601Smsmith pnp_Icheck = (struct pnp_ICstructure *)sigptr; 12140601Smsmith break; 12240601Smsmith } 12340601Smsmith 12440601Smsmith /* No signature, no BIOS */ 12540601Smsmith if (pnp_Icheck == NULL) 12640601Smsmith return(1); 12740601Smsmith 12840601Smsmith /* 12940601Smsmith * Fetch the system table parameters as a test of the BIOS 13040601Smsmith */ 13140601Smsmith result = biospnp_f00(vsegofs(&pnp_NumNodes), vsegofs(&pnp_NodeSize)); 13240601Smsmith if (result != PNP_SUCCESS) { 13340601Smsmith return(1); 13440601Smsmith } 13540601Smsmith 13640601Smsmith /* 13740601Smsmith * Look for the PnP ISA configuration table 13840601Smsmith */ 13940601Smsmith result = biospnp_f40(vsegofs(&icfg)); 14040601Smsmith switch (result) { 14140601Smsmith case PNP_SUCCESS: 14240601Smsmith /* If the BIOS found some PnP devices, take its hint for the read port */ 14340601Smsmith if ((icfg.ic_revision == 1) && (icfg.ic_nCSN > 0)) 14440601Smsmith isapnp_readport = icfg.ic_rdport; 14540601Smsmith break; 14640601Smsmith case PNP_FUNCTION_NOT_SUPPORTED: 14740601Smsmith /* The BIOS says there is no ISA bus (should we trust that this works?) */ 14840601Smsmith printf("PnP BIOS claims no ISA bus\n"); 14940601Smsmith isapnp_readport = -1; 15040601Smsmith break; 15140601Smsmith } 15240601Smsmith return(0); 15340601Smsmith} 15440601Smsmith 15540601Smsmithstatic void 15640601Smsmithbiospnp_enumerate(void) 15740601Smsmith{ 15840601Smsmith u_int8_t Node; 15940601Smsmith struct pnp_devNode *devNodeBuffer; 16040601Smsmith int result; 16140601Smsmith struct pnpinfo *pi; 16240601Smsmith int count; 16340601Smsmith 16440601Smsmith /* Init/check state */ 16540601Smsmith if (biospnp_init()) 16640601Smsmith return; 16740601Smsmith 168153535Ssobomax devNodeBuffer = (struct pnp_devNode *)alloca(pnp_NodeSize); 16940601Smsmith Node = 0; 17040601Smsmith count = 1000; 17140601Smsmith while((Node != 0xff) && (count-- > 0)) { 17240601Smsmith result = biospnp_f01(vsegofs(&Node), vsegofs(devNodeBuffer), 0x1); 17340601Smsmith if (result != PNP_SUCCESS) { 17440601Smsmith printf("PnP BIOS node %d: error 0x%x\n", Node, result); 17540601Smsmith } else { 17640601Smsmith pi = pnp_allocinfo(); 17740601Smsmith pnp_addident(pi, pnp_eisaformat(devNodeBuffer->dn_id)); 17840601Smsmith biospnp_scanresdata(pi, devNodeBuffer); 17940601Smsmith pnp_addinfo(pi); 18040601Smsmith } 18140601Smsmith } 18240601Smsmith} 18340601Smsmith 18440601Smsmith/* 18540601Smsmith * Scan the resource data in the node's data area for compatible device IDs 18640601Smsmith * and descriptions. 18740601Smsmith */ 18840601Smsmithstatic void 18940601Smsmithbiospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn) 19040601Smsmith{ 19164187Sjhb u_int tag, i, rlen, dlen; 19240601Smsmith u_int8_t *p; 19340601Smsmith char *str; 19440601Smsmith 19540601Smsmith p = dn->dn_data; /* point to resource data */ 19640601Smsmith dlen = dn->dn_size - (p - (u_int8_t *)dn); /* length of resource data */ 19740601Smsmith 19840601Smsmith for (i = 0; i < dlen; i+= rlen) { 19940601Smsmith tag = p[i]; 20040601Smsmith i++; 20140601Smsmith if (PNP_RES_TYPE(tag) == 0) { 20240601Smsmith rlen = PNP_SRES_LEN(tag); 20340601Smsmith /* small resource */ 20440601Smsmith switch (PNP_SRES_NUM(tag)) { 20540601Smsmith 20640601Smsmith case COMP_DEVICE_ID: 20740601Smsmith /* got a compatible device ID */ 20840601Smsmith pnp_addident(pi, pnp_eisaformat(p + i)); 20940601Smsmith break; 21040601Smsmith 21140601Smsmith case END_TAG: 21240601Smsmith return; 21340601Smsmith } 21440601Smsmith } else { 21540601Smsmith /* large resource */ 21640601Smsmith rlen = *(u_int16_t *)(p + i); 21740601Smsmith i += sizeof(u_int16_t); 21840601Smsmith 21940601Smsmith switch(PNP_LRES_NUM(tag)) { 22040601Smsmith 22140601Smsmith case ID_STRING_ANSI: 22240601Smsmith str = malloc(rlen + 1); 22340601Smsmith bcopy(p + i, str, rlen); 22440601Smsmith str[rlen] = 0; 22540601Smsmith if (pi->pi_desc == NULL) { 22640601Smsmith pi->pi_desc = str; 22740601Smsmith } else { 22840601Smsmith free(str); 22940601Smsmith } 23040601Smsmith break; 23140601Smsmith } 23240601Smsmith } 23340601Smsmith } 23440601Smsmith} 23540601Smsmith 23640601Smsmith 23740601Smsmith/* 23840601Smsmith * Make a 16-bit realmode PnP BIOS call. 23940601Smsmith * 24040601Smsmith * The first argument passed is the function number, the last is the 24140601Smsmith * BIOS data segment selector. Intermediate arguments may be 16 or 24240601Smsmith * 32 bytes in length, and are described by the format string. 24340601Smsmith * 24440601Smsmith * Arguments to the BIOS functions must be packed on the stack, hence 24540601Smsmith * this evil. 24640601Smsmith */ 24740601Smsmithstatic int 24864187Sjhbbiospnp_call(int func, const char *fmt, ...) 24940601Smsmith{ 25040601Smsmith va_list ap; 25164187Sjhb const char *p; 25240601Smsmith u_int8_t *argp; 25340601Smsmith u_int32_t args[4]; 25440601Smsmith u_int32_t i; 25540601Smsmith 25640601Smsmith /* function number first */ 25740601Smsmith argp = (u_int8_t *)args; 25840601Smsmith *(u_int16_t *)argp = func; 25940601Smsmith argp += sizeof(u_int16_t); 26040601Smsmith 26140601Smsmith /* take args according to format */ 26240601Smsmith va_start(ap, fmt); 26340601Smsmith for (p = fmt; *p != 0; p++) { 26440601Smsmith switch(*p) { 26540601Smsmith 26640601Smsmith case 'w': 267118607Sjhb i = va_arg(ap, u_int); 26840601Smsmith *(u_int16_t *)argp = i; 26940601Smsmith argp += sizeof(u_int16_t); 27040601Smsmith break; 27140601Smsmith 27240601Smsmith case 'l': 27340601Smsmith i = va_arg(ap, u_int32_t); 27440601Smsmith *(u_int32_t *)argp = i; 27540601Smsmith argp += sizeof(u_int32_t); 27640601Smsmith break; 27740601Smsmith } 27840601Smsmith } 279236213Skevlo va_end(ap); 28040601Smsmith 28140601Smsmith /* BIOS segment last */ 28240601Smsmith *(u_int16_t *)argp = pnp_Icheck->pnp_rmds; 28340601Smsmith argp += sizeof(u_int16_t); 28440601Smsmith 28540601Smsmith /* prepare for call */ 28640601Smsmith v86.ctl = V86_ADDR | V86_CALLF; 28740601Smsmith v86.addr = ((u_int32_t)pnp_Icheck->pnp_rmcs << 16) + pnp_Icheck->pnp_rmip; 28840601Smsmith 28940601Smsmith /* call with packed stack and return */ 29040601Smsmith v86bios(args[0], args[1], args[2], args[3]); 29140601Smsmith return(v86.eax & 0xffff); 29240601Smsmith} 293