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; 156 int ret = -EINVAL; 157 char *name; 158 159 pkobj = kobject_create_and_add("memory_ranges", acpi_kobj); 160 161 for (int i = 0; i < mrrm_mem_entry_num; i++) { 162 name = kasprintf(GFP_KERNEL, "range%d", i); 163 if (!name) { 164 ret = -ENOMEM; 165 break; 166 } 167 168 kobj = kobject_create_and_add(name, pkobj); 169 170 ret = sysfs_create_groups(kobj, memory_range_groups); 171 if (ret) 172 return ret; 173 } 174 175 return ret; 176 } 177 178 static __init int mrrm_init(void) 179 { 180 int ret; 181 182 ret = acpi_table_parse(ACPI_SIG_MRRM, acpi_parse_mrrm); 183 if (ret < 0) 184 return ret; 185 186 return add_boot_memory_ranges(); 187 } 188 device_initcall(mrrm_init); 189