fw_table.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * fw_tables.c - Parsing support for ACPI and ACPI-like tables provided by
  4. * platform or device firmware
  5. *
  6. * Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
  7. * Copyright (C) 2023 Intel Corp.
  8. */
  9. #include <linux/errno.h>
  10. #include <linux/acpi.h>
  11. #include <linux/init.h>
  12. #include <linux/kernel.h>
  13. #include <linux/string.h>
  14. #include <linux/types.h>
  15. #include <linux/fw_table.h>
  16. enum acpi_subtable_type {
  17. ACPI_SUBTABLE_COMMON,
  18. ACPI_SUBTABLE_HMAT,
  19. ACPI_SUBTABLE_PRMT,
  20. ACPI_SUBTABLE_CEDT,
  21. CDAT_SUBTABLE,
  22. };
  23. struct acpi_subtable_entry {
  24. union acpi_subtable_headers *hdr;
  25. enum acpi_subtable_type type;
  26. };
  27. static unsigned long __init_or_fwtbl_lib
  28. acpi_get_entry_type(struct acpi_subtable_entry *entry)
  29. {
  30. switch (entry->type) {
  31. case ACPI_SUBTABLE_COMMON:
  32. return entry->hdr->common.type;
  33. case ACPI_SUBTABLE_HMAT:
  34. return entry->hdr->hmat.type;
  35. case ACPI_SUBTABLE_PRMT:
  36. return 0;
  37. case ACPI_SUBTABLE_CEDT:
  38. return entry->hdr->cedt.type;
  39. case CDAT_SUBTABLE:
  40. return entry->hdr->cdat.type;
  41. }
  42. return 0;
  43. }
  44. static unsigned long __init_or_fwtbl_lib
  45. acpi_get_entry_length(struct acpi_subtable_entry *entry)
  46. {
  47. switch (entry->type) {
  48. case ACPI_SUBTABLE_COMMON:
  49. return entry->hdr->common.length;
  50. case ACPI_SUBTABLE_HMAT:
  51. return entry->hdr->hmat.length;
  52. case ACPI_SUBTABLE_PRMT:
  53. return entry->hdr->prmt.length;
  54. case ACPI_SUBTABLE_CEDT:
  55. return entry->hdr->cedt.length;
  56. case CDAT_SUBTABLE: {
  57. __le16 length = (__force __le16)entry->hdr->cdat.length;
  58. return le16_to_cpu(length);
  59. }
  60. }
  61. return 0;
  62. }
  63. static unsigned long __init_or_fwtbl_lib
  64. acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
  65. {
  66. switch (entry->type) {
  67. case ACPI_SUBTABLE_COMMON:
  68. return sizeof(entry->hdr->common);
  69. case ACPI_SUBTABLE_HMAT:
  70. return sizeof(entry->hdr->hmat);
  71. case ACPI_SUBTABLE_PRMT:
  72. return sizeof(entry->hdr->prmt);
  73. case ACPI_SUBTABLE_CEDT:
  74. return sizeof(entry->hdr->cedt);
  75. case CDAT_SUBTABLE:
  76. return sizeof(entry->hdr->cdat);
  77. }
  78. return 0;
  79. }
  80. static enum acpi_subtable_type __init_or_fwtbl_lib
  81. acpi_get_subtable_type(char *id)
  82. {
  83. if (strncmp(id, ACPI_SIG_HMAT, 4) == 0)
  84. return ACPI_SUBTABLE_HMAT;
  85. if (strncmp(id, ACPI_SIG_PRMT, 4) == 0)
  86. return ACPI_SUBTABLE_PRMT;
  87. if (strncmp(id, ACPI_SIG_CEDT, 4) == 0)
  88. return ACPI_SUBTABLE_CEDT;
  89. if (strncmp(id, ACPI_SIG_CDAT, 4) == 0)
  90. return CDAT_SUBTABLE;
  91. return ACPI_SUBTABLE_COMMON;
  92. }
  93. static unsigned long __init_or_fwtbl_lib
  94. acpi_table_get_length(enum acpi_subtable_type type,
  95. union fw_table_header *header)
  96. {
  97. if (type == CDAT_SUBTABLE) {
  98. __le32 length = (__force __le32)header->cdat.length;
  99. return le32_to_cpu(length);
  100. }
  101. return header->acpi.length;
  102. }
  103. static __init_or_fwtbl_lib int call_handler(struct acpi_subtable_proc *proc,
  104. union acpi_subtable_headers *hdr,
  105. unsigned long end)
  106. {
  107. if (proc->handler)
  108. return proc->handler(hdr, end);
  109. if (proc->handler_arg)
  110. return proc->handler_arg(hdr, proc->arg, end);
  111. return -EINVAL;
  112. }
  113. /**
  114. * acpi_parse_entries_array - for each proc_num find a suitable subtable
  115. *
  116. * @id: table id (for debugging purposes)
  117. * @table_size: size of the root table
  118. * @max_length: maximum size of the table (ignore if 0)
  119. * @table_header: where does the table start?
  120. * @proc: array of acpi_subtable_proc struct containing entry id
  121. * and associated handler with it
  122. * @proc_num: how big proc is?
  123. * @max_entries: how many entries can we process?
  124. *
  125. * For each proc_num find a subtable with proc->id and run proc->handler
  126. * on it. Assumption is that there's only single handler for particular
  127. * entry id.
  128. *
  129. * The table_size is not the size of the complete ACPI table (the length
  130. * field in the header struct), but only the size of the root table; i.e.,
  131. * the offset from the very first byte of the complete ACPI table, to the
  132. * first byte of the very first subtable.
  133. *
  134. * On success returns sum of all matching entries for all proc handlers.
  135. * Otherwise, -ENODEV or -EINVAL is returned.
  136. */
  137. int __init_or_fwtbl_lib
  138. acpi_parse_entries_array(char *id, unsigned long table_size,
  139. union fw_table_header *table_header,
  140. unsigned long max_length,
  141. struct acpi_subtable_proc *proc,
  142. int proc_num, unsigned int max_entries)
  143. {
  144. unsigned long table_len, table_end, subtable_len, entry_len;
  145. struct acpi_subtable_entry entry;
  146. enum acpi_subtable_type type;
  147. int count = 0;
  148. int i;
  149. type = acpi_get_subtable_type(id);
  150. table_len = acpi_table_get_length(type, table_header);
  151. if (max_length && max_length < table_len)
  152. table_len = max_length;
  153. table_end = (unsigned long)table_header + table_len;
  154. /* Parse all entries looking for a match. */
  155. entry.type = type;
  156. entry.hdr = (union acpi_subtable_headers *)
  157. ((unsigned long)table_header + table_size);
  158. subtable_len = acpi_get_subtable_header_length(&entry);
  159. while (((unsigned long)entry.hdr) + subtable_len < table_end) {
  160. for (i = 0; i < proc_num; i++) {
  161. if (acpi_get_entry_type(&entry) != proc[i].id)
  162. continue;
  163. if (!max_entries || count < max_entries)
  164. if (call_handler(&proc[i], entry.hdr, table_end))
  165. return -EINVAL;
  166. proc[i].count++;
  167. count++;
  168. break;
  169. }
  170. /*
  171. * If entry->length is 0, break from this loop to avoid
  172. * infinite loop.
  173. */
  174. entry_len = acpi_get_entry_length(&entry);
  175. if (entry_len == 0) {
  176. pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id);
  177. return -EINVAL;
  178. }
  179. entry.hdr = (union acpi_subtable_headers *)
  180. ((unsigned long)entry.hdr + entry_len);
  181. }
  182. if (max_entries && count > max_entries) {
  183. pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n",
  184. id, proc->id, count - max_entries, count);
  185. }
  186. return count;
  187. }
  188. int __init_or_fwtbl_lib
  189. cdat_table_parse(enum acpi_cdat_type type,
  190. acpi_tbl_entry_handler_arg handler_arg,
  191. void *arg,
  192. struct acpi_table_cdat *table_header,
  193. unsigned long length)
  194. {
  195. struct acpi_subtable_proc proc = {
  196. .id = type,
  197. .handler_arg = handler_arg,
  198. .arg = arg,
  199. };
  200. if (!table_header)
  201. return -EINVAL;
  202. return acpi_parse_entries_array(ACPI_SIG_CDAT,
  203. sizeof(struct acpi_table_cdat),
  204. (union fw_table_header *)table_header,
  205. length, &proc, 1, 0);
  206. }
  207. EXPORT_SYMBOL_FWTBL_LIB(cdat_table_parse);