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