Merge branch 'acpi-tables'

Merge updates related to the handling of static (data-only) ACPI tables
for 6.16-rc1:

 - Add __nonstring annotations for unterminated strings in the static
   ACPI tables parsing code (Kees Cook).

 - Add support for parsing the MRRM ACPI table and sysfs files to
   describe memory regions listed in it (Tony Luck, Anil Keshavamurthy).

 - Remove an (explicitly) unused header file include from the VIOT ACPI
   table parser file (Andy Shevchenko).

 - Improve logging around acpi_initialize_tables() (Bartosz Szczepanek).

* acpi-tables:
  ACPI: MRRM: Fix default max memory region
  ACPI: tables: Improve logging around acpi_initialize_tables()
  ACPI: VIOT: Remove (explicitly) unused header
  ACPI: Add documentation for exposing MRRM data
  ACPI: MRRM: Add /sys files to describe memory ranges
  ACPI: MRRM: Minimal parse of ACPI MRRM table
  ACPI: tables: Add __nonstring annotations for unterminated strings
This commit is contained in:
Rafael J. Wysocki 2025-05-26 18:36:39 +02:00
commit 5349b0051b
8 changed files with 225 additions and 3 deletions

View File

@ -248,3 +248,24 @@ Description:
# cat ff_pwr_btn
7 enabled
What: /sys/firmware/acpi/memory_ranges/rangeX
Date: February 2025
Contact: Tony Luck <tony.luck@intel.com>
Description:
On systems with the ACPI MRRM table reports the parameters for
each range.
base: Starting system physical address.
length: Length of this range in bytes.
node: NUMA node that this range belongs to. Negative numbers
indicate that the node number could not be determined (e.g
for an address range that is reserved for future hot add of
memory).
local_region_id: ID associated with access by agents
local to this range of addresses.
remote_region_id: ID associated with access by agents
non-local to this range of addresses.

View File

@ -38,6 +38,7 @@ config X86_64
select ARCH_HAS_ELFCORE_COMPAT
select ZONE_DMA32
select EXECMEM if DYNAMIC_FTRACE
select ACPI_MRRM if ACPI
config FORCE_DYNAMIC_FTRACE
def_bool y

View File

@ -576,6 +576,9 @@ config ACPI_FFH
Enable this feature if you want to set up and install the FFH Address
Space handler to handle FFH OpRegion in the firmware.
config ACPI_MRRM
bool
source "drivers/acpi/pmic/Kconfig"
config ACPI_VIOT

View File

@ -66,6 +66,7 @@ acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o
acpi-$(CONFIG_ACPI_PRMT) += prmt.o
acpi-$(CONFIG_ACPI_PCC) += acpi_pcc.o
acpi-$(CONFIG_ACPI_FFH) += acpi_ffh.o
acpi-$(CONFIG_ACPI_MRRM) += acpi_mrrm.o
# Address translation
acpi-$(CONFIG_ACPI_ADXL) += acpi_adxl.o

183
drivers/acpi/acpi_mrrm.c Normal file
View File

@ -0,0 +1,183 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2025, Intel Corporation.
*
* Memory Range and Region Mapping (MRRM) structure
*
* Parse and report the platform's MRRM table in /sys.
*/
#define pr_fmt(fmt) "acpi/mrrm: " fmt
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/sysfs.h>
/* Default assume one memory region covering all system memory, per the spec */
static int max_mem_region = 1;
/* Access for use by resctrl file system */
int acpi_mrrm_max_mem_region(void)
{
return max_mem_region;
}
struct mrrm_mem_range_entry {
u64 base;
u64 length;
int node;
u8 local_region_id;
u8 remote_region_id;
};
static struct mrrm_mem_range_entry *mrrm_mem_range_entry;
static u32 mrrm_mem_entry_num;
static int get_node_num(struct mrrm_mem_range_entry *e)
{
unsigned int nid;
for_each_online_node(nid) {
for (int z = 0; z < MAX_NR_ZONES; z++) {
struct zone *zone = NODE_DATA(nid)->node_zones + z;
if (!populated_zone(zone))
continue;
if (zone_intersects(zone, PHYS_PFN(e->base), PHYS_PFN(e->length)))
return zone_to_nid(zone);
}
}
return -ENOENT;
}
static __init int acpi_parse_mrrm(struct acpi_table_header *table)
{
struct acpi_mrrm_mem_range_entry *mre_entry;
struct acpi_table_mrrm *mrrm;
void *mre, *mrrm_end;
int mre_count = 0;
mrrm = (struct acpi_table_mrrm *)table;
if (!mrrm)
return -ENODEV;
if (mrrm->flags & ACPI_MRRM_FLAGS_REGION_ASSIGNMENT_OS)
return -EOPNOTSUPP;
mrrm_end = (void *)mrrm + mrrm->header.length - 1;
mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
while (mre < mrrm_end) {
mre_entry = mre;
mre_count++;
mre += mre_entry->header.length;
}
if (!mre_count) {
pr_info(FW_BUG "No ranges listed in MRRM table\n");
return -EINVAL;
}
mrrm_mem_range_entry = kmalloc_array(mre_count, sizeof(*mrrm_mem_range_entry),
GFP_KERNEL | __GFP_ZERO);
if (!mrrm_mem_range_entry)
return -ENOMEM;
mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
while (mre < mrrm_end) {
struct mrrm_mem_range_entry *e;
mre_entry = mre;
e = mrrm_mem_range_entry + mrrm_mem_entry_num;
e->base = mre_entry->addr_base;
e->length = mre_entry->addr_len;
e->node = get_node_num(e);
if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_LOCAL)
e->local_region_id = mre_entry->local_region_id;
else
e->local_region_id = -1;
if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_REMOTE)
e->remote_region_id = mre_entry->remote_region_id;
else
e->remote_region_id = -1;
mrrm_mem_entry_num++;
mre += mre_entry->header.length;
}
max_mem_region = mrrm->max_mem_region;
return 0;
}
#define RANGE_ATTR(name, fmt) \
static ssize_t name##_show(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf) \
{ \
struct mrrm_mem_range_entry *mre; \
const char *kname = kobject_name(kobj); \
int n, ret; \
\
ret = kstrtoint(kname + 5, 10, &n); \
if (ret) \
return ret; \
\
mre = mrrm_mem_range_entry + n; \
\
return sysfs_emit(buf, fmt, mre->name); \
} \
static struct kobj_attribute name##_attr = __ATTR_RO(name)
RANGE_ATTR(base, "0x%llx\n");
RANGE_ATTR(length, "0x%llx\n");
RANGE_ATTR(node, "%d\n");
RANGE_ATTR(local_region_id, "%d\n");
RANGE_ATTR(remote_region_id, "%d\n");
static struct attribute *memory_range_attrs[] = {
&base_attr.attr,
&length_attr.attr,
&node_attr.attr,
&local_region_id_attr.attr,
&remote_region_id_attr.attr,
NULL
};
ATTRIBUTE_GROUPS(memory_range);
static __init int add_boot_memory_ranges(void)
{
struct kobject *pkobj, *kobj;
int ret = -EINVAL;
char *name;
pkobj = kobject_create_and_add("memory_ranges", acpi_kobj);
for (int i = 0; i < mrrm_mem_entry_num; i++) {
name = kasprintf(GFP_KERNEL, "range%d", i);
if (!name)
break;
kobj = kobject_create_and_add(name, pkobj);
ret = sysfs_create_groups(kobj, memory_range_groups);
if (ret)
return ret;
}
return ret;
}
static __init int mrrm_init(void)
{
int ret;
ret = acpi_table_parse(ACPI_SIG_MRRM, acpi_parse_mrrm);
if (ret < 0)
return ret;
return add_boot_memory_ranges();
}
device_initcall(mrrm_init);

View File

@ -396,7 +396,7 @@ static u8 __init acpi_table_checksum(u8 *buffer, u32 length)
}
/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */
static const char table_sigs[][ACPI_NAMESEG_SIZE] __initconst = {
static const char table_sigs[][ACPI_NAMESEG_SIZE] __nonstring_array __initconst = {
ACPI_SIG_BERT, ACPI_SIG_BGRT, ACPI_SIG_CPEP, ACPI_SIG_ECDT,
ACPI_SIG_EINJ, ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT,
ACPI_SIG_MSCT, ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT,
@ -719,8 +719,12 @@ int __init acpi_locate_initial_tables(void)
}
status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0);
if (ACPI_FAILURE(status))
if (ACPI_FAILURE(status)) {
const char *msg = acpi_format_exception(status);
pr_warn("Failed to initialize tables, status=0x%x (%s)", status, msg);
return -EINVAL;
}
return 0;
}

View File

@ -19,11 +19,11 @@
#define pr_fmt(fmt) "ACPI: VIOT: " fmt
#include <linux/acpi_viot.h>
#include <linux/fwnode.h>
#include <linux/iommu.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/property.h>
struct viot_iommu {
/* Node offset within the table */

View File

@ -772,6 +772,10 @@ int acpi_get_local_u64_address(acpi_handle handle, u64 *addr);
int acpi_get_local_address(acpi_handle handle, u32 *addr);
const char *acpi_get_subsystem_id(acpi_handle handle);
#ifdef CONFIG_ACPI_MRRM
int acpi_mrrm_max_mem_region(void);
#endif
#else /* !CONFIG_ACPI */
#define acpi_disabled 1
@ -1092,6 +1096,11 @@ static inline acpi_handle acpi_get_processor_handle(int cpu)
return NULL;
}
static inline int acpi_mrrm_max_mem_region(void)
{
return 1;
}
#endif /* !CONFIG_ACPI */
#ifdef CONFIG_ACPI_HMAT