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