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 int call_handler(struct acpi_subtable_proc *proc, 89 union acpi_subtable_headers *hdr, 90 unsigned long end) 91 { 92 if (proc->handler) 93 return proc->handler(hdr, end); 94 if (proc->handler_arg) 95 return proc->handler_arg(hdr, proc->arg, end); 96 return -EINVAL; 97 } 98 99 /** 100 * acpi_parse_entries_array - for each proc_num find a suitable subtable 101 * 102 * @id: table id (for debugging purposes) 103 * @table_size: size of the root table 104 * @table_header: where does the table start? 105 * @proc: array of acpi_subtable_proc struct containing entry id 106 * and associated handler with it 107 * @proc_num: how big proc is? 108 * @max_entries: how many entries can we process? 109 * 110 * For each proc_num find a subtable with proc->id and run proc->handler 111 * on it. Assumption is that there's only single handler for particular 112 * entry id. 113 * 114 * The table_size is not the size of the complete ACPI table (the length 115 * field in the header struct), but only the size of the root table; i.e., 116 * the offset from the very first byte of the complete ACPI table, to the 117 * first byte of the very first subtable. 118 * 119 * On success returns sum of all matching entries for all proc handlers. 120 * Otherwise, -ENODEV or -EINVAL is returned. 121 */ 122 int __init_or_acpilib 123 acpi_parse_entries_array(char *id, unsigned long table_size, 124 struct acpi_table_header *table_header, 125 struct acpi_subtable_proc *proc, 126 int proc_num, unsigned int max_entries) 127 { 128 unsigned long table_end, subtable_len, entry_len; 129 struct acpi_subtable_entry entry; 130 int count = 0; 131 int i; 132 133 table_end = (unsigned long)table_header + table_header->length; 134 135 /* Parse all entries looking for a match. */ 136 137 entry.type = acpi_get_subtable_type(id); 138 entry.hdr = (union acpi_subtable_headers *) 139 ((unsigned long)table_header + table_size); 140 subtable_len = acpi_get_subtable_header_length(&entry); 141 142 while (((unsigned long)entry.hdr) + subtable_len < table_end) { 143 for (i = 0; i < proc_num; i++) { 144 if (acpi_get_entry_type(&entry) != proc[i].id) 145 continue; 146 147 if (!max_entries || count < max_entries) 148 if (call_handler(&proc[i], entry.hdr, table_end)) 149 return -EINVAL; 150 151 proc[i].count++; 152 count++; 153 break; 154 } 155 156 /* 157 * If entry->length is 0, break from this loop to avoid 158 * infinite loop. 159 */ 160 entry_len = acpi_get_entry_length(&entry); 161 if (entry_len == 0) { 162 pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id); 163 return -EINVAL; 164 } 165 166 entry.hdr = (union acpi_subtable_headers *) 167 ((unsigned long)entry.hdr + entry_len); 168 } 169 170 if (max_entries && count > max_entries) { 171 pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", 172 id, proc->id, count - max_entries, count); 173 } 174 175 return count; 176 } 177