2013-02-08 15:37:06 +00:00
/*
* efi . c - EFI subsystem
*
* Copyright ( C ) 2001 , 2003 , 2004 Dell < Matt_Domsch @ dell . com >
* Copyright ( C ) 2004 Intel Corporation < matthew . e . tolentino @ intel . com >
* Copyright ( C ) 2013 Tom Gundersen < teg @ jklm . no >
*
* This code registers / sys / firmware / efi { , / efivars } when EFI is supported ,
* allowing the efivarfs to be mounted or the efivars module to be loaded .
* The existance of / sys / firmware / efi may also be used by userspace to
* determine that the system supports EFI .
*
* This file is released under the GPLv2 .
*/
2013-09-05 11:34:54 +01:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2013-02-08 15:37:06 +00:00
# include <linux/kobject.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/device.h>
# include <linux/efi.h>
2013-12-30 12:12:12 -05:00
# include <linux/of.h>
# include <linux/of_fdt.h>
2013-09-05 11:34:54 +01:00
# include <linux/io.h>
2014-07-09 18:39:29 +08:00
# include <linux/platform_device.h>
2013-09-05 11:34:54 +01:00
struct efi __read_mostly efi = {
. mps = EFI_INVALID_TABLE_ADDR ,
. acpi = EFI_INVALID_TABLE_ADDR ,
. acpi20 = EFI_INVALID_TABLE_ADDR ,
. smbios = EFI_INVALID_TABLE_ADDR ,
. sal_systab = EFI_INVALID_TABLE_ADDR ,
. boot_info = EFI_INVALID_TABLE_ADDR ,
. hcdp = EFI_INVALID_TABLE_ADDR ,
. uga = EFI_INVALID_TABLE_ADDR ,
. uv_systab = EFI_INVALID_TABLE_ADDR ,
2013-12-20 18:02:17 +08:00
. fw_vendor = EFI_INVALID_TABLE_ADDR ,
. runtime = EFI_INVALID_TABLE_ADDR ,
. config_table = EFI_INVALID_TABLE_ADDR ,
2013-09-05 11:34:54 +01:00
} ;
EXPORT_SYMBOL ( efi ) ;
2013-02-08 15:37:06 +00:00
static struct kobject * efi_kobj ;
static struct kobject * efivars_kobj ;
/*
* Let ' s not leave out systab information that snuck into
* the efivars driver
*/
static ssize_t systab_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
char * str = buf ;
if ( ! kobj | | ! buf )
return - EINVAL ;
if ( efi . mps ! = EFI_INVALID_TABLE_ADDR )
str + = sprintf ( str , " MPS=0x%lx \n " , efi . mps ) ;
if ( efi . acpi20 ! = EFI_INVALID_TABLE_ADDR )
str + = sprintf ( str , " ACPI20=0x%lx \n " , efi . acpi20 ) ;
if ( efi . acpi ! = EFI_INVALID_TABLE_ADDR )
str + = sprintf ( str , " ACPI=0x%lx \n " , efi . acpi ) ;
if ( efi . smbios ! = EFI_INVALID_TABLE_ADDR )
str + = sprintf ( str , " SMBIOS=0x%lx \n " , efi . smbios ) ;
if ( efi . hcdp ! = EFI_INVALID_TABLE_ADDR )
str + = sprintf ( str , " HCDP=0x%lx \n " , efi . hcdp ) ;
if ( efi . boot_info ! = EFI_INVALID_TABLE_ADDR )
str + = sprintf ( str , " BOOTINFO=0x%lx \n " , efi . boot_info ) ;
if ( efi . uga ! = EFI_INVALID_TABLE_ADDR )
str + = sprintf ( str , " UGA=0x%lx \n " , efi . uga ) ;
return str - buf ;
}
static struct kobj_attribute efi_attr_systab =
__ATTR ( systab , 0400 , systab_show , NULL ) ;
2013-12-20 18:02:17 +08:00
# define EFI_FIELD(var) efi.var
# define EFI_ATTR_SHOW(name) \
static ssize_t name # # _show ( struct kobject * kobj , \
struct kobj_attribute * attr , char * buf ) \
{ \
return sprintf ( buf , " 0x%lx \n " , EFI_FIELD ( name ) ) ; \
}
EFI_ATTR_SHOW ( fw_vendor ) ;
EFI_ATTR_SHOW ( runtime ) ;
EFI_ATTR_SHOW ( config_table ) ;
static struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO ( fw_vendor ) ;
static struct kobj_attribute efi_attr_runtime = __ATTR_RO ( runtime ) ;
static struct kobj_attribute efi_attr_config_table = __ATTR_RO ( config_table ) ;
2013-02-08 15:37:06 +00:00
static struct attribute * efi_subsys_attrs [ ] = {
& efi_attr_systab . attr ,
2013-12-20 18:02:17 +08:00
& efi_attr_fw_vendor . attr ,
& efi_attr_runtime . attr ,
& efi_attr_config_table . attr ,
NULL ,
2013-02-08 15:37:06 +00:00
} ;
2013-12-20 18:02:17 +08:00
static umode_t efi_attr_is_visible ( struct kobject * kobj ,
struct attribute * attr , int n )
{
2014-06-30 19:52:58 +02:00
if ( attr = = & efi_attr_fw_vendor . attr ) {
if ( efi_enabled ( EFI_PARAVIRT ) | |
efi . fw_vendor = = EFI_INVALID_TABLE_ADDR )
return 0 ;
} else if ( attr = = & efi_attr_runtime . attr ) {
if ( efi . runtime = = EFI_INVALID_TABLE_ADDR )
return 0 ;
} else if ( attr = = & efi_attr_config_table . attr ) {
if ( efi . config_table = = EFI_INVALID_TABLE_ADDR )
return 0 ;
}
2013-12-20 18:02:17 +08:00
2014-06-30 19:52:58 +02:00
return attr - > mode ;
2013-12-20 18:02:17 +08:00
}
2013-02-08 15:37:06 +00:00
static struct attribute_group efi_subsys_attr_group = {
. attrs = efi_subsys_attrs ,
2013-12-20 18:02:17 +08:00
. is_visible = efi_attr_is_visible ,
2013-02-08 15:37:06 +00:00
} ;
static struct efivars generic_efivars ;
static struct efivar_operations generic_ops ;
static int generic_ops_register ( void )
{
generic_ops . get_variable = efi . get_variable ;
generic_ops . set_variable = efi . set_variable ;
generic_ops . get_next_variable = efi . get_next_variable ;
2013-04-30 11:30:24 +01:00
generic_ops . query_variable_store = efi_query_variable_store ;
2013-02-08 15:37:06 +00:00
return efivars_register ( & generic_efivars , & generic_ops , efi_kobj ) ;
}
static void generic_ops_unregister ( void )
{
efivars_unregister ( & generic_efivars ) ;
}
/*
* We register the efi subsystem with the firmware subsystem and the
* efivars subsystem with the efi subsystem , if the system was booted with
* EFI .
*/
static int __init efisubsys_init ( void )
{
int error ;
if ( ! efi_enabled ( EFI_BOOT ) )
return 0 ;
/* We register the efi directory at /sys/firmware/efi */
efi_kobj = kobject_create_and_add ( " efi " , firmware_kobj ) ;
if ( ! efi_kobj ) {
pr_err ( " efi: Firmware registration failed. \n " ) ;
return - ENOMEM ;
}
error = generic_ops_register ( ) ;
if ( error )
goto err_put ;
error = sysfs_create_group ( efi_kobj , & efi_subsys_attr_group ) ;
if ( error ) {
pr_err ( " efi: Sysfs attribute export failed with error %d. \n " ,
error ) ;
goto err_unregister ;
}
2013-12-20 18:02:18 +08:00
error = efi_runtime_map_init ( efi_kobj ) ;
if ( error )
goto err_remove_group ;
2013-02-08 15:37:06 +00:00
/* and the standard mountpoint for efivarfs */
efivars_kobj = kobject_create_and_add ( " efivars " , efi_kobj ) ;
if ( ! efivars_kobj ) {
pr_err ( " efivars: Subsystem registration failed. \n " ) ;
error = - ENOMEM ;
goto err_remove_group ;
}
return 0 ;
err_remove_group :
sysfs_remove_group ( efi_kobj , & efi_subsys_attr_group ) ;
err_unregister :
generic_ops_unregister ( ) ;
err_put :
kobject_put ( efi_kobj ) ;
return error ;
}
subsys_initcall ( efisubsys_init ) ;
2013-09-05 11:34:54 +01:00
2013-09-05 11:34:55 +01:00
/*
* We can ' t ioremap data in EFI boot services RAM , because we ' ve already mapped
* it as RAM . So , look it up in the existing EFI memory map instead . Only
* callable after efi_enter_virtual_mode and before efi_free_boot_services .
*/
void __iomem * efi_lookup_mapped_addr ( u64 phys_addr )
{
struct efi_memory_map * map ;
void * p ;
map = efi . memmap ;
if ( ! map )
return NULL ;
if ( WARN_ON ( ! map - > map ) )
return NULL ;
for ( p = map - > map ; p < map - > map_end ; p + = map - > desc_size ) {
efi_memory_desc_t * md = p ;
u64 size = md - > num_pages < < EFI_PAGE_SHIFT ;
u64 end = md - > phys_addr + size ;
if ( ! ( md - > attribute & EFI_MEMORY_RUNTIME ) & &
md - > type ! = EFI_BOOT_SERVICES_CODE & &
md - > type ! = EFI_BOOT_SERVICES_DATA )
continue ;
if ( ! md - > virt_addr )
continue ;
if ( phys_addr > = md - > phys_addr & & phys_addr < end ) {
phys_addr + = md - > virt_addr - md - > phys_addr ;
return ( __force void __iomem * ) ( unsigned long ) phys_addr ;
}
}
return NULL ;
}
2013-09-05 11:34:54 +01:00
static __initdata efi_config_table_type_t common_tables [ ] = {
{ ACPI_20_TABLE_GUID , " ACPI 2.0 " , & efi . acpi20 } ,
{ ACPI_TABLE_GUID , " ACPI " , & efi . acpi } ,
{ HCDP_TABLE_GUID , " HCDP " , & efi . hcdp } ,
{ MPS_TABLE_GUID , " MPS " , & efi . mps } ,
{ SAL_SYSTEM_TABLE_GUID , " SALsystab " , & efi . sal_systab } ,
{ SMBIOS_TABLE_GUID , " SMBIOS " , & efi . smbios } ,
{ UGA_IO_PROTOCOL_GUID , " UGA " , & efi . uga } ,
2014-02-13 17:16:36 +09:00
{ NULL_GUID , NULL , NULL } ,
2013-09-05 11:34:54 +01:00
} ;
static __init int match_config_table ( efi_guid_t * guid ,
unsigned long table ,
efi_config_table_type_t * table_types )
{
u8 str [ EFI_VARIABLE_GUID_LEN + 1 ] ;
int i ;
if ( table_types ) {
efi_guid_unparse ( guid , str ) ;
for ( i = 0 ; efi_guidcmp ( table_types [ i ] . guid , NULL_GUID ) ; i + + ) {
efi_guid_unparse ( & table_types [ i ] . guid , str ) ;
if ( ! efi_guidcmp ( * guid , table_types [ i ] . guid ) ) {
* ( table_types [ i ] . ptr ) = table ;
pr_cont ( " %s=0x%lx " ,
table_types [ i ] . name , table ) ;
return 1 ;
}
}
}
return 0 ;
}
int __init efi_config_init ( efi_config_table_type_t * arch_tables )
{
void * config_tables , * tablep ;
int i , sz ;
if ( efi_enabled ( EFI_64BIT ) )
sz = sizeof ( efi_config_table_64_t ) ;
else
sz = sizeof ( efi_config_table_32_t ) ;
/*
* Let ' s see what config tables the firmware passed to us .
*/
config_tables = early_memremap ( efi . systab - > tables ,
efi . systab - > nr_tables * sz ) ;
if ( config_tables = = NULL ) {
pr_err ( " Could not map Configuration table! \n " ) ;
return - ENOMEM ;
}
tablep = config_tables ;
pr_info ( " " ) ;
for ( i = 0 ; i < efi . systab - > nr_tables ; i + + ) {
efi_guid_t guid ;
unsigned long table ;
if ( efi_enabled ( EFI_64BIT ) ) {
u64 table64 ;
guid = ( ( efi_config_table_64_t * ) tablep ) - > guid ;
table64 = ( ( efi_config_table_64_t * ) tablep ) - > table ;
table = table64 ;
# ifndef CONFIG_64BIT
if ( table64 > > 32 ) {
pr_cont ( " \n " ) ;
pr_err ( " Table located above 4GB, disabling EFI. \n " ) ;
2014-06-30 19:52:56 +02:00
early_memunmap ( config_tables ,
2013-09-05 11:34:54 +01:00
efi . systab - > nr_tables * sz ) ;
return - EINVAL ;
}
# endif
} else {
guid = ( ( efi_config_table_32_t * ) tablep ) - > guid ;
table = ( ( efi_config_table_32_t * ) tablep ) - > table ;
}
if ( ! match_config_table ( & guid , table , common_tables ) )
match_config_table ( & guid , table , arch_tables ) ;
tablep + = sz ;
}
pr_cont ( " \n " ) ;
2014-06-30 19:52:56 +02:00
early_memunmap ( config_tables , efi . systab - > nr_tables * sz ) ;
2014-01-15 13:36:33 +00:00
set_bit ( EFI_CONFIG_TABLES , & efi . flags ) ;
2013-09-05 11:34:54 +01:00
return 0 ;
}
2013-12-30 12:12:12 -05:00
2014-07-09 18:39:29 +08:00
# ifdef CONFIG_EFI_VARS_MODULE
static int __init efi_load_efivars ( void )
{
struct platform_device * pdev ;
if ( ! efi_enabled ( EFI_RUNTIME_SERVICES ) )
return 0 ;
pdev = platform_device_register_simple ( " efivars " , 0 , NULL , 0 ) ;
return IS_ERR ( pdev ) ? PTR_ERR ( pdev ) : 0 ;
}
device_initcall ( efi_load_efivars ) ;
# endif
2013-12-30 12:12:12 -05:00
# ifdef CONFIG_EFI_PARAMS_FROM_FDT
# define UEFI_PARAM(name, prop, field) \
{ \
{ name } , \
{ prop } , \
offsetof ( struct efi_fdt_params , field ) , \
FIELD_SIZEOF ( struct efi_fdt_params , field ) \
}
static __initdata struct {
const char name [ 32 ] ;
const char propname [ 32 ] ;
int offset ;
int size ;
} dt_params [ ] = {
UEFI_PARAM ( " System Table " , " linux,uefi-system-table " , system_table ) ,
UEFI_PARAM ( " MemMap Address " , " linux,uefi-mmap-start " , mmap ) ,
UEFI_PARAM ( " MemMap Size " , " linux,uefi-mmap-size " , mmap_size ) ,
UEFI_PARAM ( " MemMap Desc. Size " , " linux,uefi-mmap-desc-size " , desc_size ) ,
UEFI_PARAM ( " MemMap Desc. Version " , " linux,uefi-mmap-desc-ver " , desc_ver )
} ;
struct param_info {
int verbose ;
2014-07-08 16:54:18 +01:00
int found ;
2013-12-30 12:12:12 -05:00
void * params ;
} ;
static int __init fdt_find_uefi_params ( unsigned long node , const char * uname ,
int depth , void * data )
{
struct param_info * info = data ;
2014-06-02 11:31:06 +01:00
const void * prop ;
void * dest ;
2013-12-30 12:12:12 -05:00
u64 val ;
2014-06-02 11:31:06 +01:00
int i , len ;
2013-12-30 12:12:12 -05:00
if ( depth ! = 1 | |
( strcmp ( uname , " chosen " ) ! = 0 & & strcmp ( uname , " chosen@0 " ) ! = 0 ) )
return 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( dt_params ) ; i + + ) {
prop = of_get_flat_dt_prop ( node , dt_params [ i ] . propname , & len ) ;
2014-07-08 16:54:18 +01:00
if ( ! prop )
2013-12-30 12:12:12 -05:00
return 0 ;
dest = info - > params + dt_params [ i ] . offset ;
2014-07-08 16:54:18 +01:00
info - > found + + ;
2013-12-30 12:12:12 -05:00
val = of_read_number ( prop , len / sizeof ( u32 ) ) ;
if ( dt_params [ i ] . size = = sizeof ( u32 ) )
* ( u32 * ) dest = val ;
else
* ( u64 * ) dest = val ;
if ( info - > verbose )
pr_info ( " %s: 0x%0*llx \n " , dt_params [ i ] . name ,
dt_params [ i ] . size * 2 , val ) ;
}
return 1 ;
}
int __init efi_get_fdt_params ( struct efi_fdt_params * params , int verbose )
{
struct param_info info ;
2014-07-08 16:54:18 +01:00
int ret ;
pr_info ( " Getting EFI parameters from FDT: \n " ) ;
2013-12-30 12:12:12 -05:00
info . verbose = verbose ;
2014-07-08 16:54:18 +01:00
info . found = 0 ;
2013-12-30 12:12:12 -05:00
info . params = params ;
2014-07-08 16:54:18 +01:00
ret = of_scan_flat_dt ( fdt_find_uefi_params , & info ) ;
if ( ! info . found )
pr_info ( " UEFI not found. \n " ) ;
else if ( ! ret )
pr_err ( " Can't find '%s' in device tree! \n " ,
dt_params [ info . found ] . name ) ;
return ret ;
2013-12-30 12:12:12 -05:00
}
# endif /* CONFIG_EFI_PARAMS_FROM_FDT */