cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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