xref: /freebsd/sys/isa/pnpparse.c (revision e453e498cbb88570a3ff7b3679de65c88707da95)
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
pnp_printf(uint32_t id,char * fmt,...)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
pnp_parse_desc(device_t dev,u_char tag,u_char * res,int len,struct isa_config * config,int ldn)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
pnp_parse_dependant(device_t dev,u_char * resources,int len,struct isa_config * config,int ldn)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
pnp_merge_resources(device_t dev,struct isa_config * from,struct isa_config * to)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
pnp_parse_resources(device_t dev,u_char * resources,int len,int ldn)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
pnp_scan_resources(device_t dev,u_char * resources,int len,struct isa_config * config,int ldn,pnp_scan_cb * cb)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