233 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * fw_tables.c - Parsing support for ACPI and ACPI-like tables provided by4 * platform or device firmware5 *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 17enum acpi_subtable_type {18 ACPI_SUBTABLE_COMMON,19 ACPI_SUBTABLE_HMAT,20 ACPI_SUBTABLE_PRMT,21 ACPI_SUBTABLE_CEDT,22 CDAT_SUBTABLE,23};24 25struct acpi_subtable_entry {26 union acpi_subtable_headers *hdr;27 enum acpi_subtable_type type;28};29 30static unsigned long __init_or_fwtbl_lib31acpi_get_entry_type(struct acpi_subtable_entry *entry)32{33 switch (entry->type) {34 case ACPI_SUBTABLE_COMMON:35 return entry->hdr->common.type;36 case ACPI_SUBTABLE_HMAT:37 return entry->hdr->hmat.type;38 case ACPI_SUBTABLE_PRMT:39 return 0;40 case ACPI_SUBTABLE_CEDT:41 return entry->hdr->cedt.type;42 case CDAT_SUBTABLE:43 return entry->hdr->cdat.type;44 }45 return 0;46}47 48static unsigned long __init_or_fwtbl_lib49acpi_get_entry_length(struct acpi_subtable_entry *entry)50{51 switch (entry->type) {52 case ACPI_SUBTABLE_COMMON:53 return entry->hdr->common.length;54 case ACPI_SUBTABLE_HMAT:55 return entry->hdr->hmat.length;56 case ACPI_SUBTABLE_PRMT:57 return entry->hdr->prmt.length;58 case ACPI_SUBTABLE_CEDT:59 return entry->hdr->cedt.length;60 case CDAT_SUBTABLE: {61 __le16 length = (__force __le16)entry->hdr->cdat.length;62 63 return le16_to_cpu(length);64 }65 }66 return 0;67}68 69static unsigned long __init_or_fwtbl_lib70acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)71{72 switch (entry->type) {73 case ACPI_SUBTABLE_COMMON:74 return sizeof(entry->hdr->common);75 case ACPI_SUBTABLE_HMAT:76 return sizeof(entry->hdr->hmat);77 case ACPI_SUBTABLE_PRMT:78 return sizeof(entry->hdr->prmt);79 case ACPI_SUBTABLE_CEDT:80 return sizeof(entry->hdr->cedt);81 case CDAT_SUBTABLE:82 return sizeof(entry->hdr->cdat);83 }84 return 0;85}86 87static enum acpi_subtable_type __init_or_fwtbl_lib88acpi_get_subtable_type(char *id)89{90 if (strncmp(id, ACPI_SIG_HMAT, 4) == 0)91 return ACPI_SUBTABLE_HMAT;92 if (strncmp(id, ACPI_SIG_PRMT, 4) == 0)93 return ACPI_SUBTABLE_PRMT;94 if (strncmp(id, ACPI_SIG_CEDT, 4) == 0)95 return ACPI_SUBTABLE_CEDT;96 if (strncmp(id, ACPI_SIG_CDAT, 4) == 0)97 return CDAT_SUBTABLE;98 return ACPI_SUBTABLE_COMMON;99}100 101static unsigned long __init_or_fwtbl_lib102acpi_table_get_length(enum acpi_subtable_type type,103 union fw_table_header *header)104{105 if (type == CDAT_SUBTABLE) {106 __le32 length = (__force __le32)header->cdat.length;107 108 return le32_to_cpu(length);109 }110 111 return header->acpi.length;112}113 114static __init_or_fwtbl_lib int call_handler(struct acpi_subtable_proc *proc,115 union acpi_subtable_headers *hdr,116 unsigned long end)117{118 if (proc->handler)119 return proc->handler(hdr, end);120 if (proc->handler_arg)121 return proc->handler_arg(hdr, proc->arg, end);122 return -EINVAL;123}124 125/**126 * acpi_parse_entries_array - for each proc_num find a suitable subtable127 *128 * @id: table id (for debugging purposes)129 * @table_size: size of the root table130 * @max_length: maximum size of the table (ignore if 0)131 * @table_header: where does the table start?132 * @proc: array of acpi_subtable_proc struct containing entry id133 * and associated handler with it134 * @proc_num: how big proc is?135 * @max_entries: how many entries can we process?136 *137 * For each proc_num find a subtable with proc->id and run proc->handler138 * on it. Assumption is that there's only single handler for particular139 * entry id.140 *141 * The table_size is not the size of the complete ACPI table (the length142 * field in the header struct), but only the size of the root table; i.e.,143 * the offset from the very first byte of the complete ACPI table, to the144 * first byte of the very first subtable.145 *146 * On success returns sum of all matching entries for all proc handlers.147 * Otherwise, -ENODEV or -EINVAL is returned.148 */149int __init_or_fwtbl_lib150acpi_parse_entries_array(char *id, unsigned long table_size,151 union fw_table_header *table_header,152 unsigned long max_length,153 struct acpi_subtable_proc *proc,154 int proc_num, unsigned int max_entries)155{156 unsigned long table_len, table_end, subtable_len, entry_len;157 struct acpi_subtable_entry entry;158 enum acpi_subtable_type type;159 int count = 0;160 int i;161 162 type = acpi_get_subtable_type(id);163 table_len = acpi_table_get_length(type, table_header);164 if (max_length && max_length < table_len)165 table_len = max_length;166 table_end = (unsigned long)table_header + table_len;167 168 /* Parse all entries looking for a match. */169 170 entry.type = type;171 entry.hdr = (union acpi_subtable_headers *)172 ((unsigned long)table_header + table_size);173 subtable_len = acpi_get_subtable_header_length(&entry);174 175 while (((unsigned long)entry.hdr) + subtable_len < table_end) {176 for (i = 0; i < proc_num; i++) {177 if (acpi_get_entry_type(&entry) != proc[i].id)178 continue;179 180 if (!max_entries || count < max_entries)181 if (call_handler(&proc[i], entry.hdr, table_end))182 return -EINVAL;183 184 proc[i].count++;185 count++;186 break;187 }188 189 /*190 * If entry->length is 0, break from this loop to avoid191 * infinite loop.192 */193 entry_len = acpi_get_entry_length(&entry);194 if (entry_len == 0) {195 pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id);196 return -EINVAL;197 }198 199 entry.hdr = (union acpi_subtable_headers *)200 ((unsigned long)entry.hdr + entry_len);201 }202 203 if (max_entries && count > max_entries) {204 pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n",205 id, proc->id, count - max_entries, count);206 }207 208 return count;209}210 211int __init_or_fwtbl_lib212cdat_table_parse(enum acpi_cdat_type type,213 acpi_tbl_entry_handler_arg handler_arg,214 void *arg,215 struct acpi_table_cdat *table_header,216 unsigned long length)217{218 struct acpi_subtable_proc proc = {219 .id = type,220 .handler_arg = handler_arg,221 .arg = arg,222 };223 224 if (!table_header)225 return -EINVAL;226 227 return acpi_parse_entries_array(ACPI_SIG_CDAT,228 sizeof(struct acpi_table_cdat),229 (union fw_table_header *)table_header,230 length, &proc, 1, 0);231}232EXPORT_SYMBOL_FWTBL_LIB(cdat_table_parse);233