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