mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-08 23:12:03 +00:00
Merge branch 'for-6.7/cxl-qtg' into cxl/next
Merge some prep-work for CXL QOS class support. This cycle saw large collisions with mm on this topic, so the bulk of this topic needs to wait.
This commit is contained in:
commit
624eda92ab
@ -369,6 +369,21 @@ Description:
|
||||
provided it is currently idle / not bound to a driver.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/decoderX.Y/qos_class
|
||||
Date: May, 2023
|
||||
KernelVersion: v6.5
|
||||
Contact: linux-cxl@vger.kernel.org
|
||||
Description:
|
||||
(RO) For CXL host platforms that support "QoS Telemmetry" this
|
||||
root-decoder-only attribute conveys a platform specific cookie
|
||||
that identifies a QoS performance class for the CXL Window.
|
||||
This class-id can be compared against a similar "qos_class"
|
||||
published for each memory-type that an endpoint supports. While
|
||||
it is not required that endpoints map their local memory-class
|
||||
to a matching platform class, mismatches are not recommended and
|
||||
there are platform specific side-effects that may result.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/regionZ/uuid
|
||||
Date: May, 2022
|
||||
KernelVersion: v6.0
|
||||
|
@ -294,6 +294,8 @@ F: drivers/pnp/pnpacpi/
|
||||
F: include/acpi/
|
||||
F: include/linux/acpi.h
|
||||
F: include/linux/fwnode.h
|
||||
F: include/linux/fw_table.h
|
||||
F: lib/fw_table.c
|
||||
F: tools/power/acpi/
|
||||
|
||||
ACPI APEI
|
||||
|
@ -12,6 +12,7 @@ menuconfig ACPI
|
||||
select PNP
|
||||
select NLS
|
||||
select CRC32
|
||||
select FIRMWARE_TABLE
|
||||
default y if X86
|
||||
help
|
||||
Advanced Configuration and Power Interface (ACPI) support for
|
||||
|
@ -37,18 +37,6 @@ static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata;
|
||||
|
||||
static int acpi_apic_instance __initdata_or_acpilib;
|
||||
|
||||
enum acpi_subtable_type {
|
||||
ACPI_SUBTABLE_COMMON,
|
||||
ACPI_SUBTABLE_HMAT,
|
||||
ACPI_SUBTABLE_PRMT,
|
||||
ACPI_SUBTABLE_CEDT,
|
||||
};
|
||||
|
||||
struct acpi_subtable_entry {
|
||||
union acpi_subtable_headers *hdr;
|
||||
enum acpi_subtable_type type;
|
||||
};
|
||||
|
||||
/*
|
||||
* Disable table checksum verification for the early stage due to the size
|
||||
* limitation of the current x86 early mapping implementation.
|
||||
@ -237,167 +225,6 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long __init_or_acpilib
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long __init_or_acpilib
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long __init_or_acpilib
|
||||
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);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum acpi_subtable_type __init_or_acpilib
|
||||
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;
|
||||
return ACPI_SUBTABLE_COMMON;
|
||||
}
|
||||
|
||||
static __init_or_acpilib bool has_handler(struct acpi_subtable_proc *proc)
|
||||
{
|
||||
return proc->handler || proc->handler_arg;
|
||||
}
|
||||
|
||||
static __init_or_acpilib 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
|
||||
* @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.
|
||||
*/
|
||||
static int __init_or_acpilib acpi_parse_entries_array(
|
||||
char *id, unsigned long table_size,
|
||||
struct acpi_table_header *table_header, struct acpi_subtable_proc *proc,
|
||||
int proc_num, unsigned int max_entries)
|
||||
{
|
||||
struct acpi_subtable_entry entry;
|
||||
unsigned long table_end, subtable_len, entry_len;
|
||||
int count = 0;
|
||||
int errs = 0;
|
||||
int i;
|
||||
|
||||
table_end = (unsigned long)table_header + table_header->length;
|
||||
|
||||
/* Parse all entries looking for a match. */
|
||||
|
||||
entry.type = acpi_get_subtable_type(id);
|
||||
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) {
|
||||
if (max_entries && count >= max_entries)
|
||||
break;
|
||||
|
||||
for (i = 0; i < proc_num; i++) {
|
||||
if (acpi_get_entry_type(&entry) != proc[i].id)
|
||||
continue;
|
||||
if (!has_handler(&proc[i]) ||
|
||||
(!errs &&
|
||||
call_handler(&proc[i], entry.hdr, table_end))) {
|
||||
errs++;
|
||||
continue;
|
||||
}
|
||||
|
||||
proc[i].count++;
|
||||
break;
|
||||
}
|
||||
if (i != proc_num)
|
||||
count++;
|
||||
|
||||
/*
|
||||
* 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] found the maximum %i entries\n",
|
||||
id, proc->id, count);
|
||||
}
|
||||
|
||||
return errs ? -EINVAL : count;
|
||||
}
|
||||
|
||||
int __init_or_acpilib acpi_table_parse_entries_array(
|
||||
char *id, unsigned long table_size, struct acpi_subtable_proc *proc,
|
||||
int proc_num, unsigned int max_entries)
|
||||
|
@ -289,6 +289,9 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cxlrd->qos_class = cfmws->qtg_id;
|
||||
|
||||
rc = cxl_decoder_add(cxld, target_map);
|
||||
err_xormap:
|
||||
if (rc)
|
||||
|
@ -596,6 +596,16 @@ static int cxl_cdat_read_table(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char cdat_checksum(void *buf, size_t size)
|
||||
{
|
||||
unsigned char sum, *data = buf;
|
||||
size_t i;
|
||||
|
||||
for (sum = 0, i = 0; i < size; i++)
|
||||
sum += data[i];
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_cdat_data - Read the CDAT data on this port
|
||||
* @port: Port to read data from
|
||||
@ -604,18 +614,30 @@ static int cxl_cdat_read_table(struct device *dev,
|
||||
*/
|
||||
void read_cdat_data(struct cxl_port *port)
|
||||
{
|
||||
struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
|
||||
struct device *host = cxlmd->dev.parent;
|
||||
struct device *uport = port->uport_dev;
|
||||
struct device *dev = &port->dev;
|
||||
struct pci_doe_mb *cdat_doe;
|
||||
struct pci_dev *pdev = NULL;
|
||||
struct cxl_memdev *cxlmd;
|
||||
size_t cdat_length;
|
||||
void *cdat_table;
|
||||
int rc;
|
||||
|
||||
if (!dev_is_pci(host))
|
||||
if (is_cxl_memdev(uport)) {
|
||||
struct device *host;
|
||||
|
||||
cxlmd = to_cxl_memdev(uport);
|
||||
host = cxlmd->dev.parent;
|
||||
if (dev_is_pci(host))
|
||||
pdev = to_pci_dev(host);
|
||||
} else if (dev_is_pci(uport)) {
|
||||
pdev = to_pci_dev(uport);
|
||||
}
|
||||
|
||||
if (!pdev)
|
||||
return;
|
||||
cdat_doe = pci_find_doe_mailbox(to_pci_dev(host),
|
||||
PCI_DVSEC_VENDOR_ID_CXL,
|
||||
|
||||
cdat_doe = pci_find_doe_mailbox(pdev, PCI_DVSEC_VENDOR_ID_CXL,
|
||||
CXL_DOE_PROTOCOL_TABLE_ACCESS);
|
||||
if (!cdat_doe) {
|
||||
dev_dbg(dev, "No CDAT mailbox\n");
|
||||
@ -635,15 +657,21 @@ void read_cdat_data(struct cxl_port *port)
|
||||
return;
|
||||
|
||||
rc = cxl_cdat_read_table(dev, cdat_doe, cdat_table, &cdat_length);
|
||||
if (rc) {
|
||||
/* Don't leave table data allocated on error */
|
||||
devm_kfree(dev, cdat_table);
|
||||
dev_err(dev, "CDAT data read error\n");
|
||||
return;
|
||||
}
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
port->cdat.table = cdat_table + sizeof(__le32);
|
||||
cdat_table = cdat_table + sizeof(__le32);
|
||||
if (cdat_checksum(cdat_table, cdat_length))
|
||||
goto err;
|
||||
|
||||
port->cdat.table = cdat_table;
|
||||
port->cdat.length = cdat_length;
|
||||
return;
|
||||
|
||||
err:
|
||||
/* Don't leave table data allocated on error */
|
||||
devm_kfree(dev, cdat_table);
|
||||
dev_err(dev, "Failed to read/validate CDAT.\n");
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
|
||||
|
||||
|
@ -284,6 +284,15 @@ static ssize_t interleave_ways_show(struct device *dev,
|
||||
|
||||
static DEVICE_ATTR_RO(interleave_ways);
|
||||
|
||||
static ssize_t qos_class_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", cxlrd->qos_class);
|
||||
}
|
||||
static DEVICE_ATTR_RO(qos_class);
|
||||
|
||||
static struct attribute *cxl_decoder_base_attrs[] = {
|
||||
&dev_attr_start.attr,
|
||||
&dev_attr_size.attr,
|
||||
@ -303,6 +312,7 @@ static struct attribute *cxl_decoder_root_attrs[] = {
|
||||
&dev_attr_cap_type2.attr,
|
||||
&dev_attr_cap_type3.attr,
|
||||
&dev_attr_target_list.attr,
|
||||
&dev_attr_qos_class.attr,
|
||||
SET_CXL_REGION_ATTR(create_pmem_region)
|
||||
SET_CXL_REGION_ATTR(create_ram_region)
|
||||
SET_CXL_REGION_ATTR(delete_region)
|
||||
@ -1735,6 +1745,7 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
|
||||
}
|
||||
|
||||
atomic_set(&cxlrd->region_id, rc);
|
||||
cxlrd->qos_class = CXL_QOS_CLASS_INVALID;
|
||||
return cxlrd;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_root_decoder_alloc, CXL);
|
||||
|
@ -328,6 +328,7 @@ enum cxl_decoder_type {
|
||||
*/
|
||||
#define CXL_DECODER_MAX_INTERLEAVE 16
|
||||
|
||||
#define CXL_QOS_CLASS_INVALID -1
|
||||
|
||||
/**
|
||||
* struct cxl_decoder - Common CXL HDM Decoder Attributes
|
||||
@ -439,6 +440,7 @@ typedef struct cxl_dport *(*cxl_calc_hb_fn)(struct cxl_root_decoder *cxlrd,
|
||||
* @calc_hb: which host bridge covers the n'th position by granularity
|
||||
* @platform_data: platform specific configuration data
|
||||
* @range_lock: sync region autodiscovery by address range
|
||||
* @qos_class: QoS performance class cookie
|
||||
* @cxlsd: base cxl switch decoder
|
||||
*/
|
||||
struct cxl_root_decoder {
|
||||
@ -447,6 +449,7 @@ struct cxl_root_decoder {
|
||||
cxl_calc_hb_fn calc_hb;
|
||||
void *platform_data;
|
||||
struct mutex range_lock;
|
||||
int qos_class;
|
||||
struct cxl_switch_decoder cxlsd;
|
||||
};
|
||||
|
||||
|
@ -62,6 +62,9 @@ static int cxl_switch_port_probe(struct cxl_port *port)
|
||||
struct cxl_hdm *cxlhdm;
|
||||
int rc;
|
||||
|
||||
/* Cache the data early to ensure is_visible() works */
|
||||
read_cdat_data(port);
|
||||
|
||||
rc = devm_cxl_port_enumerate_dports(port);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
@ -465,6 +465,9 @@ struct acpi_cdat_sslbe {
|
||||
u16 reserved;
|
||||
};
|
||||
|
||||
#define ACPI_CDAT_SSLBIS_US_PORT 0x0100
|
||||
#define ACPI_CDAT_SSLBIS_ANY_PORT 0xffff
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* CEDT - CXL Early Discovery Table
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/fw_table.h>
|
||||
|
||||
struct irq_domain;
|
||||
struct irq_domain_ops;
|
||||
@ -24,6 +25,16 @@ struct irq_domain_ops;
|
||||
#endif
|
||||
#include <acpi/acpi.h>
|
||||
|
||||
#ifdef CONFIG_ACPI_TABLE_LIB
|
||||
#define EXPORT_SYMBOL_ACPI_LIB(x) EXPORT_SYMBOL_NS_GPL(x, ACPI)
|
||||
#define __init_or_acpilib
|
||||
#define __initdata_or_acpilib
|
||||
#else
|
||||
#define EXPORT_SYMBOL_ACPI_LIB(x)
|
||||
#define __init_or_acpilib __init
|
||||
#define __initdata_or_acpilib __initdata
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
#include <linux/list.h>
|
||||
@ -119,21 +130,8 @@ enum acpi_address_range_id {
|
||||
|
||||
|
||||
/* Table Handlers */
|
||||
union acpi_subtable_headers {
|
||||
struct acpi_subtable_header common;
|
||||
struct acpi_hmat_structure hmat;
|
||||
struct acpi_prmt_module_header prmt;
|
||||
struct acpi_cedt_header cedt;
|
||||
};
|
||||
|
||||
typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table);
|
||||
|
||||
typedef int (*acpi_tbl_entry_handler)(union acpi_subtable_headers *header,
|
||||
const unsigned long end);
|
||||
|
||||
typedef int (*acpi_tbl_entry_handler_arg)(union acpi_subtable_headers *header,
|
||||
void *arg, const unsigned long end);
|
||||
|
||||
/* Debugger support */
|
||||
|
||||
struct acpi_debugger_ops {
|
||||
@ -207,14 +205,6 @@ static inline int acpi_debugger_notify_command_complete(void)
|
||||
(!entry) || (unsigned long)entry + sizeof(*entry) > end || \
|
||||
((struct acpi_subtable_header *)entry)->length < sizeof(*entry))
|
||||
|
||||
struct acpi_subtable_proc {
|
||||
int id;
|
||||
acpi_tbl_entry_handler handler;
|
||||
acpi_tbl_entry_handler_arg handler_arg;
|
||||
void *arg;
|
||||
int count;
|
||||
};
|
||||
|
||||
void __iomem *__acpi_map_table(unsigned long phys, unsigned long size);
|
||||
void __acpi_unmap_table(void __iomem *map, unsigned long size);
|
||||
int early_acpi_boot_init(void);
|
||||
@ -229,16 +219,6 @@ void acpi_reserve_initial_tables (void);
|
||||
void acpi_table_init_complete (void);
|
||||
int acpi_table_init (void);
|
||||
|
||||
#ifdef CONFIG_ACPI_TABLE_LIB
|
||||
#define EXPORT_SYMBOL_ACPI_LIB(x) EXPORT_SYMBOL_NS_GPL(x, ACPI)
|
||||
#define __init_or_acpilib
|
||||
#define __initdata_or_acpilib
|
||||
#else
|
||||
#define EXPORT_SYMBOL_ACPI_LIB(x)
|
||||
#define __init_or_acpilib __init
|
||||
#define __initdata_or_acpilib __initdata
|
||||
#endif
|
||||
|
||||
int acpi_table_parse(char *id, acpi_tbl_table_handler handler);
|
||||
int __init_or_acpilib acpi_table_parse_entries(char *id,
|
||||
unsigned long table_size, int entry_id,
|
||||
|
43
include/linux/fw_table.h
Normal file
43
include/linux/fw_table.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* fw_tables.h - 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.
|
||||
*/
|
||||
#ifndef _FW_TABLE_H_
|
||||
#define _FW_TABLE_H_
|
||||
|
||||
union acpi_subtable_headers;
|
||||
|
||||
typedef int (*acpi_tbl_entry_handler)(union acpi_subtable_headers *header,
|
||||
const unsigned long end);
|
||||
|
||||
typedef int (*acpi_tbl_entry_handler_arg)(union acpi_subtable_headers *header,
|
||||
void *arg, const unsigned long end);
|
||||
|
||||
struct acpi_subtable_proc {
|
||||
int id;
|
||||
acpi_tbl_entry_handler handler;
|
||||
acpi_tbl_entry_handler_arg handler_arg;
|
||||
void *arg;
|
||||
int count;
|
||||
};
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/acpi.h>
|
||||
|
||||
union acpi_subtable_headers {
|
||||
struct acpi_subtable_header common;
|
||||
struct acpi_hmat_structure hmat;
|
||||
struct acpi_prmt_module_header prmt;
|
||||
struct acpi_cedt_header cedt;
|
||||
};
|
||||
|
||||
int acpi_parse_entries_array(char *id, unsigned long table_size,
|
||||
struct acpi_table_header *table_header,
|
||||
struct acpi_subtable_proc *proc,
|
||||
int proc_num, unsigned int max_entries);
|
||||
|
||||
#endif
|
@ -764,3 +764,6 @@ config ASN1_ENCODER
|
||||
|
||||
config POLYNOMIAL
|
||||
tristate
|
||||
|
||||
config FIRMWARE_TABLE
|
||||
bool
|
||||
|
@ -405,6 +405,8 @@ obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
|
||||
|
||||
obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
|
||||
|
||||
obj-$(CONFIG_FIRMWARE_TABLE) += fw_table.o
|
||||
|
||||
# FORTIFY_SOURCE compile-time behavior tests
|
||||
TEST_FORTIFY_SRCS = $(wildcard $(srctree)/$(src)/test_fortify/*-*.c)
|
||||
TEST_FORTIFY_LOGS = $(patsubst $(srctree)/$(src)/%.c, %.log, $(TEST_FORTIFY_SRCS))
|
||||
|
189
lib/fw_table.c
Normal file
189
lib/fw_table.c
Normal file
@ -0,0 +1,189 @@
|
||||
// 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/fw_table.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
enum acpi_subtable_type {
|
||||
ACPI_SUBTABLE_COMMON,
|
||||
ACPI_SUBTABLE_HMAT,
|
||||
ACPI_SUBTABLE_PRMT,
|
||||
ACPI_SUBTABLE_CEDT,
|
||||
};
|
||||
|
||||
struct acpi_subtable_entry {
|
||||
union acpi_subtable_headers *hdr;
|
||||
enum acpi_subtable_type type;
|
||||
};
|
||||
|
||||
static unsigned long __init_or_acpilib
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long __init_or_acpilib
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long __init_or_acpilib
|
||||
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);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum acpi_subtable_type __init_or_acpilib
|
||||
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;
|
||||
return ACPI_SUBTABLE_COMMON;
|
||||
}
|
||||
|
||||
static __init_or_acpilib bool has_handler(struct acpi_subtable_proc *proc)
|
||||
{
|
||||
return proc->handler || proc->handler_arg;
|
||||
}
|
||||
|
||||
static __init_or_acpilib 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
|
||||
* @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_acpilib
|
||||
acpi_parse_entries_array(char *id, unsigned long table_size,
|
||||
struct acpi_table_header *table_header,
|
||||
struct acpi_subtable_proc *proc,
|
||||
int proc_num, unsigned int max_entries)
|
||||
{
|
||||
unsigned long table_end, subtable_len, entry_len;
|
||||
struct acpi_subtable_entry entry;
|
||||
int count = 0;
|
||||
int errs = 0;
|
||||
int i;
|
||||
|
||||
table_end = (unsigned long)table_header + table_header->length;
|
||||
|
||||
/* Parse all entries looking for a match. */
|
||||
|
||||
entry.type = acpi_get_subtable_type(id);
|
||||
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) {
|
||||
if (max_entries && count >= max_entries)
|
||||
break;
|
||||
|
||||
for (i = 0; i < proc_num; i++) {
|
||||
if (acpi_get_entry_type(&entry) != proc[i].id)
|
||||
continue;
|
||||
if (!has_handler(&proc[i]) ||
|
||||
(!errs &&
|
||||
call_handler(&proc[i], entry.hdr, table_end))) {
|
||||
errs++;
|
||||
continue;
|
||||
}
|
||||
|
||||
proc[i].count++;
|
||||
break;
|
||||
}
|
||||
if (i != proc_num)
|
||||
count++;
|
||||
|
||||
/*
|
||||
* 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] found the maximum %i entries\n",
|
||||
id, proc->id, count);
|
||||
}
|
||||
|
||||
return errs ? -EINVAL : count;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_parse_entries_array);
|
Loading…
Reference in New Issue
Block a user