Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/acpi_mrrm.c
26278 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->flags & ACPI_MRRM_FLAGS_REGION_ASSIGNMENT_OS)
67
return -EOPNOTSUPP;
68
69
mrrm_end = (void *)mrrm + mrrm->header.length - 1;
70
mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
71
while (mre < mrrm_end) {
72
mre_entry = mre;
73
mre_count++;
74
mre += mre_entry->header.length;
75
}
76
if (!mre_count) {
77
pr_info(FW_BUG "No ranges listed in MRRM table\n");
78
return -EINVAL;
79
}
80
81
mrrm_mem_range_entry = kmalloc_array(mre_count, sizeof(*mrrm_mem_range_entry),
82
GFP_KERNEL | __GFP_ZERO);
83
if (!mrrm_mem_range_entry)
84
return -ENOMEM;
85
86
mre = (void *)mrrm + sizeof(struct acpi_table_mrrm);
87
while (mre < mrrm_end) {
88
struct mrrm_mem_range_entry *e;
89
90
mre_entry = mre;
91
e = mrrm_mem_range_entry + mrrm_mem_entry_num;
92
93
e->base = mre_entry->addr_base;
94
e->length = mre_entry->addr_len;
95
e->node = get_node_num(e);
96
97
if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_LOCAL)
98
e->local_region_id = mre_entry->local_region_id;
99
else
100
e->local_region_id = -1;
101
if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_REMOTE)
102
e->remote_region_id = mre_entry->remote_region_id;
103
else
104
e->remote_region_id = -1;
105
106
mrrm_mem_entry_num++;
107
mre += mre_entry->header.length;
108
}
109
110
max_mem_region = mrrm->max_mem_region;
111
112
return 0;
113
}
114
115
#define RANGE_ATTR(name, fmt) \
116
static ssize_t name##_show(struct kobject *kobj, \
117
struct kobj_attribute *attr, char *buf) \
118
{ \
119
struct mrrm_mem_range_entry *mre; \
120
const char *kname = kobject_name(kobj); \
121
int n, ret; \
122
\
123
ret = kstrtoint(kname + 5, 10, &n); \
124
if (ret) \
125
return ret; \
126
\
127
mre = mrrm_mem_range_entry + n; \
128
\
129
return sysfs_emit(buf, fmt, mre->name); \
130
} \
131
static struct kobj_attribute name##_attr = __ATTR_RO(name)
132
133
RANGE_ATTR(base, "0x%llx\n");
134
RANGE_ATTR(length, "0x%llx\n");
135
RANGE_ATTR(node, "%d\n");
136
RANGE_ATTR(local_region_id, "%d\n");
137
RANGE_ATTR(remote_region_id, "%d\n");
138
139
static struct attribute *memory_range_attrs[] = {
140
&base_attr.attr,
141
&length_attr.attr,
142
&node_attr.attr,
143
&local_region_id_attr.attr,
144
&remote_region_id_attr.attr,
145
NULL
146
};
147
148
ATTRIBUTE_GROUPS(memory_range);
149
150
static __init int add_boot_memory_ranges(void)
151
{
152
struct kobject *pkobj, *kobj;
153
int ret = -EINVAL;
154
char *name;
155
156
pkobj = kobject_create_and_add("memory_ranges", acpi_kobj);
157
158
for (int i = 0; i < mrrm_mem_entry_num; i++) {
159
name = kasprintf(GFP_KERNEL, "range%d", i);
160
if (!name) {
161
ret = -ENOMEM;
162
break;
163
}
164
165
kobj = kobject_create_and_add(name, pkobj);
166
167
ret = sysfs_create_groups(kobj, memory_range_groups);
168
if (ret)
169
return ret;
170
}
171
172
return ret;
173
}
174
175
static __init int mrrm_init(void)
176
{
177
int ret;
178
179
ret = acpi_table_parse(ACPI_SIG_MRRM, acpi_parse_mrrm);
180
if (ret < 0)
181
return ret;
182
183
return add_boot_memory_ranges();
184
}
185
device_initcall(mrrm_init);
186
187