/*1* linux/drivers/firmware/memmap.c2* Copyright (C) 2008 SUSE LINUX Products GmbH3* by Bernhard Walle <[email protected]>4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License v2.0 as published by7* the Free Software Foundation8*9* This program is distributed in the hope that it will be useful,10* but WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12* GNU General Public License for more details.13*14*/1516#include <linux/string.h>17#include <linux/firmware-map.h>18#include <linux/kernel.h>19#include <linux/module.h>20#include <linux/types.h>21#include <linux/bootmem.h>22#include <linux/slab.h>2324/*25* Data types ------------------------------------------------------------------26*/2728/*29* Firmware map entry. Because firmware memory maps are flat and not30* hierarchical, it's ok to organise them in a linked list. No parent31* information is necessary as for the resource tree.32*/33struct firmware_map_entry {34/*35* start and end must be u64 rather than resource_size_t, because e82036* resources can lie at addresses above 4G.37*/38u64 start; /* start of the memory range */39u64 end; /* end of the memory range (incl.) */40const char *type; /* type of the memory range */41struct list_head list; /* entry for the linked list */42struct kobject kobj; /* kobject for each entry */43};4445/*46* Forward declarations --------------------------------------------------------47*/48static ssize_t memmap_attr_show(struct kobject *kobj,49struct attribute *attr, char *buf);50static ssize_t start_show(struct firmware_map_entry *entry, char *buf);51static ssize_t end_show(struct firmware_map_entry *entry, char *buf);52static ssize_t type_show(struct firmware_map_entry *entry, char *buf);5354/*55* Static data -----------------------------------------------------------------56*/5758struct memmap_attribute {59struct attribute attr;60ssize_t (*show)(struct firmware_map_entry *entry, char *buf);61};6263static struct memmap_attribute memmap_start_attr = __ATTR_RO(start);64static struct memmap_attribute memmap_end_attr = __ATTR_RO(end);65static struct memmap_attribute memmap_type_attr = __ATTR_RO(type);6667/*68* These are default attributes that are added for every memmap entry.69*/70static struct attribute *def_attrs[] = {71&memmap_start_attr.attr,72&memmap_end_attr.attr,73&memmap_type_attr.attr,74NULL75};7677static const struct sysfs_ops memmap_attr_ops = {78.show = memmap_attr_show,79};8081static struct kobj_type memmap_ktype = {82.sysfs_ops = &memmap_attr_ops,83.default_attrs = def_attrs,84};8586/*87* Registration functions ------------------------------------------------------88*/8990/*91* Firmware memory map entries. No locking is needed because the92* firmware_map_add() and firmware_map_add_early() functions are called93* in firmware initialisation code in one single thread of execution.94*/95static LIST_HEAD(map_entries);9697/**98* firmware_map_add_entry() - Does the real work to add a firmware memmap entry.99* @start: Start of the memory range.100* @end: End of the memory range (inclusive).101* @type: Type of the memory range.102* @entry: Pre-allocated (either kmalloc() or bootmem allocator), uninitialised103* entry.104*105* Common implementation of firmware_map_add() and firmware_map_add_early()106* which expects a pre-allocated struct firmware_map_entry.107**/108static int firmware_map_add_entry(u64 start, u64 end,109const char *type,110struct firmware_map_entry *entry)111{112BUG_ON(start > end);113114entry->start = start;115entry->end = end;116entry->type = type;117INIT_LIST_HEAD(&entry->list);118kobject_init(&entry->kobj, &memmap_ktype);119120list_add_tail(&entry->list, &map_entries);121122return 0;123}124125/*126* Add memmap entry on sysfs127*/128static int add_sysfs_fw_map_entry(struct firmware_map_entry *entry)129{130static int map_entries_nr;131static struct kset *mmap_kset;132133if (!mmap_kset) {134mmap_kset = kset_create_and_add("memmap", NULL, firmware_kobj);135if (!mmap_kset)136return -ENOMEM;137}138139entry->kobj.kset = mmap_kset;140if (kobject_add(&entry->kobj, NULL, "%d", map_entries_nr++))141kobject_put(&entry->kobj);142143return 0;144}145146/**147* firmware_map_add_hotplug() - Adds a firmware mapping entry when we do148* memory hotplug.149* @start: Start of the memory range.150* @end: End of the memory range (inclusive).151* @type: Type of the memory range.152*153* Adds a firmware mapping entry. This function is for memory hotplug, it is154* similar to function firmware_map_add_early(). The only difference is that155* it will create the syfs entry dynamically.156*157* Returns 0 on success, or -ENOMEM if no memory could be allocated.158**/159int __meminit firmware_map_add_hotplug(u64 start, u64 end, const char *type)160{161struct firmware_map_entry *entry;162163entry = kzalloc(sizeof(struct firmware_map_entry), GFP_ATOMIC);164if (!entry)165return -ENOMEM;166167firmware_map_add_entry(start, end, type, entry);168/* create the memmap entry */169add_sysfs_fw_map_entry(entry);170171return 0;172}173174/**175* firmware_map_add_early() - Adds a firmware mapping entry.176* @start: Start of the memory range.177* @end: End of the memory range (inclusive).178* @type: Type of the memory range.179*180* Adds a firmware mapping entry. This function uses the bootmem allocator181* for memory allocation.182*183* That function must be called before late_initcall.184*185* Returns 0 on success, or -ENOMEM if no memory could be allocated.186**/187int __init firmware_map_add_early(u64 start, u64 end, const char *type)188{189struct firmware_map_entry *entry;190191entry = alloc_bootmem(sizeof(struct firmware_map_entry));192if (WARN_ON(!entry))193return -ENOMEM;194195return firmware_map_add_entry(start, end, type, entry);196}197198/*199* Sysfs functions -------------------------------------------------------------200*/201202static ssize_t start_show(struct firmware_map_entry *entry, char *buf)203{204return snprintf(buf, PAGE_SIZE, "0x%llx\n",205(unsigned long long)entry->start);206}207208static ssize_t end_show(struct firmware_map_entry *entry, char *buf)209{210return snprintf(buf, PAGE_SIZE, "0x%llx\n",211(unsigned long long)entry->end);212}213214static ssize_t type_show(struct firmware_map_entry *entry, char *buf)215{216return snprintf(buf, PAGE_SIZE, "%s\n", entry->type);217}218219#define to_memmap_attr(_attr) container_of(_attr, struct memmap_attribute, attr)220#define to_memmap_entry(obj) container_of(obj, struct firmware_map_entry, kobj)221222static ssize_t memmap_attr_show(struct kobject *kobj,223struct attribute *attr, char *buf)224{225struct firmware_map_entry *entry = to_memmap_entry(kobj);226struct memmap_attribute *memmap_attr = to_memmap_attr(attr);227228return memmap_attr->show(entry, buf);229}230231/*232* Initialises stuff and adds the entries in the map_entries list to233* sysfs. Important is that firmware_map_add() and firmware_map_add_early()234* must be called before late_initcall. That's just because that function235* is called as late_initcall() function, which means that if you call236* firmware_map_add() or firmware_map_add_early() afterwards, the entries237* are not added to sysfs.238*/239static int __init memmap_init(void)240{241struct firmware_map_entry *entry;242243list_for_each_entry(entry, &map_entries, list)244add_sysfs_fw_map_entry(entry);245246return 0;247}248late_initcall(memmap_init);249250251252