xref: /linux/drivers/acpi/acpi_mrrm.c (revision 1fbbb629452ca16909b440b9217a28f42202dc60)
1b9020bdbSTony Luck // SPDX-License-Identifier: GPL-2.0
2b9020bdbSTony Luck /*
3b9020bdbSTony Luck  * Copyright (c) 2025, Intel Corporation.
4b9020bdbSTony Luck  *
5b9020bdbSTony Luck  * Memory Range and Region Mapping (MRRM) structure
604f53540STony Luck  *
704f53540STony Luck  * Parse and report the platform's MRRM table in /sys.
8b9020bdbSTony Luck  */
9b9020bdbSTony Luck 
10b9020bdbSTony Luck #define pr_fmt(fmt) "acpi/mrrm: " fmt
11b9020bdbSTony Luck 
12b9020bdbSTony Luck #include <linux/acpi.h>
13b9020bdbSTony Luck #include <linux/init.h>
1404f53540STony Luck #include <linux/string.h>
1504f53540STony Luck #include <linux/sysfs.h>
16b9020bdbSTony Luck 
17059717c2SAnil S Keshavamurthy /* Default assume one memory region covering all system memory, per the spec */
18059717c2SAnil S Keshavamurthy static int max_mem_region = 1;
19b9020bdbSTony Luck 
20b9020bdbSTony Luck /* Access for use by resctrl file system */
acpi_mrrm_max_mem_region(void)21b9020bdbSTony Luck int acpi_mrrm_max_mem_region(void)
22b9020bdbSTony Luck {
23b9020bdbSTony Luck 	return max_mem_region;
24b9020bdbSTony Luck }
25b9020bdbSTony Luck 
2604f53540STony Luck struct mrrm_mem_range_entry {
2704f53540STony Luck 	u64 base;
2804f53540STony Luck 	u64 length;
2904f53540STony Luck 	int node;
3004f53540STony Luck 	u8  local_region_id;
3104f53540STony Luck 	u8  remote_region_id;
3204f53540STony Luck };
3304f53540STony Luck 
3404f53540STony Luck static struct mrrm_mem_range_entry *mrrm_mem_range_entry;
3504f53540STony Luck static u32 mrrm_mem_entry_num;
3604f53540STony Luck 
get_node_num(struct mrrm_mem_range_entry * e)3704f53540STony Luck static int get_node_num(struct mrrm_mem_range_entry *e)
3804f53540STony Luck {
3904f53540STony Luck 	unsigned int nid;
4004f53540STony Luck 
4104f53540STony Luck 	for_each_online_node(nid) {
4204f53540STony Luck 		for (int z = 0; z < MAX_NR_ZONES; z++) {
4304f53540STony Luck 			struct zone *zone = NODE_DATA(nid)->node_zones + z;
4404f53540STony Luck 
4504f53540STony Luck 			if (!populated_zone(zone))
4604f53540STony Luck 				continue;
4704f53540STony Luck 			if (zone_intersects(zone, PHYS_PFN(e->base), PHYS_PFN(e->length)))
4804f53540STony Luck 				return zone_to_nid(zone);
4904f53540STony Luck 		}
5004f53540STony Luck 	}
5104f53540STony Luck 
5204f53540STony Luck 	return -ENOENT;
5304f53540STony Luck }
5404f53540STony Luck 
acpi_parse_mrrm(struct acpi_table_header * table)55b9020bdbSTony Luck static __init int acpi_parse_mrrm(struct acpi_table_header *table)
56b9020bdbSTony Luck {
5704f53540STony Luck 	struct acpi_mrrm_mem_range_entry *mre_entry;
58b9020bdbSTony Luck 	struct acpi_table_mrrm *mrrm;
5904f53540STony Luck 	void *mre, *mrrm_end;
6004f53540STony Luck 	int mre_count = 0;
61b9020bdbSTony Luck 
62b9020bdbSTony Luck 	mrrm = (struct acpi_table_mrrm *)table;
63b9020bdbSTony Luck 	if (!mrrm)
64b9020bdbSTony Luck 		return -ENODEV;
65b9020bdbSTony Luck 
6604f53540STony Luck 	if (mrrm->flags & ACPI_MRRM_FLAGS_REGION_ASSIGNMENT_OS)
6704f53540STony Luck 		return -EOPNOTSUPP;
6804f53540STony Luck 
6904f53540STony Luck 	mrrm_end = (void *)mrrm + mrrm->header.length - 1;
7004f53540STony Luck 	mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
7104f53540STony Luck 	while (mre < mrrm_end) {
7204f53540STony Luck 		mre_entry = mre;
7304f53540STony Luck 		mre_count++;
7404f53540STony Luck 		mre += mre_entry->header.length;
7504f53540STony Luck 	}
7604f53540STony Luck 	if (!mre_count) {
7704f53540STony Luck 		pr_info(FW_BUG "No ranges listed in MRRM table\n");
7804f53540STony Luck 		return -EINVAL;
7904f53540STony Luck 	}
8004f53540STony Luck 
8104f53540STony Luck 	mrrm_mem_range_entry = kmalloc_array(mre_count, sizeof(*mrrm_mem_range_entry),
8204f53540STony Luck 					     GFP_KERNEL | __GFP_ZERO);
8304f53540STony Luck 	if (!mrrm_mem_range_entry)
8404f53540STony Luck 		return -ENOMEM;
8504f53540STony Luck 
8604f53540STony Luck 	mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
8704f53540STony Luck 	while (mre < mrrm_end) {
8804f53540STony Luck 		struct mrrm_mem_range_entry *e;
8904f53540STony Luck 
9004f53540STony Luck 		mre_entry = mre;
9104f53540STony Luck 		e = mrrm_mem_range_entry + mrrm_mem_entry_num;
9204f53540STony Luck 
9304f53540STony Luck 		e->base = mre_entry->addr_base;
9404f53540STony Luck 		e->length = mre_entry->addr_len;
9504f53540STony Luck 		e->node = get_node_num(e);
9604f53540STony Luck 
9704f53540STony Luck 		if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_LOCAL)
9804f53540STony Luck 			e->local_region_id = mre_entry->local_region_id;
9904f53540STony Luck 		else
10004f53540STony Luck 			e->local_region_id = -1;
10104f53540STony Luck 		if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_REMOTE)
10204f53540STony Luck 			e->remote_region_id = mre_entry->remote_region_id;
10304f53540STony Luck 		else
10404f53540STony Luck 			e->remote_region_id = -1;
10504f53540STony Luck 
10604f53540STony Luck 		mrrm_mem_entry_num++;
10704f53540STony Luck 		mre += mre_entry->header.length;
10804f53540STony Luck 	}
10904f53540STony Luck 
110b9020bdbSTony Luck 	max_mem_region = mrrm->max_mem_region;
111b9020bdbSTony Luck 
112b9020bdbSTony Luck 	return 0;
113b9020bdbSTony Luck }
114b9020bdbSTony Luck 
11504f53540STony Luck #define RANGE_ATTR(name, fmt)						\
11604f53540STony Luck static ssize_t name##_show(struct kobject *kobj,			\
11704f53540STony Luck 			  struct kobj_attribute *attr, char *buf)	\
11804f53540STony Luck {									\
11904f53540STony Luck 	struct mrrm_mem_range_entry *mre;				\
12004f53540STony Luck 	const char *kname = kobject_name(kobj);				\
12104f53540STony Luck 	int n, ret;							\
12204f53540STony Luck 									\
12304f53540STony Luck 	ret = kstrtoint(kname + 5, 10, &n);				\
12404f53540STony Luck 	if (ret)							\
12504f53540STony Luck 		return ret;						\
12604f53540STony Luck 									\
12704f53540STony Luck 	mre = mrrm_mem_range_entry + n;					\
12804f53540STony Luck 									\
12904f53540STony Luck 	return sysfs_emit(buf, fmt, mre->name);				\
13004f53540STony Luck }									\
13104f53540STony Luck static struct kobj_attribute name##_attr = __ATTR_RO(name)
13204f53540STony Luck 
13304f53540STony Luck RANGE_ATTR(base, "0x%llx\n");
13404f53540STony Luck RANGE_ATTR(length, "0x%llx\n");
13504f53540STony Luck RANGE_ATTR(node, "%d\n");
13604f53540STony Luck RANGE_ATTR(local_region_id, "%d\n");
13704f53540STony Luck RANGE_ATTR(remote_region_id, "%d\n");
13804f53540STony Luck 
13904f53540STony Luck static struct attribute *memory_range_attrs[] = {
14004f53540STony Luck 	&base_attr.attr,
14104f53540STony Luck 	&length_attr.attr,
14204f53540STony Luck 	&node_attr.attr,
14304f53540STony Luck 	&local_region_id_attr.attr,
14404f53540STony Luck 	&remote_region_id_attr.attr,
14504f53540STony Luck 	NULL
14604f53540STony Luck };
14704f53540STony Luck 
14804f53540STony Luck ATTRIBUTE_GROUPS(memory_range);
14904f53540STony Luck 
add_boot_memory_ranges(void)15004f53540STony Luck static __init int add_boot_memory_ranges(void)
15104f53540STony Luck {
15204f53540STony Luck 	struct kobject *pkobj, *kobj;
15304f53540STony Luck 	int ret = -EINVAL;
15404f53540STony Luck 	char *name;
15504f53540STony Luck 
15604f53540STony Luck 	pkobj = kobject_create_and_add("memory_ranges", acpi_kobj);
15704f53540STony Luck 
15804f53540STony Luck 	for (int i = 0; i < mrrm_mem_entry_num; i++) {
15904f53540STony Luck 		name = kasprintf(GFP_KERNEL, "range%d", i);
160*9375e5e3SDan Carpenter 		if (!name) {
161*9375e5e3SDan Carpenter 			ret = -ENOMEM;
16204f53540STony Luck 			break;
163*9375e5e3SDan Carpenter 		}
16404f53540STony Luck 
16504f53540STony Luck 		kobj = kobject_create_and_add(name, pkobj);
16604f53540STony Luck 
16704f53540STony Luck 		ret = sysfs_create_groups(kobj, memory_range_groups);
16804f53540STony Luck 		if (ret)
16904f53540STony Luck 			return ret;
17004f53540STony Luck 	}
17104f53540STony Luck 
17204f53540STony Luck 	return ret;
17304f53540STony Luck }
17404f53540STony Luck 
mrrm_init(void)175b9020bdbSTony Luck static __init int mrrm_init(void)
176b9020bdbSTony Luck {
177b9020bdbSTony Luck 	int ret;
178b9020bdbSTony Luck 
179b9020bdbSTony Luck 	ret = acpi_table_parse(ACPI_SIG_MRRM, acpi_parse_mrrm);
18004f53540STony Luck 	if (ret < 0)
181b9020bdbSTony Luck 		return ret;
18204f53540STony Luck 
18304f53540STony Luck 	return add_boot_memory_ranges();
184b9020bdbSTony Luck }
185b9020bdbSTony Luck device_initcall(mrrm_init);
186