Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233// SPDX-License-Identifier: GPL-2.0-or-later/* * fw_tables.c - Parsing support for ACPI and ACPI-like tables provided by * platform or device firmware * * Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * Copyright (C) 2023 Intel Corp. */#include <linux/errno.h>#include <linux/acpi.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/types.h>#include <linux/fw_table.h>
enum acpi_subtable_type { ACPI_SUBTABLE_COMMON, ACPI_SUBTABLE_HMAT, ACPI_SUBTABLE_PRMT, ACPI_SUBTABLE_CEDT, CDAT_SUBTABLE,};
struct acpi_subtable_entry { union acpi_subtable_headers *hdr; enum acpi_subtable_type type;};
static unsigned long __init_or_fwtbl_libacpi_get_entry_type(struct acpi_subtable_entry *entry){ switch (entry->type) { case ACPI_SUBTABLE_COMMON: return entry->hdr->common.type; case ACPI_SUBTABLE_HMAT: return entry->hdr->hmat.type; case ACPI_SUBTABLE_PRMT: return 0; case ACPI_SUBTABLE_CEDT: return entry->hdr->cedt.type; case CDAT_SUBTABLE: return entry->hdr->cdat.type; } return 0;}
static unsigned long __init_or_fwtbl_libacpi_get_entry_length(struct acpi_subtable_entry *entry){ switch (entry->type) { case ACPI_SUBTABLE_COMMON: return entry->hdr->common.length; case ACPI_SUBTABLE_HMAT: return entry->hdr->hmat.length; case ACPI_SUBTABLE_PRMT: return entry->hdr->prmt.length; case ACPI_SUBTABLE_CEDT: return entry->hdr->cedt.length; case CDAT_SUBTABLE: { __le16 length = (__force __le16)entry->hdr->cdat.length;
return le16_to_cpu(length); } } return 0;}
static unsigned long __init_or_fwtbl_libacpi_get_subtable_header_length(struct acpi_subtable_entry *entry){ switch (entry->type) { case ACPI_SUBTABLE_COMMON: return sizeof(entry->hdr->common); case ACPI_SUBTABLE_HMAT: return sizeof(entry->hdr->hmat); case ACPI_SUBTABLE_PRMT: return sizeof(entry->hdr->prmt); case ACPI_SUBTABLE_CEDT: return sizeof(entry->hdr->cedt); case CDAT_SUBTABLE: return sizeof(entry->hdr->cdat); } return 0;}
static enum acpi_subtable_type __init_or_fwtbl_libacpi_get_subtable_type(char *id){ if (strncmp(id, ACPI_SIG_HMAT, 4) == 0) return ACPI_SUBTABLE_HMAT; if (strncmp(id, ACPI_SIG_PRMT, 4) == 0) return ACPI_SUBTABLE_PRMT; if (strncmp(id, ACPI_SIG_CEDT, 4) == 0) return ACPI_SUBTABLE_CEDT; if (strncmp(id, ACPI_SIG_CDAT, 4) == 0) return CDAT_SUBTABLE; return ACPI_SUBTABLE_COMMON;}
static unsigned long __init_or_fwtbl_libacpi_table_get_length(enum acpi_subtable_type type, union fw_table_header *header){ if (type == CDAT_SUBTABLE) { __le32 length = (__force __le32)header->cdat.length;
return le32_to_cpu(length); }
return header->acpi.length;}
static __init_or_fwtbl_lib int call_handler(struct acpi_subtable_proc *proc, union acpi_subtable_headers *hdr, unsigned long end){ if (proc->handler) return proc->handler(hdr, end); if (proc->handler_arg) return proc->handler_arg(hdr, proc->arg, end); return -EINVAL;}
/** * acpi_parse_entries_array - for each proc_num find a suitable subtable * * @id: table id (for debugging purposes) * @table_size: size of the root table * @max_length: maximum size of the table (ignore if 0) * @table_header: where does the table start? * @proc: array of acpi_subtable_proc struct containing entry id * and associated handler with it * @proc_num: how big proc is? * @max_entries: how many entries can we process? * * For each proc_num find a subtable with proc->id and run proc->handler * on it. Assumption is that there's only single handler for particular * entry id. * * The table_size is not the size of the complete ACPI table (the length * field in the header struct), but only the size of the root table; i.e., * the offset from the very first byte of the complete ACPI table, to the * first byte of the very first subtable. * * On success returns sum of all matching entries for all proc handlers. * Otherwise, -ENODEV or -EINVAL is returned. */int __init_or_fwtbl_libacpi_parse_entries_array(char *id, unsigned long table_size, union fw_table_header *table_header, unsigned long max_length, struct acpi_subtable_proc *proc, int proc_num, unsigned int max_entries){ unsigned long table_len, table_end, subtable_len, entry_len; struct acpi_subtable_entry entry; enum acpi_subtable_type type; int count = 0; int i;
type = acpi_get_subtable_type(id); table_len = acpi_table_get_length(type, table_header); if (max_length && max_length < table_len) table_len = max_length; table_end = (unsigned long)table_header + table_len;
/* Parse all entries looking for a match. */
entry.type = type; entry.hdr = (union acpi_subtable_headers *) ((unsigned long)table_header + table_size); subtable_len = acpi_get_subtable_header_length(&entry);
while (((unsigned long)entry.hdr) + subtable_len < table_end) { for (i = 0; i < proc_num; i++) { if (acpi_get_entry_type(&entry) != proc[i].id) continue;
if (!max_entries || count < max_entries) if (call_handler(&proc[i], entry.hdr, table_end)) return -EINVAL;
proc[i].count++; count++; break; }
/* * If entry->length is 0, break from this loop to avoid * infinite loop. */ entry_len = acpi_get_entry_length(&entry); if (entry_len == 0) { pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id); return -EINVAL; }
entry.hdr = (union acpi_subtable_headers *) ((unsigned long)entry.hdr + entry_len); }
if (max_entries && count > max_entries) { pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", id, proc->id, count - max_entries, count); }
return count;}
int __init_or_fwtbl_libcdat_table_parse(enum acpi_cdat_type type, acpi_tbl_entry_handler_arg handler_arg, void *arg, struct acpi_table_cdat *table_header, unsigned long length){ struct acpi_subtable_proc proc = { .id = type, .handler_arg = handler_arg, .arg = arg, };
if (!table_header) return -EINVAL;
return acpi_parse_entries_array(ACPI_SIG_CDAT, sizeof(struct acpi_table_cdat), (union fw_table_header *)table_header, length, &proc, 1, 0);}EXPORT_SYMBOL_FWTBL_LIB(cdat_table_parse);