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