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