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