2010-07-15 06:46:30 +04:00
/*
* sysfs . c - ACPI sysfs interface to userspace .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/moduleparam.h>
2013-12-03 04:49:16 +04:00
# include <linux/acpi.h>
2010-07-15 06:46:30 +04:00
2013-03-04 02:08:16 +04:00
# include "internal.h"
2010-07-15 06:46:30 +04:00
# define _COMPONENT ACPI_SYSTEM_COMPONENT
ACPI_MODULE_NAME ( " sysfs " ) ;
# ifdef CONFIG_ACPI_DEBUG
/*
* ACPI debug sysfs I / F , including :
* / sys / modules / acpi / parameters / debug_layer
* / sys / modules / acpi / parameters / debug_level
* / sys / modules / acpi / parameters / trace_method_name
* / sys / modules / acpi / parameters / trace_state
* / sys / modules / acpi / parameters / trace_debug_layer
* / sys / modules / acpi / parameters / trace_debug_level
*/
struct acpi_dlayer {
const char * name ;
unsigned long value ;
} ;
struct acpi_dlevel {
const char * name ;
unsigned long value ;
} ;
# define ACPI_DEBUG_INIT(v) { .name = #v, .value = v }
static const struct acpi_dlayer acpi_debug_layers [ ] = {
ACPI_DEBUG_INIT ( ACPI_UTILITIES ) ,
ACPI_DEBUG_INIT ( ACPI_HARDWARE ) ,
ACPI_DEBUG_INIT ( ACPI_EVENTS ) ,
ACPI_DEBUG_INIT ( ACPI_TABLES ) ,
ACPI_DEBUG_INIT ( ACPI_NAMESPACE ) ,
ACPI_DEBUG_INIT ( ACPI_PARSER ) ,
ACPI_DEBUG_INIT ( ACPI_DISPATCHER ) ,
ACPI_DEBUG_INIT ( ACPI_EXECUTER ) ,
ACPI_DEBUG_INIT ( ACPI_RESOURCES ) ,
ACPI_DEBUG_INIT ( ACPI_CA_DEBUGGER ) ,
ACPI_DEBUG_INIT ( ACPI_OS_SERVICES ) ,
ACPI_DEBUG_INIT ( ACPI_CA_DISASSEMBLER ) ,
ACPI_DEBUG_INIT ( ACPI_COMPILER ) ,
ACPI_DEBUG_INIT ( ACPI_TOOLS ) ,
ACPI_DEBUG_INIT ( ACPI_BUS_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_AC_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_BATTERY_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_BUTTON_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_SBS_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_FAN_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_PCI_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_POWER_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_CONTAINER_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_SYSTEM_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_THERMAL_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_MEMORY_DEVICE_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_VIDEO_COMPONENT ) ,
ACPI_DEBUG_INIT ( ACPI_PROCESSOR_COMPONENT ) ,
} ;
static const struct acpi_dlevel acpi_debug_levels [ ] = {
ACPI_DEBUG_INIT ( ACPI_LV_INIT ) ,
ACPI_DEBUG_INIT ( ACPI_LV_DEBUG_OBJECT ) ,
ACPI_DEBUG_INIT ( ACPI_LV_INFO ) ,
2015-08-05 11:23:43 +03:00
ACPI_DEBUG_INIT ( ACPI_LV_REPAIR ) ,
2015-08-05 11:23:51 +03:00
ACPI_DEBUG_INIT ( ACPI_LV_TRACE_POINT ) ,
2010-07-15 06:46:30 +04:00
ACPI_DEBUG_INIT ( ACPI_LV_INIT_NAMES ) ,
ACPI_DEBUG_INIT ( ACPI_LV_PARSE ) ,
ACPI_DEBUG_INIT ( ACPI_LV_LOAD ) ,
ACPI_DEBUG_INIT ( ACPI_LV_DISPATCH ) ,
ACPI_DEBUG_INIT ( ACPI_LV_EXEC ) ,
ACPI_DEBUG_INIT ( ACPI_LV_NAMES ) ,
ACPI_DEBUG_INIT ( ACPI_LV_OPREGION ) ,
ACPI_DEBUG_INIT ( ACPI_LV_BFIELD ) ,
ACPI_DEBUG_INIT ( ACPI_LV_TABLES ) ,
ACPI_DEBUG_INIT ( ACPI_LV_VALUES ) ,
ACPI_DEBUG_INIT ( ACPI_LV_OBJECTS ) ,
ACPI_DEBUG_INIT ( ACPI_LV_RESOURCES ) ,
ACPI_DEBUG_INIT ( ACPI_LV_USER_REQUESTS ) ,
ACPI_DEBUG_INIT ( ACPI_LV_PACKAGE ) ,
ACPI_DEBUG_INIT ( ACPI_LV_ALLOCATIONS ) ,
ACPI_DEBUG_INIT ( ACPI_LV_FUNCTIONS ) ,
ACPI_DEBUG_INIT ( ACPI_LV_OPTIMIZATIONS ) ,
ACPI_DEBUG_INIT ( ACPI_LV_MUTEX ) ,
ACPI_DEBUG_INIT ( ACPI_LV_THREADS ) ,
ACPI_DEBUG_INIT ( ACPI_LV_IO ) ,
ACPI_DEBUG_INIT ( ACPI_LV_INTERRUPTS ) ,
ACPI_DEBUG_INIT ( ACPI_LV_AML_DISASSEMBLE ) ,
ACPI_DEBUG_INIT ( ACPI_LV_VERBOSE_INFO ) ,
ACPI_DEBUG_INIT ( ACPI_LV_FULL_TABLES ) ,
ACPI_DEBUG_INIT ( ACPI_LV_EVENTS ) ,
} ;
2010-08-30 10:08:02 +04:00
static int param_get_debug_layer ( char * buffer , const struct kernel_param * kp )
2010-07-15 06:46:30 +04:00
{
int result = 0 ;
int i ;
result = sprintf ( buffer , " %-25s \t Hex SET \n " , " Description " ) ;
for ( i = 0 ; i < ARRAY_SIZE ( acpi_debug_layers ) ; i + + ) {
result + = sprintf ( buffer + result , " %-25s \t 0x%08lX [%c] \n " ,
acpi_debug_layers [ i ] . name ,
acpi_debug_layers [ i ] . value ,
( acpi_dbg_layer & acpi_debug_layers [ i ] . value )
? ' * ' : ' ' ) ;
}
result + =
sprintf ( buffer + result , " %-25s \t 0x%08X [%c] \n " , " ACPI_ALL_DRIVERS " ,
ACPI_ALL_DRIVERS ,
( acpi_dbg_layer & ACPI_ALL_DRIVERS ) = =
ACPI_ALL_DRIVERS ? ' * ' : ( acpi_dbg_layer & ACPI_ALL_DRIVERS )
= = 0 ? ' ' : ' - ' ) ;
result + =
sprintf ( buffer + result ,
" -- \n debug_layer = 0x%08X ( * = enabled) \n " ,
acpi_dbg_layer ) ;
return result ;
}
2010-08-30 10:08:02 +04:00
static int param_get_debug_level ( char * buffer , const struct kernel_param * kp )
2010-07-15 06:46:30 +04:00
{
int result = 0 ;
int i ;
result = sprintf ( buffer , " %-25s \t Hex SET \n " , " Description " ) ;
for ( i = 0 ; i < ARRAY_SIZE ( acpi_debug_levels ) ; i + + ) {
result + = sprintf ( buffer + result , " %-25s \t 0x%08lX [%c] \n " ,
acpi_debug_levels [ i ] . name ,
acpi_debug_levels [ i ] . value ,
( acpi_dbg_level & acpi_debug_levels [ i ] . value )
? ' * ' : ' ' ) ;
}
result + =
sprintf ( buffer + result , " -- \n debug_level = 0x%08X (* = enabled) \n " ,
acpi_dbg_level ) ;
return result ;
}
2011-06-25 21:07:52 +04:00
static const struct kernel_param_ops param_ops_debug_layer = {
2010-08-30 10:08:02 +04:00
. set = param_set_uint ,
. get = param_get_debug_layer ,
} ;
2011-06-25 21:07:52 +04:00
static const struct kernel_param_ops param_ops_debug_level = {
2010-08-30 10:08:02 +04:00
. set = param_set_uint ,
. get = param_get_debug_level ,
} ;
module_param_cb ( debug_layer , & param_ops_debug_layer , & acpi_dbg_layer , 0644 ) ;
module_param_cb ( debug_level , & param_ops_debug_level , & acpi_dbg_level , 0644 ) ;
2010-07-15 06:46:30 +04:00
2015-08-05 11:23:57 +03:00
static char trace_method_name [ 1024 ] ;
2010-07-15 06:46:30 +04:00
2015-08-05 11:23:51 +03:00
int param_set_trace_method_name ( const char * val , const struct kernel_param * kp )
2010-07-15 06:46:30 +04:00
{
2015-08-05 11:23:51 +03:00
u32 saved_flags = 0 ;
2015-08-05 11:23:57 +03:00
bool is_abs_path = true ;
2010-07-15 06:46:30 +04:00
2015-08-05 11:23:57 +03:00
if ( * val ! = ' \\ ' )
is_abs_path = false ;
if ( ( is_abs_path & & strlen ( val ) > 1023 ) | |
( ! is_abs_path & & strlen ( val ) > 1022 ) ) {
2015-08-05 11:23:51 +03:00
pr_err ( " %s: string parameter too long \n " , kp - > name ) ;
return - ENOSPC ;
2010-07-15 06:46:30 +04:00
}
2015-08-05 11:23:51 +03:00
/*
* It ' s not safe to update acpi_gbl_trace_method_name without
* having the tracer stopped , so we save the original tracer
* state and disable it .
*/
saved_flags = acpi_gbl_trace_flags ;
( void ) acpi_debug_trace ( NULL ,
acpi_gbl_trace_dbg_level ,
acpi_gbl_trace_dbg_layer ,
0 ) ;
/* This is a hack. We can't kmalloc in early boot. */
2015-08-05 11:23:57 +03:00
if ( is_abs_path )
strcpy ( trace_method_name , val ) ;
else {
trace_method_name [ 0 ] = ' \\ ' ;
strcpy ( trace_method_name + 1 , val ) ;
}
2015-08-05 11:23:51 +03:00
/* Restore the original tracer state */
( void ) acpi_debug_trace ( trace_method_name ,
acpi_gbl_trace_dbg_level ,
acpi_gbl_trace_dbg_layer ,
saved_flags ) ;
2010-07-15 06:46:30 +04:00
2015-08-05 11:23:51 +03:00
return 0 ;
}
2010-07-15 06:46:30 +04:00
2015-08-05 11:23:51 +03:00
static int param_get_trace_method_name ( char * buffer , const struct kernel_param * kp )
{
return scnprintf ( buffer , PAGE_SIZE , " %s " , acpi_gbl_trace_method_name ) ;
}
static const struct kernel_param_ops param_ops_trace_method = {
. set = param_set_trace_method_name ,
. get = param_get_trace_method_name ,
} ;
static const struct kernel_param_ops param_ops_trace_attrib = {
. set = param_set_uint ,
. get = param_get_uint ,
} ;
module_param_cb ( trace_method_name , & param_ops_trace_method , & trace_method_name , 0644 ) ;
module_param_cb ( trace_debug_layer , & param_ops_trace_attrib , & acpi_gbl_trace_dbg_layer , 0644 ) ;
module_param_cb ( trace_debug_level , & param_ops_trace_attrib , & acpi_gbl_trace_dbg_level , 0644 ) ;
static int param_set_trace_state ( const char * val , struct kernel_param * kp )
{
acpi_status status ;
const char * method = trace_method_name ;
u32 flags = 0 ;
/* So "xxx-once" comparison should go prior than "xxx" comparison */
# define acpi_compare_param(val, key) \
strncmp ( ( val ) , ( key ) , sizeof ( key ) - 1 )
if ( ! acpi_compare_param ( val , " enable " ) ) {
method = NULL ;
flags = ACPI_TRACE_ENABLED ;
} else if ( ! acpi_compare_param ( val , " disable " ) )
method = NULL ;
else if ( ! acpi_compare_param ( val , " method-once " ) )
flags = ACPI_TRACE_ENABLED | ACPI_TRACE_ONESHOT ;
else if ( ! acpi_compare_param ( val , " method " ) )
flags = ACPI_TRACE_ENABLED ;
else if ( ! acpi_compare_param ( val , " opcode-once " ) )
flags = ACPI_TRACE_ENABLED | ACPI_TRACE_ONESHOT | ACPI_TRACE_OPCODE ;
else if ( ! acpi_compare_param ( val , " opcode " ) )
flags = ACPI_TRACE_ENABLED | ACPI_TRACE_OPCODE ;
else
return - EINVAL ;
status = acpi_debug_trace ( method ,
acpi_gbl_trace_dbg_level ,
acpi_gbl_trace_dbg_layer ,
flags ) ;
if ( ACPI_FAILURE ( status ) )
return - EBUSY ;
return 0 ;
2010-07-15 06:46:30 +04:00
}
static int param_get_trace_state ( char * buffer , struct kernel_param * kp )
{
2015-08-05 11:23:51 +03:00
if ( ! ( acpi_gbl_trace_flags & ACPI_TRACE_ENABLED ) )
2010-07-15 06:46:30 +04:00
return sprintf ( buffer , " disable " ) ;
else {
2015-08-05 11:23:51 +03:00
if ( acpi_gbl_trace_method_name ) {
if ( acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT )
return sprintf ( buffer , " method-once " ) ;
else
return sprintf ( buffer , " method " ) ;
} else
2010-07-15 06:46:30 +04:00
return sprintf ( buffer , " enable " ) ;
}
return 0 ;
}
module_param_call ( trace_state , param_set_trace_state , param_get_trace_state ,
NULL , 0644 ) ;
# endif /* CONFIG_ACPI_DEBUG */
2011-05-26 14:26:23 +04:00
/* /sys/modules/acpi/parameters/aml_debug_output */
module_param_named ( aml_debug_output , acpi_gbl_enable_aml_debug_object ,
2014-01-24 04:49:51 +04:00
byte , 0644 ) ;
2011-05-26 14:26:23 +04:00
MODULE_PARM_DESC ( aml_debug_output ,
" To enable/disable the ACPI Debug Object output. " ) ;
2010-07-15 06:46:30 +04:00
/* /sys/module/acpi/parameters/acpica_version */
static int param_get_acpica_version ( char * buffer , struct kernel_param * kp )
{
int result ;
result = sprintf ( buffer , " %x " , ACPI_CA_VERSION ) ;
return result ;
}
module_param_call ( acpica_version , NULL , param_get_acpica_version , NULL , 0444 ) ;
/*
* ACPI table sysfs I / F :
* / sys / firmware / acpi / tables /
* / sys / firmware / acpi / tables / dynamic /
*/
static LIST_HEAD ( acpi_table_attr_list ) ;
static struct kobject * tables_kobj ;
static struct kobject * dynamic_tables_kobj ;
2013-03-04 02:08:16 +04:00
static struct kobject * hotplug_kobj ;
2010-07-15 06:46:30 +04:00
2016-09-13 12:48:34 +03:00
# define ACPI_MAX_TABLE_INSTANCES 999
# define ACPI_INST_SIZE 4 /* including trailing 0 */
2010-07-15 06:46:30 +04:00
struct acpi_table_attr {
struct bin_attribute attr ;
2016-09-13 12:48:34 +03:00
char name [ ACPI_NAME_SIZE ] ;
2010-07-15 06:46:30 +04:00
int instance ;
2016-09-13 12:48:34 +03:00
char filename [ ACPI_NAME_SIZE + ACPI_INST_SIZE ] ;
2010-07-15 06:46:30 +04:00
struct list_head node ;
} ;
static ssize_t acpi_table_show ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * bin_attr , char * buf ,
loff_t offset , size_t count )
{
struct acpi_table_attr * table_attr =
container_of ( bin_attr , struct acpi_table_attr , attr ) ;
struct acpi_table_header * table_header = NULL ;
acpi_status status ;
2016-09-13 12:48:34 +03:00
status = acpi_get_table ( table_attr - > name , table_attr - > instance ,
& table_header ) ;
2010-07-15 06:46:30 +04:00
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
return memory_read_from_buffer ( buf , count , & offset ,
table_header , table_header - > length ) ;
}
2016-09-13 12:48:34 +03:00
static int acpi_table_attr_init ( struct kobject * tables_obj ,
struct acpi_table_attr * table_attr ,
struct acpi_table_header * table_header )
2010-07-15 06:46:30 +04:00
{
struct acpi_table_header * header = NULL ;
struct acpi_table_attr * attr = NULL ;
2016-09-13 12:48:34 +03:00
char instance_str [ ACPI_INST_SIZE ] ;
2010-07-15 06:46:30 +04:00
sysfs_attr_init ( & table_attr - > attr . attr ) ;
2016-09-13 12:48:34 +03:00
ACPI_MOVE_NAME ( table_attr - > name , table_header - > signature ) ;
2010-07-15 06:46:30 +04:00
list_for_each_entry ( attr , & acpi_table_attr_list , node ) {
2016-09-13 12:48:34 +03:00
if ( ACPI_COMPARE_NAME ( table_attr - > name , attr - > name ) )
2010-07-15 06:46:30 +04:00
if ( table_attr - > instance < attr - > instance )
table_attr - > instance = attr - > instance ;
}
table_attr - > instance + + ;
2016-09-13 12:48:34 +03:00
if ( table_attr - > instance > ACPI_MAX_TABLE_INSTANCES ) {
pr_warn ( " %4.4s: too many table instances \n " ,
table_attr - > name ) ;
return - ERANGE ;
}
2010-07-15 06:46:30 +04:00
2016-09-13 12:48:34 +03:00
ACPI_MOVE_NAME ( table_attr - > filename , table_header - > signature ) ;
table_attr - > filename [ ACPI_NAME_SIZE ] = ' \0 ' ;
2010-07-15 06:46:30 +04:00
if ( table_attr - > instance > 1 | | ( table_attr - > instance = = 1 & &
! acpi_get_table
2016-09-13 12:48:34 +03:00
( table_header - > signature , 2 , & header ) ) ) {
snprintf ( instance_str , sizeof ( instance_str ) , " %u " ,
table_attr - > instance ) ;
strcat ( table_attr - > filename , instance_str ) ;
}
2010-07-15 06:46:30 +04:00
2013-11-19 12:54:47 +04:00
table_attr - > attr . size = table_header - > length ;
2010-07-15 06:46:30 +04:00
table_attr - > attr . read = acpi_table_show ;
2016-09-13 12:48:34 +03:00
table_attr - > attr . attr . name = table_attr - > filename ;
2010-07-15 06:46:30 +04:00
table_attr - > attr . attr . mode = 0400 ;
2016-09-13 12:48:34 +03:00
return sysfs_create_bin_file ( tables_obj , & table_attr - > attr ) ;
2010-07-15 06:46:30 +04:00
}
2016-07-08 19:13:09 +03:00
acpi_status acpi_sysfs_table_handler ( u32 event , void * table , void * context )
2010-07-15 06:46:30 +04:00
{
struct acpi_table_attr * table_attr ;
switch ( event ) {
ACPI / sysfs: Fix an issue for LoadTable opcode
OEM tables can be installed via RSDT/XSDT, in this case, they have already
been created under /sys/firmware/acpi/tables.
For this kind of tables, normally LoadTable opcode will be executed to load
them. If LoadTable opcode is executed after acpi_sysfs_init(),
acpi_sysfs_table_handler() will be invoked, thus a redundant table file
will be created under /sys/firmware/acpi/tables/dynamic. Then running
"acpidump" on such platform results in an error, complaining blank empty
table (see Link 1 below).
The bug can be reproduced by customizing an OEM1 table, allowing it to be
overridden via 'table_sigs' (drivers/acpi/tables.c), adding the following
code to the customized DSDT to load it:
Name (OEMH, Zero)
Name (OEMF, One)
If (LEqual (OEMF, One)) {
Store (LoadTable ("OEM1", "Intel", "Test"), OEMH)
Store (Zero, OEMF)
}
In order to make sure that the OEM1 table is installed after
acpi_sysfs_init(), acpi_sysfs_init() can be moved before invoking
acpi_load_tables(). Then the following command execution result can be
seen:
# acpidump > acpidump.txt
Could not read table header: /sysfs/firmware/acpi/tables/dynamic/OEM12
Could not get ACPI table at index 17, AE_BAD_HEADER
Link: https://bugzilla.kernel.org/show_bug.cgi?id=150841 # [1]
Link: https://github.com/acpica/acpica/commit/ed6a5fbc
Reported-by: Jason Voelz <jason.voelz@intel.com>
Reported-by: Francisco Leoner <francisco.j.lenoer.soto@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2016-09-13 12:48:27 +03:00
case ACPI_TABLE_EVENT_INSTALL :
2010-07-15 06:46:30 +04:00
table_attr =
kzalloc ( sizeof ( struct acpi_table_attr ) , GFP_KERNEL ) ;
if ( ! table_attr )
return AE_NO_MEMORY ;
2016-09-13 12:48:34 +03:00
if ( acpi_table_attr_init ( dynamic_tables_kobj ,
table_attr , table ) ) {
2010-07-15 06:46:30 +04:00
kfree ( table_attr ) ;
return AE_ERROR ;
2016-09-13 12:48:34 +03:00
}
list_add_tail ( & table_attr - > node , & acpi_table_attr_list ) ;
2010-07-15 06:46:30 +04:00
break ;
ACPI / sysfs: Fix an issue for LoadTable opcode
OEM tables can be installed via RSDT/XSDT, in this case, they have already
been created under /sys/firmware/acpi/tables.
For this kind of tables, normally LoadTable opcode will be executed to load
them. If LoadTable opcode is executed after acpi_sysfs_init(),
acpi_sysfs_table_handler() will be invoked, thus a redundant table file
will be created under /sys/firmware/acpi/tables/dynamic. Then running
"acpidump" on such platform results in an error, complaining blank empty
table (see Link 1 below).
The bug can be reproduced by customizing an OEM1 table, allowing it to be
overridden via 'table_sigs' (drivers/acpi/tables.c), adding the following
code to the customized DSDT to load it:
Name (OEMH, Zero)
Name (OEMF, One)
If (LEqual (OEMF, One)) {
Store (LoadTable ("OEM1", "Intel", "Test"), OEMH)
Store (Zero, OEMF)
}
In order to make sure that the OEM1 table is installed after
acpi_sysfs_init(), acpi_sysfs_init() can be moved before invoking
acpi_load_tables(). Then the following command execution result can be
seen:
# acpidump > acpidump.txt
Could not read table header: /sysfs/firmware/acpi/tables/dynamic/OEM12
Could not get ACPI table at index 17, AE_BAD_HEADER
Link: https://bugzilla.kernel.org/show_bug.cgi?id=150841 # [1]
Link: https://github.com/acpica/acpica/commit/ed6a5fbc
Reported-by: Jason Voelz <jason.voelz@intel.com>
Reported-by: Francisco Leoner <francisco.j.lenoer.soto@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2016-09-13 12:48:27 +03:00
case ACPI_TABLE_EVENT_LOAD :
2010-07-15 06:46:30 +04:00
case ACPI_TABLE_EVENT_UNLOAD :
ACPI / sysfs: Fix an issue for LoadTable opcode
OEM tables can be installed via RSDT/XSDT, in this case, they have already
been created under /sys/firmware/acpi/tables.
For this kind of tables, normally LoadTable opcode will be executed to load
them. If LoadTable opcode is executed after acpi_sysfs_init(),
acpi_sysfs_table_handler() will be invoked, thus a redundant table file
will be created under /sys/firmware/acpi/tables/dynamic. Then running
"acpidump" on such platform results in an error, complaining blank empty
table (see Link 1 below).
The bug can be reproduced by customizing an OEM1 table, allowing it to be
overridden via 'table_sigs' (drivers/acpi/tables.c), adding the following
code to the customized DSDT to load it:
Name (OEMH, Zero)
Name (OEMF, One)
If (LEqual (OEMF, One)) {
Store (LoadTable ("OEM1", "Intel", "Test"), OEMH)
Store (Zero, OEMF)
}
In order to make sure that the OEM1 table is installed after
acpi_sysfs_init(), acpi_sysfs_init() can be moved before invoking
acpi_load_tables(). Then the following command execution result can be
seen:
# acpidump > acpidump.txt
Could not read table header: /sysfs/firmware/acpi/tables/dynamic/OEM12
Could not get ACPI table at index 17, AE_BAD_HEADER
Link: https://bugzilla.kernel.org/show_bug.cgi?id=150841 # [1]
Link: https://github.com/acpica/acpica/commit/ed6a5fbc
Reported-by: Jason Voelz <jason.voelz@intel.com>
Reported-by: Francisco Leoner <francisco.j.lenoer.soto@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2016-09-13 12:48:27 +03:00
case ACPI_TABLE_EVENT_UNINSTALL :
2010-07-15 06:46:30 +04:00
/*
* we do not need to do anything right now
* because the table is not deleted from the
* global table list when unloading it .
*/
break ;
default :
return AE_BAD_PARAMETER ;
}
return AE_OK ;
}
static int acpi_tables_sysfs_init ( void )
{
struct acpi_table_attr * table_attr ;
struct acpi_table_header * table_header = NULL ;
2013-11-21 14:20:23 +04:00
int table_index ;
acpi_status status ;
int ret ;
2010-07-15 06:46:30 +04:00
tables_kobj = kobject_create_and_add ( " tables " , acpi_kobj ) ;
if ( ! tables_kobj )
goto err ;
dynamic_tables_kobj = kobject_create_and_add ( " dynamic " , tables_kobj ) ;
if ( ! dynamic_tables_kobj )
goto err_dynamic_tables ;
2013-11-21 14:20:23 +04:00
for ( table_index = 0 ; ; table_index + + ) {
status = acpi_get_table_by_index ( table_index , & table_header ) ;
if ( status = = AE_BAD_PARAMETER )
break ;
if ( ACPI_FAILURE ( status ) )
continue ;
table_attr = kzalloc ( sizeof ( * table_attr ) , GFP_KERNEL ) ;
if ( ! table_attr )
return - ENOMEM ;
2016-09-13 12:48:34 +03:00
ret = acpi_table_attr_init ( tables_kobj ,
table_attr , table_header ) ;
2013-11-21 14:20:23 +04:00
if ( ret ) {
kfree ( table_attr ) ;
return ret ;
2010-07-15 06:46:30 +04:00
}
2013-11-21 14:20:23 +04:00
list_add_tail ( & table_attr - > node , & acpi_table_attr_list ) ;
}
2010-07-15 06:46:30 +04:00
kobject_uevent ( tables_kobj , KOBJ_ADD ) ;
kobject_uevent ( dynamic_tables_kobj , KOBJ_ADD ) ;
2016-07-08 19:13:09 +03:00
return 0 ;
2010-07-15 06:46:30 +04:00
err_dynamic_tables :
kobject_put ( tables_kobj ) ;
err :
return - ENOMEM ;
}
/*
* Detailed ACPI IRQ counters :
* / sys / firmware / acpi / interrupts /
*/
u32 acpi_irq_handled ;
u32 acpi_irq_not_handled ;
# define COUNT_GPE 0
# define COUNT_SCI 1 /* acpi_irq_handled */
# define COUNT_SCI_NOT 2 /* acpi_irq_not_handled */
# define COUNT_ERROR 3 /* other */
# define NUM_COUNTERS_EXTRA 4
struct event_counter {
u32 count ;
u32 flags ;
} ;
static struct event_counter * all_counters ;
static u32 num_gpes ;
static u32 num_counters ;
static struct attribute * * all_attrs ;
static u32 acpi_gpe_count ;
static struct attribute_group interrupt_stats_attr_group = {
. name = " interrupts " ,
} ;
static struct kobj_attribute * counter_attrs ;
static void delete_gpe_attr_array ( void )
{
struct event_counter * tmp = all_counters ;
all_counters = NULL ;
kfree ( tmp ) ;
if ( counter_attrs ) {
int i ;
for ( i = 0 ; i < num_gpes ; i + + )
kfree ( counter_attrs [ i ] . attr . name ) ;
kfree ( counter_attrs ) ;
}
kfree ( all_attrs ) ;
return ;
}
2010-12-13 08:39:26 +03:00
static void gpe_count ( u32 gpe_number )
2010-07-15 06:46:30 +04:00
{
acpi_gpe_count + + ;
if ( ! all_counters )
return ;
if ( gpe_number < num_gpes )
all_counters [ gpe_number ] . count + + ;
else
all_counters [ num_gpes + ACPI_NUM_FIXED_EVENTS +
COUNT_ERROR ] . count + + ;
return ;
}
2010-12-13 08:39:26 +03:00
static void fixed_event_count ( u32 event_number )
2010-07-15 06:46:30 +04:00
{
if ( ! all_counters )
return ;
if ( event_number < ACPI_NUM_FIXED_EVENTS )
all_counters [ num_gpes + event_number ] . count + + ;
else
all_counters [ num_gpes + ACPI_NUM_FIXED_EVENTS +
COUNT_ERROR ] . count + + ;
return ;
}
2012-10-31 06:25:36 +04:00
static void acpi_global_event_handler ( u32 event_type , acpi_handle device ,
2010-12-13 08:39:26 +03:00
u32 event_number , void * context )
{
if ( event_type = = ACPI_EVENT_TYPE_GPE )
gpe_count ( event_number ) ;
if ( event_type = = ACPI_EVENT_TYPE_FIXED )
fixed_event_count ( event_number ) ;
}
2010-07-15 06:46:30 +04:00
static int get_status ( u32 index , acpi_event_status * status ,
acpi_handle * handle )
{
2016-05-05 16:23:04 +03:00
int result ;
2010-07-15 06:46:30 +04:00
if ( index > = num_gpes + ACPI_NUM_FIXED_EVENTS )
2016-05-05 16:23:04 +03:00
return - EINVAL ;
2010-07-15 06:46:30 +04:00
if ( index < num_gpes ) {
result = acpi_get_gpe_device ( index , handle ) ;
if ( result ) {
ACPI_EXCEPTION ( ( AE_INFO , AE_NOT_FOUND ,
2012-11-29 15:53:14 +04:00
" Invalid GPE 0x%x " , index ) ) ;
2016-05-05 16:23:04 +03:00
return result ;
2010-07-15 06:46:30 +04:00
}
result = acpi_get_gpe_status ( * handle , index , status ) ;
} else if ( index < ( num_gpes + ACPI_NUM_FIXED_EVENTS ) )
result = acpi_get_event_status ( index - num_gpes , status ) ;
return result ;
}
static ssize_t counter_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
int index = attr - counter_attrs ;
int size ;
acpi_handle handle ;
acpi_event_status status ;
int result = 0 ;
all_counters [ num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI ] . count =
acpi_irq_handled ;
all_counters [ num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT ] . count =
acpi_irq_not_handled ;
all_counters [ num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE ] . count =
acpi_gpe_count ;
2015-03-04 13:48:35 +03:00
size = sprintf ( buf , " %8u " , all_counters [ index ] . count ) ;
2010-07-15 06:46:30 +04:00
/* "gpe_all" or "sci" */
if ( index > = num_gpes + ACPI_NUM_FIXED_EVENTS )
goto end ;
result = get_status ( index , & status , & handle ) ;
if ( result )
goto end ;
2016-08-04 11:43:45 +03:00
if ( status & ACPI_EVENT_FLAG_ENABLE_SET )
size + = sprintf ( buf + size , " EN " ) ;
else
size + = sprintf ( buf + size , " " ) ;
if ( status & ACPI_EVENT_FLAG_STATUS_SET )
size + = sprintf ( buf + size , " STS " ) ;
else
size + = sprintf ( buf + size , " " ) ;
2014-10-10 06:40:05 +04:00
if ( ! ( status & ACPI_EVENT_FLAG_HAS_HANDLER ) )
2016-08-04 11:43:45 +03:00
size + = sprintf ( buf + size , " invalid " ) ;
2010-07-15 06:46:30 +04:00
else if ( status & ACPI_EVENT_FLAG_ENABLED )
2016-08-04 11:43:45 +03:00
size + = sprintf ( buf + size , " enabled " ) ;
2010-07-15 06:46:30 +04:00
else if ( status & ACPI_EVENT_FLAG_WAKE_ENABLED )
2016-08-04 11:43:45 +03:00
size + = sprintf ( buf + size , " wake_enabled " ) ;
2010-07-15 06:46:30 +04:00
else
2016-08-04 11:43:45 +03:00
size + = sprintf ( buf + size , " disabled " ) ;
if ( status & ACPI_EVENT_FLAG_MASKED )
size + = sprintf ( buf + size , " masked " ) ;
else
size + = sprintf ( buf + size , " unmasked " ) ;
2010-07-15 06:46:30 +04:00
end :
size + = sprintf ( buf + size , " \n " ) ;
return result ? result : size ;
}
/*
* counter_set ( ) sets the specified counter .
* setting the total " sci " file to any value clears all counters .
* enable / disable / clear a gpe / fixed event in user space .
*/
static ssize_t counter_set ( struct kobject * kobj ,
struct kobj_attribute * attr , const char * buf ,
size_t size )
{
int index = attr - counter_attrs ;
acpi_event_status status ;
acpi_handle handle ;
int result = 0 ;
2013-09-12 11:32:05 +04:00
unsigned long tmp ;
2010-07-15 06:46:30 +04:00
if ( index = = num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI ) {
int i ;
for ( i = 0 ; i < num_counters ; + + i )
all_counters [ i ] . count = 0 ;
acpi_gpe_count = 0 ;
acpi_irq_handled = 0 ;
acpi_irq_not_handled = 0 ;
goto end ;
}
/* show the event status for both GPEs and Fixed Events */
result = get_status ( index , & status , & handle ) ;
if ( result )
goto end ;
2014-10-10 06:40:05 +04:00
if ( ! ( status & ACPI_EVENT_FLAG_HAS_HANDLER ) ) {
2010-07-15 06:46:30 +04:00
printk ( KERN_WARNING PREFIX
" Can not change Invalid GPE/Fixed Event status \n " ) ;
return - EINVAL ;
}
if ( index < num_gpes ) {
if ( ! strcmp ( buf , " disable \n " ) & &
( status & ACPI_EVENT_FLAG_ENABLED ) )
result = acpi_disable_gpe ( handle , index ) ;
else if ( ! strcmp ( buf , " enable \n " ) & &
! ( status & ACPI_EVENT_FLAG_ENABLED ) )
result = acpi_enable_gpe ( handle , index ) ;
else if ( ! strcmp ( buf , " clear \n " ) & &
2016-08-04 11:43:45 +03:00
( status & ACPI_EVENT_FLAG_STATUS_SET ) )
2010-07-15 06:46:30 +04:00
result = acpi_clear_gpe ( handle , index ) ;
2016-08-04 11:43:45 +03:00
else if ( ! strcmp ( buf , " mask \n " ) )
result = acpi_mask_gpe ( handle , index , TRUE ) ;
else if ( ! strcmp ( buf , " unmask \n " ) )
result = acpi_mask_gpe ( handle , index , FALSE ) ;
2013-09-12 11:32:05 +04:00
else if ( ! kstrtoul ( buf , 0 , & tmp ) )
all_counters [ index ] . count = tmp ;
2010-07-15 06:46:30 +04:00
else
2013-09-12 11:32:05 +04:00
result = - EINVAL ;
2010-07-15 06:46:30 +04:00
} else if ( index < num_gpes + ACPI_NUM_FIXED_EVENTS ) {
int event = index - num_gpes ;
if ( ! strcmp ( buf , " disable \n " ) & &
2016-08-04 11:43:45 +03:00
( status & ACPI_EVENT_FLAG_ENABLE_SET ) )
2010-07-15 06:46:30 +04:00
result = acpi_disable_event ( event , ACPI_NOT_ISR ) ;
else if ( ! strcmp ( buf , " enable \n " ) & &
2016-08-04 11:43:45 +03:00
! ( status & ACPI_EVENT_FLAG_ENABLE_SET ) )
2010-07-15 06:46:30 +04:00
result = acpi_enable_event ( event , ACPI_NOT_ISR ) ;
else if ( ! strcmp ( buf , " clear \n " ) & &
2016-08-04 11:43:45 +03:00
( status & ACPI_EVENT_FLAG_STATUS_SET ) )
2010-07-15 06:46:30 +04:00
result = acpi_clear_event ( event ) ;
2013-09-12 11:32:05 +04:00
else if ( ! kstrtoul ( buf , 0 , & tmp ) )
all_counters [ index ] . count = tmp ;
2010-07-15 06:46:30 +04:00
else
2013-09-12 11:32:05 +04:00
result = - EINVAL ;
2010-07-15 06:46:30 +04:00
} else
all_counters [ index ] . count = strtoul ( buf , NULL , 0 ) ;
if ( ACPI_FAILURE ( result ) )
result = - EINVAL ;
end :
return result ? result : size ;
}
void acpi_irq_stats_init ( void )
{
2010-12-13 08:39:26 +03:00
acpi_status status ;
2010-07-15 06:46:30 +04:00
int i ;
if ( all_counters )
return ;
num_gpes = acpi_current_gpe_count ;
num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA ;
all_attrs = kzalloc ( sizeof ( struct attribute * ) * ( num_counters + 1 ) ,
GFP_KERNEL ) ;
if ( all_attrs = = NULL )
return ;
all_counters = kzalloc ( sizeof ( struct event_counter ) * ( num_counters ) ,
GFP_KERNEL ) ;
if ( all_counters = = NULL )
goto fail ;
2012-10-31 06:25:36 +04:00
status = acpi_install_global_event_handler ( acpi_global_event_handler , NULL ) ;
2010-12-13 08:39:26 +03:00
if ( ACPI_FAILURE ( status ) )
goto fail ;
2010-07-15 06:46:30 +04:00
counter_attrs = kzalloc ( sizeof ( struct kobj_attribute ) * ( num_counters ) ,
GFP_KERNEL ) ;
if ( counter_attrs = = NULL )
goto fail ;
for ( i = 0 ; i < num_counters ; + + i ) {
char buffer [ 12 ] ;
char * name ;
if ( i < num_gpes )
sprintf ( buffer , " gpe%02X " , i ) ;
else if ( i = = num_gpes + ACPI_EVENT_PMTIMER )
sprintf ( buffer , " ff_pmtimer " ) ;
else if ( i = = num_gpes + ACPI_EVENT_GLOBAL )
sprintf ( buffer , " ff_gbl_lock " ) ;
else if ( i = = num_gpes + ACPI_EVENT_POWER_BUTTON )
sprintf ( buffer , " ff_pwr_btn " ) ;
else if ( i = = num_gpes + ACPI_EVENT_SLEEP_BUTTON )
sprintf ( buffer , " ff_slp_btn " ) ;
else if ( i = = num_gpes + ACPI_EVENT_RTC )
sprintf ( buffer , " ff_rt_clk " ) ;
else if ( i = = num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE )
sprintf ( buffer , " gpe_all " ) ;
else if ( i = = num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI )
sprintf ( buffer , " sci " ) ;
else if ( i = = num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT )
sprintf ( buffer , " sci_not " ) ;
else if ( i = = num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR )
sprintf ( buffer , " error " ) ;
else
sprintf ( buffer , " bug%02X " , i ) ;
2013-07-04 02:04:55 +04:00
name = kstrdup ( buffer , GFP_KERNEL ) ;
2010-07-15 06:46:30 +04:00
if ( name = = NULL )
goto fail ;
sysfs_attr_init ( & counter_attrs [ i ] . attr ) ;
counter_attrs [ i ] . attr . name = name ;
counter_attrs [ i ] . attr . mode = 0644 ;
counter_attrs [ i ] . show = counter_show ;
counter_attrs [ i ] . store = counter_set ;
all_attrs [ i ] = & counter_attrs [ i ] . attr ;
}
interrupt_stats_attr_group . attrs = all_attrs ;
if ( ! sysfs_create_group ( acpi_kobj , & interrupt_stats_attr_group ) )
return ;
fail :
delete_gpe_attr_array ( ) ;
return ;
}
static void __exit interrupt_stats_exit ( void )
{
sysfs_remove_group ( acpi_kobj , & interrupt_stats_attr_group ) ;
delete_gpe_attr_array ( ) ;
return ;
}
2011-11-04 06:33:46 +04:00
static ssize_t
acpi_show_profile ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
return sprintf ( buf , " %d \n " , acpi_gbl_FADT . preferred_profile ) ;
}
static const struct device_attribute pm_profile_attr =
__ATTR ( pm_profile , S_IRUGO , acpi_show_profile , NULL ) ;
2013-03-04 02:08:16 +04:00
static ssize_t hotplug_enabled_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct acpi_hotplug_profile * hotplug = to_acpi_hotplug_profile ( kobj ) ;
return sprintf ( buf , " %d \n " , hotplug - > enabled ) ;
}
static ssize_t hotplug_enabled_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t size )
{
struct acpi_hotplug_profile * hotplug = to_acpi_hotplug_profile ( kobj ) ;
unsigned int val ;
if ( kstrtouint ( buf , 10 , & val ) | | val > 1 )
return - EINVAL ;
acpi_scan_hotplug_enabled ( hotplug , val ) ;
return size ;
}
static struct kobj_attribute hotplug_enabled_attr =
__ATTR ( enabled , S_IRUGO | S_IWUSR , hotplug_enabled_show ,
hotplug_enabled_store ) ;
static struct attribute * hotplug_profile_attrs [ ] = {
& hotplug_enabled_attr . attr ,
NULL
} ;
2013-03-19 03:16:15 +04:00
static struct kobj_type acpi_hotplug_profile_ktype = {
2013-03-04 02:08:16 +04:00
. sysfs_ops = & kobj_sysfs_ops ,
. default_attrs = hotplug_profile_attrs ,
} ;
void acpi_sysfs_add_hotplug_profile ( struct acpi_hotplug_profile * hotplug ,
const char * name )
{
int error ;
if ( ! hotplug_kobj )
goto err_out ;
2013-09-26 00:23:20 +04:00
error = kobject_init_and_add ( & hotplug - > kobj ,
& acpi_hotplug_profile_ktype , hotplug_kobj , " %s " , name ) ;
2013-03-04 02:08:16 +04:00
if ( error )
goto err_out ;
kobject_uevent ( & hotplug - > kobj , KOBJ_ADD ) ;
return ;
err_out :
pr_err ( PREFIX " Unable to add hotplug profile '%s' \n " , name ) ;
}
2013-05-03 02:26:16 +04:00
static ssize_t force_remove_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
return sprintf ( buf , " %d \n " , ! ! acpi_force_hot_remove ) ;
}
static ssize_t force_remove_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t size )
{
bool val ;
int ret ;
ret = strtobool ( buf , & val ) ;
if ( ret < 0 )
return ret ;
lock_device_hotplug ( ) ;
acpi_force_hot_remove = val ;
unlock_device_hotplug ( ) ;
return size ;
}
static const struct kobj_attribute force_remove_attr =
__ATTR ( force_remove , S_IRUGO | S_IWUSR , force_remove_show ,
force_remove_store ) ;
2010-07-15 06:46:30 +04:00
int __init acpi_sysfs_init ( void )
{
int result ;
result = acpi_tables_sysfs_init ( ) ;
2011-11-04 06:33:46 +04:00
if ( result )
return result ;
2013-03-04 02:08:16 +04:00
hotplug_kobj = kobject_create_and_add ( " hotplug " , acpi_kobj ) ;
2015-10-15 19:19:30 +03:00
if ( ! hotplug_kobj )
return - ENOMEM ;
2013-05-03 02:26:16 +04:00
result = sysfs_create_file ( hotplug_kobj , & force_remove_attr . attr ) ;
if ( result )
return result ;
2011-11-04 06:33:46 +04:00
result = sysfs_create_file ( acpi_kobj , & pm_profile_attr . attr ) ;
2010-07-15 06:46:30 +04:00
return result ;
}