2013-12-20 14:02:21 +04:00
/*
* Architecture specific sysfs attributes in / sys / kernel
*
* Copyright ( C ) 2007 , Intel Corp .
* Huang Ying < ying . huang @ intel . com >
* Copyright ( C ) 2013 , 2013 Red Hat , Inc .
* Dave Young < dyoung @ redhat . com >
*
* This file is released under the GPLv2
*/
# include <linux/kobject.h>
# include <linux/string.h>
# include <linux/sysfs.h>
# include <linux/init.h>
# include <linux/stat.h>
# include <linux/slab.h>
# include <linux/mm.h>
2014-01-03 07:54:31 +04:00
# include <asm/io.h>
2013-12-20 14:02:21 +04:00
# include <asm/setup.h>
static ssize_t version_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
return sprintf ( buf , " 0x%04x \n " , boot_params . hdr . version ) ;
}
static struct kobj_attribute boot_params_version_attr = __ATTR_RO ( version ) ;
static ssize_t boot_params_data_read ( struct file * fp , struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
{
memcpy ( buf , ( void * ) & boot_params + off , count ) ;
return count ;
}
static struct bin_attribute boot_params_data_attr = {
. attr = {
. name = " data " ,
. mode = S_IRUGO ,
} ,
. read = boot_params_data_read ,
. size = sizeof ( boot_params ) ,
} ;
static struct attribute * boot_params_version_attrs [ ] = {
& boot_params_version_attr . attr ,
NULL ,
} ;
static struct bin_attribute * boot_params_data_attrs [ ] = {
& boot_params_data_attr ,
NULL ,
} ;
static struct attribute_group boot_params_attr_group = {
. attrs = boot_params_version_attrs ,
. bin_attrs = boot_params_data_attrs ,
} ;
static int kobj_to_setup_data_nr ( struct kobject * kobj , int * nr )
{
const char * name ;
name = kobject_name ( kobj ) ;
return kstrtoint ( name , 10 , nr ) ;
}
static int get_setup_data_paddr ( int nr , u64 * paddr )
{
int i = 0 ;
struct setup_data * data ;
u64 pa_data = boot_params . hdr . setup_data ;
while ( pa_data ) {
if ( nr = = i ) {
* paddr = pa_data ;
return 0 ;
}
data = ioremap_cache ( pa_data , sizeof ( * data ) ) ;
if ( ! data )
return - ENOMEM ;
pa_data = data - > next ;
iounmap ( data ) ;
i + + ;
}
return - EINVAL ;
}
static int __init get_setup_data_size ( int nr , size_t * size )
{
int i = 0 ;
struct setup_data * data ;
u64 pa_data = boot_params . hdr . setup_data ;
while ( pa_data ) {
data = ioremap_cache ( pa_data , sizeof ( * data ) ) ;
if ( ! data )
return - ENOMEM ;
if ( nr = = i ) {
* size = data - > len ;
iounmap ( data ) ;
return 0 ;
}
pa_data = data - > next ;
iounmap ( data ) ;
i + + ;
}
return - EINVAL ;
}
static ssize_t type_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
int nr , ret ;
u64 paddr ;
struct setup_data * data ;
ret = kobj_to_setup_data_nr ( kobj , & nr ) ;
if ( ret )
return ret ;
ret = get_setup_data_paddr ( nr , & paddr ) ;
if ( ret )
return ret ;
data = ioremap_cache ( paddr , sizeof ( * data ) ) ;
if ( ! data )
return - ENOMEM ;
ret = sprintf ( buf , " 0x%x \n " , data - > type ) ;
iounmap ( data ) ;
return ret ;
}
static ssize_t setup_data_data_read ( struct file * fp ,
struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf ,
loff_t off , size_t count )
{
int nr , ret = 0 ;
u64 paddr ;
struct setup_data * data ;
void * p ;
ret = kobj_to_setup_data_nr ( kobj , & nr ) ;
if ( ret )
return ret ;
ret = get_setup_data_paddr ( nr , & paddr ) ;
if ( ret )
return ret ;
data = ioremap_cache ( paddr , sizeof ( * data ) ) ;
if ( ! data )
return - ENOMEM ;
if ( off > data - > len ) {
ret = - EINVAL ;
goto out ;
}
if ( count > data - > len - off )
count = data - > len - off ;
if ( ! count )
goto out ;
ret = count ;
p = ioremap_cache ( paddr + sizeof ( * data ) , data - > len ) ;
if ( ! p ) {
ret = - ENOMEM ;
goto out ;
}
memcpy ( buf , p + off , count ) ;
iounmap ( p ) ;
out :
iounmap ( data ) ;
return ret ;
}
static struct kobj_attribute type_attr = __ATTR_RO ( type ) ;
static struct bin_attribute data_attr = {
. attr = {
. name = " data " ,
. mode = S_IRUGO ,
} ,
. read = setup_data_data_read ,
} ;
static struct attribute * setup_data_type_attrs [ ] = {
& type_attr . attr ,
NULL ,
} ;
static struct bin_attribute * setup_data_data_attrs [ ] = {
& data_attr ,
NULL ,
} ;
static struct attribute_group setup_data_attr_group = {
. attrs = setup_data_type_attrs ,
. bin_attrs = setup_data_data_attrs ,
} ;
static int __init create_setup_data_node ( struct kobject * parent ,
struct kobject * * kobjp , int nr )
{
int ret = 0 ;
size_t size ;
struct kobject * kobj ;
char name [ 16 ] ; /* should be enough for setup_data nodes numbers */
snprintf ( name , 16 , " %d " , nr ) ;
kobj = kobject_create_and_add ( name , parent ) ;
if ( ! kobj )
return - ENOMEM ;
ret = get_setup_data_size ( nr , & size ) ;
if ( ret )
goto out_kobj ;
data_attr . size = size ;
ret = sysfs_create_group ( kobj , & setup_data_attr_group ) ;
if ( ret )
goto out_kobj ;
* kobjp = kobj ;
return 0 ;
out_kobj :
kobject_put ( kobj ) ;
return ret ;
}
static void __init cleanup_setup_data_node ( struct kobject * kobj )
{
sysfs_remove_group ( kobj , & setup_data_attr_group ) ;
kobject_put ( kobj ) ;
}
static int __init get_setup_data_total_num ( u64 pa_data , int * nr )
{
int ret = 0 ;
struct setup_data * data ;
* nr = 0 ;
while ( pa_data ) {
* nr + = 1 ;
data = ioremap_cache ( pa_data , sizeof ( * data ) ) ;
if ( ! data ) {
ret = - ENOMEM ;
goto out ;
}
pa_data = data - > next ;
iounmap ( data ) ;
}
out :
return ret ;
}
static int __init create_setup_data_nodes ( struct kobject * parent )
{
struct kobject * setup_data_kobj , * * kobjp ;
u64 pa_data ;
int i , j , nr , ret = 0 ;
pa_data = boot_params . hdr . setup_data ;
if ( ! pa_data )
return 0 ;
setup_data_kobj = kobject_create_and_add ( " setup_data " , parent ) ;
if ( ! setup_data_kobj ) {
ret = - ENOMEM ;
goto out ;
}
ret = get_setup_data_total_num ( pa_data , & nr ) ;
if ( ret )
goto out_setup_data_kobj ;
kobjp = kmalloc ( sizeof ( * kobjp ) * nr , GFP_KERNEL ) ;
if ( ! kobjp ) {
ret = - ENOMEM ;
goto out_setup_data_kobj ;
}
for ( i = 0 ; i < nr ; i + + ) {
ret = create_setup_data_node ( setup_data_kobj , kobjp + i , i ) ;
if ( ret )
goto out_clean_nodes ;
}
kfree ( kobjp ) ;
return 0 ;
out_clean_nodes :
for ( j = i - 1 ; j > 0 ; j - - )
cleanup_setup_data_node ( * ( kobjp + j ) ) ;
kfree ( kobjp ) ;
out_setup_data_kobj :
kobject_put ( setup_data_kobj ) ;
out :
return ret ;
}
static int __init boot_params_ksysfs_init ( void )
{
int ret ;
struct kobject * boot_params_kobj ;
boot_params_kobj = kobject_create_and_add ( " boot_params " ,
kernel_kobj ) ;
if ( ! boot_params_kobj ) {
ret = - ENOMEM ;
goto out ;
}
ret = sysfs_create_group ( boot_params_kobj , & boot_params_attr_group ) ;
if ( ret )
goto out_boot_params_kobj ;
ret = create_setup_data_nodes ( boot_params_kobj ) ;
if ( ret )
goto out_create_group ;
return 0 ;
out_create_group :
sysfs_remove_group ( boot_params_kobj , & boot_params_attr_group ) ;
out_boot_params_kobj :
kobject_put ( boot_params_kobj ) ;
out :
return ret ;
}
arch_initcall ( boot_params_ksysfs_init ) ;