18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/drivers/firmware/memmap.c
48c2ecf20Sopenharmony_ci *  Copyright (C) 2008 SUSE LINUX Products GmbH
58c2ecf20Sopenharmony_ci *  by Bernhard Walle <bernhard.walle@gmx.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/string.h>
98c2ecf20Sopenharmony_ci#include <linux/firmware-map.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/memblock.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/mm.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * Data types ------------------------------------------------------------------
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * Firmware map entry. Because firmware memory maps are flat and not
238c2ecf20Sopenharmony_ci * hierarchical, it's ok to organise them in a linked list. No parent
248c2ecf20Sopenharmony_ci * information is necessary as for the resource tree.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_cistruct firmware_map_entry {
278c2ecf20Sopenharmony_ci	/*
288c2ecf20Sopenharmony_ci	 * start and end must be u64 rather than resource_size_t, because e820
298c2ecf20Sopenharmony_ci	 * resources can lie at addresses above 4G.
308c2ecf20Sopenharmony_ci	 */
318c2ecf20Sopenharmony_ci	u64			start;	/* start of the memory range */
328c2ecf20Sopenharmony_ci	u64			end;	/* end of the memory range (incl.) */
338c2ecf20Sopenharmony_ci	const char		*type;	/* type of the memory range */
348c2ecf20Sopenharmony_ci	struct list_head	list;	/* entry for the linked list */
358c2ecf20Sopenharmony_ci	struct kobject		kobj;   /* kobject for each entry */
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * Forward declarations --------------------------------------------------------
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_cistatic ssize_t memmap_attr_show(struct kobject *kobj,
428c2ecf20Sopenharmony_ci				struct attribute *attr, char *buf);
438c2ecf20Sopenharmony_cistatic ssize_t start_show(struct firmware_map_entry *entry, char *buf);
448c2ecf20Sopenharmony_cistatic ssize_t end_show(struct firmware_map_entry *entry, char *buf);
458c2ecf20Sopenharmony_cistatic ssize_t type_show(struct firmware_map_entry *entry, char *buf);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic struct firmware_map_entry * __meminit
488c2ecf20Sopenharmony_cifirmware_map_find_entry(u64 start, u64 end, const char *type);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * Static data -----------------------------------------------------------------
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct memmap_attribute {
558c2ecf20Sopenharmony_ci	struct attribute attr;
568c2ecf20Sopenharmony_ci	ssize_t (*show)(struct firmware_map_entry *entry, char *buf);
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic struct memmap_attribute memmap_start_attr = __ATTR_RO(start);
608c2ecf20Sopenharmony_cistatic struct memmap_attribute memmap_end_attr   = __ATTR_RO(end);
618c2ecf20Sopenharmony_cistatic struct memmap_attribute memmap_type_attr  = __ATTR_RO(type);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/*
648c2ecf20Sopenharmony_ci * These are default attributes that are added for every memmap entry.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic struct attribute *def_attrs[] = {
678c2ecf20Sopenharmony_ci	&memmap_start_attr.attr,
688c2ecf20Sopenharmony_ci	&memmap_end_attr.attr,
698c2ecf20Sopenharmony_ci	&memmap_type_attr.attr,
708c2ecf20Sopenharmony_ci	NULL
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic const struct sysfs_ops memmap_attr_ops = {
748c2ecf20Sopenharmony_ci	.show = memmap_attr_show,
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/* Firmware memory map entries. */
788c2ecf20Sopenharmony_cistatic LIST_HEAD(map_entries);
798c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(map_entries_lock);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/*
828c2ecf20Sopenharmony_ci * For memory hotplug, there is no way to free memory map entries allocated
838c2ecf20Sopenharmony_ci * by boot mem after the system is up. So when we hot-remove memory whose
848c2ecf20Sopenharmony_ci * map entry is allocated by bootmem, we need to remember the storage and
858c2ecf20Sopenharmony_ci * reuse it when the memory is hot-added again.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistatic LIST_HEAD(map_entries_bootmem);
888c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(map_entries_bootmem_lock);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic inline struct firmware_map_entry *
928c2ecf20Sopenharmony_cito_memmap_entry(struct kobject *kobj)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	return container_of(kobj, struct firmware_map_entry, kobj);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic void __meminit release_firmware_map_entry(struct kobject *kobj)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct firmware_map_entry *entry = to_memmap_entry(kobj);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (PageReserved(virt_to_page(entry))) {
1028c2ecf20Sopenharmony_ci		/*
1038c2ecf20Sopenharmony_ci		 * Remember the storage allocated by bootmem, and reuse it when
1048c2ecf20Sopenharmony_ci		 * the memory is hot-added again. The entry will be added to
1058c2ecf20Sopenharmony_ci		 * map_entries_bootmem here, and deleted from &map_entries in
1068c2ecf20Sopenharmony_ci		 * firmware_map_remove_entry().
1078c2ecf20Sopenharmony_ci		 */
1088c2ecf20Sopenharmony_ci		spin_lock(&map_entries_bootmem_lock);
1098c2ecf20Sopenharmony_ci		list_add(&entry->list, &map_entries_bootmem);
1108c2ecf20Sopenharmony_ci		spin_unlock(&map_entries_bootmem_lock);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		return;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	kfree(entry);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic struct kobj_type __refdata memmap_ktype = {
1198c2ecf20Sopenharmony_ci	.release	= release_firmware_map_entry,
1208c2ecf20Sopenharmony_ci	.sysfs_ops	= &memmap_attr_ops,
1218c2ecf20Sopenharmony_ci	.default_attrs	= def_attrs,
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/*
1258c2ecf20Sopenharmony_ci * Registration functions ------------------------------------------------------
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/**
1298c2ecf20Sopenharmony_ci * firmware_map_add_entry() - Does the real work to add a firmware memmap entry.
1308c2ecf20Sopenharmony_ci * @start: Start of the memory range.
1318c2ecf20Sopenharmony_ci * @end:   End of the memory range (exclusive).
1328c2ecf20Sopenharmony_ci * @type:  Type of the memory range.
1338c2ecf20Sopenharmony_ci * @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised
1348c2ecf20Sopenharmony_ci *         entry.
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * Common implementation of firmware_map_add() and firmware_map_add_early()
1378c2ecf20Sopenharmony_ci * which expects a pre-allocated struct firmware_map_entry.
1388c2ecf20Sopenharmony_ci *
1398c2ecf20Sopenharmony_ci * Return: 0 always
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_cistatic int firmware_map_add_entry(u64 start, u64 end,
1428c2ecf20Sopenharmony_ci				  const char *type,
1438c2ecf20Sopenharmony_ci				  struct firmware_map_entry *entry)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	BUG_ON(start > end);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	entry->start = start;
1488c2ecf20Sopenharmony_ci	entry->end = end - 1;
1498c2ecf20Sopenharmony_ci	entry->type = type;
1508c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&entry->list);
1518c2ecf20Sopenharmony_ci	kobject_init(&entry->kobj, &memmap_ktype);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	spin_lock(&map_entries_lock);
1548c2ecf20Sopenharmony_ci	list_add_tail(&entry->list, &map_entries);
1558c2ecf20Sopenharmony_ci	spin_unlock(&map_entries_lock);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/**
1618c2ecf20Sopenharmony_ci * firmware_map_remove_entry() - Does the real work to remove a firmware
1628c2ecf20Sopenharmony_ci * memmap entry.
1638c2ecf20Sopenharmony_ci * @entry: removed entry.
1648c2ecf20Sopenharmony_ci *
1658c2ecf20Sopenharmony_ci * The caller must hold map_entries_lock, and release it properly.
1668c2ecf20Sopenharmony_ci */
1678c2ecf20Sopenharmony_cistatic inline void firmware_map_remove_entry(struct firmware_map_entry *entry)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	list_del(&entry->list);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/*
1738c2ecf20Sopenharmony_ci * Add memmap entry on sysfs
1748c2ecf20Sopenharmony_ci */
1758c2ecf20Sopenharmony_cistatic int add_sysfs_fw_map_entry(struct firmware_map_entry *entry)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	static int map_entries_nr;
1788c2ecf20Sopenharmony_ci	static struct kset *mmap_kset;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (entry->kobj.state_in_sysfs)
1818c2ecf20Sopenharmony_ci		return -EEXIST;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (!mmap_kset) {
1848c2ecf20Sopenharmony_ci		mmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj);
1858c2ecf20Sopenharmony_ci		if (!mmap_kset)
1868c2ecf20Sopenharmony_ci			return -ENOMEM;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	entry->kobj.kset = mmap_kset;
1908c2ecf20Sopenharmony_ci	if (kobject_add(&entry->kobj, NULL, "%d", map_entries_nr++))
1918c2ecf20Sopenharmony_ci		kobject_put(&entry->kobj);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return 0;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/*
1978c2ecf20Sopenharmony_ci * Remove memmap entry on sysfs
1988c2ecf20Sopenharmony_ci */
1998c2ecf20Sopenharmony_cistatic inline void remove_sysfs_fw_map_entry(struct firmware_map_entry *entry)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	kobject_put(&entry->kobj);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/**
2058c2ecf20Sopenharmony_ci * firmware_map_find_entry_in_list() - Search memmap entry in a given list.
2068c2ecf20Sopenharmony_ci * @start: Start of the memory range.
2078c2ecf20Sopenharmony_ci * @end:   End of the memory range (exclusive).
2088c2ecf20Sopenharmony_ci * @type:  Type of the memory range.
2098c2ecf20Sopenharmony_ci * @list:  In which to find the entry.
2108c2ecf20Sopenharmony_ci *
2118c2ecf20Sopenharmony_ci * This function is to find the memmap entey of a given memory range in a
2128c2ecf20Sopenharmony_ci * given list. The caller must hold map_entries_lock, and must not release
2138c2ecf20Sopenharmony_ci * the lock until the processing of the returned entry has completed.
2148c2ecf20Sopenharmony_ci *
2158c2ecf20Sopenharmony_ci * Return: Pointer to the entry to be found on success, or NULL on failure.
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_cistatic struct firmware_map_entry * __meminit
2188c2ecf20Sopenharmony_cifirmware_map_find_entry_in_list(u64 start, u64 end, const char *type,
2198c2ecf20Sopenharmony_ci				struct list_head *list)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct firmware_map_entry *entry;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	list_for_each_entry(entry, list, list)
2248c2ecf20Sopenharmony_ci		if ((entry->start == start) && (entry->end == end) &&
2258c2ecf20Sopenharmony_ci		    (!strcmp(entry->type, type))) {
2268c2ecf20Sopenharmony_ci			return entry;
2278c2ecf20Sopenharmony_ci		}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return NULL;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci/**
2338c2ecf20Sopenharmony_ci * firmware_map_find_entry() - Search memmap entry in map_entries.
2348c2ecf20Sopenharmony_ci * @start: Start of the memory range.
2358c2ecf20Sopenharmony_ci * @end:   End of the memory range (exclusive).
2368c2ecf20Sopenharmony_ci * @type:  Type of the memory range.
2378c2ecf20Sopenharmony_ci *
2388c2ecf20Sopenharmony_ci * This function is to find the memmap entey of a given memory range.
2398c2ecf20Sopenharmony_ci * The caller must hold map_entries_lock, and must not release the lock
2408c2ecf20Sopenharmony_ci * until the processing of the returned entry has completed.
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * Return: Pointer to the entry to be found on success, or NULL on failure.
2438c2ecf20Sopenharmony_ci */
2448c2ecf20Sopenharmony_cistatic struct firmware_map_entry * __meminit
2458c2ecf20Sopenharmony_cifirmware_map_find_entry(u64 start, u64 end, const char *type)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	return firmware_map_find_entry_in_list(start, end, type, &map_entries);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/**
2518c2ecf20Sopenharmony_ci * firmware_map_find_entry_bootmem() - Search memmap entry in map_entries_bootmem.
2528c2ecf20Sopenharmony_ci * @start: Start of the memory range.
2538c2ecf20Sopenharmony_ci * @end:   End of the memory range (exclusive).
2548c2ecf20Sopenharmony_ci * @type:  Type of the memory range.
2558c2ecf20Sopenharmony_ci *
2568c2ecf20Sopenharmony_ci * This function is similar to firmware_map_find_entry except that it find the
2578c2ecf20Sopenharmony_ci * given entry in map_entries_bootmem.
2588c2ecf20Sopenharmony_ci *
2598c2ecf20Sopenharmony_ci * Return: Pointer to the entry to be found on success, or NULL on failure.
2608c2ecf20Sopenharmony_ci */
2618c2ecf20Sopenharmony_cistatic struct firmware_map_entry * __meminit
2628c2ecf20Sopenharmony_cifirmware_map_find_entry_bootmem(u64 start, u64 end, const char *type)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	return firmware_map_find_entry_in_list(start, end, type,
2658c2ecf20Sopenharmony_ci					       &map_entries_bootmem);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/**
2698c2ecf20Sopenharmony_ci * firmware_map_add_hotplug() - Adds a firmware mapping entry when we do
2708c2ecf20Sopenharmony_ci * memory hotplug.
2718c2ecf20Sopenharmony_ci * @start: Start of the memory range.
2728c2ecf20Sopenharmony_ci * @end:   End of the memory range (exclusive)
2738c2ecf20Sopenharmony_ci * @type:  Type of the memory range.
2748c2ecf20Sopenharmony_ci *
2758c2ecf20Sopenharmony_ci * Adds a firmware mapping entry. This function is for memory hotplug, it is
2768c2ecf20Sopenharmony_ci * similar to function firmware_map_add_early(). The only difference is that
2778c2ecf20Sopenharmony_ci * it will create the syfs entry dynamically.
2788c2ecf20Sopenharmony_ci *
2798c2ecf20Sopenharmony_ci * Return: 0 on success, or -ENOMEM if no memory could be allocated.
2808c2ecf20Sopenharmony_ci */
2818c2ecf20Sopenharmony_ciint __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct firmware_map_entry *entry;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	entry = firmware_map_find_entry(start, end - 1, type);
2868c2ecf20Sopenharmony_ci	if (entry)
2878c2ecf20Sopenharmony_ci		return 0;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	entry = firmware_map_find_entry_bootmem(start, end - 1, type);
2908c2ecf20Sopenharmony_ci	if (!entry) {
2918c2ecf20Sopenharmony_ci		entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC);
2928c2ecf20Sopenharmony_ci		if (!entry)
2938c2ecf20Sopenharmony_ci			return -ENOMEM;
2948c2ecf20Sopenharmony_ci	} else {
2958c2ecf20Sopenharmony_ci		/* Reuse storage allocated by bootmem. */
2968c2ecf20Sopenharmony_ci		spin_lock(&map_entries_bootmem_lock);
2978c2ecf20Sopenharmony_ci		list_del(&entry->list);
2988c2ecf20Sopenharmony_ci		spin_unlock(&map_entries_bootmem_lock);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		memset(entry, 0, sizeof(*entry));
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	firmware_map_add_entry(start, end, type, entry);
3048c2ecf20Sopenharmony_ci	/* create the memmap entry */
3058c2ecf20Sopenharmony_ci	add_sysfs_fw_map_entry(entry);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci/**
3118c2ecf20Sopenharmony_ci * firmware_map_add_early() - Adds a firmware mapping entry.
3128c2ecf20Sopenharmony_ci * @start: Start of the memory range.
3138c2ecf20Sopenharmony_ci * @end:   End of the memory range.
3148c2ecf20Sopenharmony_ci * @type:  Type of the memory range.
3158c2ecf20Sopenharmony_ci *
3168c2ecf20Sopenharmony_ci * Adds a firmware mapping entry. This function uses the bootmem allocator
3178c2ecf20Sopenharmony_ci * for memory allocation.
3188c2ecf20Sopenharmony_ci *
3198c2ecf20Sopenharmony_ci * That function must be called before late_initcall.
3208c2ecf20Sopenharmony_ci *
3218c2ecf20Sopenharmony_ci * Return: 0 on success, or -ENOMEM if no memory could be allocated.
3228c2ecf20Sopenharmony_ci */
3238c2ecf20Sopenharmony_ciint __init firmware_map_add_early(u64 start, u64 end, const char *type)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct firmware_map_entry *entry;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	entry = memblock_alloc(sizeof(struct firmware_map_entry),
3288c2ecf20Sopenharmony_ci			       SMP_CACHE_BYTES);
3298c2ecf20Sopenharmony_ci	if (WARN_ON(!entry))
3308c2ecf20Sopenharmony_ci		return -ENOMEM;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return firmware_map_add_entry(start, end, type, entry);
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/**
3368c2ecf20Sopenharmony_ci * firmware_map_remove() - remove a firmware mapping entry
3378c2ecf20Sopenharmony_ci * @start: Start of the memory range.
3388c2ecf20Sopenharmony_ci * @end:   End of the memory range.
3398c2ecf20Sopenharmony_ci * @type:  Type of the memory range.
3408c2ecf20Sopenharmony_ci *
3418c2ecf20Sopenharmony_ci * removes a firmware mapping entry.
3428c2ecf20Sopenharmony_ci *
3438c2ecf20Sopenharmony_ci * Return: 0 on success, or -EINVAL if no entry.
3448c2ecf20Sopenharmony_ci */
3458c2ecf20Sopenharmony_ciint __meminit firmware_map_remove(u64 start, u64 end, const char *type)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct firmware_map_entry *entry;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	spin_lock(&map_entries_lock);
3508c2ecf20Sopenharmony_ci	entry = firmware_map_find_entry(start, end - 1, type);
3518c2ecf20Sopenharmony_ci	if (!entry) {
3528c2ecf20Sopenharmony_ci		spin_unlock(&map_entries_lock);
3538c2ecf20Sopenharmony_ci		return -EINVAL;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	firmware_map_remove_entry(entry);
3578c2ecf20Sopenharmony_ci	spin_unlock(&map_entries_lock);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* remove the memmap entry */
3608c2ecf20Sopenharmony_ci	remove_sysfs_fw_map_entry(entry);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return 0;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci/*
3668c2ecf20Sopenharmony_ci * Sysfs functions -------------------------------------------------------------
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic ssize_t start_show(struct firmware_map_entry *entry, char *buf)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "0x%llx\n",
3728c2ecf20Sopenharmony_ci		(unsigned long long)entry->start);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic ssize_t end_show(struct firmware_map_entry *entry, char *buf)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "0x%llx\n",
3788c2ecf20Sopenharmony_ci		(unsigned long long)entry->end);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic ssize_t type_show(struct firmware_map_entry *entry, char *buf)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%s\n", entry->type);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic inline struct memmap_attribute *to_memmap_attr(struct attribute *attr)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	return container_of(attr, struct memmap_attribute, attr);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic ssize_t memmap_attr_show(struct kobject *kobj,
3928c2ecf20Sopenharmony_ci				struct attribute *attr, char *buf)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct firmware_map_entry *entry = to_memmap_entry(kobj);
3958c2ecf20Sopenharmony_ci	struct memmap_attribute *memmap_attr = to_memmap_attr(attr);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return memmap_attr->show(entry, buf);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/*
4018c2ecf20Sopenharmony_ci * Initialises stuff and adds the entries in the map_entries list to
4028c2ecf20Sopenharmony_ci * sysfs. Important is that firmware_map_add() and firmware_map_add_early()
4038c2ecf20Sopenharmony_ci * must be called before late_initcall. That's just because that function
4048c2ecf20Sopenharmony_ci * is called as late_initcall() function, which means that if you call
4058c2ecf20Sopenharmony_ci * firmware_map_add() or firmware_map_add_early() afterwards, the entries
4068c2ecf20Sopenharmony_ci * are not added to sysfs.
4078c2ecf20Sopenharmony_ci */
4088c2ecf20Sopenharmony_cistatic int __init firmware_memmap_init(void)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct firmware_map_entry *entry;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	list_for_each_entry(entry, &map_entries, list)
4138c2ecf20Sopenharmony_ci		add_sysfs_fw_map_entry(entry);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	return 0;
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_cilate_initcall(firmware_memmap_init);
4188c2ecf20Sopenharmony_ci
419