memmap.c (11924B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * linux/drivers/firmware/memmap.c 4 * Copyright (C) 2008 SUSE LINUX Products GmbH 5 * by Bernhard Walle <bernhard.walle@gmx.de> 6 */ 7 8#include <linux/string.h> 9#include <linux/firmware-map.h> 10#include <linux/kernel.h> 11#include <linux/module.h> 12#include <linux/types.h> 13#include <linux/memblock.h> 14#include <linux/slab.h> 15#include <linux/mm.h> 16 17/* 18 * Data types ------------------------------------------------------------------ 19 */ 20 21/* 22 * Firmware map entry. Because firmware memory maps are flat and not 23 * hierarchical, it's ok to organise them in a linked list. No parent 24 * information is necessary as for the resource tree. 25 */ 26struct firmware_map_entry { 27 /* 28 * start and end must be u64 rather than resource_size_t, because e820 29 * resources can lie at addresses above 4G. 30 */ 31 u64 start; /* start of the memory range */ 32 u64 end; /* end of the memory range (incl.) */ 33 const char *type; /* type of the memory range */ 34 struct list_head list; /* entry for the linked list */ 35 struct kobject kobj; /* kobject for each entry */ 36}; 37 38/* 39 * Forward declarations -------------------------------------------------------- 40 */ 41static ssize_t memmap_attr_show(struct kobject *kobj, 42 struct attribute *attr, char *buf); 43static ssize_t start_show(struct firmware_map_entry *entry, char *buf); 44static ssize_t end_show(struct firmware_map_entry *entry, char *buf); 45static ssize_t type_show(struct firmware_map_entry *entry, char *buf); 46 47static struct firmware_map_entry * __meminit 48firmware_map_find_entry(u64 start, u64 end, const char *type); 49 50/* 51 * Static data ----------------------------------------------------------------- 52 */ 53 54struct memmap_attribute { 55 struct attribute attr; 56 ssize_t (*show)(struct firmware_map_entry *entry, char *buf); 57}; 58 59static struct memmap_attribute memmap_start_attr = __ATTR_RO(start); 60static struct memmap_attribute memmap_end_attr = __ATTR_RO(end); 61static struct memmap_attribute memmap_type_attr = __ATTR_RO(type); 62 63/* 64 * These are default attributes that are added for every memmap entry. 65 */ 66static struct attribute *def_attrs[] = { 67 &memmap_start_attr.attr, 68 &memmap_end_attr.attr, 69 &memmap_type_attr.attr, 70 NULL 71}; 72ATTRIBUTE_GROUPS(def); 73 74static const struct sysfs_ops memmap_attr_ops = { 75 .show = memmap_attr_show, 76}; 77 78/* Firmware memory map entries. */ 79static LIST_HEAD(map_entries); 80static DEFINE_SPINLOCK(map_entries_lock); 81 82/* 83 * For memory hotplug, there is no way to free memory map entries allocated 84 * by boot mem after the system is up. So when we hot-remove memory whose 85 * map entry is allocated by bootmem, we need to remember the storage and 86 * reuse it when the memory is hot-added again. 87 */ 88static LIST_HEAD(map_entries_bootmem); 89static DEFINE_SPINLOCK(map_entries_bootmem_lock); 90 91 92static inline struct firmware_map_entry * 93to_memmap_entry(struct kobject *kobj) 94{ 95 return container_of(kobj, struct firmware_map_entry, kobj); 96} 97 98static void __meminit release_firmware_map_entry(struct kobject *kobj) 99{ 100 struct firmware_map_entry *entry = to_memmap_entry(kobj); 101 102 if (PageReserved(virt_to_page(entry))) { 103 /* 104 * Remember the storage allocated by bootmem, and reuse it when 105 * the memory is hot-added again. The entry will be added to 106 * map_entries_bootmem here, and deleted from &map_entries in 107 * firmware_map_remove_entry(). 108 */ 109 spin_lock(&map_entries_bootmem_lock); 110 list_add(&entry->list, &map_entries_bootmem); 111 spin_unlock(&map_entries_bootmem_lock); 112 113 return; 114 } 115 116 kfree(entry); 117} 118 119static struct kobj_type __refdata memmap_ktype = { 120 .release = release_firmware_map_entry, 121 .sysfs_ops = &memmap_attr_ops, 122 .default_groups = def_groups, 123}; 124 125/* 126 * Registration functions ------------------------------------------------------ 127 */ 128 129/** 130 * firmware_map_add_entry() - Does the real work to add a firmware memmap entry. 131 * @start: Start of the memory range. 132 * @end: End of the memory range (exclusive). 133 * @type: Type of the memory range. 134 * @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised 135 * entry. 136 * 137 * Common implementation of firmware_map_add() and firmware_map_add_early() 138 * which expects a pre-allocated struct firmware_map_entry. 139 * 140 * Return: 0 always 141 */ 142static int firmware_map_add_entry(u64 start, u64 end, 143 const char *type, 144 struct firmware_map_entry *entry) 145{ 146 BUG_ON(start > end); 147 148 entry->start = start; 149 entry->end = end - 1; 150 entry->type = type; 151 INIT_LIST_HEAD(&entry->list); 152 kobject_init(&entry->kobj, &memmap_ktype); 153 154 spin_lock(&map_entries_lock); 155 list_add_tail(&entry->list, &map_entries); 156 spin_unlock(&map_entries_lock); 157 158 return 0; 159} 160 161/** 162 * firmware_map_remove_entry() - Does the real work to remove a firmware 163 * memmap entry. 164 * @entry: removed entry. 165 * 166 * The caller must hold map_entries_lock, and release it properly. 167 */ 168static inline void firmware_map_remove_entry(struct firmware_map_entry *entry) 169{ 170 list_del(&entry->list); 171} 172 173/* 174 * Add memmap entry on sysfs 175 */ 176static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry) 177{ 178 static int map_entries_nr; 179 static struct kset *mmap_kset; 180 181 if (entry->kobj.state_in_sysfs) 182 return -EEXIST; 183 184 if (!mmap_kset) { 185 mmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj); 186 if (!mmap_kset) 187 return -ENOMEM; 188 } 189 190 entry->kobj.kset = mmap_kset; 191 if (kobject_add(&entry->kobj, NULL, "%d", map_entries_nr++)) 192 kobject_put(&entry->kobj); 193 194 return 0; 195} 196 197/* 198 * Remove memmap entry on sysfs 199 */ 200static inline void remove_sysfs_fw_map_entry(struct firmware_map_entry *entry) 201{ 202 kobject_put(&entry->kobj); 203} 204 205/** 206 * firmware_map_find_entry_in_list() - Search memmap entry in a given list. 207 * @start: Start of the memory range. 208 * @end: End of the memory range (exclusive). 209 * @type: Type of the memory range. 210 * @list: In which to find the entry. 211 * 212 * This function is to find the memmap entey of a given memory range in a 213 * given list. The caller must hold map_entries_lock, and must not release 214 * the lock until the processing of the returned entry has completed. 215 * 216 * Return: Pointer to the entry to be found on success, or NULL on failure. 217 */ 218static struct firmware_map_entry * __meminit 219firmware_map_find_entry_in_list(u64 start, u64 end, const char *type, 220 struct list_head *list) 221{ 222 struct firmware_map_entry *entry; 223 224 list_for_each_entry(entry, list, list) 225 if ((entry->start == start) && (entry->end == end) && 226 (!strcmp(entry->type, type))) { 227 return entry; 228 } 229 230 return NULL; 231} 232 233/** 234 * firmware_map_find_entry() - Search memmap entry in map_entries. 235 * @start: Start of the memory range. 236 * @end: End of the memory range (exclusive). 237 * @type: Type of the memory range. 238 * 239 * This function is to find the memmap entey of a given memory range. 240 * The caller must hold map_entries_lock, and must not release the lock 241 * until the processing of the returned entry has completed. 242 * 243 * Return: Pointer to the entry to be found on success, or NULL on failure. 244 */ 245static struct firmware_map_entry * __meminit 246firmware_map_find_entry(u64 start, u64 end, const char *type) 247{ 248 return firmware_map_find_entry_in_list(start, end, type, &map_entries); 249} 250 251/** 252 * firmware_map_find_entry_bootmem() - Search memmap entry in map_entries_bootmem. 253 * @start: Start of the memory range. 254 * @end: End of the memory range (exclusive). 255 * @type: Type of the memory range. 256 * 257 * This function is similar to firmware_map_find_entry except that it find the 258 * given entry in map_entries_bootmem. 259 * 260 * Return: Pointer to the entry to be found on success, or NULL on failure. 261 */ 262static struct firmware_map_entry * __meminit 263firmware_map_find_entry_bootmem(u64 start, u64 end, const char *type) 264{ 265 return firmware_map_find_entry_in_list(start, end, type, 266 &map_entries_bootmem); 267} 268 269/** 270 * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do 271 * memory hotplug. 272 * @start: Start of the memory range. 273 * @end: End of the memory range (exclusive) 274 * @type: Type of the memory range. 275 * 276 * Adds a firmware mapping entry. This function is for memory hotplug, it is 277 * similar to function firmware_map_add_early(). The only difference is that 278 * it will create the syfs entry dynamically. 279 * 280 * Return: 0 on success, or -ENOMEM if no memory could be allocated. 281 */ 282int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type) 283{ 284 struct firmware_map_entry *entry; 285 286 entry = firmware_map_find_entry(start, end - 1, type); 287 if (entry) 288 return 0; 289 290 entry = firmware_map_find_entry_bootmem(start, end - 1, type); 291 if (!entry) { 292 entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC); 293 if (!entry) 294 return -ENOMEM; 295 } else { 296 /* Reuse storage allocated by bootmem. */ 297 spin_lock(&map_entries_bootmem_lock); 298 list_del(&entry->list); 299 spin_unlock(&map_entries_bootmem_lock); 300 301 memset(entry, 0, sizeof(*entry)); 302 } 303 304 firmware_map_add_entry(start, end, type, entry); 305 /* create the memmap entry */ 306 add_sysfs_fw_map_entry(entry); 307 308 return 0; 309} 310 311/** 312 * firmware_map_add_early() - Adds a firmware mapping entry. 313 * @start: Start of the memory range. 314 * @end: End of the memory range. 315 * @type: Type of the memory range. 316 * 317 * Adds a firmware mapping entry. This function uses the bootmem allocator 318 * for memory allocation. 319 * 320 * That function must be called before late_initcall. 321 * 322 * Return: 0 on success, or -ENOMEM if no memory could be allocated. 323 */ 324int __init firmware_map_add_early(u64 start, u64 end, const char *type) 325{ 326 struct firmware_map_entry *entry; 327 328 entry = memblock_alloc(sizeof(struct firmware_map_entry), 329 SMP_CACHE_BYTES); 330 if (WARN_ON(!entry)) 331 return -ENOMEM; 332 333 return firmware_map_add_entry(start, end, type, entry); 334} 335 336/** 337 * firmware_map_remove() - remove a firmware mapping entry 338 * @start: Start of the memory range. 339 * @end: End of the memory range. 340 * @type: Type of the memory range. 341 * 342 * removes a firmware mapping entry. 343 * 344 * Return: 0 on success, or -EINVAL if no entry. 345 */ 346int __meminit firmware_map_remove(u64 start, u64 end, const char *type) 347{ 348 struct firmware_map_entry *entry; 349 350 spin_lock(&map_entries_lock); 351 entry = firmware_map_find_entry(start, end - 1, type); 352 if (!entry) { 353 spin_unlock(&map_entries_lock); 354 return -EINVAL; 355 } 356 357 firmware_map_remove_entry(entry); 358 spin_unlock(&map_entries_lock); 359 360 /* remove the memmap entry */ 361 remove_sysfs_fw_map_entry(entry); 362 363 return 0; 364} 365 366/* 367 * Sysfs functions ------------------------------------------------------------- 368 */ 369 370static ssize_t start_show(struct firmware_map_entry *entry, char *buf) 371{ 372 return snprintf(buf, PAGE_SIZE, "0x%llx\n", 373 (unsigned long long)entry->start); 374} 375 376static ssize_t end_show(struct firmware_map_entry *entry, char *buf) 377{ 378 return snprintf(buf, PAGE_SIZE, "0x%llx\n", 379 (unsigned long long)entry->end); 380} 381 382static ssize_t type_show(struct firmware_map_entry *entry, char *buf) 383{ 384 return snprintf(buf, PAGE_SIZE, "%s\n", entry->type); 385} 386 387static inline struct memmap_attribute *to_memmap_attr(struct attribute *attr) 388{ 389 return container_of(attr, struct memmap_attribute, attr); 390} 391 392static ssize_t memmap_attr_show(struct kobject *kobj, 393 struct attribute *attr, char *buf) 394{ 395 struct firmware_map_entry *entry = to_memmap_entry(kobj); 396 struct memmap_attribute *memmap_attr = to_memmap_attr(attr); 397 398 return memmap_attr->show(entry, buf); 399} 400 401/* 402 * Initialises stuff and adds the entries in the map_entries list to 403 * sysfs. Important is that firmware_map_add() and firmware_map_add_early() 404 * must be called before late_initcall. That's just because that function 405 * is called as late_initcall() function, which means that if you call 406 * firmware_map_add() or firmware_map_add_early() afterwards, the entries 407 * are not added to sysfs. 408 */ 409static int __init firmware_memmap_init(void) 410{ 411 struct firmware_map_entry *entry; 412 413 list_for_each_entry(entry, &map_entries, list) 414 add_sysfs_fw_map_entry(entry); 415 416 return 0; 417} 418late_initcall(firmware_memmap_init); 419