// 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_lib
acpi_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_lib
acpi_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_lib
acpi_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_lib
acpi_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_lib
acpi_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_lib
acpi_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_lib
cdat_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);
Messung V0.5 in Prozent C=98 H=98 G=97
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland