xref: /freebsd/stand/i386/libi386/biospnp.c (revision 3e15b01d)
1ca987d46SWarner Losh /*-
2ca987d46SWarner Losh  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3ca987d46SWarner Losh  * All rights reserved.
4ca987d46SWarner Losh  *
5ca987d46SWarner Losh  * Redistribution and use in source and binary forms, with or without
6ca987d46SWarner Losh  * modification, are permitted provided that the following conditions
7ca987d46SWarner Losh  * are met:
8ca987d46SWarner Losh  * 1. Redistributions of source code must retain the above copyright
9ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer.
10ca987d46SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
11ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
12ca987d46SWarner Losh  *    documentation and/or other materials provided with the distribution.
13ca987d46SWarner Losh  *
14ca987d46SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15ca987d46SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ca987d46SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ca987d46SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ca987d46SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ca987d46SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ca987d46SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ca987d46SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ca987d46SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ca987d46SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ca987d46SWarner Losh  * SUCH DAMAGE.
25ca987d46SWarner Losh  */
26ca987d46SWarner Losh 
27ca987d46SWarner Losh /*
28ca987d46SWarner Losh  * PnP BIOS enumerator.
29ca987d46SWarner Losh  */
30ca987d46SWarner Losh 
31ca987d46SWarner Losh #include <stand.h>
32ca987d46SWarner Losh #include <machine/stdarg.h>
33ca987d46SWarner Losh #include <bootstrap.h>
34ca987d46SWarner Losh #include <isapnp.h>
35ca987d46SWarner Losh #include <btxv86.h>
36ca987d46SWarner Losh 
37ca987d46SWarner Losh 
38ca987d46SWarner Losh static int	biospnp_init(void);
39ca987d46SWarner Losh static void	biospnp_enumerate(void);
40ca987d46SWarner Losh 
41ca987d46SWarner Losh struct pnphandler biospnphandler =
42ca987d46SWarner Losh {
43ca987d46SWarner Losh     "PnP BIOS",
44ca987d46SWarner Losh     biospnp_enumerate
45ca987d46SWarner Losh };
46ca987d46SWarner Losh 
47ca987d46SWarner Losh struct pnp_ICstructure
48ca987d46SWarner Losh {
4956e53cb8SWarner Losh     uint8_t	pnp_signature[4];
5056e53cb8SWarner Losh     uint8_t	pnp_version;
5156e53cb8SWarner Losh     uint8_t	pnp_length;
5256e53cb8SWarner Losh     uint16_t	pnp_BIOScontrol;
5356e53cb8SWarner Losh     uint8_t	pnp_checksum;
5456e53cb8SWarner Losh     uint32_t	pnp_eventflag;
5556e53cb8SWarner Losh     uint16_t	pnp_rmip;
5656e53cb8SWarner Losh     uint16_t	pnp_rmcs;
5756e53cb8SWarner Losh     uint16_t	pnp_pmip;
5856e53cb8SWarner Losh     uint32_t	pnp_pmcs;
5956e53cb8SWarner Losh     uint8_t	pnp_OEMdev[4];
6056e53cb8SWarner Losh     uint16_t	pnp_rmds;
6156e53cb8SWarner Losh     uint32_t	pnp_pmds;
62ca987d46SWarner Losh } __packed;
63ca987d46SWarner Losh 
64ca987d46SWarner Losh struct pnp_devNode
65ca987d46SWarner Losh {
6656e53cb8SWarner Losh     uint16_t	dn_size;
6756e53cb8SWarner Losh     uint8_t	dn_handle;
6856e53cb8SWarner Losh     uint8_t	dn_id[4];
6956e53cb8SWarner Losh     uint8_t	dn_type[3];
7056e53cb8SWarner Losh     uint16_t	dn_attrib;
7156e53cb8SWarner Losh     uint8_t	dn_data[1];
72ca987d46SWarner Losh } __packed;
73ca987d46SWarner Losh 
74ca987d46SWarner Losh struct pnp_isaConfiguration
75ca987d46SWarner Losh {
7656e53cb8SWarner Losh     uint8_t	ic_revision;
7756e53cb8SWarner Losh     uint8_t	ic_nCSN;
7856e53cb8SWarner Losh     uint16_t	ic_rdport;
7956e53cb8SWarner Losh     uint16_t	ic_reserved;
80ca987d46SWarner Losh } __packed;
81ca987d46SWarner Losh 
82ca987d46SWarner Losh static struct pnp_ICstructure	*pnp_Icheck = NULL;
8356e53cb8SWarner Losh static uint16_t			pnp_NumNodes;
8456e53cb8SWarner Losh static uint16_t			pnp_NodeSize;
85ca987d46SWarner Losh 
86ca987d46SWarner Losh static void	biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn);
87ca987d46SWarner Losh static int	biospnp_call(int func, const char *fmt, ...);
88ca987d46SWarner Losh 
8956e53cb8SWarner Losh #define vsegofs(vptr)	(((uint32_t)VTOPSEG(vptr) << 16) + VTOPOFF(vptr))
90ca987d46SWarner Losh 
9156e53cb8SWarner Losh typedef void    v86bios_t(uint32_t, uint32_t, uint32_t, uint32_t);
92ca987d46SWarner Losh v86bios_t	*v86bios = (v86bios_t *)v86int;
93ca987d46SWarner Losh 
94ca987d46SWarner Losh #define	biospnp_f00(NumNodes, NodeSize)			biospnp_call(0x00, "ll", NumNodes, NodeSize)
95ca987d46SWarner Losh #define biospnp_f01(Node, devNodeBuffer, Control)	biospnp_call(0x01, "llw", Node, devNodeBuffer, Control)
96ca987d46SWarner Losh #define biospnp_f40(Configuration)			biospnp_call(0x40, "l", Configuration)
97ca987d46SWarner Losh 
98ca987d46SWarner Losh /* PnP BIOS return codes */
99ca987d46SWarner Losh #define PNP_SUCCESS			0x00
100ca987d46SWarner Losh #define PNP_FUNCTION_NOT_SUPPORTED	0x80
101ca987d46SWarner Losh 
102ca987d46SWarner Losh /*
103ca987d46SWarner Losh  * Initialisation: locate the PnP BIOS, test that we can call it.
104ca987d46SWarner Losh  * Returns nonzero if the PnP BIOS is not usable on this system.
105ca987d46SWarner Losh  */
106ca987d46SWarner Losh static int
biospnp_init(void)107ca987d46SWarner Losh biospnp_init(void)
108ca987d46SWarner Losh {
109ca987d46SWarner Losh     struct pnp_isaConfiguration	icfg;
110ca987d46SWarner Losh     char			*sigptr;
111ca987d46SWarner Losh     int				result;
112ca987d46SWarner Losh 
113ca987d46SWarner Losh     /* Search for the $PnP signature */
114ca987d46SWarner Losh     pnp_Icheck = NULL;
115ca987d46SWarner Losh     for (sigptr = PTOV(0xf0000); sigptr < PTOV(0xfffff); sigptr += 16)
116ca987d46SWarner Losh 	if (!bcmp(sigptr, "$PnP", 4)) {
117ca987d46SWarner Losh 	    pnp_Icheck = (struct pnp_ICstructure *)sigptr;
118ca987d46SWarner Losh 	    break;
119ca987d46SWarner Losh 	}
120ca987d46SWarner Losh 
121ca987d46SWarner Losh     /* No signature, no BIOS */
122ca987d46SWarner Losh     if (pnp_Icheck == NULL)
123ca987d46SWarner Losh 	return(1);
124ca987d46SWarner Losh 
125ca987d46SWarner Losh     /*
126ca987d46SWarner Losh      * Fetch the system table parameters as a test of the BIOS
127ca987d46SWarner Losh      */
128ca987d46SWarner Losh     result = biospnp_f00(vsegofs(&pnp_NumNodes), vsegofs(&pnp_NodeSize));
129ca987d46SWarner Losh     if (result != PNP_SUCCESS) {
130ca987d46SWarner Losh 	return(1);
131ca987d46SWarner Losh     }
132ca987d46SWarner Losh 
133ca987d46SWarner Losh     /*
134ca987d46SWarner Losh      * Look for the PnP ISA configuration table
135ca987d46SWarner Losh      */
136ca987d46SWarner Losh     result = biospnp_f40(vsegofs(&icfg));
137ca987d46SWarner Losh     switch (result) {
138ca987d46SWarner Losh     case PNP_SUCCESS:
139ca987d46SWarner Losh 	/* If the BIOS found some PnP devices, take its hint for the read port */
140ca987d46SWarner Losh 	if ((icfg.ic_revision == 1) && (icfg.ic_nCSN > 0))
141ca987d46SWarner Losh 	    isapnp_readport = icfg.ic_rdport;
142ca987d46SWarner Losh 	break;
143ca987d46SWarner Losh     case PNP_FUNCTION_NOT_SUPPORTED:
144ca987d46SWarner Losh 	/* The BIOS says there is no ISA bus (should we trust that this works?) */
145ca987d46SWarner Losh 	printf("PnP BIOS claims no ISA bus\n");
146ca987d46SWarner Losh 	isapnp_readport = -1;
147ca987d46SWarner Losh 	break;
148ca987d46SWarner Losh     }
149ca987d46SWarner Losh     return(0);
150ca987d46SWarner Losh }
151ca987d46SWarner Losh 
152ca987d46SWarner Losh static void
biospnp_enumerate(void)153ca987d46SWarner Losh biospnp_enumerate(void)
154ca987d46SWarner Losh {
15556e53cb8SWarner Losh     uint8_t		Node;
156ca987d46SWarner Losh     struct pnp_devNode	*devNodeBuffer;
157588ff074SWarner Losh     uint8_t		buffer[max(pnp_NodeSize, sizeof(*devNodeBuffer))];
158ca987d46SWarner Losh     int			result;
159ca987d46SWarner Losh     struct pnpinfo	*pi;
160ca987d46SWarner Losh     int			count;
161ca987d46SWarner Losh 
162ca987d46SWarner Losh     /* Init/check state */
163ca987d46SWarner Losh     if (biospnp_init())
164ca987d46SWarner Losh 	return;
165ca987d46SWarner Losh 
166588ff074SWarner Losh     devNodeBuffer = (struct pnp_devNode *)buffer;
167ca987d46SWarner Losh     Node = 0;
168ca987d46SWarner Losh     count = 1000;
169ca987d46SWarner Losh     while((Node != 0xff) && (count-- > 0)) {
170ca987d46SWarner Losh 	result = biospnp_f01(vsegofs(&Node), vsegofs(devNodeBuffer), 0x1);
171ca987d46SWarner Losh 	if (result != PNP_SUCCESS) {
172ca987d46SWarner Losh 	    printf("PnP BIOS node %d: error 0x%x\n", Node, result);
173ca987d46SWarner Losh 	} else {
174ca987d46SWarner Losh 	    pi = pnp_allocinfo();
175ca987d46SWarner Losh 	    pnp_addident(pi, pnp_eisaformat(devNodeBuffer->dn_id));
176ca987d46SWarner Losh 	    biospnp_scanresdata(pi, devNodeBuffer);
177ca987d46SWarner Losh 	    pnp_addinfo(pi);
178ca987d46SWarner Losh 	}
179ca987d46SWarner Losh     }
180ca987d46SWarner Losh }
181ca987d46SWarner Losh 
182ca987d46SWarner Losh /*
183ca987d46SWarner Losh  * Scan the resource data in the node's data area for compatible device IDs
184ca987d46SWarner Losh  * and descriptions.
185ca987d46SWarner Losh  */
186ca987d46SWarner Losh static void
biospnp_scanresdata(struct pnpinfo * pi,struct pnp_devNode * dn)187ca987d46SWarner Losh biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn)
188ca987d46SWarner Losh {
189ca987d46SWarner Losh     u_int	tag, i, rlen, dlen;
19056e53cb8SWarner Losh     uint8_t	*p;
191ca987d46SWarner Losh     char	*str;
192ca987d46SWarner Losh 
193ca987d46SWarner Losh     p = dn->dn_data;			/* point to resource data */
19456e53cb8SWarner Losh     dlen = dn->dn_size - (p - (uint8_t *)dn);	/* length of resource data */
195ca987d46SWarner Losh 
196ca987d46SWarner Losh     for (i = 0; i < dlen; i+= rlen) {
197ca987d46SWarner Losh 	tag = p[i];
198ca987d46SWarner Losh 	i++;
199ca987d46SWarner Losh 	if (PNP_RES_TYPE(tag) == 0) {
200ca987d46SWarner Losh 	    rlen = PNP_SRES_LEN(tag);
201ca987d46SWarner Losh 	    /* small resource */
202ca987d46SWarner Losh 	    switch (PNP_SRES_NUM(tag)) {
203ca987d46SWarner Losh 
204ca987d46SWarner Losh 	    case COMP_DEVICE_ID:
205ca987d46SWarner Losh 		/* got a compatible device ID */
206ca987d46SWarner Losh 		pnp_addident(pi, pnp_eisaformat(p + i));
207ca987d46SWarner Losh 		break;
208ca987d46SWarner Losh 
209ca987d46SWarner Losh 	    case END_TAG:
210ca987d46SWarner Losh 		return;
211ca987d46SWarner Losh 	    }
212ca987d46SWarner Losh 	} else {
213ca987d46SWarner Losh 	    /* large resource */
21456e53cb8SWarner Losh 	    rlen = *(uint16_t *)(p + i);
21556e53cb8SWarner Losh 	    i += sizeof(uint16_t);
216ca987d46SWarner Losh 
217ca987d46SWarner Losh 	    switch(PNP_LRES_NUM(tag)) {
218ca987d46SWarner Losh 
219ca987d46SWarner Losh 	    case ID_STRING_ANSI:
220ca987d46SWarner Losh 		str = malloc(rlen + 1);
221ca987d46SWarner Losh 		bcopy(p + i, str, rlen);
222ca987d46SWarner Losh 		str[rlen] = 0;
223ca987d46SWarner Losh 		if (pi->pi_desc == NULL) {
224ca987d46SWarner Losh 		    pi->pi_desc = str;
225ca987d46SWarner Losh 		} else {
226ca987d46SWarner Losh 		    free(str);
227ca987d46SWarner Losh 		}
228ca987d46SWarner Losh 		break;
229ca987d46SWarner Losh 	    }
230ca987d46SWarner Losh 	}
231ca987d46SWarner Losh     }
232ca987d46SWarner Losh }
233ca987d46SWarner Losh 
234ca987d46SWarner Losh 
235ca987d46SWarner Losh /*
236ca987d46SWarner Losh  * Make a 16-bit realmode PnP BIOS call.
237ca987d46SWarner Losh  *
238ca987d46SWarner Losh  * The first argument passed is the function number, the last is the
239ca987d46SWarner Losh  * BIOS data segment selector.  Intermediate arguments may be 16 or
240ca987d46SWarner Losh  * 32 bytes in length, and are described by the format string.
241ca987d46SWarner Losh  *
242ca987d46SWarner Losh  * Arguments to the BIOS functions must be packed on the stack, hence
243ca987d46SWarner Losh  * this evil.
244ca987d46SWarner Losh  */
245ca987d46SWarner Losh static int
biospnp_call(int func,const char * fmt,...)246ca987d46SWarner Losh biospnp_call(int func, const char *fmt, ...)
247ca987d46SWarner Losh {
248ca987d46SWarner Losh     va_list	ap;
249ca987d46SWarner Losh     const char	*p;
25056e53cb8SWarner Losh     uint8_t	*argp;
25156e53cb8SWarner Losh     uint32_t	args[4];
25256e53cb8SWarner Losh     uint32_t	i;
253ca987d46SWarner Losh 
254ca987d46SWarner Losh     /* function number first */
25556e53cb8SWarner Losh     argp = (uint8_t *)args;
25656e53cb8SWarner Losh     *(uint16_t *)argp = func;
25756e53cb8SWarner Losh     argp += sizeof(uint16_t);
258ca987d46SWarner Losh 
259ca987d46SWarner Losh     /* take args according to format */
260ca987d46SWarner Losh     va_start(ap, fmt);
261ca987d46SWarner Losh     for (p = fmt; *p != 0; p++) {
262ca987d46SWarner Losh 	switch(*p) {
263ca987d46SWarner Losh 
264ca987d46SWarner Losh 	case 'w':
265ca987d46SWarner Losh 	    i = va_arg(ap, u_int);
26656e53cb8SWarner Losh 	    *(uint16_t *)argp = i;
26756e53cb8SWarner Losh 	    argp += sizeof(uint16_t);
268ca987d46SWarner Losh 	    break;
269ca987d46SWarner Losh 
270ca987d46SWarner Losh 	case 'l':
27156e53cb8SWarner Losh 	    i = va_arg(ap, uint32_t);
27256e53cb8SWarner Losh 	    *(uint32_t *)argp = i;
27356e53cb8SWarner Losh 	    argp += sizeof(uint32_t);
274ca987d46SWarner Losh 	    break;
275ca987d46SWarner Losh 	}
276ca987d46SWarner Losh     }
277ca987d46SWarner Losh     va_end(ap);
278ca987d46SWarner Losh 
279ca987d46SWarner Losh     /* BIOS segment last */
28056e53cb8SWarner Losh     *(uint16_t *)argp = pnp_Icheck->pnp_rmds;
28156e53cb8SWarner Losh     argp += sizeof(uint16_t);
282ca987d46SWarner Losh 
283ca987d46SWarner Losh     /* prepare for call */
284ca987d46SWarner Losh     v86.ctl = V86_ADDR | V86_CALLF;
28556e53cb8SWarner Losh     v86.addr = ((uint32_t)pnp_Icheck->pnp_rmcs << 16) + pnp_Icheck->pnp_rmip;
286ca987d46SWarner Losh 
287ca987d46SWarner Losh     /* call with packed stack and return */
288ca987d46SWarner Losh     v86bios(args[0], args[1], args[2], args[3]);
289ca987d46SWarner Losh     return(v86.eax & 0xffff);
290ca987d46SWarner Losh }
291