Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/isa/pnpparse.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 1999 Doug Rabson
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/param.h>
30
#include <sys/systm.h>
31
#include <sys/malloc.h>
32
#include <sys/module.h>
33
#include <sys/bus.h>
34
#include <sys/stdarg.h>
35
36
#include <isa/isavar.h>
37
#include <isa/pnpreg.h>
38
#include <isa/pnpvar.h>
39
40
#define MAXDEP 8
41
42
#define I16(p) ((p)[0] + ((p)[1] << 8))
43
#define I32(p) (I16(p) + (I16((p)+2) << 16))
44
45
void
46
pnp_printf(uint32_t id, char *fmt, ...)
47
{
48
va_list ap;
49
50
va_start(ap, fmt);
51
printf("%s: ", pnp_eisaformat(id));
52
vprintf(fmt, ap);
53
va_end(ap);
54
}
55
56
/* parse a single descriptor */
57
58
static int
59
pnp_parse_desc(device_t dev, u_char tag, u_char *res, int len,
60
struct isa_config *config, int ldn)
61
{
62
char buf[100];
63
uint32_t id;
64
uint32_t compat_id;
65
int temp;
66
67
id = isa_get_logicalid(dev);
68
69
if (PNP_RES_TYPE(tag) == 0) {
70
71
/* Small resource */
72
switch (PNP_SRES_NUM(tag)) {
73
74
case PNP_TAG_VERSION:
75
case PNP_TAG_VENDOR:
76
/* these descriptors are quietly ignored */
77
break;
78
79
case PNP_TAG_LOGICAL_DEVICE:
80
case PNP_TAG_START_DEPENDANT:
81
case PNP_TAG_END_DEPENDANT:
82
if (bootverbose)
83
pnp_printf(id, "unexpected small tag %d\n",
84
PNP_SRES_NUM(tag));
85
/* shouldn't happen; quit now */
86
return (1);
87
88
case PNP_TAG_COMPAT_DEVICE:
89
/*
90
* Got a compatible device id resource.
91
* Should keep a list of compat ids in the device.
92
*/
93
bcopy(res, &compat_id, 4);
94
if (isa_get_compatid(dev) == 0)
95
isa_set_compatid(dev, compat_id);
96
break;
97
98
case PNP_TAG_IRQ_FORMAT:
99
if (config->ic_nirq == ISA_NIRQ) {
100
pnp_printf(id, "too many irqs\n");
101
return (1);
102
}
103
if (I16(res) == 0) {
104
/* a null descriptor */
105
config->ic_irqmask[config->ic_nirq] = 0;
106
config->ic_nirq++;
107
break;
108
}
109
if (bootverbose)
110
pnp_printf(id, "adding irq mask %#02x\n",
111
I16(res));
112
config->ic_irqmask[config->ic_nirq] = I16(res);
113
config->ic_nirq++;
114
break;
115
116
case PNP_TAG_DMA_FORMAT:
117
if (config->ic_ndrq == ISA_NDRQ) {
118
pnp_printf(id, "too many drqs\n");
119
return (1);
120
}
121
if (res[0] == 0) {
122
/* a null descriptor */
123
config->ic_drqmask[config->ic_ndrq] = 0;
124
config->ic_ndrq++;
125
break;
126
}
127
if (bootverbose)
128
pnp_printf(id, "adding dma mask %#02x\n",
129
res[0]);
130
config->ic_drqmask[config->ic_ndrq] = res[0];
131
config->ic_ndrq++;
132
break;
133
134
case PNP_TAG_IO_RANGE:
135
if (config->ic_nport == ISA_NPORT) {
136
pnp_printf(id, "too many ports\n");
137
return (1);
138
}
139
if (res[6] == 0) {
140
/* a null descriptor */
141
config->ic_port[config->ic_nport].ir_start = 0;
142
config->ic_port[config->ic_nport].ir_end = 0;
143
config->ic_port[config->ic_nport].ir_size = 0;
144
config->ic_port[config->ic_nport].ir_align = 0;
145
config->ic_nport++;
146
break;
147
}
148
if (bootverbose) {
149
pnp_printf(id, "adding io range "
150
"%#x-%#x, size=%#x, "
151
"align=%#x\n",
152
I16(res + 1),
153
I16(res + 3) + res[6]-1,
154
res[6], res[5]);
155
}
156
config->ic_port[config->ic_nport].ir_start =
157
I16(res + 1);
158
config->ic_port[config->ic_nport].ir_end =
159
I16(res + 3) + res[6] - 1;
160
config->ic_port[config->ic_nport].ir_size = res[6];
161
if (res[5] == 0) {
162
/* Make sure align is at least one */
163
res[5] = 1;
164
}
165
config->ic_port[config->ic_nport].ir_align = res[5];
166
config->ic_nport++;
167
pnp_check_quirks(isa_get_vendorid(dev),
168
isa_get_logicalid(dev), ldn, config);
169
break;
170
171
case PNP_TAG_IO_FIXED:
172
if (config->ic_nport == ISA_NPORT) {
173
pnp_printf(id, "too many ports\n");
174
return (1);
175
}
176
if (res[2] == 0) {
177
/* a null descriptor */
178
config->ic_port[config->ic_nport].ir_start = 0;
179
config->ic_port[config->ic_nport].ir_end = 0;
180
config->ic_port[config->ic_nport].ir_size = 0;
181
config->ic_port[config->ic_nport].ir_align = 0;
182
config->ic_nport++;
183
break;
184
}
185
if (bootverbose) {
186
pnp_printf(id, "adding fixed io range "
187
"%#x-%#x, size=%#x, "
188
"align=%#x\n",
189
I16(res),
190
I16(res) + res[2] - 1,
191
res[2], 1);
192
}
193
config->ic_port[config->ic_nport].ir_start = I16(res);
194
config->ic_port[config->ic_nport].ir_end =
195
I16(res) + res[2] - 1;
196
config->ic_port[config->ic_nport].ir_size = res[2];
197
config->ic_port[config->ic_nport].ir_align = 1;
198
config->ic_nport++;
199
break;
200
201
case PNP_TAG_END:
202
if (bootverbose)
203
pnp_printf(id, "end config\n");
204
return (1);
205
206
default:
207
/* Skip this resource */
208
pnp_printf(id, "unexpected small tag %d\n",
209
PNP_SRES_NUM(tag));
210
break;
211
}
212
} else {
213
/* Large resource */
214
switch (PNP_LRES_NUM(tag)) {
215
216
case PNP_TAG_ID_UNICODE:
217
case PNP_TAG_LARGE_VENDOR:
218
/* these descriptors are quietly ignored */
219
break;
220
221
case PNP_TAG_ID_ANSI:
222
if (len > sizeof(buf) - 1)
223
len = sizeof(buf) - 1;
224
bcopy(res, buf, len);
225
226
/*
227
* Trim trailing spaces and garbage.
228
*/
229
while (len > 0 && buf[len - 1] <= ' ')
230
len--;
231
buf[len] = '\0';
232
device_set_desc_copy(dev, buf);
233
break;
234
235
case PNP_TAG_MEMORY_RANGE:
236
if (config->ic_nmem == ISA_NMEM) {
237
pnp_printf(id, "too many memory ranges\n");
238
return (1);
239
}
240
if (I16(res + 7) == 0) {
241
/* a null descriptor */
242
config->ic_mem[config->ic_nmem].ir_start = 0;
243
config->ic_mem[config->ic_nmem].ir_end = 0;
244
config->ic_mem[config->ic_nmem].ir_size = 0;
245
config->ic_mem[config->ic_nmem].ir_align = 0;
246
config->ic_nmem++;
247
break;
248
}
249
if (bootverbose) {
250
temp = I16(res + 7) << 8;
251
pnp_printf(id, "adding memory range "
252
"%#x-%#x, size=%#x, "
253
"align=%#x\n",
254
I16(res + 1) << 8,
255
(I16(res + 3) << 8) + temp - 1,
256
temp, I16(res + 5));
257
}
258
config->ic_mem[config->ic_nmem].ir_start =
259
I16(res + 1) << 8;
260
config->ic_mem[config->ic_nmem].ir_end =
261
(I16(res + 3) << 8) + (I16(res + 7) << 8) - 1;
262
config->ic_mem[config->ic_nmem].ir_size =
263
I16(res + 7) << 8;
264
config->ic_mem[config->ic_nmem].ir_align = I16(res + 5);
265
if (!config->ic_mem[config->ic_nmem].ir_align)
266
config->ic_mem[config->ic_nmem].ir_align =
267
0x10000;
268
config->ic_nmem++;
269
break;
270
271
case PNP_TAG_MEMORY32_RANGE:
272
if (config->ic_nmem == ISA_NMEM) {
273
pnp_printf(id, "too many memory ranges\n");
274
return (1);
275
}
276
if (I32(res + 13) == 0) {
277
/* a null descriptor */
278
config->ic_mem[config->ic_nmem].ir_start = 0;
279
config->ic_mem[config->ic_nmem].ir_end = 0;
280
config->ic_mem[config->ic_nmem].ir_size = 0;
281
config->ic_mem[config->ic_nmem].ir_align = 0;
282
config->ic_nmem++;
283
break;
284
}
285
if (bootverbose) {
286
pnp_printf(id, "adding memory32 range "
287
"%#x-%#x, size=%#x, "
288
"align=%#x\n",
289
I32(res + 1),
290
I32(res + 5) + I32(res + 13) - 1,
291
I32(res + 13), I32(res + 9));
292
}
293
config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
294
config->ic_mem[config->ic_nmem].ir_end =
295
I32(res + 5) + I32(res + 13) - 1;
296
config->ic_mem[config->ic_nmem].ir_size = I32(res + 13);
297
config->ic_mem[config->ic_nmem].ir_align = I32(res + 9);
298
config->ic_nmem++;
299
break;
300
301
case PNP_TAG_MEMORY32_FIXED:
302
if (config->ic_nmem == ISA_NMEM) {
303
pnp_printf(id, "too many memory ranges\n");
304
return (1);
305
}
306
if (I32(res + 5) == 0) {
307
/* a null descriptor */
308
config->ic_mem[config->ic_nmem].ir_start = 0;
309
config->ic_mem[config->ic_nmem].ir_end = 0;
310
config->ic_mem[config->ic_nmem].ir_size = 0;
311
config->ic_mem[config->ic_nmem].ir_align = 0;
312
break;
313
}
314
if (bootverbose) {
315
pnp_printf(id, "adding fixed memory32 range "
316
"%#x-%#x, size=%#x\n",
317
I32(res + 1),
318
I32(res + 1) + I32(res + 5) - 1,
319
I32(res + 5));
320
}
321
config->ic_mem[config->ic_nmem].ir_start = I32(res + 1);
322
config->ic_mem[config->ic_nmem].ir_end =
323
I32(res + 1) + I32(res + 5) - 1;
324
config->ic_mem[config->ic_nmem].ir_size = I32(res + 5);
325
config->ic_mem[config->ic_nmem].ir_align = 1;
326
config->ic_nmem++;
327
break;
328
329
default:
330
/* Skip this resource */
331
pnp_printf(id, "unexpected large tag %d\n",
332
PNP_SRES_NUM(tag));
333
break;
334
}
335
}
336
337
return (0);
338
}
339
340
/*
341
* Parse a single "dependent" resource combination.
342
*/
343
344
u_char
345
*pnp_parse_dependant(device_t dev, u_char *resources, int len,
346
struct isa_config *config, int ldn)
347
{
348
349
return pnp_scan_resources(dev, resources, len, config, ldn,
350
pnp_parse_desc);
351
}
352
353
static void
354
pnp_merge_resources(device_t dev, struct isa_config *from,
355
struct isa_config *to)
356
{
357
device_t parent;
358
int i;
359
360
parent = device_get_parent(dev);
361
for (i = 0; i < from->ic_nmem; i++) {
362
if (to->ic_nmem == ISA_NMEM) {
363
device_printf(parent, "too many memory ranges\n");
364
return;
365
}
366
to->ic_mem[to->ic_nmem] = from->ic_mem[i];
367
to->ic_nmem++;
368
}
369
for (i = 0; i < from->ic_nport; i++) {
370
if (to->ic_nport == ISA_NPORT) {
371
device_printf(parent, "too many port ranges\n");
372
return;
373
}
374
to->ic_port[to->ic_nport] = from->ic_port[i];
375
to->ic_nport++;
376
}
377
for (i = 0; i < from->ic_nirq; i++) {
378
if (to->ic_nirq == ISA_NIRQ) {
379
device_printf(parent, "too many irq ranges\n");
380
return;
381
}
382
to->ic_irqmask[to->ic_nirq] = from->ic_irqmask[i];
383
to->ic_nirq++;
384
}
385
for (i = 0; i < from->ic_ndrq; i++) {
386
if (to->ic_ndrq == ISA_NDRQ) {
387
device_printf(parent, "too many drq ranges\n");
388
return;
389
}
390
to->ic_drqmask[to->ic_ndrq] = from->ic_drqmask[i];
391
to->ic_ndrq++;
392
}
393
}
394
395
/*
396
* Parse resource data for Logical Devices, make a list of available
397
* resource configurations, and add them to the device.
398
*
399
* This function exits as soon as it gets an error reading *ANY*
400
* Resource Data or it reaches the end of Resource Data.
401
*/
402
403
void
404
pnp_parse_resources(device_t dev, u_char *resources, int len, int ldn)
405
{
406
struct isa_config *configs;
407
struct isa_config *config;
408
device_t parent;
409
int priorities[1 + MAXDEP];
410
u_char *start;
411
u_char *p;
412
u_char tag;
413
uint32_t id;
414
int ncfgs;
415
int l;
416
int i;
417
418
parent = device_get_parent(dev);
419
id = isa_get_logicalid(dev);
420
421
configs = (struct isa_config *)malloc(sizeof(*configs)*(1 + MAXDEP),
422
M_DEVBUF, M_NOWAIT | M_ZERO);
423
if (configs == NULL) {
424
device_printf(parent, "No memory to parse PNP data\n");
425
return;
426
}
427
config = &configs[0];
428
priorities[0] = 0;
429
ncfgs = 1;
430
431
p = resources;
432
start = NULL;
433
while (len > 0) {
434
tag = *p++;
435
len--;
436
if (PNP_RES_TYPE(tag) == 0) {
437
/* Small resource */
438
l = PNP_SRES_LEN(tag);
439
if (len < l) {
440
len = 0;
441
continue;
442
}
443
len -= l;
444
445
switch (PNP_SRES_NUM(tag)) {
446
447
case PNP_TAG_START_DEPENDANT:
448
if (start != NULL) {
449
/*
450
* Copy the common resources first,
451
* then parse the "dependent" resources.
452
*/
453
pnp_merge_resources(dev, &configs[0],
454
config);
455
pnp_parse_dependant(dev, start,
456
p - start - 1,
457
config, ldn);
458
}
459
start = p + l;
460
if (ncfgs > MAXDEP) {
461
device_printf(parent, "too many dependent configs (%d)\n", MAXDEP);
462
len = 0;
463
break;
464
}
465
config = &configs[ncfgs];
466
/*
467
* If the priority is not specified,
468
* then use the default of 'acceptable'
469
*/
470
if (l > 0)
471
priorities[ncfgs] = p[0];
472
else
473
priorities[ncfgs] = 1;
474
if (bootverbose)
475
pnp_printf(id, "start dependent (%d)\n",
476
priorities[ncfgs]);
477
ncfgs++;
478
break;
479
480
case PNP_TAG_END_DEPENDANT:
481
if (start == NULL) {
482
device_printf(parent,
483
"malformed resources\n");
484
len = 0;
485
break;
486
}
487
/*
488
* Copy the common resources first,
489
* then parse the "dependent" resources.
490
*/
491
pnp_merge_resources(dev, &configs[0], config);
492
pnp_parse_dependant(dev, start, p - start - 1,
493
config, ldn);
494
start = NULL;
495
if (bootverbose)
496
pnp_printf(id, "end dependent\n");
497
/*
498
* Back to the common part; clear it
499
* as its contents has already been copied
500
* to each dependent.
501
*/
502
config = &configs[0];
503
bzero(config, sizeof(*config));
504
break;
505
506
case PNP_TAG_END:
507
if (start != NULL) {
508
device_printf(parent,
509
"malformed resources\n");
510
}
511
len = 0;
512
break;
513
514
default:
515
if (start != NULL)
516
/* defer parsing a dependent section */
517
break;
518
if (pnp_parse_desc(dev, tag, p, l, config, ldn))
519
len = 0;
520
break;
521
}
522
p += l;
523
} else {
524
/* Large resource */
525
if (len < 2) {
526
len = 0;
527
break;
528
}
529
l = I16(p);
530
p += 2;
531
len -= 2;
532
if (len < l) {
533
len = 0;
534
break;
535
}
536
len -= l;
537
if (start == NULL &&
538
pnp_parse_desc(dev, tag, p, l, config, ldn)) {
539
len = 0;
540
break;
541
}
542
p += l;
543
}
544
}
545
546
if (ncfgs == 1) {
547
/* Single config without dependants */
548
ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]);
549
free(configs, M_DEVBUF);
550
return;
551
}
552
553
for (i = 1; i < ncfgs; i++) {
554
/*
555
* Merge the remaining part of the common resources,
556
* if any. Strictly speaking, there shouldn't be common/main
557
* resources after the END_DEPENDENT tag.
558
*/
559
pnp_merge_resources(dev, &configs[0], &configs[i]);
560
ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]);
561
}
562
563
free(configs, M_DEVBUF);
564
}
565
566
u_char
567
*pnp_scan_resources(device_t dev, u_char *resources, int len,
568
struct isa_config *config, int ldn, pnp_scan_cb *cb)
569
{
570
u_char *p;
571
u_char tag;
572
int l;
573
574
p = resources;
575
while (len > 0) {
576
tag = *p++;
577
len--;
578
if (PNP_RES_TYPE(tag) == 0) {
579
/* small resource */
580
l = PNP_SRES_LEN(tag);
581
if (len < l)
582
break;
583
if ((*cb)(dev, tag, p, l, config, ldn))
584
return (p + l);
585
if (PNP_SRES_NUM(tag) == PNP_TAG_END)
586
return (p + l);
587
} else {
588
/* large resource */
589
if (len < 2)
590
break;
591
l = I16(p);
592
p += 2;
593
len -= 2;
594
if (len < l)
595
break;
596
if ((*cb)(dev, tag, p, l, config, ldn))
597
return (p + l);
598
}
599
p += l;
600
len -= l;
601
}
602
return NULL;
603
}
604
605