Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/acpi_mrrm.c
50409 views
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
unsigned int nid;
40
41
for_each_online_node(nid) {
42
for (int z = 0; z < MAX_NR_ZONES; z++) {
43
struct zone *zone = NODE_DATA(nid)->node_zones + z;
44
45
if (!populated_zone(zone))
46
continue;
47
if (zone_intersects(zone, PHYS_PFN(e->base), PHYS_PFN(e->length)))
48
return zone_to_nid(zone);
49
}
50
}
51
52
return -ENOENT;
53
}
54
55
static __init int acpi_parse_mrrm(struct acpi_table_header *table)
56
{
57
struct acpi_mrrm_mem_range_entry *mre_entry;
58
struct acpi_table_mrrm *mrrm;
59
void *mre, *mrrm_end;
60
int mre_count = 0;
61
62
mrrm = (struct acpi_table_mrrm *)table;
63
if (!mrrm)
64
return -ENODEV;
65
66
if (mrrm->header.revision != 1)
67
return -EINVAL;
68
69
if (mrrm->flags & ACPI_MRRM_FLAGS_REGION_ASSIGNMENT_OS)
70
return -EOPNOTSUPP;
71
72
mrrm_end = (void *)mrrm + mrrm->header.length - 1;
73
mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
74
while (mre < mrrm_end) {
75
mre_entry = mre;
76
mre_count++;
77
mre += mre_entry->header.length;
78
}
79
if (!mre_count) {
80
pr_info(FW_BUG "No ranges listed in MRRM table\n");
81
return -EINVAL;
82
}
83
84
mrrm_mem_range_entry = kmalloc_array(mre_count, sizeof(*mrrm_mem_range_entry),
85
GFP_KERNEL | __GFP_ZERO);
86
if (!mrrm_mem_range_entry)
87
return -ENOMEM;
88
89
mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
90
while (mre < mrrm_end) {
91
struct mrrm_mem_range_entry *e;
92
93
mre_entry = mre;
94
e = mrrm_mem_range_entry + mrrm_mem_entry_num;
95
96
e->base = mre_entry->addr_base;
97
e->length = mre_entry->addr_len;
98
e->node = get_node_num(e);
99
100
if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_LOCAL)
101
e->local_region_id = mre_entry->local_region_id;
102
else
103
e->local_region_id = -1;
104
if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_REMOTE)
105
e->remote_region_id = mre_entry->remote_region_id;
106
else
107
e->remote_region_id = -1;
108
109
mrrm_mem_entry_num++;
110
mre += mre_entry->header.length;
111
}
112
113
max_mem_region = mrrm->max_mem_region;
114
115
return 0;
116
}
117
118
#define RANGE_ATTR(name, fmt) \
119
static ssize_t name##_show(struct kobject *kobj, \
120
struct kobj_attribute *attr, char *buf) \
121
{ \
122
struct mrrm_mem_range_entry *mre; \
123
const char *kname = kobject_name(kobj); \
124
int n, ret; \
125
\
126
ret = kstrtoint(kname + 5, 10, &n); \
127
if (ret) \
128
return ret; \
129
\
130
mre = mrrm_mem_range_entry + n; \
131
\
132
return sysfs_emit(buf, fmt, mre->name); \
133
} \
134
static struct kobj_attribute name##_attr = __ATTR_RO(name)
135
136
RANGE_ATTR(base, "0x%llx\n");
137
RANGE_ATTR(length, "0x%llx\n");
138
RANGE_ATTR(node, "%d\n");
139
RANGE_ATTR(local_region_id, "%d\n");
140
RANGE_ATTR(remote_region_id, "%d\n");
141
142
static struct attribute *memory_range_attrs[] = {
143
&base_attr.attr,
144
&length_attr.attr,
145
&node_attr.attr,
146
&local_region_id_attr.attr,
147
&remote_region_id_attr.attr,
148
NULL
149
};
150
151
ATTRIBUTE_GROUPS(memory_range);
152
153
static __init int add_boot_memory_ranges(void)
154
{
155
struct kobject *pkobj, *kobj, **kobjs;
156
int ret = -EINVAL;
157
char name[16];
158
int i;
159
160
pkobj = kobject_create_and_add("memory_ranges", acpi_kobj);
161
if (!pkobj)
162
return -ENOMEM;
163
164
kobjs = kcalloc(mrrm_mem_entry_num, sizeof(*kobjs), GFP_KERNEL);
165
if (!kobjs) {
166
kobject_put(pkobj);
167
return -ENOMEM;
168
}
169
170
for (i = 0; i < mrrm_mem_entry_num; i++) {
171
scnprintf(name, sizeof(name), "range%d", i);
172
kobj = kobject_create_and_add(name, pkobj);
173
if (!kobj) {
174
ret = -ENOMEM;
175
goto cleanup;
176
}
177
178
ret = sysfs_create_groups(kobj, memory_range_groups);
179
if (ret) {
180
kobject_put(kobj);
181
goto cleanup;
182
}
183
kobjs[i] = kobj;
184
}
185
186
kfree(kobjs);
187
return 0;
188
189
cleanup:
190
for (int j = 0; j < i; j++) {
191
if (kobjs[j]) {
192
sysfs_remove_groups(kobjs[j], memory_range_groups);
193
kobject_put(kobjs[j]);
194
}
195
}
196
kfree(kobjs);
197
kobject_put(pkobj);
198
return ret;
199
}
200
201
static __init int mrrm_init(void)
202
{
203
int ret;
204
205
ret = acpi_table_parse(ACPI_SIG_MRRM, acpi_parse_mrrm);
206
if (ret < 0)
207
return ret;
208
209
return add_boot_memory_ranges();
210
}
211
device_initcall(mrrm_init);
212
213