// SPDX-License-Identifier: GPL-2.0/* * sysfs.c - ACPI sysfs interface to userspace. */ #define pr_fmt(fmt) "ACPI: " fmt #include <linux/acpi.h>#include <linux/bitmap.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/kstrtox.h>#include <linux/moduleparam.h> #include "internal.h" #ifdef CONFIG_ACPI_DEBUG/* * ACPI debug sysfs I/F, including: * /sys/module/acpi/parameters/debug_layer * /sys/module/acpi/parameters/debug_level * /sys/module/acpi/parameters/trace_method_name * /sys/module/acpi/parameters/trace_state * /sys/module/acpi/parameters/trace_debug_layer * /sys/module/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),}; 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), ACPI_DEBUG_INIT(ACPI_LV_REPAIR), ACPI_DEBUG_INIT(ACPI_LV_TRACE_POINT), 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),}; static int param_get_debug_layer(char *buffer, const struct kernel_param *kp){ int result = 0; int i; result = sprintf(buffer, "%-25s\tHex SET\n", "Description"); for (i = 0; i < ARRAY_SIZE(acpi_debug_layers); i++) { result += sprintf(buffer + result, "%-25s\t0x%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\t0x%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, "--\ndebug_layer = 0x%08X ( * = enabled)\n", acpi_dbg_layer); return result;} static int param_get_debug_level(char *buffer, const struct kernel_param *kp){ int result = 0; int i; result = sprintf(buffer, "%-25s\tHex SET\n", "Description"); for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) { result += sprintf(buffer + result, "%-25s\t0x%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, "--\ndebug_level = 0x%08X (* = enabled)\n", acpi_dbg_level); return result;} static const struct kernel_param_ops param_ops_debug_layer = { .set = param_set_uint, .get = param_get_debug_layer,}; static const struct kernel_param_ops param_ops_debug_level = { .set = param_set_uint, .get = param_get_debug_level,}; module_param_cb(debug_layer, ¶m_ops_debug_layer, &acpi_dbg_layer, 0644);module_param_cb(debug_level, ¶m_ops_debug_level, &acpi_dbg_level, 0644); static char trace_method_name[1024]; static int param_set_trace_method_name(const char *val, const struct kernel_param *kp){ u32 saved_flags = 0; bool is_abs_path = true; if (*val != '\\') is_abs_path = false; if ((is_abs_path && strlen(val) > 1023) || (!is_abs_path && strlen(val) > 1022)) { pr_err("%s: string parameter too long\n", kp->name); return -ENOSPC; } /* * 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. */ if (is_abs_path) strcpy(trace_method_name, val); else { trace_method_name[0] = '\\'; strcpy(trace_method_name+1, val); } /* Restore the original tracer state */ (void)acpi_debug_trace(trace_method_name, acpi_gbl_trace_dbg_level, acpi_gbl_trace_dbg_layer, saved_flags); return 0;} static int param_get_trace_method_name(char *buffer, const struct kernel_param *kp){ return sysfs_emit(buffer, "%s\n", 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, ¶m_ops_trace_method, &trace_method_name, 0644);module_param_cb(trace_debug_layer, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_layer, 0644);module_param_cb(trace_debug_level, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_level, 0644); static int param_set_trace_state(const char *val, const 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;} static int param_get_trace_state(char *buffer, const struct kernel_param *kp){ if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED)) return sprintf(buffer, "disable\n"); if (!acpi_gbl_trace_method_name) return sprintf(buffer, "enable\n"); if (acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT) return sprintf(buffer, "method-once\n"); else return sprintf(buffer, "method\n");} module_param_call(trace_state, param_set_trace_state, param_get_trace_state, NULL, 0644);#endif /* CONFIG_ACPI_DEBUG */ /* /sys/module/acpi/parameters/aml_debug_output */ module_param_named(aml_debug_output, acpi_gbl_enable_aml_debug_object, byte, 0644);MODULE_PARM_DESC(aml_debug_output, "To enable/disable the ACPI Debug Object output."); /* /sys/module/acpi/parameters/acpica_version */static int param_get_acpica_version(char *buffer, const struct kernel_param *kp){ int result; result = sprintf(buffer, "%x\n", 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/data/ * /sys/firmware/acpi/tables/dynamic/ */ static LIST_HEAD(acpi_table_attr_list);static struct kobject *tables_kobj;static struct kobject *tables_data_kobj;static struct kobject *dynamic_tables_kobj;static struct kobject *hotplug_kobj; #define ACPI_MAX_TABLE_INSTANCES 999#define ACPI_INST_SIZE 4 /* including trailing 0 */ struct acpi_table_attr { struct bin_attribute attr; char name[ACPI_NAMESEG_SIZE]; int instance; char filename[ACPI_NAMESEG_SIZE+ACPI_INST_SIZE]; struct list_head node;}; struct acpi_data_attr { struct bin_attribute attr; u64 addr; char filename[ACPI_NAMESEG_SIZE+ACPI_INST_SIZE];}; static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj, const 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; ssize_t rc; status = acpi_get_table(table_attr->name, table_attr->instance, &table_header); if (ACPI_FAILURE(status)) return -ENODEV; rc = memory_read_from_buffer(buf, count, &offset, table_header, table_header->length); acpi_put_table(table_header); return rc;} static int acpi_table_attr_init(struct kobject *tables_obj, struct acpi_table_attr *table_attr, struct acpi_table_header *table_header){ struct acpi_table_header *header = NULL; struct acpi_table_attr *attr = NULL; char instance_str[ACPI_INST_SIZE]; sysfs_attr_init(&table_attr->attr.attr); ACPI_COPY_NAMESEG(table_attr->name, table_header->signature); list_for_each_entry(attr, &acpi_table_attr_list, node) { if (ACPI_COMPARE_NAMESEG(table_attr->name, attr->name)) if (table_attr->instance < attr->instance) table_attr->instance = attr->instance; } table_attr->instance++; if (table_attr->instance > ACPI_MAX_TABLE_INSTANCES) { pr_warn("%4.4s: too many table instances\n", table_attr->name); return -ERANGE; } ACPI_COPY_NAMESEG(table_attr->filename, table_header->signature); table_attr->filename[ACPI_NAMESEG_SIZE] = '\0'; if (table_attr->instance > 1 || (table_attr->instance == 1 && !acpi_get_table (table_header->signature, 2, &header))) { snprintf(instance_str, sizeof(instance_str), "%u", table_attr->instance); strcat(table_attr->filename, instance_str); } table_attr->attr.size = table_header->length; table_attr->attr.read = acpi_table_show; table_attr->attr.attr.name = table_attr->filename; table_attr->attr.attr.mode = 0400; return sysfs_create_bin_file(tables_obj, &table_attr->attr);} acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context){ struct acpi_table_attr *table_attr; switch (event) { case ACPI_TABLE_EVENT_INSTALL: table_attr = kzalloc_obj(*table_attr); if (!table_attr) return AE_NO_MEMORY; if (acpi_table_attr_init(dynamic_tables_kobj, table_attr, table)) { kfree(table_attr); return AE_ERROR; } list_add_tail(&table_attr->node, &acpi_table_attr_list); break; case ACPI_TABLE_EVENT_LOAD: case ACPI_TABLE_EVENT_UNLOAD: case ACPI_TABLE_EVENT_UNINSTALL: /* * 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 ssize_t acpi_data_show(struct file *filp, struct kobject *kobj, const struct bin_attribute *bin_attr, char *buf, loff_t offset, size_t count){ struct acpi_data_attr *data_attr; void __iomem *base; ssize_t size; data_attr = container_of(bin_attr, struct acpi_data_attr, attr); size = data_attr->attr.size; if (offset < 0) return -EINVAL; if (offset >= size) return 0; if (count > size - offset) count = size - offset; base = acpi_os_map_iomem(data_attr->addr, size); if (!base) return -ENOMEM; memcpy_fromio(buf, base + offset, count); acpi_os_unmap_iomem(base, size); return count;} static int acpi_bert_data_init(void *th, struct acpi_data_attr *data_attr){ struct acpi_table_bert *bert = th; if (bert->header.length < sizeof(struct acpi_table_bert) || bert->region_length < sizeof(struct acpi_bert_region)) { kfree(data_attr); return -EINVAL; } data_attr->addr = bert->address; data_attr->attr.size = bert->region_length; return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr);} static int acpi_ccel_data_init(void *th, struct acpi_data_attr *data_attr){ struct acpi_table_ccel *ccel = th; if (ccel->header.length < sizeof(struct acpi_table_ccel) || !ccel->log_area_start_address || !ccel->log_area_minimum_length) { kfree(data_attr); return -EINVAL; } data_attr->addr = ccel->log_area_start_address; data_attr->attr.size = ccel->log_area_minimum_length; return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr);} static struct acpi_data_obj { char *name; int (*fn)(void *, struct acpi_data_attr *);} acpi_data_objs[] = { { ACPI_SIG_BERT, acpi_bert_data_init }, { ACPI_SIG_CCEL, acpi_ccel_data_init },}; #define NUM_ACPI_DATA_OBJS ARRAY_SIZE(acpi_data_objs) static int acpi_table_data_init(struct acpi_table_header *th, struct acpi_table_attr *table_attr){ struct acpi_data_attr *data_attr; int i; for (i = 0; i < NUM_ACPI_DATA_OBJS; i++) { if (ACPI_COMPARE_NAMESEG(th->signature, acpi_data_objs[i].name)) { data_attr = kzalloc_obj(*data_attr); if (!data_attr) return -ENOMEM; sysfs_attr_init(&data_attr->attr.attr); data_attr->attr.read = acpi_data_show; data_attr->attr.attr.mode = 0400; strscpy(data_attr->filename, table_attr->filename); data_attr->attr.attr.name = data_attr->filename;Lines 1–500 of 1045. Load more
// SPDX-License-Identifier: GPL-2.0 /* * sysfs.c - ACPI sysfs interface to userspace. */ #define pr_fmt(fmt) "ACPI: " fmt #include <linux/acpi.h> #include <linux/bitmap.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/kstrtox.h> #include <linux/moduleparam.h> #include "internal.h" #ifdef CONFIG_ACPI_DEBUG /* * ACPI debug sysfs I/F, including: * /sys/module/acpi/parameters/debug_layer * /sys/module/acpi/parameters/debug_level * /sys/module/acpi/parameters/trace_method_name * /sys/module/acpi/parameters/trace_state * /sys/module/acpi/parameters/trace_debug_layer * /sys/module/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), }; 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), ACPI_DEBUG_INIT(ACPI_LV_REPAIR), ACPI_DEBUG_INIT(ACPI_LV_TRACE_POINT), 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), }; static int param_get_debug_layer(char *buffer, const struct kernel_param *kp) { int result = 0; int i; result = sprintf(buffer, "%-25s\tHex SET\n", "Description"); for (i = 0; i < ARRAY_SIZE(acpi_debug_layers); i++) { result += sprintf(buffer + result, "%-25s\t0x%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\t0x%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, "--\ndebug_layer = 0x%08X ( * = enabled)\n", acpi_dbg_layer); return result; } static int param_get_debug_level(char *buffer, const struct kernel_param *kp) { int result = 0; int i; result = sprintf(buffer, "%-25s\tHex SET\n", "Description"); for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) { result += sprintf(buffer + result, "%-25s\t0x%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, "--\ndebug_level = 0x%08X (* = enabled)\n", acpi_dbg_level); return result; } static const struct kernel_param_ops param_ops_debug_layer = { .set = param_set_uint, .get = param_get_debug_layer, }; static const struct kernel_param_ops param_ops_debug_level = { .set = param_set_uint, .get = param_get_debug_level, }; module_param_cb(debug_layer, ¶m_ops_debug_layer, &acpi_dbg_layer, 0644); module_param_cb(debug_level, ¶m_ops_debug_level, &acpi_dbg_level, 0644); static char trace_method_name[1024]; static int param_set_trace_method_name(const char *val, const struct kernel_param *kp) { u32 saved_flags = 0; bool is_abs_path = true; if (*val != '\\') is_abs_path = false; if ((is_abs_path && strlen(val) > 1023) || (!is_abs_path && strlen(val) > 1022)) { pr_err("%s: string parameter too long\n", kp->name); return -ENOSPC; } /* * 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. */ if (is_abs_path) strcpy(trace_method_name, val); else { trace_method_name[0] = '\\'; strcpy(trace_method_name+1, val); } /* Restore the original tracer state */ (void)acpi_debug_trace(trace_method_name, acpi_gbl_trace_dbg_level, acpi_gbl_trace_dbg_layer, saved_flags); return 0; } static int param_get_trace_method_name(char *buffer, const struct kernel_param *kp) { return sysfs_emit(buffer, "%s\n", 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, ¶m_ops_trace_method, &trace_method_name, 0644); module_param_cb(trace_debug_layer, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_layer, 0644); module_param_cb(trace_debug_level, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_level, 0644); static int param_set_trace_state(const char *val, const 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; } static int param_get_trace_state(char *buffer, const struct kernel_param *kp) { if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED)) return sprintf(buffer, "disable\n"); if (!acpi_gbl_trace_method_name) return sprintf(buffer, "enable\n"); if (acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT) return sprintf(buffer, "method-once\n"); else return sprintf(buffer, "method\n"); } module_param_call(trace_state, param_set_trace_state, param_get_trace_state, NULL, 0644); #endif /* CONFIG_ACPI_DEBUG */ /* /sys/module/acpi/parameters/aml_debug_output */ module_param_named(aml_debug_output, acpi_gbl_enable_aml_debug_object, byte, 0644); MODULE_PARM_DESC(aml_debug_output, "To enable/disable the ACPI Debug Object output."); /* /sys/module/acpi/parameters/acpica_version */ static int param_get_acpica_version(char *buffer, const struct kernel_param *kp) { int result; result = sprintf(buffer, "%x\n", 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/data/ * /sys/firmware/acpi/tables/dynamic/ */ static LIST_HEAD(acpi_table_attr_list); static struct kobject *tables_kobj; static struct kobject *tables_data_kobj; static struct kobject *dynamic_tables_kobj; static struct kobject *hotplug_kobj; #define ACPI_MAX_TABLE_INSTANCES 999 #define ACPI_INST_SIZE 4 /* including trailing 0 */ struct acpi_table_attr { struct bin_attribute attr; char name[ACPI_NAMESEG_SIZE]; int instance; char filename[ACPI_NAMESEG_SIZE+ACPI_INST_SIZE]; struct list_head node; }; struct acpi_data_attr { struct bin_attribute attr; u64 addr; char filename[ACPI_NAMESEG_SIZE+ACPI_INST_SIZE]; }; static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj, const 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; ssize_t rc; status = acpi_get_table(table_attr->name, table_attr->instance, &table_header); if (ACPI_FAILURE(status)) return -ENODEV; rc = memory_read_from_buffer(buf, count, &offset, table_header, table_header->length); acpi_put_table(table_header); return rc; } static int acpi_table_attr_init(struct kobject *tables_obj, struct acpi_table_attr *table_attr, struct acpi_table_header *table_header) { struct acpi_table_header *header = NULL; struct acpi_table_attr *attr = NULL; char instance_str[ACPI_INST_SIZE]; sysfs_attr_init(&table_attr->attr.attr); ACPI_COPY_NAMESEG(table_attr->name, table_header->signature); list_for_each_entry(attr, &acpi_table_attr_list, node) { if (ACPI_COMPARE_NAMESEG(table_attr->name, attr->name)) if (table_attr->instance < attr->instance) table_attr->instance = attr->instance; } table_attr->instance++; if (table_attr->instance > ACPI_MAX_TABLE_INSTANCES) { pr_warn("%4.4s: too many table instances\n", table_attr->name); return -ERANGE; } ACPI_COPY_NAMESEG(table_attr->filename, table_header->signature); table_attr->filename[ACPI_NAMESEG_SIZE] = '\0'; if (table_attr->instance > 1 || (table_attr->instance == 1 && !acpi_get_table (table_header->signature, 2, &header))) { snprintf(instance_str, sizeof(instance_str), "%u", table_attr->instance); strcat(table_attr->filename, instance_str); } table_attr->attr.size = table_header->length; table_attr->attr.read = acpi_table_show; table_attr->attr.attr.name = table_attr->filename; table_attr->attr.attr.mode = 0400; return sysfs_create_bin_file(tables_obj, &table_attr->attr); } acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context) { struct acpi_table_attr *table_attr; switch (event) { case ACPI_TABLE_EVENT_INSTALL: table_attr = kzalloc_obj(*table_attr); if (!table_attr) return AE_NO_MEMORY; if (acpi_table_attr_init(dynamic_tables_kobj, table_attr, table)) { kfree(table_attr); return AE_ERROR; } list_add_tail(&table_attr->node, &acpi_table_attr_list); break; case ACPI_TABLE_EVENT_LOAD: case ACPI_TABLE_EVENT_UNLOAD: case ACPI_TABLE_EVENT_UNINSTALL: /* * 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 ssize_t acpi_data_show(struct file *filp, struct kobject *kobj, const struct bin_attribute *bin_attr, char *buf, loff_t offset, size_t count) { struct acpi_data_attr *data_attr; void __iomem *base; ssize_t size; data_attr = container_of(bin_attr, struct acpi_data_attr, attr); size = data_attr->attr.size; if (offset < 0) return -EINVAL; if (offset >= size) return 0; if (count > size - offset) count = size - offset; base = acpi_os_map_iomem(data_attr->addr, size); if (!base) return -ENOMEM; memcpy_fromio(buf, base + offset, count); acpi_os_unmap_iomem(base, size); return count; } static int acpi_bert_data_init(void *th, struct acpi_data_attr *data_attr) { struct acpi_table_bert *bert = th; if (bert->header.length < sizeof(struct acpi_table_bert) || bert->region_length < sizeof(struct acpi_bert_region)) { kfree(data_attr); return -EINVAL; } data_attr->addr = bert->address; data_attr->attr.size = bert->region_length; return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr); } static int acpi_ccel_data_init(void *th, struct acpi_data_attr *data_attr) { struct acpi_table_ccel *ccel = th; if (ccel->header.length < sizeof(struct acpi_table_ccel) || !ccel->log_area_start_address || !ccel->log_area_minimum_length) { kfree(data_attr); return -EINVAL; } data_attr->addr = ccel->log_area_start_address; data_attr->attr.size = ccel->log_area_minimum_length; return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr); } static struct acpi_data_obj { char *name; int (*fn)(void *, struct acpi_data_attr *); } acpi_data_objs[] = { { ACPI_SIG_BERT, acpi_bert_data_init }, { ACPI_SIG_CCEL, acpi_ccel_data_init }, }; #define NUM_ACPI_DATA_OBJS ARRAY_SIZE(acpi_data_objs) static int acpi_table_data_init(struct acpi_table_header *th, struct acpi_table_attr *table_attr) { struct acpi_data_attr *data_attr; int i; for (i = 0; i < NUM_ACPI_DATA_OBJS; i++) { if (ACPI_COMPARE_NAMESEG(th->signature, acpi_data_objs[i].name)) { data_attr = kzalloc_obj(*data_attr); if (!data_attr) return -ENOMEM; sysfs_attr_init(&data_attr->attr.attr); data_attr->attr.read = acpi_data_show; data_attr->attr.attr.mode = 0400; strscpy(data_attr->filename, table_attr->filename); data_attr->attr.attr.name = data_attr->filename;