linux x64 memmap,drivers/firmware/memmap.c · liang/linux - Gitee.com

/*

* linux/drivers/firmware/memmap.c

* Copyright (C) 2008 SUSE LINUX Products GmbH

* by Bernhard Walle

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License v2.0 as published by

* the Free Software Foundation

*

* This program is distributed in the hope that it will be useful,

* but WITHOUT ANY WARRANTY; without even the implied warranty of

* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

* GNU General Public License for more details.

*

*/

#include

#include

#include

#include

#include

#include

#include

#include

/*

* Data types ------------------------------------------------------------------

*/

/*

* Firmware map entry. Because firmware memory maps are flat and not

* hierarchical, it's ok to organise them in a linked list. No parent

* information is necessary as for the resource tree.

*/

struct firmware_map_entry {

/*

* start and end must be u64 rather than resource_size_t, because e820

* resources can lie at addresses above 4G.

*/

u64start;/* start of the memory range */

u64end;/* end of the memory range (incl.) */

const char*type;/* type of the memory range */

struct list_headlist;/* entry for the linked list */

struct kobjectkobj; /* kobject for each entry */

};

/*

* Forward declarations --------------------------------------------------------

*/

static ssize_t memmap_attr_show(struct kobject *kobj,

struct attribute *attr, char *buf);

static ssize_t start_show(struct firmware_map_entry *entry, char *buf);

static ssize_t end_show(struct firmware_map_entry *entry, char *buf);

static ssize_t type_show(struct firmware_map_entry *entry, char *buf);

static struct firmware_map_entry * __meminit

firmware_map_find_entry(u64 start, u64 end, const char *type);

/*

* Static data -----------------------------------------------------------------

*/

struct memmap_attribute {

struct attribute attr;

ssize_t (*show)(struct firmware_map_entry *entry, char *buf);

};

static struct memmap_attribute memmap_start_attr = __ATTR_RO(start);

static struct memmap_attribute memmap_end_attr = __ATTR_RO(end);

static struct memmap_attribute memmap_type_attr = __ATTR_RO(type);

/*

* These are default attributes that are added for every memmap entry.

*/

static struct attribute *def_attrs[] = {

&memmap_start_attr.attr,

&memmap_end_attr.attr,

&memmap_type_attr.attr,

NULL

};

static const struct sysfs_ops memmap_attr_ops = {

.show = memmap_attr_show,

};

/* Firmware memory map entries. */

static LIST_HEAD(map_entries);

static DEFINE_SPINLOCK(map_entries_lock);

/*

* For memory hotplug, there is no way to free memory map entries allocated

* by boot mem after the system is up. So when we hot-remove memory whose

* map entry is allocated by bootmem, we need to remember the storage and

* reuse it when the memory is hot-added again.

*/

static LIST_HEAD(map_entries_bootmem);

static DEFINE_SPINLOCK(map_entries_bootmem_lock);

static inline struct firmware_map_entry *

to_memmap_entry(struct kobject *kobj)

{

return container_of(kobj, struct firmware_map_entry, kobj);

}

static void __meminit release_firmware_map_entry(struct kobject *kobj)

{

struct firmware_map_entry *entry = to_memmap_entry(kobj);

if (PageReserved(virt_to_page(entry))) {

/*

* Remember the storage allocated by bootmem, and reuse it when

* the memory is hot-added again. The entry will be added to

* map_entries_bootmem here, and deleted from &map_entries in

* firmware_map_remove_entry().

*/

spin_lock(&map_entries_bootmem_lock);

list_add(&entry->list, &map_entries_bootmem);

spin_unlock(&map_entries_bootmem_lock);

return;

}

kfree(entry);

}

static struct kobj_type __refdata memmap_ktype = {

.release= release_firmware_map_entry,

.sysfs_ops= &memmap_attr_ops,

.default_attrs= def_attrs,

};

/*

* Registration functions ------------------------------------------------------

*/

/**

* firmware_map_add_entry() - Does the real work to add a firmware memmap entry.

* @start: Start of the memory range.

* @end: End of the memory range (exclusive).

* @type: Type of the memory range.

* @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised

* entry.

*

* Common implementation of firmware_map_add() and firmware_map_add_early()

* which expects a pre-allocated struct firmware_map_entry.

*

* Return: 0 always

*/

static int firmware_map_add_entry(u64 start, u64 end,

const char *type,

struct firmware_map_entry *entry)

{

BUG_ON(start > end);

entry->start = start;

entry->end = end - 1;

entry->type = type;

INIT_LIST_HEAD(&entry->list);

kobject_init(&entry->kobj, &memmap_ktype);

spin_lock(&map_entries_lock);

list_add_tail(&entry->list, &map_entries);

spin_unlock(&map_entries_lock);

return 0;

}

/**

* firmware_map_remove_entry() - Does the real work to remove a firmware

* memmap entry.

* @entry: removed entry.

*

* The caller must hold map_entries_lock, and release it properly.

*/

static inline void firmware_map_remove_entry(struct firmware_map_entry *entry)

{

list_del(&entry->list);

}

/*

* Add memmap entry on sysfs

*/

static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry)

{

static int map_entries_nr;

static struct kset *mmap_kset;

if (entry->kobj.state_in_sysfs)

return -EEXIST;

if (!mmap_kset) {

mmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj);

if (!mmap_kset)

return -ENOMEM;

}

entry->kobj.kset = mmap_kset;

if (kobject_add(&entry->kobj, NULL, "%d", map_entries_nr++))

kobject_put(&entry->kobj);

return 0;

}

/*

* Remove memmap entry on sysfs

*/

static inline void remove_sysfs_fw_map_entry(struct firmware_map_entry *entry)

{

kobject_put(&entry->kobj);

}

/**

* firmware_map_find_entry_in_list() - Search memmap entry in a given list.

* @start: Start of the memory range.

* @end: End of the memory range (exclusive).

* @type: Type of the memory range.

* @list: In which to find the entry.

*

* This function is to find the memmap entey of a given memory range in a

* given list. The caller must hold map_entries_lock, and must not release

* the lock until the processing of the returned entry has completed.

*

* Return: Pointer to the entry to be found on success, or NULL on failure.

*/

static struct firmware_map_entry * __meminit

firmware_map_find_entry_in_list(u64 start, u64 end, const char *type,

struct list_head *list)

{

struct firmware_map_entry *entry;

list_for_each_entry(entry, list, list)

if ((entry->start == start) && (entry->end == end) &&

(!strcmp(entry->type, type))) {

return entry;

}

return NULL;

}

/**

* firmware_map_find_entry() - Search memmap entry in map_entries.

* @start: Start of the memory range.

* @end: End of the memory range (exclusive).

* @type: Type of the memory range.

*

* This function is to find the memmap entey of a given memory range.

* The caller must hold map_entries_lock, and must not release the lock

* until the processing of the returned entry has completed.

*

* Return: Pointer to the entry to be found on success, or NULL on failure.

*/

static struct firmware_map_entry * __meminit

firmware_map_find_entry(u64 start, u64 end, const char *type)

{

return firmware_map_find_entry_in_list(start, end, type, &map_entries);

}

/**

* firmware_map_find_entry_bootmem() - Search memmap entry in map_entries_bootmem.

* @start: Start of the memory range.

* @end: End of the memory range (exclusive).

* @type: Type of the memory range.

*

* This function is similar to firmware_map_find_entry except that it find the

* given entry in map_entries_bootmem.

*

* Return: Pointer to the entry to be found on success, or NULL on failure.

*/

static struct firmware_map_entry * __meminit

firmware_map_find_entry_bootmem(u64 start, u64 end, const char *type)

{

return firmware_map_find_entry_in_list(start, end, type,

&map_entries_bootmem);

}

/**

* firmware_map_add_hotplug() - Adds a firmware mapping entry when we do

* memory hotplug.

* @start: Start of the memory range.

* @end: End of the memory range (exclusive)

* @type: Type of the memory range.

*

* Adds a firmware mapping entry. This function is for memory hotplug, it is

* similar to function firmware_map_add_early(). The only difference is that

* it will create the syfs entry dynamically.

*

* Return: 0 on success, or -ENOMEM if no memory could be allocated.

*/

int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type)

{

struct firmware_map_entry *entry;

entry = firmware_map_find_entry(start, end - 1, type);

if (entry)

return 0;

entry = firmware_map_find_entry_bootmem(start, end - 1, type);

if (!entry) {

entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC);

if (!entry)

return -ENOMEM;

} else {

/* Reuse storage allocated by bootmem. */

spin_lock(&map_entries_bootmem_lock);

list_del(&entry->list);

spin_unlock(&map_entries_bootmem_lock);

memset(entry, 0, sizeof(*entry));

}

firmware_map_add_entry(start, end, type, entry);

/* create the memmap entry */

add_sysfs_fw_map_entry(entry);

return 0;

}

/**

* firmware_map_add_early() - Adds a firmware mapping entry.

* @start: Start of the memory range.

* @end: End of the memory range.

* @type: Type of the memory range.

*

* Adds a firmware mapping entry. This function uses the bootmem allocator

* for memory allocation.

*

* That function must be called before late_initcall.

*

* Return: 0 on success, or -ENOMEM if no memory could be allocated.

*/

int __init firmware_map_add_early(u64 start, u64 end, const char *type)

{

struct firmware_map_entry *entry;

entry = memblock_virt_alloc(sizeof(struct firmware_map_entry), 0);

if (WARN_ON(!entry))

return -ENOMEM;

return firmware_map_add_entry(start, end, type, entry);

}

/**

* firmware_map_remove() - remove a firmware mapping entry

* @start: Start of the memory range.

* @end: End of the memory range.

* @type: Type of the memory range.

*

* removes a firmware mapping entry.

*

* Return: 0 on success, or -EINVAL if no entry.

*/

int __meminit firmware_map_remove(u64 start, u64 end, const char *type)

{

struct firmware_map_entry *entry;

spin_lock(&map_entries_lock);

entry = firmware_map_find_entry(start, end - 1, type);

if (!entry) {

spin_unlock(&map_entries_lock);

return -EINVAL;

}

firmware_map_remove_entry(entry);

spin_unlock(&map_entries_lock);

/* remove the memmap entry */

remove_sysfs_fw_map_entry(entry);

return 0;

}

/*

* Sysfs functions -------------------------------------------------------------

*/

static ssize_t start_show(struct firmware_map_entry *entry, char *buf)

{

return snprintf(buf, PAGE_SIZE, "0x%llx\n",

(unsigned long long)entry->start);

}

static ssize_t end_show(struct firmware_map_entry *entry, char *buf)

{

return snprintf(buf, PAGE_SIZE, "0x%llx\n",

(unsigned long long)entry->end);

}

static ssize_t type_show(struct firmware_map_entry *entry, char *buf)

{

return snprintf(buf, PAGE_SIZE, "%s\n", entry->type);

}

static inline struct memmap_attribute *to_memmap_attr(struct attribute *attr)

{

return container_of(attr, struct memmap_attribute, attr);

}

static ssize_t memmap_attr_show(struct kobject *kobj,

struct attribute *attr, char *buf)

{

struct firmware_map_entry *entry = to_memmap_entry(kobj);

struct memmap_attribute *memmap_attr = to_memmap_attr(attr);

return memmap_attr->show(entry, buf);

}

/*

* Initialises stuff and adds the entries in the map_entries list to

* sysfs. Important is that firmware_map_add() and firmware_map_add_early()

* must be called before late_initcall. That's just because that function

* is called as late_initcall() function, which means that if you call

* firmware_map_add() or firmware_map_add_early() afterwards, the entries

* are not added to sysfs.

*/

static int __init firmware_memmap_init(void)

{

struct firmware_map_entry *entry;

list_for_each_entry(entry, &map_entries, list)

add_sysfs_fw_map_entry(entry);

return 0;

}

late_initcall(firmware_memmap_init);

一键复制

编辑

Web IDE

原始数据

按行查看

历史

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值