pnpparse.c 15 KB

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