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 16 enum acpi_subtable_type { 17 ACPI_SUBTABLE_COMMON, 18 ACPI_SUBTABLE_HMAT, 19 ACPI_SUBTABLE_PRMT, 20 ACPI_SUBTABLE_CEDT, 21 }; 22 23 struct acpi_subtable_entry { 24 union acpi_subtable_headers *hdr; 25 enum acpi_subtable_type type; 26 }; 27 28 static unsigned long __init_or_acpilib 29 acpi_get_entry_type(struct acpi_subtable_entry *entry) 30 { 31 switch (entry->type) { 32 case ACPI_SUBTABLE_COMMON: 33 return entry->hdr->common.type; 34 case ACPI_SUBTABLE_HMAT: 35 return entry->hdr->hmat.type; 36 case ACPI_SUBTABLE_PRMT: 37 return 0; 38 case ACPI_SUBTABLE_CEDT: 39 return entry->hdr->cedt.type; 40 } 41 return 0; 42 } 43 44 static unsigned long __init_or_acpilib 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 } 57 return 0; 58 } 59 60 static unsigned long __init_or_acpilib 61 acpi_get_subtable_header_length(struct acpi_subtable_entry *entry) 62 { 63 switch (entry->type) { 64 case ACPI_SUBTABLE_COMMON: 65 return sizeof(entry->hdr->common); 66 case ACPI_SUBTABLE_HMAT: 67 return sizeof(entry->hdr->hmat); 68 case ACPI_SUBTABLE_PRMT: 69 return sizeof(entry->hdr->prmt); 70 case ACPI_SUBTABLE_CEDT: 71 return sizeof(entry->hdr->cedt); 72 } 73 return 0; 74 } 75 76 static enum acpi_subtable_type __init_or_acpilib 77 acpi_get_subtable_type(char *id) 78 { 79 if (strncmp(id, ACPI_SIG_HMAT, 4) == 0) 80 return ACPI_SUBTABLE_HMAT; 81 if (strncmp(id, ACPI_SIG_PRMT, 4) == 0) 82 return ACPI_SUBTABLE_PRMT; 83 if (strncmp(id, ACPI_SIG_CEDT, 4) == 0) 84 return ACPI_SUBTABLE_CEDT; 85 return ACPI_SUBTABLE_COMMON; 86 } 87 88 static __init_or_acpilib bool has_handler(struct acpi_subtable_proc *proc) 89 { 90 return proc->handler || proc->handler_arg; 91 } 92 93 static __init_or_acpilib int call_handler(struct acpi_subtable_proc *proc, 94 union acpi_subtable_headers *hdr, 95 unsigned long end) 96 { 97 if (proc->handler) 98 return proc->handler(hdr, end); 99 if (proc->handler_arg) 100 return proc->handler_arg(hdr, proc->arg, end); 101 return -EINVAL; 102 } 103 104 /** 105 * acpi_parse_entries_array - for each proc_num find a suitable subtable 106 * 107 * @id: table id (for debugging purposes) 108 * @table_size: size of the root table 109 * @table_header: where does the table start? 110 * @proc: array of acpi_subtable_proc struct containing entry id 111 * and associated handler with it 112 * @proc_num: how big proc is? 113 * @max_entries: how many entries can we process? 114 * 115 * For each proc_num find a subtable with proc->id and run proc->handler 116 * on it. Assumption is that there's only single handler for particular 117 * entry id. 118 * 119 * The table_size is not the size of the complete ACPI table (the length 120 * field in the header struct), but only the size of the root table; i.e., 121 * the offset from the very first byte of the complete ACPI table, to the 122 * first byte of the very first subtable. 123 * 124 * On success returns sum of all matching entries for all proc handlers. 125 * Otherwise, -ENODEV or -EINVAL is returned. 126 */ 127 int __init_or_acpilib 128 acpi_parse_entries_array(char *id, unsigned long table_size, 129 struct acpi_table_header *table_header, 130 struct acpi_subtable_proc *proc, 131 int proc_num, unsigned int max_entries) 132 { 133 unsigned long table_end, subtable_len, entry_len; 134 struct acpi_subtable_entry entry; 135 int count = 0; 136 int errs = 0; 137 int i; 138 139 table_end = (unsigned long)table_header + table_header->length; 140 141 /* Parse all entries looking for a match. */ 142 143 entry.type = acpi_get_subtable_type(id); 144 entry.hdr = (union acpi_subtable_headers *) 145 ((unsigned long)table_header + table_size); 146 subtable_len = acpi_get_subtable_header_length(&entry); 147 148 while (((unsigned long)entry.hdr) + subtable_len < table_end) { 149 if (max_entries && count >= max_entries) 150 break; 151 152 for (i = 0; i < proc_num; i++) { 153 if (acpi_get_entry_type(&entry) != proc[i].id) 154 continue; 155 if (!has_handler(&proc[i]) || 156 (!errs && 157 call_handler(&proc[i], entry.hdr, table_end))) { 158 errs++; 159 continue; 160 } 161 162 proc[i].count++; 163 break; 164 } 165 if (i != proc_num) 166 count++; 167 168 /* 169 * If entry->length is 0, break from this loop to avoid 170 * infinite loop. 171 */ 172 entry_len = acpi_get_entry_length(&entry); 173 if (entry_len == 0) { 174 pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id); 175 return -EINVAL; 176 } 177 178 entry.hdr = (union acpi_subtable_headers *) 179 ((unsigned long)entry.hdr + entry_len); 180 } 181 182 if (max_entries && count > max_entries) { 183 pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n", 184 id, proc->id, count); 185 } 186 187 return errs ? -EINVAL : count; 188 } 189