Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/i386/libi386/biospnp.c
34879 views
1
/*-
2
* Copyright (c) 1998 Michael Smith <[email protected]>
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 <sys/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
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
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
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
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
292