xref: /linux/drivers/acpi/acpi_mrrm.c (revision 72a35790da31acc12f05d35466e46254439d5dcd)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2025, Intel Corporation.
4  *
5  * Memory Range and Region Mapping (MRRM) structure
6  *
7  * Parse and report the platform's MRRM table in /sys.
8  */
9 
10 #define pr_fmt(fmt) "acpi/mrrm: " fmt
11 
12 #include <linux/acpi.h>
13 #include <linux/init.h>
14 #include <linux/string.h>
15 #include <linux/sysfs.h>
16 
17 /* Default assume one memory region covering all system memory, per the spec */
18 static int max_mem_region = 1;
19 
20 /* Access for use by resctrl file system */
21 int acpi_mrrm_max_mem_region(void)
22 {
23 	return max_mem_region;
24 }
25 
26 struct mrrm_mem_range_entry {
27 	u64 base;
28 	u64 length;
29 	int node;
30 	u8  local_region_id;
31 	u8  remote_region_id;
32 };
33 
34 static struct mrrm_mem_range_entry *mrrm_mem_range_entry;
35 static u32 mrrm_mem_entry_num;
36 
37 static int get_node_num(struct mrrm_mem_range_entry *e)
38 {
39 	struct zone *zone;
40 
41 	for_each_populated_zone(zone) {
42 		if (zone_intersects(zone, PHYS_PFN(e->base), PHYS_PFN(e->length)))
43 			return zone_to_nid(zone);
44 	}
45 
46 	return -ENOENT;
47 }
48 
49 static __init int acpi_parse_mrrm(struct acpi_table_header *table)
50 {
51 	struct acpi_mrrm_mem_range_entry *mre_entry;
52 	struct acpi_table_mrrm *mrrm;
53 	void *mre, *mrrm_end;
54 	int mre_count = 0;
55 
56 	mrrm = (struct acpi_table_mrrm *)table;
57 	if (!mrrm)
58 		return -ENODEV;
59 
60 	if (mrrm->header.revision != 1)
61 		return -EINVAL;
62 
63 	if (mrrm->flags & ACPI_MRRM_FLAGS_REGION_ASSIGNMENT_OS)
64 		return -EOPNOTSUPP;
65 
66 	mrrm_end = (void *)mrrm + mrrm->header.length - 1;
67 	mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
68 	while (mre < mrrm_end) {
69 		mre_entry = mre;
70 		mre_count++;
71 		mre += mre_entry->header.length;
72 	}
73 	if (!mre_count) {
74 		pr_info(FW_BUG "No ranges listed in MRRM table\n");
75 		return -EINVAL;
76 	}
77 
78 	mrrm_mem_range_entry = kmalloc_objs(*mrrm_mem_range_entry, mre_count,
79 					    GFP_KERNEL | __GFP_ZERO);
80 	if (!mrrm_mem_range_entry)
81 		return -ENOMEM;
82 
83 	mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
84 	while (mre < mrrm_end) {
85 		struct mrrm_mem_range_entry *e;
86 
87 		mre_entry = mre;
88 		e = mrrm_mem_range_entry + mrrm_mem_entry_num;
89 
90 		e->base = mre_entry->addr_base;
91 		e->length = mre_entry->addr_len;
92 		e->node = get_node_num(e);
93 
94 		if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_LOCAL)
95 			e->local_region_id = mre_entry->local_region_id;
96 		else
97 			e->local_region_id = -1;
98 		if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_REMOTE)
99 			e->remote_region_id = mre_entry->remote_region_id;
100 		else
101 			e->remote_region_id = -1;
102 
103 		mrrm_mem_entry_num++;
104 		mre += mre_entry->header.length;
105 	}
106 
107 	max_mem_region = mrrm->max_mem_region;
108 
109 	return 0;
110 }
111 
112 #define RANGE_ATTR(name, fmt)						\
113 static ssize_t name##_show(struct kobject *kobj,			\
114 			  struct kobj_attribute *attr, char *buf)	\
115 {									\
116 	struct mrrm_mem_range_entry *mre;				\
117 	const char *kname = kobject_name(kobj);				\
118 	int n, ret;							\
119 									\
120 	ret = kstrtoint(kname + 5, 10, &n);				\
121 	if (ret)							\
122 		return ret;						\
123 									\
124 	mre = mrrm_mem_range_entry + n;					\
125 									\
126 	return sysfs_emit(buf, fmt, mre->name);				\
127 }									\
128 static struct kobj_attribute name##_attr = __ATTR_RO(name)
129 
130 RANGE_ATTR(base, "0x%llx\n");
131 RANGE_ATTR(length, "0x%llx\n");
132 RANGE_ATTR(node, "%d\n");
133 RANGE_ATTR(local_region_id, "%d\n");
134 RANGE_ATTR(remote_region_id, "%d\n");
135 
136 static struct attribute *memory_range_attrs[] = {
137 	&base_attr.attr,
138 	&length_attr.attr,
139 	&node_attr.attr,
140 	&local_region_id_attr.attr,
141 	&remote_region_id_attr.attr,
142 	NULL
143 };
144 
145 ATTRIBUTE_GROUPS(memory_range);
146 
147 static __init int add_boot_memory_ranges(void)
148 {
149 	struct kobject *pkobj, *kobj, **kobjs;
150 	int ret = -EINVAL;
151 	char name[16];
152 	int i;
153 
154 	pkobj = kobject_create_and_add("memory_ranges", acpi_kobj);
155 	if (!pkobj)
156 		return -ENOMEM;
157 
158 	kobjs = kzalloc_objs(*kobjs, mrrm_mem_entry_num);
159 	if (!kobjs) {
160 		kobject_put(pkobj);
161 		return -ENOMEM;
162 	}
163 
164 	for (i = 0; i < mrrm_mem_entry_num; i++) {
165 		scnprintf(name, sizeof(name), "range%d", i);
166 		kobj = kobject_create_and_add(name, pkobj);
167 		if (!kobj) {
168 			ret = -ENOMEM;
169 			goto cleanup;
170 		}
171 
172 		ret = sysfs_create_groups(kobj, memory_range_groups);
173 		if (ret) {
174 			kobject_put(kobj);
175 			goto cleanup;
176 		}
177 		kobjs[i] = kobj;
178 	}
179 
180 	kfree(kobjs);
181 	return 0;
182 
183 cleanup:
184 	for (int j = 0; j < i; j++) {
185 		if (kobjs[j]) {
186 			sysfs_remove_groups(kobjs[j], memory_range_groups);
187 			kobject_put(kobjs[j]);
188 		}
189 	}
190 	kfree(kobjs);
191 	kobject_put(pkobj);
192 	return ret;
193 }
194 
195 static __init int mrrm_init(void)
196 {
197 	int ret;
198 
199 	ret = acpi_table_parse(ACPI_SIG_MRRM, acpi_parse_mrrm);
200 	if (ret < 0)
201 		return ret;
202 
203 	return add_boot_memory_ranges();
204 }
205 device_initcall(mrrm_init);
206