Commit 5349b005 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

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
parents 57356d98 059717c2
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -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.
+1 −0
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
// 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);
Loading