brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 1629181 Raw
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