2008-06-27 15:12:54 +04:00
/*
* linux / drivers / firmware / memmap . c
* Copyright ( C ) 2008 SUSE LINUX Products GmbH
2009-02-19 01:48:40 +03:00
* by Bernhard Walle < bernhard . walle @ gmx . de >
2008-06-27 15:12:54 +04:00
*
* 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 <linux/string.h>
# include <linux/firmware-map.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/bootmem.h>
/*
* 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 {
2009-06-17 02:31:16 +04:00
/*
* start and end must be u64 rather than resource_size_t , because e820
* resources can lie at addresses above 4 G .
*/
u64 start ; /* start of the memory range */
u64 end ; /* end of the memory range (incl.) */
2008-06-27 15:12:54 +04:00
const char * type ; /* type of the memory range */
struct list_head list ; /* entry for the linked list */
struct kobject kobj ; /* 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 data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
struct memmap_attribute {
struct attribute attr ;
ssize_t ( * show ) ( struct firmware_map_entry * entry , char * buf ) ;
} ;
2009-01-08 05:09:15 +03:00
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 ) ;
2008-06-27 15:12:54 +04:00
/*
* 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 struct sysfs_ops memmap_attr_ops = {
. show = memmap_attr_show ,
} ;
static struct kobj_type memmap_ktype = {
. sysfs_ops = & memmap_attr_ops ,
. default_attrs = def_attrs ,
} ;
/*
* Registration functions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/*
2008-08-13 02:09:14 +04:00
* Firmware memory map entries . No locking is needed because the
* firmware_map_add ( ) and firmware_map_add_early ( ) functions are called
* in firmware initialisation code in one single thread of execution .
2008-06-27 15:12:54 +04:00
*/
static LIST_HEAD ( map_entries ) ;
/**
2008-08-13 02:09:14 +04:00
* firmware_map_add_entry ( ) - Does the real work to add a firmware memmap entry .
2008-06-27 15:12:54 +04:00
* @ start : Start of the memory range .
* @ end : End of the memory range ( inclusive ) .
* @ type : Type of the memory range .
* @ entry : Pre - allocated ( either kmalloc ( ) or bootmem allocator ) , uninitialised
* entry .
2008-08-13 02:09:14 +04:00
*
* Common implementation of firmware_map_add ( ) and firmware_map_add_early ( )
* which expects a pre - allocated struct firmware_map_entry .
* */
2009-06-17 02:31:16 +04:00
static int firmware_map_add_entry ( u64 start , u64 end ,
2008-06-27 15:12:54 +04:00
const char * type ,
struct firmware_map_entry * entry )
{
BUG_ON ( start > end ) ;
entry - > start = start ;
entry - > end = end ;
entry - > type = type ;
INIT_LIST_HEAD ( & entry - > list ) ;
kobject_init ( & entry - > kobj , & memmap_ktype ) ;
list_add_tail ( & entry - > list , & map_entries ) ;
return 0 ;
}
2008-08-13 02:09:14 +04:00
/**
* firmware_map_add ( ) - Adds a firmware mapping entry .
* @ start : Start of the memory range .
* @ end : End of the memory range ( inclusive ) .
* @ type : Type of the memory range .
*
* This function uses kmalloc ( ) for memory
* allocation . Use firmware_map_add_early ( ) if you want to use the bootmem
* allocator .
*
* That function must be called before late_initcall .
*
* Returns 0 on success , or - ENOMEM if no memory could be allocated .
* */
2009-06-17 02:31:16 +04:00
int firmware_map_add ( u64 start , u64 end , const char * type )
2008-06-27 15:12:54 +04:00
{
struct firmware_map_entry * entry ;
entry = kmalloc ( sizeof ( struct firmware_map_entry ) , GFP_ATOMIC ) ;
if ( ! entry )
return - ENOMEM ;
return firmware_map_add_entry ( start , end , type , entry ) ;
}
2008-08-13 02:09:14 +04:00
/**
* firmware_map_add_early ( ) - Adds a firmware mapping entry .
* @ start : Start of the memory range .
* @ end : End of the memory range ( inclusive ) .
* @ type : Type of the memory range .
*
* Adds a firmware mapping entry . This function uses the bootmem allocator
* for memory allocation . Use firmware_map_add ( ) if you want to use kmalloc ( ) .
*
* That function must be called before late_initcall .
*
* Returns 0 on success , or - ENOMEM if no memory could be allocated .
* */
2009-06-17 02:31:16 +04:00
int __init firmware_map_add_early ( u64 start , u64 end , const char * type )
2008-06-27 15:12:54 +04:00
{
struct firmware_map_entry * entry ;
2009-09-22 04:03:06 +04:00
entry = alloc_bootmem ( sizeof ( struct firmware_map_entry ) ) ;
2008-08-13 02:09:14 +04:00
if ( WARN_ON ( ! entry ) )
2008-06-27 15:12:54 +04:00
return - ENOMEM ;
return firmware_map_add_entry ( start , end , type , entry ) ;
}
/*
* Sysfs functions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static ssize_t start_show ( struct firmware_map_entry * entry , char * buf )
{
2008-07-27 02:22:28 +04:00
return snprintf ( buf , PAGE_SIZE , " 0x%llx \n " ,
( unsigned long long ) entry - > start ) ;
2008-06-27 15:12:54 +04:00
}
static ssize_t end_show ( struct firmware_map_entry * entry , char * buf )
{
2008-07-27 02:22:28 +04:00
return snprintf ( buf , PAGE_SIZE , " 0x%llx \n " ,
( unsigned long long ) entry - > end ) ;
2008-06-27 15:12:54 +04:00
}
static ssize_t type_show ( struct firmware_map_entry * entry , char * buf )
{
return snprintf ( buf , PAGE_SIZE , " %s \n " , entry - > type ) ;
}
# define to_memmap_attr(_attr) container_of(_attr, struct memmap_attribute, attr)
# define to_memmap_entry(obj) container_of(obj, struct firmware_map_entry, kobj)
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 ( )
2008-08-13 02:09:14 +04:00
* 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 .
2008-06-27 15:12:54 +04:00
*/
static int __init memmap_init ( void )
{
int i = 0 ;
struct firmware_map_entry * entry ;
struct kset * memmap_kset ;
memmap_kset = kset_create_and_add ( " memmap " , NULL , firmware_kobj ) ;
2008-08-13 02:09:14 +04:00
if ( WARN_ON ( ! memmap_kset ) )
2008-06-27 15:12:54 +04:00
return - ENOMEM ;
list_for_each_entry ( entry , & map_entries , list ) {
entry - > kobj . kset = memmap_kset ;
2008-08-13 02:09:14 +04:00
if ( kobject_add ( & entry - > kobj , NULL , " %d " , i + + ) )
kobject_put ( & entry - > kobj ) ;
2008-06-27 15:12:54 +04:00
}
return 0 ;
}
late_initcall ( memmap_init ) ;